From 4338493e46b28ca6331f6170cb3da5ef6a276fb3 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:44:42 -0800 Subject: [PATCH] Frontend support for merge flag with bulk edit permissions --- src-ui/messages.xlf | 34 ++++++++++++----- .../common/input/switch/switch.component.html | 2 +- .../permissions-dialog.component.html | 15 +++++--- .../permissions-dialog.component.spec.ts | 21 +++++++++++ .../permissions-dialog.component.ts | 20 ++++++++-- .../bulk-editor/bulk-editor.component.spec.ts | 20 +++++++++- .../bulk-editor/bulk-editor.component.ts | 13 +++++-- .../manage/mail/mail.component.spec.ts | 9 +++-- .../components/manage/mail/mail.component.ts | 37 +++++++++++-------- .../management-list.component.spec.ts | 10 ++++- .../management-list.component.ts | 5 ++- .../rest/abstract-name-filter-service.spec.ts | 12 ++++-- .../rest/abstract-name-filter-service.ts | 4 +- 13 files changed, 146 insertions(+), 56 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index e7263e0f2..56866f512 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -620,7 +620,7 @@ src/app/components/common/permissions-dialog/permissions-dialog.component.html - 20 + 23 src/app/components/dashboard/dashboard.component.html @@ -2438,7 +2438,7 @@ src/app/components/common/permissions-dialog/permissions-dialog.component.html - 23 + 26 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -2505,7 +2505,7 @@ src/app/components/common/permissions-dialog/permissions-dialog.component.html - 22 + 25 src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -3923,6 +3923,13 @@ 15 + + Merge with existing permissions + + src/app/components/common/permissions-dialog/permissions-dialog.component.html + 14 + + Set permissions @@ -3937,11 +3944,18 @@ 33 - - Note that permissions set here will override any existing permissions + + Existing owner, user and group permissions will be merged with these settings. src/app/components/common/permissions-dialog/permissions-dialog.component.ts - 71 + 74 + + + + Any and all existing owner, user and group permissions will be replaced. + + src/app/components/common/permissions-dialog/permissions-dialog.component.ts + 75 @@ -6100,18 +6114,18 @@ Permissions updated src/app/components/manage/mail/mail.component.ts - 211 + 212 Error updating permissions src/app/components/manage/mail/mail.component.ts - 215 + 217 src/app/components/manage/management-list/management-list.component.ts - 300 + 301 @@ -6277,7 +6291,7 @@ Permissions updated successfully src/app/components/manage/management-list/management-list.component.ts - 293 + 294 diff --git a/src-ui/src/app/components/common/input/switch/switch.component.html b/src-ui/src/app/components/common/input/switch/switch.component.html index 489106fcb..5acef7a75 100644 --- a/src-ui/src/app/components/common/input/switch/switch.component.html +++ b/src-ui/src/app/components/common/input/switch/switch.component.html @@ -15,7 +15,7 @@ } } -
+
@if (horizontal) { diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html index 37ab7efbd..fd88002ba 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html @@ -5,12 +5,15 @@
@@ -20,5 +23,5 @@ Loading... } - +
diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts index 3f601d771..bc8ccdecb 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts @@ -11,6 +11,7 @@ import { NgSelectModule } from '@ng-select/ng-select' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { PermissionsUserComponent } from '../input/permissions/permissions-user/permissions-user.component' import { PermissionsGroupComponent } from '../input/permissions/permissions-group/permissions-group.component' +import { SwitchComponent } from '../input/switch/switch.component' const set_permissions = { owner: 10, @@ -37,6 +38,7 @@ describe('PermissionsDialogComponent', () => { PermissionsDialogComponent, SafeHtmlPipe, SelectComponent, + SwitchComponent, PermissionsFormComponent, PermissionsUserComponent, PermissionsGroupComponent, @@ -112,4 +114,23 @@ describe('PermissionsDialogComponent', () => { expect(component.title).toEqual(`Edit permissions for ${obj.name}`) expect(component.permissions).toEqual(set_permissions) }) + + it('should toggle hint based on object existence (if editing) or merge flag', () => { + component.form.get('merge').setValue(true) + expect(component.hint.includes('Existing')).toBeTruthy() + component.form.get('merge').setValue(false) + expect(component.hint.includes('will be replaced')).toBeTruthy() + component.object = {} + expect(component.hint).toBeNull() + }) + + it('should emit permissions and merge flag on confirm', () => { + const confirmSpy = jest.spyOn(component.confirmClicked, 'emit') + component.form.get('permissions_form').setValue(set_permissions) + component.confirm() + expect(confirmSpy).toHaveBeenCalledWith({ + permissions: set_permissions, + merge: true, + }) + }) }) diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts index 9a514387c..afe1bebb3 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts @@ -32,6 +32,7 @@ export class PermissionsDialogComponent { this.o = o this.title = $localize`Edit permissions for ` + o['name'] this.form.patchValue({ + merge: true, permissions_form: { owner: o.owner, set_permissions: o.permissions, @@ -43,8 +44,9 @@ export class PermissionsDialogComponent { return this.o } - form = new FormGroup({ + public form = new FormGroup({ permissions_form: new FormControl(), + merge: new FormControl(true), }) buttonsEnabled: boolean = true @@ -66,11 +68,21 @@ export class PermissionsDialogComponent { } } - @Input() - message = - $localize`Note that permissions set here will override any existing permissions` + get hint(): string { + if (this.object) return null + return this.form.get('merge').value + ? $localize`Existing owner, user and group permissions will be merged with these settings.` + : $localize`Any and all existing owner, user and group permissions will be replaced.` + } cancelClicked() { this.activeModal.close() } + + confirm() { + this.confirmClicked.emit({ + permissions: this.permissions, + merge: this.form.get('merge').value, + }) + } } diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts index 8edfcc7e1..42f8b6d1d 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts @@ -41,6 +41,7 @@ import { PermissionsUserComponent } from '../../common/input/permissions/permiss import { NgSelectModule } from '@ng-select/ng-select' import { GroupService } from 'src/app/services/rest/group.service' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { SwitchComponent } from '../../common/input/switch/switch.component' const selectionData: SelectionData = { selected_tags: [ @@ -81,6 +82,7 @@ describe('BulkEditorComponent', () => { SelectComponent, PermissionsGroupComponent, PermissionsUserComponent, + SwitchComponent, ], providers: [ PermissionsService, @@ -851,7 +853,18 @@ describe('BulkEditorComponent', () => { fixture.detectChanges() component.setPermissions() expect(modal).not.toBeUndefined() - modal.componentInstance.confirmClicked.next() + const perms = { + permissions: { + view_users: [], + change_users: [], + view_groups: [], + change_groups: [], + }, + } + modal.componentInstance.confirmClicked.emit({ + permissions: perms, + merge: true, + }) let req = httpTestingController.expectOne( `${environment.apiBaseUrl}documents/bulk_edit/` ) @@ -859,7 +872,10 @@ describe('BulkEditorComponent', () => { expect(req.request.body).toEqual({ documents: [3, 4], method: 'set_permissions', - parameters: undefined, + parameters: { + permissions: perms.permissions, + merge: true, + }, }) httpTestingController.match( `${environment.apiBaseUrl}documents/?page=1&page_size=50&ordering=-created&truncate_content=true` diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 1c7e0b9c6..49d4c070f 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -540,9 +540,14 @@ export class BulkEditorComponent let modal = this.modalService.open(PermissionsDialogComponent, { backdrop: 'static', }) - modal.componentInstance.confirmClicked.subscribe((permissions) => { - modal.componentInstance.buttonsEnabled = false - this.executeBulkOperation(modal, 'set_permissions', permissions) - }) + modal.componentInstance.confirmClicked.subscribe( + ({ permissions, merge }) => { + modal.componentInstance.buttonsEnabled = false + this.executeBulkOperation(modal, 'set_permissions', { + ...permissions, + merge, + }) + } + ) } } diff --git a/src-ui/src/app/components/manage/mail/mail.component.spec.ts b/src-ui/src/app/components/manage/mail/mail.component.spec.ts index 5680d8faa..fcc5bcc6b 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.spec.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.spec.ts @@ -41,6 +41,7 @@ import { TagsComponent } from '../../common/input/tags/tags.component' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { SwitchComponent } from '../../common/input/switch/switch.component' const mailAccounts = [ { id: 1, name: 'account1' }, @@ -82,6 +83,7 @@ describe('MailComponent', () => { PermissionsGroupComponent, PermissionsDialogComponent, PermissionsFormComponent, + SwitchComponent, ], providers: [CustomDatePipe, DatePipe, PermissionsGuard], imports: [ @@ -267,11 +269,11 @@ describe('MailComponent', () => { rulePatchSpy.mockReturnValueOnce( throwError(() => new Error('error saving perms')) ) - dialog.confirmClicked.emit(perms) + dialog.confirmClicked.emit({ permissions: perms, merge: true }) expect(rulePatchSpy).toHaveBeenCalled() expect(toastErrorSpy).toHaveBeenCalled() rulePatchSpy.mockReturnValueOnce(of(mailRules[0] as MailRule)) - dialog.confirmClicked.emit(perms) + dialog.confirmClicked.emit({ permissions: perms, merge: true }) expect(toastInfoSpy).toHaveBeenCalledWith('Permissions updated') modalService.dismissAll() @@ -299,8 +301,7 @@ describe('MailComponent', () => { expect(modal).not.toBeUndefined() let dialog = modal.componentInstance as PermissionsDialogComponent expect(dialog.object).toEqual(mailAccounts[0]) - dialog = modal.componentInstance as PermissionsDialogComponent - dialog.confirmClicked.emit(perms) + dialog.confirmClicked.emit({ permissions: perms, merge: true }) expect(accountPatchSpy).toHaveBeenCalled() }) }) diff --git a/src-ui/src/app/components/manage/mail/mail.component.ts b/src-ui/src/app/components/manage/mail/mail.component.ts index 9bb33c03b..d8820ed38 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.ts @@ -200,22 +200,27 @@ export class MailComponent const dialog: PermissionsDialogComponent = modal.componentInstance as PermissionsDialogComponent dialog.object = object - modal.componentInstance.confirmClicked.subscribe((permissions) => { - modal.componentInstance.buttonsEnabled = false - const service: AbstractPaperlessService = - 'account' in object ? this.mailRuleService : this.mailAccountService - object.owner = permissions['owner'] - object['set_permissions'] = permissions['set_permissions'] - service.patch(object).subscribe({ - next: () => { - this.toastService.showInfo($localize`Permissions updated`) - modal.close() - }, - error: (e) => { - this.toastService.showError($localize`Error updating permissions`, e) - }, - }) - }) + modal.componentInstance.confirmClicked.subscribe( + ({ permissions, merge }) => { + modal.componentInstance.buttonsEnabled = false + const service: AbstractPaperlessService = + 'account' in object ? this.mailRuleService : this.mailAccountService + object.owner = permissions['owner'] + object['set_permissions'] = permissions['set_permissions'] + service.patch(object).subscribe({ + next: () => { + this.toastService.showInfo($localize`Permissions updated`) + modal.close() + }, + error: (e) => { + this.toastService.showError( + $localize`Error updating permissions`, + e + ) + }, + }) + } + ) } userCanEdit(obj: ObjectWithPermissions): boolean { diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts index 44a3452d7..6196a3c8a 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts @@ -264,13 +264,19 @@ describe('ManagementListComponent', () => { throwError(() => new Error('error setting permissions')) ) const errorToastSpy = jest.spyOn(toastService, 'showError') - modal.componentInstance.confirmClicked.emit() + modal.componentInstance.confirmClicked.emit({ + permissions: {}, + merge: true, + }) expect(bulkEditPermsSpy).toHaveBeenCalled() expect(errorToastSpy).toHaveBeenCalled() const successToastSpy = jest.spyOn(toastService, 'showInfo') bulkEditPermsSpy.mockReturnValueOnce(of('OK')) - modal.componentInstance.confirmClicked.emit() + modal.componentInstance.confirmClicked.emit({ + permissions: {}, + merge: true, + }) expect(bulkEditPermsSpy).toHaveBeenCalled() expect(successToastSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.ts b/src-ui/src/app/components/manage/management-list/management-list.component.ts index e20b5d4a7..a89b5e4f6 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.ts @@ -279,12 +279,13 @@ export abstract class ManagementListComponent backdrop: 'static', }) modal.componentInstance.confirmClicked.subscribe( - (permissions: { owner: number; set_permissions: PermissionsObject }) => { + ({ permissions, merge }) => { modal.componentInstance.buttonsEnabled = false this.service .bulk_update_permissions( Array.from(this.selectedObjects), - permissions + permissions, + merge ) .subscribe({ next: () => { diff --git a/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts b/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts index 70ae211e5..e09270701 100644 --- a/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts +++ b/src-ui/src/app/services/rest/abstract-name-filter-service.spec.ts @@ -53,10 +53,14 @@ export const commonAbstractNameFilterPaperlessServiceTests = ( }, } subscription = service - .bulk_update_permissions([1, 2], { - owner, - set_permissions: permissions, - }) + .bulk_update_permissions( + [1, 2], + { + owner, + set_permissions: permissions, + }, + true + ) .subscribe() const req = httpTestingController.expectOne( `${environment.apiBaseUrl}bulk_edit_object_perms/` diff --git a/src-ui/src/app/services/rest/abstract-name-filter-service.ts b/src-ui/src/app/services/rest/abstract-name-filter-service.ts index 5e0377cb9..b38994086 100644 --- a/src-ui/src/app/services/rest/abstract-name-filter-service.ts +++ b/src-ui/src/app/services/rest/abstract-name-filter-service.ts @@ -26,13 +26,15 @@ export abstract class AbstractNameFilterService< bulk_update_permissions( objects: Array, - permissions: { owner: number; set_permissions: PermissionsObject } + permissions: { owner: number; set_permissions: PermissionsObject }, + merge: boolean ): Observable { return this.http.post(`${this.baseUrl}bulk_edit_object_perms/`, { objects, object_type: this.resourceName, owner: permissions.owner, permissions: permissions.set_permissions, + merge, }) } }