diff --git a/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.test.ts b/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.test.ts
index 4ba1b465fc..63cc94fa23 100644
--- a/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.test.ts
+++ b/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.test.ts
@@ -27,7 +27,6 @@
//++
import {opApiModule} from '../../../../angular-modules';
-import {WorkPackageResourceInterface} from './work-package-resource.service';
import {WorkPackageCacheService} from '../../../work-packages/work-package-cache.service';
import IHttpBackendService = angular.IHttpBackendService;
import SinonStub = Sinon.SinonStub;
@@ -49,6 +48,7 @@ describe('WorkPackageResource service', () => {
var workPackage: any;
const createWorkPackage = () => {
+ source = source || {};
workPackage = new WorkPackageResource(source);
};
@@ -363,4 +363,81 @@ describe('WorkPackageResource service', () => {
});
});
});
+
+ describe('when using removeAttachment', () => {
+ var file;
+ var attachment;
+
+ beforeEach(() => {
+ file = {};
+ attachment = {
+ $isHal: true,
+ 'delete': sinon.stub()
+ };
+
+ createWorkPackage();
+ workPackage.attachments = {elements: [attachment]};
+ workPackage.pendingAttachments.push(file);
+ });
+
+ describe('when the attachment is a regular file', () => {
+ beforeEach(() => {
+ workPackage.removeAttachment(file);
+ });
+
+ it('should be removed from the pending attachments', () => {
+ expect(workPackage.pendingAttachments).to.have.length(0);
+ });
+ });
+
+ describe('when the attachment is an attachment resource', () => {
+ var deletion;
+
+ beforeEach(() => {
+ deletion = $q.defer();
+ attachment.delete.returns(deletion.promise);
+ sinon.stub(workPackage, 'updateAttachments');
+
+ workPackage.removeAttachment(attachment);
+ });
+
+ it('should call its delete method', () => {
+ expect(attachment.delete.calledOnce).to.be.true;
+ });
+
+ describe('when the deletion gets resolved', () => {
+ beforeEach(() => {
+ deletion.resolve();
+ $rootScope.$apply();
+ });
+
+ it('should call updateAttachments()', () => {
+ expect(workPackage.updateAttachments.calledOnce).to.be.true;
+ });
+ });
+
+ describe('when an error occurs', () => {
+ var error;
+
+ beforeEach(() => {
+ error = {foo: 'bar'};
+ sinon.stub(wpNotificationsService, 'handleErrorResponse');
+ deletion.reject(error);
+ $rootScope.$apply();
+ });
+
+ it('should call the handleErrorResponse notification', () => {
+ const calledWithErrorAndWorkPackage = wpNotificationsService
+ .handleErrorResponse
+ .calledWith(error, workPackage);
+
+ expect(calledWithErrorAndWorkPackage).to.be.true;
+ });
+
+ it('should not remove the attachment from the elements array', () => {
+ expect(workPackage.attachments.elements).to.have.length(1);
+ });
+ });
+ });
+ });
});
diff --git a/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.ts b/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.ts
index 20d3b0738c..055f9613e3 100644
--- a/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.ts
+++ b/frontend/app/components/api/api-v3/hal-resources/work-package-resource.service.ts
@@ -198,6 +198,29 @@ export class WorkPackageResource extends HalResource {
);
}
+ /**
+ * Remove the given attachment either from the pending attachments or from
+ * the attachment collection, if it is a resource.
+ *
+ * Removing it from the elements array assures that the view gets updated immediately.
+ * If an error occurs, the user gets notified and the attachment is pushed to the elements.
+ */
+ public removeAttachment(attachment) {
+ if (attachment.$isHal) {
+ attachment.delete()
+ .then(() => {
+ this.updateAttachments();
+ })
+ .catch(error => {
+ wpNotificationsService.handleErrorResponse(error, this);
+ this.attachments.elements.push(attachment);
+ });
+ }
+
+ _.pull(this.attachments.elements, attachment);
+ _.pull(this.pendingAttachments, attachment);
+ }
+
/**
* Upload the pending attachments if the work package exists.
* Do nothing, if the work package is being created.
diff --git a/frontend/app/components/work-packages/wp-single-view/wp-single-view.directive.html b/frontend/app/components/work-packages/wp-single-view/wp-single-view.directive.html
index 49e488f02a..41409bed9e 100644
--- a/frontend/app/components/work-packages/wp-single-view/wp-single-view.directive.html
+++ b/frontend/app/components/work-packages/wp-single-view/wp-single-view.directive.html
@@ -103,7 +103,7 @@
+ ng-if="!$ctrl.hideEmptyFields || $ctrl.workPackage.attachments.elements.length">
-
-
+
diff --git a/frontend/app/components/work-packages/wp-single-view/wp-single-view.directive.ts b/frontend/app/components/work-packages/wp-single-view/wp-single-view.directive.ts
index 781df3d636..892a0b85a4 100644
--- a/frontend/app/components/work-packages/wp-single-view/wp-single-view.directive.ts
+++ b/frontend/app/components/work-packages/wp-single-view/wp-single-view.directive.ts
@@ -41,7 +41,6 @@ export class WorkPackageSingleViewController {
public singleViewWp;
public groupedFields: any[] = [];
public hideEmptyFields: boolean = true;
- public attachments: any[] = [];
public text: any;
public scope: any;
@@ -72,10 +71,6 @@ export class WorkPackageSingleViewController {
$scope.$on('workPackageUpdatedInEditor', () => {
this.wpNotificationsService.showSave(this.workPackage);
});
-
- if (this.workPackage && this.workPackage.attachments) {
- this.attachments = this.workPackage.attachments.elements;
- }
}
public shouldHideGroup(group) {
diff --git a/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list-item.html b/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list-item.html
new file mode 100644
index 0000000000..e13d1bad6f
--- /dev/null
+++ b/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list-item.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+ {{ ::attachment.fileName || attachment.name }}
+
+
+
+
+
+
+
+
+
diff --git a/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.html b/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.html
new file mode 100644
index 0000000000..ef77a90d15
--- /dev/null
+++ b/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.html
@@ -0,0 +1,16 @@
+
diff --git a/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.test.ts b/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.test.ts
new file mode 100644
index 0000000000..a6af0fc688
--- /dev/null
+++ b/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.test.ts
@@ -0,0 +1,238 @@
+//-- copyright
+// OpenProject is a project management system.
+// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License version 3.
+//
+// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+// Copyright (C) 2006-2013 Jean-Philippe Lang
+// Copyright (C) 2010-2013 the ChiliProject Team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// See doc/COPYRIGHT.rdoc for more details.
+//++
+
+import {wpDirectivesModule, opTemplatesModule} from '../../../angular-modules';
+import {WorkPackageAttachmentListController} from './wp-attachment-list.directive';
+import IRootScopeService = angular.IRootScopeService;
+import ICompileService = angular.ICompileService;
+import IScope = angular.IScope;
+import IAugmentedJQuery = angular.IAugmentedJQuery;
+import IDirective = angular.IDirective;
+
+describe('wpAttachmentList directive', () => {
+ var scope: any;
+ var element: IAugmentedJQuery;
+ var controller: WorkPackageAttachmentListController;
+ var workPackage: any;
+ var compile;
+
+ var template;
+
+ beforeEach(angular.mock.module(
+ wpDirectivesModule.name,
+ opTemplatesModule.name,
+
+ $compileProvider => {
+ // Mock ngClick, because triggerHandler doesn't work on the delete button for some reason.
+ $compileProvider.directive('ngClick', (): IDirective => ({
+ restrict: 'A',
+ priority: 100,
+ scope: {
+ ngClick: '&',
+ },
+ controller: angular.noop,
+ controllerAs: '$ctrl',
+ bindToController: true
+ }));
+ }
+ ));
+ beforeEach(angular.mock.inject(function ($rootScope: IRootScopeService,
+ $compile: ICompileService,
+ I18n) {
+ const html = '';
+ scope = $rootScope.$new();
+ workPackage = {};
+ scope.workPackage = workPackage;
+
+ const t = sinon.stub(I18n, 't');
+ t
+ .withArgs('js.text_attachment_destroy_confirmation')
+ .returns('confirm destruction');
+
+ t
+ .withArgs('js.label_remove_file')
+ .returns('something fileName');
+
+ compile = () => {
+ element = $compile(html)(scope);
+ scope.$apply();
+ controller = element.controller('wpAttachmentList');
+
+ const root = element.find('.work-package--attachments--files');
+ const wrapper = root.children().first();
+ const listItem = root.find('.inplace-edit--read');
+ const fileName = listItem.find('.work-package--attachments--filename');
+ const triggerLink = listItem.find('.inplace-editing--trigger-link');
+ const deleteIcon = listItem.first().find('.inplace-edit--icon-wrapper').first();
+
+ template = {root, wrapper, listItem, fileName, triggerLink, deleteIcon};
+ };
+
+ compile();
+ }));
+
+ afterEach(angular.mock.inject(I18n => I18n.t.restore()));
+
+ it('should not be empty', () => {
+ expect(element.html()).to.be.ok;
+ });
+
+ it('should show no files', () => {
+ expect(template.wrapper.children()).to.have.length(0);
+ });
+
+ describe('when the work package has attachments', () => {
+ var attachment;
+ var attachments;
+
+ beforeEach(angular.mock.inject($q => {
+ attachment = {
+ name: 'name',
+ fileName: 'fileName'
+ };
+ attachments = [attachment, attachment];
+
+ workPackage.attachments = {
+ elements: attachments,
+ $load: sinon.stub()
+ };
+ workPackage.pendingAttachments = attachments;
+ workPackage.removeAttachment = sinon.stub();
+
+ workPackage.attachments.$load.returns($q.when(workPackage.attachments));
+
+ compile();
+ }));
+
+ it('should be rendered', () => {
+ expect(template.root).to.have.length(1);
+ });
+
+ it('should load the attachments', () => {
+ expect(workPackage.attachments.$load.calledOnce).to.be.true;
+ });
+
+ it('should show the existing and pending attachments', () => {
+ expect(template.listItem).to.have.length(4);
+ });
+
+ it('should have an element that contains the file name', () => {
+ expect(template.fileName.text()).to.contain(attachment.fileName);
+ });
+
+ it('should have a link that points nowhere', () => {
+ expect(template.fileName.attr('href')).to.equal('#');
+ });
+
+ it('should have a delete icon with a title that contains the file name', () => {
+ const icon = template.deleteIcon.find('[icon-title]');
+ expect(icon.attr('icon-title')).to.contain(attachment.fileName);
+ });
+
+ it('should have a confirm-popup attribute with the destroyConfirmation text value', () => {
+ expect(template.deleteIcon.attr('confirm-popup')).to.equal('confirm destruction');
+ });
+
+ describe('when using the delete button', () => {
+ beforeEach(() => {
+ template.deleteIcon.controller('ngClick').ngClick();
+ });
+
+ it('should call the removeAttachment method of the work package', () => {
+ expect(workPackage.removeAttachment.called).to.be.true;
+ });
+ });
+
+ describe('when the attachment has a download location', () => {
+ beforeEach(() => {
+ attachment.downloadLocation = {href: 'download'};
+ compile();
+ });
+
+ it('should link to that location', () => {
+ expect(template.fileName.attr('href')).to.equal(attachment.downloadLocation.href);
+ });
+ });
+
+ describe('when the attachment has no file name', () => {
+ beforeEach(() => {
+ attachment.fileName = '';
+ compile();
+ });
+
+ it('should contain an element that contains the attachment name', () => {
+ expect(template.fileName.text()).to.contain(attachment.name);
+ });
+ });
+
+ describe('when focusing an element', () => {
+ var focusElement;
+
+ const testFocus = prepare => {
+ beforeEach(() => {
+ prepare();
+ focusElement.triggerHandler('focus');
+ });
+
+ it('should set the `-focus` class for that attachment', () => {
+ expect(template.triggerLink.hasClass('-focus')).to.be.true;
+ });
+
+ it('should be focused', () => {
+ expect(controller.isFocused(attachment)).to.be.true;
+ });
+
+ describe('when setting the focus somewhere else', () => {
+ beforeEach(() => {
+ focusElement.triggerHandler('blur');
+ });
+
+ it('should unset the `-focus` class for that attachment', () => {
+ expect(template.triggerLink.hasClass('-focus')).to.be.false;
+ });
+
+ it('should not be focused', () => {
+ expect(controller.isFocused(attachment)).to.be.false;
+ });
+ });
+ };
+
+ describe('when focusing the file name element', () => {
+ testFocus(() => {
+ focusElement = template.fileName;
+ });
+ });
+
+ describe('when focusing the delete icon', () => {
+ testFocus(() => {
+ focusElement = template.deleteIcon;
+ });
+ });
+ });
+ });
+});
diff --git a/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.ts b/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.ts
new file mode 100644
index 0000000000..da0e9d036c
--- /dev/null
+++ b/frontend/app/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.ts
@@ -0,0 +1,93 @@
+//-- copyright
+// OpenProject is a project management system.
+// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License version 3.
+//
+// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+// Copyright (C) 2006-2013 Jean-Philippe Lang
+// Copyright (C) 2010-2013 the ChiliProject Team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// See doc/COPYRIGHT.rdoc for more details.
+//++
+
+import {wpDirectivesModule} from '../../../angular-modules';
+import {WorkPackageResourceInterface} from '../../api/api-v3/hal-resources/work-package-resource.service';
+import {UploadFile} from '../../api/op-file-upload/op-file-upload.service';
+import {HalResource} from '../../api/api-v3/hal-resources/hal-resource.service';
+
+export class WorkPackageAttachmentListController {
+ public workPackage: WorkPackageResourceInterface;
+ public text: any = {};
+
+ public itemTemplateUrl =
+ '/components/wp-attachments/wp-attachment-list/wp-attachment-list-item.html';
+
+ private focusedAttachment: any = null;
+
+ constructor(protected wpNotificationsService, I18n) {
+ this.text = {
+ destroyConfirmation: I18n.t('js.text_attachment_destroy_confirmation'),
+ removeFile: arg => I18n.t('js.label_remove_file', arg)
+ };
+
+ if (this.workPackage.attachments) {
+ this.workPackage.attachments.$load().then(attachments => {
+ this.workPackage.attachments.elements = attachments.elements;
+ });
+ }
+ }
+
+ /**
+ * Focus an attachment.
+ */
+ public focus(attachment) {
+ this.focusedAttachment = attachment;
+ }
+
+ /**
+ * Reset the focus.
+ */
+ public undoFocus() {
+ this.focus(null);
+ }
+
+ /**
+ * Return whether an attachment is focused.
+ */
+ public isFocused(attachment): boolean {
+ return this.focusedAttachment === attachment;
+ }
+}
+
+function wpAttachmentListDirective() {
+ return {
+ restrict: 'E',
+ templateUrl: '/components/wp-attachments/wp-attachment-list/wp-attachment-list.directive.html',
+
+ scope: {
+ workPackage: '='
+ },
+
+ controller: WorkPackageAttachmentListController,
+ controllerAs: '$ctrl',
+ bindToController: true
+ };
+}
+
+wpDirectivesModule.directive('wpAttachmentList', wpAttachmentListDirective);
diff --git a/frontend/app/components/wp-attachments/wp-attachments.directive.html b/frontend/app/components/wp-attachments/wp-attachments.directive.html
deleted file mode 100644
index b7ac5edbe4..0000000000
--- a/frontend/app/components/wp-attachments/wp-attachments.directive.html
+++ /dev/null
@@ -1,42 +0,0 @@
-
diff --git a/frontend/app/components/wp-attachments/wp-attachments.directive.test.ts b/frontend/app/components/wp-attachments/wp-attachments.directive.test.ts
deleted file mode 100644
index 886e358ca0..0000000000
--- a/frontend/app/components/wp-attachments/wp-attachments.directive.test.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-//-- copyright
-// OpenProject is a project management system.
-// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License version 3.
-//
-// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-// Copyright (C) 2006-2013 Jean-Philippe Lang
-// Copyright (C) 2010-2013 the ChiliProject Team
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//
-// See doc/COPYRIGHT.rdoc for more details.
-//++
-
-import {WorkPackageAttachmentsController} from './wp-attachments.directive';
-import {
- openprojectModule, wpDirectivesModule, opTemplatesModule,
- wpServicesModule, opConfigModule
-} from '../../angular-modules';
-import IQService = angular.IQService;
-
-describe('wpAttachments directive', () => {
- var $q:IQService;
- var files;
- var controller:WorkPackageAttachmentsController;
- var workPackage = {};
-
- beforeEach(angular.mock.module(
- openprojectModule.name,
- wpDirectivesModule.name,
- opTemplatesModule.name
- ));
-
- var loadPromise;
- var apiPromise;
-
- beforeEach(angular.mock.inject(function ($rootScope, $compile, $httpBackend, _$q_) {
- $q = _$q_;
-
- files = [{type: 'directory'}, {type: 'file'}];
- apiPromise = $q.when('');
- loadPromise = $q.when([]);
-
- // Skip the work package cache update
- $httpBackend.expectGET('/api/v3/work_packages/1234').respond(200, {});
-
- const element = angular.element('');
- const scope = $rootScope.$new();
-
- scope.workPackage = workPackage;
-
- $compile(element)(scope);
- scope.$digest();
- element.isolateScope();
-
- controller = element.controller('wpAttachments');
- }));
-});
diff --git a/frontend/app/components/wp-attachments/wp-attachments.directive.ts b/frontend/app/components/wp-attachments/wp-attachments.directive.ts
deleted file mode 100644
index 5549f8cfec..0000000000
--- a/frontend/app/components/wp-attachments/wp-attachments.directive.ts
+++ /dev/null
@@ -1,141 +0,0 @@
-//-- copyright
-// OpenProject is a project management system.
-// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License version 3.
-//
-// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
-// Copyright (C) 2006-2013 Jean-Philippe Lang
-// Copyright (C) 2010-2013 the ChiliProject Team
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-//
-// See doc/COPYRIGHT.rdoc for more details.
-//++
-
-import {wpDirectivesModule} from '../../angular-modules';
-import {WorkPackageNotificationService} from '../wp-edit/wp-notification.service';
-import {scopedObservable} from '../../helpers/angular-rx-utils';
-import {WorkPackageCacheService} from '../work-packages/work-package-cache.service';
-import {WorkPackageResourceInterface} from '../api/api-v3/hal-resources/work-package-resource.service';
-import {CollectionResourceInterface} from '../api/api-v3/hal-resources/collection-resource.service';
-
-export class WorkPackageAttachmentsController {
- public workPackage: WorkPackageResourceInterface;
- public text: any;
- public attachments: any[] = [];
- public size: any;
- public loading: boolean = false;
-
- private currentlyFocusing;
-
- constructor(protected $scope: any,
- protected wpCacheService: WorkPackageCacheService,
- protected wpNotificationsService: WorkPackageNotificationService,
- protected I18n: op.I18n) {
-
- this.text = {
- destroyConfirmation: I18n.t('js.text_attachment_destroy_confirmation'),
- removeFile: arg => I18n.t('js.label_remove_file', arg)
- };
-
- if (this.workPackage && this.workPackage.attachments) {
- this.loadAttachments(false);
- }
-
- $scope.$on('work_packages.attachment.add', (evt, file) => {
- this.attachments.push(file);
- });
-
- if (this.workPackage.isNew) {
- this.registerCreateObserver();
- }
- else {
- this.registerEditObserver();
- }
- }
-
- private registerEditObserver() {
- scopedObservable(this.$scope, this.wpCacheService.loadWorkPackage( this.workPackage.id))
- .subscribe((wp: WorkPackageResourceInterface) => {
- this.workPackage = wp;
- this.loadAttachments(true);
- });
- }
-
- private registerCreateObserver() {
- scopedObservable(this.$scope, this.wpCacheService.onNewWorkPackage())
- .subscribe((wp: WorkPackageResourceInterface) => {
- wp.uploadAttachments(this.attachments).then(() => {
- // Reload the work package after attachments are uploaded to
- // provide the correct links, in e.g., the description
- this.wpCacheService.loadWorkPackage( wp.id, true);
- });
- });
- }
-
- public loadAttachments(refresh: boolean = true): ng.IPromise {
- this.loading = true;
-
- return this.workPackage.attachments.$load(refresh)
- .then((collection: CollectionResourceInterface) => {
- this.attachments.length = 0;
- this.attachments.push(...collection.elements);
- })
- .finally(() => {
- this.loading = false;
- });
- }
-
- public remove(file): void {
- if (!this.workPackage.isNew && file._type === 'Attachment') {
- file
- .delete()
- .then(() => {
- this.workPackage.updateAttachments();
- })
- .catch(error => {
- this.wpNotificationsService.handleErrorResponse(error, this.workPackage)
- });
- }
-
- _.pull(this.attachments, file);
- }
-
- public focus(attachment: any): void {
- this.currentlyFocusing = attachment;
- }
-
- public focusing(attachment: any): boolean {
- return this.currentlyFocusing === attachment;
- }
-}
-
-function wpAttachmentsDirective(): ng.IDirective {
- return {
- restrict: 'E',
- templateUrl: '/components/wp-attachments/wp-attachments.directive.html',
- scope: {
- workPackage: '='
- },
-
- bindToController: true,
- controller: WorkPackageAttachmentsController,
- controllerAs: '$ctrl'
- };
-}
-
-wpDirectivesModule.directive('wpAttachments', wpAttachmentsDirective);