parent
09e74481cb
commit
a4d460a8f0
@ -125,6 +125,7 @@ Core:
|
||||
easier development [#3566].
|
||||
|
||||
Mediafiles:
|
||||
- New form for uploading multiple files [#3650].
|
||||
- Fixed reloading of PDF on page change [#3274].
|
||||
- Custom CKEditor plugin for browsing mediafiles [#3337].
|
||||
- Project images always in fullscreen [#3355].
|
||||
|
@ -9,3 +9,15 @@
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
#dropzone {
|
||||
padding: 20px 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e6e8eb;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
|
||||
&.dragover {
|
||||
border-color: #317796;
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
(function () {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('OpenSlidesApp.mediafiles.create', [
|
||||
'OpenSlidesApp.mediafiles.forms',
|
||||
])
|
||||
|
||||
.controller('MediafileCreateCtrl', [
|
||||
'$scope',
|
||||
'MediafileForm',
|
||||
'ErrorMessage',
|
||||
function ($scope, MediafileForm, ErrorMessage) {
|
||||
$scope.model = {};
|
||||
$scope.alert = {};
|
||||
$scope.formFields = MediafileForm.getFormFields(true);
|
||||
|
||||
// upload and save mediafile
|
||||
$scope.save = function (mediafile) {
|
||||
if (typeof mediafile.getFile === 'function') {
|
||||
$scope.activeUpload = MediafileForm.uploadFile(mediafile).then(
|
||||
function (success) {
|
||||
$scope.closeThisDialog();
|
||||
},
|
||||
function (error) {
|
||||
$scope.activeUpload = void 0;
|
||||
$scope.alert = ErrorMessage.forAlert(error);
|
||||
},
|
||||
function (progress) {
|
||||
$scope.progress = parseInt(100.0 * progress.loaded / progress.total);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
$scope.close = function () {
|
||||
// TODO: abort() is not a function. But it is documented in the docs.
|
||||
// See https://github.com/danialfarid/ng-file-upload/issues/1844
|
||||
/*if ($scope.activeUpload) {
|
||||
$scope.activeUpload.abort();
|
||||
}*/
|
||||
$scope.closeThisDialog();
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
}());
|
@ -12,57 +12,31 @@ angular.module('OpenSlidesApp.mediafiles.forms', [
|
||||
// Service for mediafile form
|
||||
.factory('MediafileForm', [
|
||||
'gettextCatalog',
|
||||
'Upload',
|
||||
'operator',
|
||||
'User',
|
||||
function (gettextCatalog, Upload, operator, User) {
|
||||
function (gettextCatalog, operator, User) {
|
||||
return {
|
||||
// ngDialog for mediafile form
|
||||
getDialog: function (mediafile) {
|
||||
return {
|
||||
template: 'static/templates/mediafiles/mediafile-form.html',
|
||||
controller: (mediafile) ? 'MediafileUpdateCtrl' : 'MediafileCreateCtrl',
|
||||
var dialog = {
|
||||
className: 'ngdialog-theme-default wide-form',
|
||||
closeByEscape: false,
|
||||
closeByDocument: false,
|
||||
resolve: {
|
||||
mediafileId: function () {return mediafile ? mediafile.id : void 0;}
|
||||
},
|
||||
};
|
||||
},
|
||||
// upload selected file (used by create view only)
|
||||
uploadFile: function (mediafile) {
|
||||
var file = mediafile.getFile();
|
||||
if (!mediafile.title) {
|
||||
mediafile.title = file.name;
|
||||
if (mediafile) {
|
||||
dialog.template = 'static/templates/mediafiles/mediafile-form.html';
|
||||
dialog.controller = 'MediafileUpdateCtrl';
|
||||
dialog.resolve = {
|
||||
mediafileId: function () {return mediafile ? mediafile.id : void 0;}
|
||||
};
|
||||
} else {
|
||||
dialog.template = 'static/templates/mediafiles/mediafile-upload-form.html';
|
||||
dialog.controller = 'MediafileUploadCtrl';
|
||||
}
|
||||
if (!mediafile.uploader_id) {
|
||||
mediafile.uploader_id = operator.user.id;
|
||||
}
|
||||
return Upload.upload({
|
||||
url: '/rest/mediafiles/mediafile/',
|
||||
method: 'POST',
|
||||
data: {mediafile: file, title: mediafile.title, uploader_id: mediafile.uploader_id, hidden: mediafile.hidden}
|
||||
});
|
||||
return dialog;
|
||||
},
|
||||
getFormFields: function (isCreateForm) {
|
||||
getFormFields: function () {
|
||||
return [
|
||||
{
|
||||
key: 'newFile',
|
||||
type: 'file',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('File'),
|
||||
required: true,
|
||||
change: function (model, files, event, rejectedFiles) {
|
||||
var file = files ? files[0] : void 0;
|
||||
model.getFile = function () {
|
||||
return file;
|
||||
};
|
||||
model.newFile = file ? file.name : void 0;
|
||||
},
|
||||
},
|
||||
hide: !isCreateForm,
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
type: 'input',
|
||||
|
@ -120,7 +120,8 @@ angular.module('OpenSlidesApp.mediafiles.list', [
|
||||
$scope.isSelectMode = false;
|
||||
// check all checkboxes
|
||||
$scope.checkAll = function () {
|
||||
angular.forEach($scope.mediafiles, function (mediafile) {
|
||||
$scope.selectedAll = !$scope.selectedAll;
|
||||
_.forEach($scope.mediafiles, function (mediafile) {
|
||||
mediafile.selected = $scope.selectedAll;
|
||||
});
|
||||
};
|
||||
@ -128,7 +129,7 @@ angular.module('OpenSlidesApp.mediafiles.list', [
|
||||
$scope.uncheckAll = function () {
|
||||
if (!$scope.isSelectMode) {
|
||||
$scope.selectedAll = false;
|
||||
angular.forEach($scope.mediafiles, function (mediafile) {
|
||||
_.forEach($scope.mediafiles, function (mediafile) {
|
||||
mediafile.selected = false;
|
||||
});
|
||||
}
|
||||
|
@ -3,10 +3,10 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('OpenSlidesApp.mediafiles.site', [
|
||||
'OpenSlidesApp.mediafiles.create',
|
||||
'OpenSlidesApp.mediafiles.list',
|
||||
'OpenSlidesApp.mediafiles.states',
|
||||
'OpenSlidesApp.mediafiles.update',
|
||||
'OpenSlidesApp.mediafiles.upload',
|
||||
'OpenSlidesApp.mediafiles.image-plugin',
|
||||
]);
|
||||
|
||||
|
154
openslides/mediafiles/static/js/mediafiles/upload.js
Normal file
154
openslides/mediafiles/static/js/mediafiles/upload.js
Normal file
@ -0,0 +1,154 @@
|
||||
(function () {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('OpenSlidesApp.mediafiles.upload', [
|
||||
'OpenSlidesApp.mediafiles.forms',
|
||||
'ngFileUpload',
|
||||
])
|
||||
|
||||
.controller('MediafileUploadCtrl', [
|
||||
'$scope',
|
||||
'$q',
|
||||
'User',
|
||||
'Upload',
|
||||
'operator',
|
||||
'gettextCatalog',
|
||||
'ErrorMessage',
|
||||
function ($scope, $q, User, Upload, operator, gettextCatalog, ErrorMessage) {
|
||||
User.bindAll({}, $scope, 'users');
|
||||
$scope.alert = {};
|
||||
$scope.files = [];
|
||||
$scope.uploading = false;
|
||||
var idCounter = 0; // Used for uniqly identifing each file in $scope.files.
|
||||
|
||||
// Convert bytes to human readable si units.
|
||||
var humanFileSize = function (bytes) {
|
||||
if(Math.abs(bytes) < 1000) {
|
||||
return bytes + ' B';
|
||||
}
|
||||
var units = ['kB','MB','GB','TB','PB','EB','ZB','YB'];
|
||||
var i = -1;
|
||||
do {
|
||||
bytes /= 1000;
|
||||
i++;
|
||||
} while(bytes >= 1000 && i < units.length - 1);
|
||||
|
||||
return bytes.toFixed(1) + ' ' + units[i];
|
||||
};
|
||||
|
||||
$scope.addFiles = function (files) {
|
||||
files = _.map(files, function (file) {
|
||||
idCounter += 1;
|
||||
// This is a client side representation used for the template
|
||||
return {
|
||||
id: idCounter,
|
||||
file: file,
|
||||
title: file.name,
|
||||
hidden: false,
|
||||
uploader_id: operator.user.id,
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
humanSize: humanFileSize(file.size),
|
||||
type: file.type,
|
||||
progress: 0,
|
||||
};
|
||||
});
|
||||
// Add each file, that is not a duplicate to $scope.files
|
||||
_.forEach(files, function (file) {
|
||||
var duplicate = _.some($scope.files, function (_file) {
|
||||
return file.name === _file.name &&
|
||||
file.size === _file.size &&
|
||||
file.type === _file.type;
|
||||
});
|
||||
if (!duplicate) {
|
||||
$scope.files.push(file);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.removeFile = function (id) {
|
||||
$scope.files = _.filter($scope.files, function (file) {
|
||||
return file.id !== id;
|
||||
});
|
||||
};
|
||||
|
||||
// Add files via drag and drop
|
||||
$scope.$watch('dropFiles', function () {
|
||||
if ($scope.dropFiles) {
|
||||
$scope.addFiles($scope.dropFiles);
|
||||
}
|
||||
});
|
||||
|
||||
// upload all files
|
||||
$scope.upload = function () {
|
||||
$scope.uploading = true;
|
||||
var promises = _.map($scope.files, function (file) {
|
||||
// clear error
|
||||
file.error = void 0;
|
||||
|
||||
// Check, if all necessary fields are set.
|
||||
if (!file.title) {
|
||||
file.title = file.file.name;
|
||||
}
|
||||
if (!file.uploader_id) {
|
||||
file.uploader_id = operator.user.id;
|
||||
}
|
||||
|
||||
return Upload.upload({
|
||||
url: '/rest/mediafiles/mediafile/',
|
||||
method: 'POST',
|
||||
data: {
|
||||
mediafile: file.file,
|
||||
title: file.title,
|
||||
uploader_id: file.uploader_id,
|
||||
hidden: file.hidden
|
||||
},
|
||||
}).then(
|
||||
function (success) {
|
||||
$scope.removeFile(file.id);
|
||||
},
|
||||
function (error) {
|
||||
file.error = ErrorMessage.forAlert(error).msg;
|
||||
return error;
|
||||
},
|
||||
function (progress) {
|
||||
file.progress = parseInt(100.0 * progress.loaded / progress.total);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$q.all(promises).then(function (success) {
|
||||
var errors = _.filter(success, function (entry) {
|
||||
return entry;
|
||||
});
|
||||
|
||||
if (errors.length) {
|
||||
$scope.uploading = false;
|
||||
var message = gettextCatalog.getString('Some files could not be uploaded');
|
||||
$scope.alert = { type: 'danger', msg: message, show: true };
|
||||
} else {
|
||||
$scope.close();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.clear = function () {
|
||||
$scope.uploading = false;
|
||||
$scope.files = [];
|
||||
};
|
||||
|
||||
$scope.close = function () {
|
||||
$scope.closeThisDialog();
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
.run([
|
||||
'gettext',
|
||||
function (gettext) {
|
||||
gettext('Some files could not be uploaded');
|
||||
}
|
||||
]);
|
||||
|
||||
}());
|
@ -1,5 +1,4 @@
|
||||
<h1 ng-if="model.id" translate>Edit file</h1>
|
||||
<h1 ng-if="!model.id" translate>New file</h1>
|
||||
<h1 translate>Edit File</h1>
|
||||
|
||||
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
|
||||
{{ alert.msg }}
|
||||
|
@ -0,0 +1,97 @@
|
||||
<h1 translate>Upload files</h1>
|
||||
|
||||
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" close="alert={}">
|
||||
{{ alert.msg }}
|
||||
</div>
|
||||
|
||||
<form editable-form name="mediafileForm" ng-submit="upload()" novalidate>
|
||||
<div class="form-group" ng-hide="uploading">
|
||||
<div id="dropzone" ngf-drop ngf-select ng-model="dropFiles" ngf-drag-over-class="'dragover'" ngf-multiple="true">
|
||||
Drop or
|
||||
<a href= ng-disabled="files.length" type="button" ngf-select="addFiles($files)" multiple translate>
|
||||
select files
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="files.length">
|
||||
<table class="table table-striped table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate>
|
||||
File information
|
||||
</th>
|
||||
<th translate>
|
||||
Title
|
||||
</th>
|
||||
<th ng-if="!uploading && operator.hasPerms('mediafiles.can_see_hidden')" translate>
|
||||
Hidden
|
||||
</th>
|
||||
<th ng-if="!uploading && operator.hasPerms('mediafiles.can_manage')" translate>
|
||||
Uploader
|
||||
</th>
|
||||
<th ng-if="uploading" translate>
|
||||
Upload status
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="file in files">
|
||||
<td>
|
||||
<div>
|
||||
{{ $index+1 }}. {{ file.name }}
|
||||
<span class="pull-right pointer" ng-click="removeFile(file.id)">
|
||||
<i class="fa fa-times text-danger"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div ng-if="file.type"><i class="fa fa-file"></i> {{ file.type }}</div>
|
||||
<div><i class="fa fa-database"></i> {{ file.humanSize }}</div>
|
||||
<div ng-if="file.error" class="text-danger">
|
||||
{{ file.error }}
|
||||
</div>
|
||||
</td>
|
||||
<td ng-if="!uploading">
|
||||
<input type="text" class="form-control" ng-model="file.title">
|
||||
</td>
|
||||
<td ng-if="uploading">
|
||||
{{ file.title }}
|
||||
</td>
|
||||
<td ng-if="!uploading && operator.hasPerms('mediafiles.can_see_hidden')">
|
||||
<i class="fa" ng-class="file.hidden ? 'fa-check-square-o' : 'fa-square-o'"
|
||||
ng-click="file.hidden = !file.hidden"></i>
|
||||
</td>
|
||||
<td ng-if="!uploading && operator.hasPerms('mediafiles.can_manage')">
|
||||
<select chosen
|
||||
data-ng-model="file.uploader_id"
|
||||
ng-options="user.id as user.full_name for user in users"
|
||||
allow-single-deselect="true"
|
||||
search-contains="true"
|
||||
placeholder-text-single="'Select or search a participant ...' | translate"
|
||||
no-results-text="'No results available ...' | translate"
|
||||
class="form-control">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
</td>
|
||||
<td ng-if="uploading">
|
||||
<uib-progressbar value="file.progress" animate="false">
|
||||
<span class="nobr">{{ file.progress }}%</span>
|
||||
</uib-progressbar>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-field">
|
||||
<button type="submit" ng-disabled="files.length === 0 || uploading" class="btn btn-primary" translate>
|
||||
Upload
|
||||
</button>
|
||||
<button type="button" ng-disabled="uploading" ng-click="clear()" class="btn btn-default" translate>
|
||||
Clear list
|
||||
</button>
|
||||
<button type="button" ng-click="close()" class="btn btn-default pull-right" translate>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
Loading…
Reference in New Issue
Block a user