mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-04-08 00:58:52 +00:00
Include on frontend
This commit is contained in:
@@ -26,8 +26,8 @@
|
||||
<input type="checkbox" class="form-check-input" id="{{type}}_all" (change)="toggleAll($event, type)" [checked]="typesWithAllActions.has(type) || isInherited(type)" [attr.disabled]="disabled || isInherited(type) ? true : null">
|
||||
<label class="form-check-label visually-hidden" for="{{type}}_all" i18n>All</label>
|
||||
</div>
|
||||
@for (action of PermissionAction | keyvalue; track action) {
|
||||
<div class="col form-check form-check-inline" [ngbPopover]="inheritedWarning" [disablePopover]="!isInherited(type, action.key)" placement="left" triggers="mouseenter:mouseleave">
|
||||
@for (action of PermissionAction | keyvalue: sortActions; track action.key) {
|
||||
<div class="col form-check form-check-inline" [class.invisible]="!isActionSupported(PermissionType[type], action.value)" [ngbPopover]="inheritedWarning" [disablePopover]="!isInherited(type, action.key)" placement="left" triggers="mouseenter:mouseleave">
|
||||
<input type="checkbox" class="form-check-input" id="{{type}}_{{action.key}}" formControlName="{{action.key}}">
|
||||
<label class="form-check-label visually-hidden" for="{{type}}_{{action.key}}">{{action.key}}</label>
|
||||
</div>
|
||||
|
||||
@@ -75,7 +75,6 @@ describe('PermissionsSelectComponent', () => {
|
||||
it('should update on permissions set', () => {
|
||||
component.ngOnInit()
|
||||
component.writeValue(permissions)
|
||||
expect(permissionsChangeResult).toEqual(permissions)
|
||||
expect(component.typesWithAllActions).toContain('Document')
|
||||
})
|
||||
|
||||
@@ -92,13 +91,12 @@ describe('PermissionsSelectComponent', () => {
|
||||
it('disable checkboxes when permissions are inherited', () => {
|
||||
component.ngOnInit()
|
||||
component.inheritedPermissions = inheritedPermissions
|
||||
fixture.detectChanges()
|
||||
expect(component.isInherited('Document', 'Add')).toBeFalsy()
|
||||
expect(component.isInherited('Document')).toBeFalsy()
|
||||
expect(component.isInherited('Tag', 'Change')).toBeTruthy()
|
||||
const input1 = fixture.debugElement.query(By.css('input#Document_Add'))
|
||||
expect(input1.nativeElement.disabled).toBeFalsy()
|
||||
const input2 = fixture.debugElement.query(By.css('input#Tag_Change'))
|
||||
expect(input2.nativeElement.disabled).toBeTruthy()
|
||||
expect(component.form.get('Document').get('Add').disabled).toBeFalsy()
|
||||
expect(component.form.get('Tag').get('Change').disabled).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should exclude history permissions if disabled', () => {
|
||||
@@ -107,4 +105,32 @@ describe('PermissionsSelectComponent', () => {
|
||||
component = fixture.componentInstance
|
||||
expect(component.allowedTypes).not.toContain('History')
|
||||
})
|
||||
|
||||
it('should treat global statistics as view-only', () => {
|
||||
component.ngOnInit()
|
||||
fixture.detectChanges()
|
||||
|
||||
expect(
|
||||
component.isActionSupported(
|
||||
PermissionType.GlobalStatistics,
|
||||
PermissionAction.View
|
||||
)
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
component.isActionSupported(
|
||||
PermissionType.GlobalStatistics,
|
||||
PermissionAction.Add
|
||||
)
|
||||
).toBeFalsy()
|
||||
|
||||
const addInput = fixture.debugElement.query(
|
||||
By.css('input#GlobalStatistics_Add')
|
||||
)
|
||||
const viewInput = fixture.debugElement.query(
|
||||
By.css('input#GlobalStatistics_View')
|
||||
)
|
||||
|
||||
expect(addInput.nativeElement.disabled).toBeTruthy()
|
||||
expect(viewInput.nativeElement.disabled).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { KeyValuePipe } from '@angular/common'
|
||||
import { KeyValue, KeyValuePipe } from '@angular/common'
|
||||
import { Component, forwardRef, inject, Input, OnInit } from '@angular/core'
|
||||
import {
|
||||
AbstractControl,
|
||||
@@ -58,6 +58,13 @@ export class PermissionsSelectComponent
|
||||
|
||||
typesWithAllActions: Set<string> = new Set()
|
||||
|
||||
private readonly actionOrder = [
|
||||
PermissionAction.Add,
|
||||
PermissionAction.Change,
|
||||
PermissionAction.Delete,
|
||||
PermissionAction.View,
|
||||
]
|
||||
|
||||
_inheritedPermissions: string[] = []
|
||||
|
||||
@Input()
|
||||
@@ -86,7 +93,7 @@ export class PermissionsSelectComponent
|
||||
}
|
||||
this.allowedTypes.forEach((type) => {
|
||||
const control = new FormGroup({})
|
||||
for (const action in PermissionAction) {
|
||||
for (const action of Object.keys(PermissionAction)) {
|
||||
control.addControl(action, new FormControl(null))
|
||||
}
|
||||
this.form.addControl(type, control)
|
||||
@@ -106,18 +113,14 @@ export class PermissionsSelectComponent
|
||||
this.permissionsService.getPermissionKeys(permissionStr)
|
||||
|
||||
if (actionKey && typeKey) {
|
||||
if (this.form.get(typeKey)?.get(actionKey)) {
|
||||
this.form
|
||||
.get(typeKey)
|
||||
.get(actionKey)
|
||||
.patchValue(true, { emitEvent: false })
|
||||
}
|
||||
this.form
|
||||
.get(typeKey)
|
||||
?.get(actionKey)
|
||||
?.patchValue(true, { emitEvent: false })
|
||||
}
|
||||
})
|
||||
this.allowedTypes.forEach((type) => {
|
||||
if (
|
||||
Object.values(this.form.get(type).value).every((val) => val == true)
|
||||
) {
|
||||
if (this.typeHasAllActionsSelected(type)) {
|
||||
this.typesWithAllActions.add(type)
|
||||
} else {
|
||||
this.typesWithAllActions.delete(type)
|
||||
@@ -149,12 +152,15 @@ export class PermissionsSelectComponent
|
||||
this.form.valueChanges.subscribe((newValue) => {
|
||||
let permissions = []
|
||||
Object.entries(newValue).forEach(([typeKey, typeValue]) => {
|
||||
// e.g. [Document, { Add: true, View: true ... }]
|
||||
const selectedActions = Object.entries(typeValue).filter(
|
||||
([actionKey, actionValue]) => actionValue == true
|
||||
([actionKey, actionValue]) =>
|
||||
this.isActionSupported(
|
||||
PermissionType[typeKey],
|
||||
PermissionAction[actionKey]
|
||||
) && actionValue == true
|
||||
)
|
||||
|
||||
selectedActions.forEach(([actionKey, actionValue]) => {
|
||||
selectedActions.forEach(([actionKey]) => {
|
||||
permissions.push(
|
||||
(PermissionType[typeKey] as string).replace(
|
||||
'%s',
|
||||
@@ -163,7 +169,7 @@ export class PermissionsSelectComponent
|
||||
)
|
||||
})
|
||||
|
||||
if (selectedActions.length == Object.entries(typeValue).length) {
|
||||
if (this.typeHasAllActionsSelected(typeKey)) {
|
||||
this.typesWithAllActions.add(typeKey)
|
||||
} else {
|
||||
this.typesWithAllActions.delete(typeKey)
|
||||
@@ -174,19 +180,23 @@ export class PermissionsSelectComponent
|
||||
permissions.filter((p) => !this._inheritedPermissions.includes(p))
|
||||
)
|
||||
})
|
||||
|
||||
this.updateDisabledStates()
|
||||
}
|
||||
|
||||
toggleAll(event, type) {
|
||||
const typeGroup = this.form.get(type)
|
||||
if (event.target.checked) {
|
||||
Object.keys(PermissionAction).forEach((action) => {
|
||||
typeGroup.get(action).patchValue(true)
|
||||
Object.keys(PermissionAction)
|
||||
.filter((action) =>
|
||||
this.isActionSupported(PermissionType[type], PermissionAction[action])
|
||||
)
|
||||
.forEach((action) => {
|
||||
typeGroup.get(action).patchValue(event.target.checked)
|
||||
})
|
||||
|
||||
if (this.typeHasAllActionsSelected(type)) {
|
||||
this.typesWithAllActions.add(type)
|
||||
} else {
|
||||
Object.keys(PermissionAction).forEach((action) => {
|
||||
typeGroup.get(action).patchValue(false)
|
||||
})
|
||||
this.typesWithAllActions.delete(type)
|
||||
}
|
||||
}
|
||||
@@ -201,14 +211,21 @@ export class PermissionsSelectComponent
|
||||
)
|
||||
)
|
||||
} else {
|
||||
return Object.values(PermissionAction).every((action) => {
|
||||
return this._inheritedPermissions.includes(
|
||||
this.permissionsService.getPermissionCode(
|
||||
action as PermissionAction,
|
||||
PermissionType[typeKey]
|
||||
return Object.keys(PermissionAction)
|
||||
.filter((action) =>
|
||||
this.isActionSupported(
|
||||
PermissionType[typeKey],
|
||||
PermissionAction[action]
|
||||
)
|
||||
)
|
||||
})
|
||||
.every((action) => {
|
||||
return this._inheritedPermissions.includes(
|
||||
this.permissionsService.getPermissionCode(
|
||||
PermissionAction[action],
|
||||
PermissionType[typeKey]
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,12 +233,49 @@ export class PermissionsSelectComponent
|
||||
this.allowedTypes.forEach((type) => {
|
||||
const control = this.form.get(type)
|
||||
let actionControl: AbstractControl
|
||||
for (const action in PermissionAction) {
|
||||
for (const action of Object.keys(PermissionAction)) {
|
||||
actionControl = control.get(action)
|
||||
if (
|
||||
!this.isActionSupported(
|
||||
PermissionType[type],
|
||||
PermissionAction[action]
|
||||
)
|
||||
) {
|
||||
actionControl.patchValue(false, { emitEvent: false })
|
||||
actionControl.disable({ emitEvent: false })
|
||||
continue
|
||||
}
|
||||
|
||||
this.isInherited(type, action) || this.disabled
|
||||
? actionControl.disable()
|
||||
: actionControl.enable()
|
||||
? actionControl.disable({ emitEvent: false })
|
||||
: actionControl.enable({ emitEvent: false })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
isActionSupported(type: PermissionType, action: PermissionAction): boolean {
|
||||
// Global statistics only supports view permissions
|
||||
if (type === PermissionType.GlobalStatistics) {
|
||||
return action === PermissionAction.View
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private typeHasAllActionsSelected(typeKey: string): boolean {
|
||||
return Object.keys(PermissionAction)
|
||||
.filter((action) =>
|
||||
this.isActionSupported(
|
||||
PermissionType[typeKey],
|
||||
PermissionAction[action]
|
||||
)
|
||||
)
|
||||
.every((action) => this.form.get(typeKey)?.get(action)?.value == true)
|
||||
}
|
||||
|
||||
sortActions = (
|
||||
a: KeyValue<string, PermissionAction>,
|
||||
b: KeyValue<string, PermissionAction>
|
||||
): number =>
|
||||
this.actionOrder.indexOf(a.value) - this.actionOrder.indexOf(b.value)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ export enum PermissionType {
|
||||
CustomField = '%s_customfield',
|
||||
Workflow = '%s_workflow',
|
||||
ProcessedMail = '%s_processedmail',
|
||||
GlobalStatistics = '%s_global_statistics',
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
|
||||
Reference in New Issue
Block a user