Frontend support for merge flag with bulk edit permissions

This commit is contained in:
shamoon 2024-01-22 13:44:42 -08:00
parent 93f3c3aa7b
commit 4338493e46
13 changed files with 146 additions and 56 deletions

View File

@ -620,7 +620,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/permissions-dialog/permissions-dialog.component.html</context>
<context context-type="linenumber">20</context>
<context context-type="linenumber">23</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.html</context>
@ -2438,7 +2438,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/permissions-dialog/permissions-dialog.component.html</context>
<context context-type="linenumber">23</context>
<context context-type="linenumber">26</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
@ -2505,7 +2505,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/permissions-dialog/permissions-dialog.component.html</context>
<context context-type="linenumber">22</context>
<context context-type="linenumber">25</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
@ -3923,6 +3923,13 @@
<context context-type="linenumber">15</context>
</context-group>
</trans-unit>
<trans-unit id="7940755769131903278" datatype="html">
<source>Merge with existing permissions</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/permissions-dialog/permissions-dialog.component.html</context>
<context context-type="linenumber">14</context>
</context-group>
</trans-unit>
<trans-unit id="7062872617520618723" datatype="html">
<source>Set permissions</source>
<context-group purpose="location">
@ -3937,11 +3944,18 @@
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit id="8283439432608484491" datatype="html">
<source>Note that permissions set here will override any existing permissions</source>
<trans-unit id="347498040201588614" datatype="html">
<source>Existing owner, user and group permissions will be merged with these settings.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/permissions-dialog/permissions-dialog.component.ts</context>
<context context-type="linenumber">71</context>
<context context-type="linenumber">74</context>
</context-group>
</trans-unit>
<trans-unit id="3434726483516379481" datatype="html">
<source>Any and all existing owner, user and group permissions will be replaced.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/permissions-dialog/permissions-dialog.component.ts</context>
<context context-type="linenumber">75</context>
</context-group>
</trans-unit>
<trans-unit id="5947558132119506443" datatype="html">
@ -6100,18 +6114,18 @@
<source>Permissions updated</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
<context context-type="linenumber">211</context>
<context context-type="linenumber">212</context>
</context-group>
</trans-unit>
<trans-unit id="4639647950943944112" datatype="html">
<source>Error updating permissions</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
<context context-type="linenumber">215</context>
<context context-type="linenumber">217</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
<context context-type="linenumber">300</context>
<context context-type="linenumber">301</context>
</context-group>
</trans-unit>
<trans-unit id="4010735610815226758" datatype="html">
@ -6277,7 +6291,7 @@
<source>Permissions updated successfully</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.ts</context>
<context context-type="linenumber">293</context>
<context context-type="linenumber">294</context>
</context-group>
</trans-unit>
<trans-unit id="5101757640976222639" datatype="html">

View File

@ -15,7 +15,7 @@
}
</div>
}
<div [ngClass]="{'col-md-9': horizontal, 'align-items-center': horizontal, 'd-flex': horizontal}">
<div [ngClass]="{'align-items-center': horizontal, 'd-flex': horizontal}">
<div class="form-check form-switch">
<input #inputField type="checkbox" class="form-check-input" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" (blur)="onTouched()" [disabled]="disabled">
@if (horizontal) {

View File

@ -5,12 +5,15 @@
</div>
<div class="modal-body">
@if (!object && message) {
<p class="mb-3" [innerHTML]="message | safeHtml"></p>
}
<form [formGroup]="form">
<div class="form-group">
<pngx-permissions-form [users]="users" formControlName="permissions_form"></pngx-permissions-form>
</div>
<div class="form-group mt-4">
<div class="offset-lg-3 row">
<pngx-input-switch i18n-title title="Merge with existing permissions" [horizontal]="true" [hint]="hint" formControlName="merge"></pngx-input-switch>
</div>
</div>
</form>
</div>
@ -20,5 +23,5 @@
<span class="visually-hidden" i18n>Loading...</span>
}
<button type="button" class="btn btn-outline-primary" (click)="cancelClicked()" [disabled]="!buttonsEnabled" i18n>Cancel</button>
<button type="button" class="btn btn-primary" (click)="confirmClicked.emit(permissions)" [disabled]="!buttonsEnabled" i18n>Confirm</button>
<button type="button" class="btn btn-primary" (click)="confirm()" [disabled]="!buttonsEnabled" i18n>Confirm</button>
</div>

View File

@ -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,
})
})
})

View File

@ -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,
})
}
}

View File

@ -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`

View File

@ -540,9 +540,14 @@ export class BulkEditorComponent
let modal = this.modalService.open(PermissionsDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.confirmClicked.subscribe((permissions) => {
modal.componentInstance.confirmClicked.subscribe(
({ permissions, merge }) => {
modal.componentInstance.buttonsEnabled = false
this.executeBulkOperation(modal, 'set_permissions', permissions)
this.executeBulkOperation(modal, 'set_permissions', {
...permissions,
merge,
})
}
)
}
}

View File

@ -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()
})
})

View File

@ -200,7 +200,8 @@ export class MailComponent
const dialog: PermissionsDialogComponent =
modal.componentInstance as PermissionsDialogComponent
dialog.object = object
modal.componentInstance.confirmClicked.subscribe((permissions) => {
modal.componentInstance.confirmClicked.subscribe(
({ permissions, merge }) => {
modal.componentInstance.buttonsEnabled = false
const service: AbstractPaperlessService<MailRule | MailAccount> =
'account' in object ? this.mailRuleService : this.mailAccountService
@ -212,10 +213,14 @@ export class MailComponent
modal.close()
},
error: (e) => {
this.toastService.showError($localize`Error updating permissions`, e)
this.toastService.showError(
$localize`Error updating permissions`,
e
)
},
})
})
}
)
}
userCanEdit(obj: ObjectWithPermissions): boolean {

View File

@ -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()
})

View File

@ -279,12 +279,13 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
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: () => {

View File

@ -53,10 +53,14 @@ export const commonAbstractNameFilterPaperlessServiceTests = (
},
}
subscription = service
.bulk_update_permissions([1, 2], {
.bulk_update_permissions(
[1, 2],
{
owner,
set_permissions: permissions,
})
},
true
)
.subscribe()
const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}bulk_edit_object_perms/`

View File

@ -26,13 +26,15 @@ export abstract class AbstractNameFilterService<
bulk_update_permissions(
objects: Array<number>,
permissions: { owner: number; set_permissions: PermissionsObject }
permissions: { owner: number; set_permissions: PermissionsObject },
merge: boolean
): Observable<string> {
return this.http.post<string>(`${this.baseUrl}bulk_edit_object_perms/`, {
objects,
object_type: this.resourceName,
owner: permissions.owner,
permissions: permissions.set_permissions,
merge,
})
}
}