diff --git a/e2e/suites/actions/copy.test.ts b/e2e/suites/actions/copy.test.ts index c1a1c97044..743dfa5b8b 100755 --- a/e2e/suites/actions/copy.test.ts +++ b/e2e/suites/actions/copy.test.ts @@ -239,7 +239,8 @@ describe('Copy content', () => { expect(await dataTable.isItemPresent(file3InFolder)).toBe(true, `${file3InFolder} not present in destination folder`); }); - it('Copy items into a library - [C280282]', async () => { + // TODO disabled until ACA-2171 is fixed + xit('Copy items into a library - [C280282]', async () => { await dataTable.selectMultipleItems([file1, folder1]); await toolbar.clickMoreActionsCopy(); await copyDialog.selectLocation('File Libraries'); @@ -323,7 +324,8 @@ describe('Copy content', () => { expect(await dataTable.isItemPresent(`${existingFile}-1.txt`)).toBe(true, `${existingFile}-1.txt not present in destination folder`); }); - it('Copy items into a library - [C291899]', async () => { + // TODO disabled until ACA-2171 is fixed + xit('Copy items into a library - [C291899]', async () => { await dataTable.selectItem(file1, source); await toolbar.clickMoreActionsCopy(); await copyDialog.selectLocation('File Libraries'); @@ -401,7 +403,8 @@ describe('Copy content', () => { expect(await dataTable.isItemPresent(`${existingFile}-1.txt`)).toBe(true, `${existingFile}-1.txt not present in destination folder`); }); - it('Copy items into a library - [C291900]', async () => { + // TODO disabled until ACA-2171 is fixed + xit('Copy items into a library - [C291900]', async () => { await dataTable.selectItem(file1, source); await toolbar.clickMoreActionsCopy(); await copyDialog.selectLocation('File Libraries'); @@ -526,7 +529,8 @@ describe('Copy content', () => { expect(await dataTable.isItemPresent(file3InFolder)).toBe(true, `${file3InFolder} not present in destination folder`); }); - it('Copy items into a library - [C291901]', async () => { + // TODO disabled until ACA-2171 is fixed + xit('Copy items into a library - [C291901]', async () => { await dataTable.selectMultipleItems([file1, folder1], source); await toolbar.clickMoreActionsCopy(); await copyDialog.selectLocation('File Libraries'); diff --git a/e2e/suites/actions/move.test.ts b/e2e/suites/actions/move.test.ts index ded079434f..c4936326b2 100755 --- a/e2e/suites/actions/move.test.ts +++ b/e2e/suites/actions/move.test.ts @@ -250,7 +250,8 @@ describe('Move content', () => { expect(await dataTable.isItemPresent(file3InFolder)).toBe(true, `${file3InFolder} not present in destination folder`); }); - it('Move items into a library - [C291969]', async () => { + // TODO disabled until ACA-2171 is fixed + xit('Move items into a library - [C291969]', async () => { await dataTable.selectMultipleItems([file4, folder2]); await toolbar.clickMoreActionsMove(); await moveDialog.selectLocation('File Libraries'); @@ -370,7 +371,8 @@ describe('Move content', () => { expect(await dataTable.isItemPresent(`${existingFile}-1.txt`)).toBe(false, `${existingFile}-1.txt is present in destination folder`); }); - it('Move items into a library - [C291971]', async () => { + // TODO disabled until ACA-2171 is fixed + xit('Move items into a library - [C291971]', async () => { await dataTable.selectItem(file4, sourceRF); await toolbar.clickMoreActionsMove(); await moveDialog.selectLocation('File Libraries'); @@ -492,7 +494,8 @@ describe('Move content', () => { expect(await dataTable.isItemPresent(`${existingFile}-1.txt`)).toBe(false, `${existingFile}-1.txt not present in destination folder`); }); - it('Move items into a library - [C291978]', async () => { + // TODO disabled until ACA-2171 is fixed + xit('Move items into a library - [C291978]', async () => { await dataTable.selectItem(file4, sourceSF); await toolbar.clickMoreActionsMove(); await moveDialog.selectLocation('File Libraries'); @@ -682,7 +685,8 @@ describe('Move content', () => { expect(await dataTable.isItemPresent(file3InFolder)).toBe(true, `${file3InFolder} not present in destination folder`); }); - it('Move items into a library - [C291979]', async () => { + // TODO disabled until ACA-2171 is fixed + xit('Move items into a library - [C291979]', async () => { await dataTable.selectMultipleItems([file4, folder2], sourceFav); await toolbar.clickMoreActionsMove(); await moveDialog.selectLocation('File Libraries'); diff --git a/src/app/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.spec.ts b/src/app/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.spec.ts index 85ed8d52cb..05df3af261 100644 --- a/src/app/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.spec.ts +++ b/src/app/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.spec.ts @@ -24,7 +24,7 @@ */ import { ToggleEditOfflineComponent } from './toggle-edit-offline.component'; -import { EditOfflineDirective } from '../../../directives/edit-offline.directive'; +import { LockNodeDirective } from '../../../directives/lock-node.directive'; import { setupTestBed, CoreModule } from '@alfresco/adf-core'; import { TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; @@ -44,7 +44,7 @@ describe('ToggleEditOfflineComponent', () => { setupTestBed({ imports: [CoreModule], - declarations: [ToggleEditOfflineComponent, EditOfflineDirective], + declarations: [ToggleEditOfflineComponent, LockNodeDirective], providers: [ { provide: Store, @@ -115,12 +115,12 @@ describe('ToggleEditOfflineComponent', () => { ]); }); - it('should raise notification on error', () => { + it('should raise notification on lock error', () => { component.selection = { entry: { name: 'test' } }; - component.onError(); + component.onLockError(); fixture.detectChanges(); expect(dispatchSpy.calls.argsFor(0)).toEqual([ @@ -129,4 +129,19 @@ describe('ToggleEditOfflineComponent', () => { }) ]); }); + + it('should raise notification on unlock error', () => { + component.selection = { + entry: { name: 'test' } + }; + + component.onUnlockLockError(); + fixture.detectChanges(); + + expect(dispatchSpy.calls.argsFor(0)).toEqual([ + new SnackbarErrorAction('APP.MESSAGES.ERRORS.UNLOCK_NODE', { + fileName: 'test' + }) + ]); + }); }); diff --git a/src/app/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts b/src/app/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts index 2a1f634f9c..1edc7ff6af 100644 --- a/src/app/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts +++ b/src/app/components/toolbar/toggle-edit-offline/toggle-edit-offline.component.ts @@ -38,23 +38,24 @@ import { MinimalNodeEntity } from '@alfresco/js-api'; selector: 'app-toggle-edit-offline', template: ` + ` }) class TestComponent { - @ViewChild('editOffline') - directive: EditOfflineDirective; + @ViewChild('lock') + directive: LockNodeDirective; selection = null; } -describe('EditOfflineDirective', () => { +describe('LockNodeDirective', () => { let fixture; let api; let component; setupTestBed({ imports: [CoreModule], - declarations: [TestComponent, EditOfflineDirective], + declarations: [TestComponent, LockNodeDirective], providers: [ { provide: AlfrescoApiService, diff --git a/src/app/directives/edit-offline.directive.ts b/src/app/directives/lock-node.directive.ts similarity index 87% rename from src/app/directives/edit-offline.directive.ts rename to src/app/directives/lock-node.directive.ts index 0902c5731f..d89ae8f981 100644 --- a/src/app/directives/edit-offline.directive.ts +++ b/src/app/directives/lock-node.directive.ts @@ -34,19 +34,20 @@ import { NodeEntry, NodeBodyLock, SharedLinkEntry } from '@alfresco/js-api'; import { AlfrescoApiService } from '@alfresco/adf-core'; @Directive({ - selector: '[acaEditOffline]', - exportAs: 'editOffline' + selector: '[acaLockNode]', + exportAs: 'lockNode' }) -export class EditOfflineDirective { - @Input('acaEditOffline') +export class LockNodeDirective { + @Input('acaLockNode') node: NodeEntry = null; @Output() toggle: EventEmitter = new EventEmitter(); - @Output() error: EventEmitter = new EventEmitter(); + @Output() lockError: EventEmitter = new EventEmitter(); + @Output() unlockError: EventEmitter = new EventEmitter(); @HostListener('click') onClick() { - this.toggleEdit(this.node); + this.toggleLock(this.node); } constructor(private alfrescoApiService: AlfrescoApiService) {} @@ -59,7 +60,7 @@ export class EditOfflineDirective { ); } - private async toggleEdit(node: NodeEntry | SharedLinkEntry) { + private async toggleLock(node: NodeEntry | SharedLinkEntry) { const id = (node).entry.nodeId || node.entry.id; if (this.isNodeLocked()) { try { @@ -69,7 +70,7 @@ export class EditOfflineDirective { this.update(response.entry); this.toggle.emit(isLocked); } catch (error) { - this.error.emit(error); + this.unlockError.emit(error); } } else { try { @@ -79,7 +80,7 @@ export class EditOfflineDirective { this.update(response.entry); this.toggle.emit(isLocked); } catch (error) { - this.error.emit(error); + this.lockError.emit(error); } } } diff --git a/src/app/extensions/core.extensions.module.ts b/src/app/extensions/core.extensions.module.ts index 6379e8db59..885715d12e 100644 --- a/src/app/extensions/core.extensions.module.ts +++ b/src/app/extensions/core.extensions.module.ts @@ -111,7 +111,8 @@ export class CoreExtensionsModule { extensions.setEvaluators({ 'app.selection.canDelete': app.canDeleteSelection, - 'app.selection.canEditLockedFile': app.canEditLockedFile, + 'app.selection.canUnlockFile': app.canUnlockFile, + 'app.selection.canLockFile': app.canLockFile, 'app.selection.canDownload': app.canDownloadSelection, 'app.selection.notEmpty': app.hasSelection, 'app.selection.canUnshare': app.canUnshareNodes, diff --git a/src/app/extensions/evaluators/app.evaluators.ts b/src/app/extensions/evaluators/app.evaluators.ts index c767f9c6ec..00ae64f596 100644 --- a/src/app/extensions/evaluators/app.evaluators.ts +++ b/src/app/extensions/evaluators/app.evaluators.ts @@ -308,14 +308,35 @@ export function isWriteLocked( ); } -export function canEditLockedFile( +export function isUserWriteLockOwner( context: AppRuleContext, ...args: RuleParameter[] ): boolean { - return !!( - !isWriteLocked(context, ...args) || + return ( + isWriteLocked(context, ...args) && (context.selection.file.entry.properties['cm:lockOwner'] && context.selection.file.entry.properties['cm:lockOwner'].id === context.profile.id) ); } + +export function canLockFile( + context: AppRuleContext, + ...args: RuleParameter[] +): boolean { + return ( + !isWriteLocked(context, ...args) && canUpdateSelectedNode(context, ...args) + ); +} + +export function canUnlockFile( + context: AppRuleContext, + ...args: RuleParameter[] +): boolean { + const { file } = context.selection; + return ( + (isWriteLocked(context, ...args) && + context.permissions.check(file.entry, ['delete'])) || + isUserWriteLockOwner(context, ...args) + ); +} diff --git a/src/assets/app.extensions.json b/src/assets/app.extensions.json index 3c8f814166..cc17cc9460 100644 --- a/src/assets/app.extensions.json +++ b/src/assets/app.extensions.json @@ -166,7 +166,6 @@ "parameters": [ { "type": "rule", "value": "app.selection.file" }, { "type": "rule", "value": "app.navigation.isNotTrashcan" }, - { "type": "rule", "value": "app.selection.canEditLockedFile" }, { "type": "rule", "value": "core.not", @@ -223,25 +222,21 @@ "type": "core.every", "parameters": [ { "type": "rule", "value": "app.selection.canDelete" }, - { "type": "rule", "value": "app.selection.canEditLockedFile" }, { "type": "rule", "value": "app.navigation.isNotTrashcan" } ] }, { - "id": "app.toolbar.canEditLockedFile", + "id": "app.toolbar.canToggleLock", "type": "core.every", "parameters": [ { "type": "rule", "value": "app.selection.file" }, - { "type": "rule", "value": "app.selection.canEditLockedFile" }, { "type": "rule", "value": "app.navigation.isNotTrashcan" }, { "type": "rule", "value": "core.some", "parameters": [ - { "type": "rule", "value": "app.navigation.isPreview" }, - { "type": "rule", "value": "app.navigation.isPersonalFiles" }, - { "type": "rule", "value": "app.navigation.isLibraryFiles" }, - { "type": "rule", "value": "app.navigation.isRecentFiles" } + { "type": "rule", "value": "app.selection.canUnlockFile" }, + { "type": "rule", "value": "app.selection.canLockFile" } ] } ] @@ -533,12 +528,12 @@ "title": "APP.ACTIONS.MORE", "children": [ { - "id": "app.toolbar.toggleEditOffline", + "id": "app.toolbar.toggleLock", "order": 100, "type": "custom", "component": "app.toolbar.toggleEditOffline", "rules": { - "visible": "app.toolbar.canEditLockedFile" + "visible": "app.toolbar.canToggleLock" } }, { @@ -671,12 +666,12 @@ ], "contextMenu": [ { - "id": "app.context.toggleEditOffline", + "id": "app.context.toggleLock", "order": 100, "type": "custom", "component": "app.toolbar.toggleEditOffline", "rules": { - "visible": "app.toolbar.canEditLockedFile" + "visible": "app.toolbar.canToggleLock" } }, { @@ -970,12 +965,12 @@ "title": "APP.ACTIONS.MORE", "children": [ { - "id": "app.viewer.toggleEditOffline", + "id": "app.viewer.toggleLock", "order": 100, "type": "custom", "component": "app.toolbar.toggleEditOffline", "rules": { - "visible": "app.toolbar.canEditLockedFile" + "visible": "app.toolbar.canToggleLock" } }, { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 2eb5aaa9e6..386a7fe687 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -254,6 +254,7 @@ "NODE_RESTORE_PLURAL": "{{ number }} items couldn't be restored", "PERMISSION": "You don't have access to do this", "LOCK_NODE": "There was a problem locking the {{ fileName }} file", + "UNLOCK_NODE": "There was a problem unlocking the {{ fileName }} file", "TRASH": { "NODES_PURGE": { "PLURAL": "{{ number }} items couldn't be deleted",