diff --git a/css/style.scss b/css/style.scss index 64ca7f7ac..124969f2f 100644 --- a/css/style.scss +++ b/css/style.scss @@ -143,6 +143,10 @@ input.input-inline { .card { opacity: 1; + + &.file-drop { + + } } &.card-selected { @@ -657,16 +661,16 @@ input.input-inline { .drop-indicator { display: none; } -.file-drop .drop-indicator { +.card .nv-file-over, +.drop-indicator.nv-file-over { display: block; position: absolute; width: 100%; - height: calc(100% - 40px); + height: 100%; background-color: #fff; z-index: 100; opacity: 0.9; text-align: center; - margin-top: 40px; p { width: calc(100% - 20px); @@ -679,6 +683,7 @@ input.input-inline { } } + #card-meta { // TODO: use .card-block instead? height: 100%; display: flex; diff --git a/js/controller/BoardController.js b/js/controller/BoardController.js index 4d0b0c95b..7df5f1f75 100644 --- a/js/controller/BoardController.js +++ b/js/controller/BoardController.js @@ -22,7 +22,7 @@ import app from '../app/App.js'; /* global oc_defaults OC */ -app.controller('BoardController', function ($rootScope, $scope, $stateParams, StatusService, BoardService, StackService, CardService, LabelService, $state, $transitions, $filter) { +app.controller('BoardController', function ($rootScope, $scope, $stateParams, StatusService, BoardService, StackService, CardService, LabelService, $state, $transitions, $filter, FileService) { $scope.sidebar = $rootScope.sidebar; @@ -40,6 +40,7 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St $scope.labelservice = LabelService; $scope.defaultColors = ['31CC7C', '317CCC', 'FF7A66', 'F1DB50', '7C31CC', 'CC317C', '3A3B3D', 'CACBCD']; $scope.board = BoardService.getCurrent(); + $scope.uploader = FileService.uploader; // workaround for $stateParams changes not being propagated $scope.$watch(function() { @@ -353,11 +354,11 @@ app.controller('BoardController', function ($rootScope, $scope, $stateParams, St }; }; - $scope.attachmentCount = function(attachments) { - if (Array.isArray(attachments)) { - return attachments.filter((obj) => obj.deletedAt === 0).length; + $scope.attachmentCount = function(card) { + if (Array.isArray(card.attachments)) { + return card.attachments.filter((obj) => obj.deletedAt === 0).length; } - return attachments; + return card.attachmentCount; }; }); diff --git a/js/controller/CardController.js b/js/controller/CardController.js index d52b3960a..756dcafe5 100644 --- a/js/controller/CardController.js +++ b/js/controller/CardController.js @@ -23,7 +23,7 @@ /* global app moment */ import app from '../app/App.js'; -app.controller('CardController', function ($scope, $rootScope, $sce, $location, $stateParams, $interval, $timeout, $filter, BoardService, CardService, StackService, StatusService, markdownItConverter, FileUploader) { +app.controller('CardController', function ($scope, $rootScope, $sce, $location, $stateParams, $interval, $timeout, $filter, BoardService, CardService, StackService, StatusService, markdownItConverter, FileService) { $scope.sidebar = $rootScope.sidebar; $scope.status = { lastEdit: 0, @@ -31,49 +31,13 @@ app.controller('CardController', function ($scope, $rootScope, $sce, $location, }; $scope.cardservice = CardService; + $scope.fileservice = FileService; $scope.cardId = $stateParams.cardId; $scope.statusservice = StatusService.getInstance(); $scope.boardservice = BoardService; - $scope.uploader = new FileUploader(); - - $scope.runUpload = function(fileItem) { - fileItem.url = OC.generateUrl('/apps/deck/cards/' + $scope.cardId + '/attachment'); - fileItem.formData = [ - { - requesttoken: oc_requesttoken, - type: 'deck_file', - - } - ]; - $scope.uploader.uploadItem(fileItem); - }; - $scope.uploader.onAfterAddingFile = function(fileItem) { - let existingFile = $scope.cardservice.getCurrent().attachments.find((attachment) => { - return attachment.data === fileItem.file.name; - }); - if (typeof existingFile !== 'undefined') { - OC.dialogs.confirm( - `A file with the name ${fileItem.file.name} already exists. Do you want to overwrite it?`, - 'File already exists', - function(result) { - if (result) { - $scope.runUpload(fileItem); - } else { - // TODO: check for proper number and append it before the file extension - // TODO: iterate over attachments (check if matches "file.name (n)") increase n - fileItem.file.name = fileItem.file.name + '.1'; - } - } - ); - } else { - $scope.runUpload(fileItem); - } - }; - $scope.uploader.onSuccessItem = function(item, response) { - $scope.cardservice.getCurrent().attachments.push(response); - }; + $scope.isArray = angular.isArray; $scope.mimetypeForAttachment = function(attachment) { let url = OC.MimeType.getIconUrl(attachment.extendedData.mimetype); diff --git a/js/service/FileService.js b/js/service/FileService.js new file mode 100644 index 000000000..c0b26fd86 --- /dev/null +++ b/js/service/FileService.js @@ -0,0 +1,105 @@ +/* + * @copyright Copyright (c) 2018 Julius Härtl + * + * @author Julius Härtl + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import app from '../app/App.js'; + +export default class FileService { + + constructor ($http, FileUploader, CardService) { + this.uploader = new FileUploader(); + this.cardservice = CardService; + this.uploader.onAfterAddingFile = this.onAfterAddingFile.bind(this); + this.uploader.onSuccessItem = this.onSuccessItem.bind(this); + } + + + runUpload (fileItem, attachmentId) { + fileItem.url = OC.generateUrl('/apps/deck/cards/' + fileItem.cardId + '/attachment'); + if (typeof attachmentId !== 'undefined') { + fileItem.method = 'UPDATE'; + fileItem.url = OC.generateUrl('/apps/deck/cards/' + fileItem.cardId + '/attachment/' + attachmentId); + } + fileItem.formData = [ + { + requesttoken: oc_requesttoken, + type: 'deck_file', + + } + ]; + this.uploader.uploadItem(fileItem); + }; + + runUpdate (fileItem) { + fileItem.url = OC.generateUrl('/apps/deck/cards/' + fileItem.cardId + '/attachment'); + fileItem.formData = [ + { + requesttoken: oc_requesttoken, + type: 'deck_file', + + } + ]; + this.uploader.uploadItem(fileItem); + }; + + onAfterAddingFile (fileItem) { + // Fetch card details before trying to upload so we can detect filename collisions properly + let self = this; + this.cardservice.fetchOne(fileItem.cardId).then(function (data) { + let attachments = self.cardservice.get(fileItem.cardId).attachments; + let existingFile = attachments.find((attachment) => { + return attachment.data === fileItem.file.name; + }); + if (typeof existingFile !== 'undefined') { + OC.dialogs.confirm( + `A file with the name ${fileItem.file.name} already exists. Do you want to overwrite it?`, + 'File already exists', + function (result) { + if (result) { + self.runUpload(fileItem, existingFile.id); + } else { + let fileName = existingFile.extendedData.info.filename; + let foundFilesMatching = attachments.filter((attachment) => { + return attachment.extendedData.info.extension === existingFile.extendedData.info.extension + && attachment.extendedData.info.filename.startsWith(fileName); + }); + let nextIndex = foundFilesMatching.length+1; + fileItem.file.name = fileName + ' (' + nextIndex + ').' + existingFile.extendedData.info.extension; + self.runUpload(fileItem); + } + } + ); + } else { + self.runUpload(fileItem); + } + }, function (error) { + + }); + + }; + + onSuccessItem(item, response) { + this.cardservice.get(item.cardId).attachments.push(response); + }; + +} + +app.service('FileService', FileService); \ No newline at end of file diff --git a/templates/part.board.mainView.php b/templates/part.board.mainView.php index dcf301c9e..717770d59 100644 --- a/templates/part.board.mainView.php +++ b/templates/part.board.mainView.php @@ -61,7 +61,11 @@ data-as-sortable-item ng-click="$event.stopPropagation()" ui-sref="board.card({boardId: id, cardId: c.id})" - ng-class="{'archived': cardservice.get(c.id).archived, 'has-labels': cardservice.get(c.id).labels.length>0, 'current': cardservice.get(c.id).id == params.cardId }"> + ng-class="{'archived': cardservice.get(c.id).archived, 'has-labels': cardservice.get(c.id).labels.length>0, 'current': cardservice.get(c.id).id == params.cardId }" + nv-file-drop="" uploader="uploader" options="{cardId: c.id}"> +
+

t('Drop your files here to upload it to the card')); ?>

+

{{ cardservice.get(c.id).title }}

@@ -84,9 +88,9 @@ {{ getCheckboxes(cardservice.get(c.id).description)[0] }}/{{ getCheckboxes(cardservice.get(c.id).description)[1] }}
-
+
- {{ attachmentCount(cardservice.get(c.id).attachments) }} + {{ attachmentCount(cardservice.get(c.id)) }}
diff --git a/templates/part.card.php b/templates/part.card.php index dff818da8..c863be219 100644 --- a/templates/part.card.php +++ b/templates/part.card.php @@ -1,5 +1,5 @@ -
-
+
+

t('Drop your files here to upload it to the card')); ?>

@@ -90,11 +90,11 @@

t('Attachments')); ?>

- +
-
+
    -
  • +