From c7b46ac861f75ff5e2f906bfce5f1e81b4a55c0e Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 13 Nov 2022 07:23:33 -0800 Subject: [PATCH] skeleton user / group admin dialogs [WIP] --- src-ui/src/app/app.module.ts | 6 + .../correspondent-edit-dialog.component.ts | 9 +- .../document-type-edit-dialog.component.ts | 9 +- .../edit-dialog/edit-dialog.component.ts | 3 +- .../group-edit-dialog.component.html | 19 +++ .../group-edit-dialog.component.scss | 0 .../group-edit-dialog.component.ts | 32 ++++ .../storage-path-edit-dialog.component.ts | 9 +- .../tag-edit-dialog.component.ts | 9 +- .../user-edit-dialog.component.html | 34 ++++ .../user-edit-dialog.component.scss | 0 .../user-edit-dialog.component.ts | 51 ++++++ .../common/input/select/select.component.html | 1 + .../common/input/select/select.component.ts | 3 + .../permissions-select.component.html | 18 ++ .../permissions-select.component.scss | 0 .../permissions-select.component.ts | 79 +++++++++ .../manage/settings/settings.component.html | 76 +++++++++ .../manage/settings/settings.component.ts | 161 +++++++++++++++++- .../src/app/services/permissions.service.ts | 28 ++- src/paperless/views.py | 2 +- 21 files changed, 515 insertions(+), 34 deletions(-) create mode 100644 src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html create mode 100644 src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.scss create mode 100644 src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.ts create mode 100644 src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html create mode 100644 src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.scss create mode 100644 src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts create mode 100644 src-ui/src/app/components/common/permissions-select/permissions-select.component.html create mode 100644 src-ui/src/app/components/common/permissions-select/permissions-select.component.scss create mode 100644 src-ui/src/app/components/common/permissions-select/permissions-select.component.ts diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index cf8fbba96..3d862b06b 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -78,6 +78,9 @@ import { StoragePathEditDialogComponent } from './components/common/edit-dialog/ import { SettingsService } from './services/settings.service' import { TasksComponent } from './components/manage/tasks/tasks.component' import { TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap' +import { UserEditDialogComponent } from './components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component' +import { GroupEditDialogComponent } from './components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component' +import { PermissionsSelectComponent } from './components/common/permissions-select/permissions-select.component' import localeBe from '@angular/common/locales/be' import localeCs from '@angular/common/locales/cs' @@ -183,6 +186,9 @@ function initializeApp(settings: SettingsService) { DocumentAsnComponent, DocumentCommentsComponent, TasksComponent, + UserEditDialogComponent, + GroupEditDialogComponent, + PermissionsSelectComponent, ], imports: [ BrowserModule, diff --git a/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.ts index 86be7414d..7361e5e4b 100644 --- a/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.ts @@ -5,7 +5,6 @@ import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit- import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model' import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent' import { CorrespondentService } from 'src/app/services/rest/correspondent.service' -import { ToastService } from 'src/app/services/toast.service' @Component({ selector: 'app-correspondent-edit-dialog', @@ -13,12 +12,8 @@ import { ToastService } from 'src/app/services/toast.service' styleUrls: ['./correspondent-edit-dialog.component.scss'], }) export class CorrespondentEditDialogComponent extends EditDialogComponent { - constructor( - service: CorrespondentService, - activeModal: NgbActiveModal, - toastService: ToastService - ) { - super(service, activeModal, toastService) + constructor(service: CorrespondentService, activeModal: NgbActiveModal) { + super(service, activeModal) } getCreateTitle() { diff --git a/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.ts index bcd2363fd..d565e66e1 100644 --- a/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.ts @@ -5,7 +5,6 @@ import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit- import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model' import { PaperlessDocumentType } from 'src/app/data/paperless-document-type' import { DocumentTypeService } from 'src/app/services/rest/document-type.service' -import { ToastService } from 'src/app/services/toast.service' @Component({ selector: 'app-document-type-edit-dialog', @@ -13,12 +12,8 @@ import { ToastService } from 'src/app/services/toast.service' styleUrls: ['./document-type-edit-dialog.component.scss'], }) export class DocumentTypeEditDialogComponent extends EditDialogComponent { - constructor( - service: DocumentTypeService, - activeModal: NgbActiveModal, - toastService: ToastService - ) { - super(service, activeModal, toastService) + constructor(service: DocumentTypeService, activeModal: NgbActiveModal) { + super(service, activeModal) } getCreateTitle() { diff --git a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts index 92b16a93d..25262ae4f 100644 --- a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts @@ -14,8 +14,7 @@ export abstract class EditDialogComponent { constructor( private service: AbstractPaperlessService, - private activeModal: NgbActiveModal, - private toastService: ToastService + private activeModal: NgbActiveModal ) {} @Input() diff --git a/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html new file mode 100644 index 000000000..610a254b1 --- /dev/null +++ b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html @@ -0,0 +1,19 @@ +
+ + + +
diff --git a/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.scss b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.ts new file mode 100644 index 000000000..30b1cf684 --- /dev/null +++ b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.ts @@ -0,0 +1,32 @@ +import { Component } from '@angular/core' +import { FormControl, FormGroup } from '@angular/forms' +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' +import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' +import { PaperlessGroup } from 'src/app/data/paperless-group' +import { GroupService } from 'src/app/services/rest/group.service' + +@Component({ + selector: 'app-group-edit-dialog', + templateUrl: './group-edit-dialog.component.html', + styleUrls: ['./group-edit-dialog.component.scss'], +}) +export class GroupEditDialogComponent extends EditDialogComponent { + constructor(service: GroupService, activeModal: NgbActiveModal) { + super(service, activeModal) + } + + getCreateTitle() { + return $localize`Create new user group` + } + + getEditTitle() { + return $localize`Edit user group` + } + + getForm(): FormGroup { + return new FormGroup({ + name: new FormControl(''), + permissions: new FormControl(''), + }) + } +} diff --git a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts index 54a91cb88..6be9dfe28 100644 --- a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts @@ -5,7 +5,6 @@ import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit- import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model' import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path' import { StoragePathService } from 'src/app/services/rest/storage-path.service' -import { ToastService } from 'src/app/services/toast.service' @Component({ selector: 'app-storage-path-edit-dialog', @@ -13,12 +12,8 @@ import { ToastService } from 'src/app/services/toast.service' styleUrls: ['./storage-path-edit-dialog.component.scss'], }) export class StoragePathEditDialogComponent extends EditDialogComponent { - constructor( - service: StoragePathService, - activeModal: NgbActiveModal, - toastService: ToastService - ) { - super(service, activeModal, toastService) + constructor(service: StoragePathService, activeModal: NgbActiveModal) { + super(service, activeModal) } get pathHint() { diff --git a/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.ts index 18d476dfe..db106d990 100644 --- a/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.ts @@ -4,7 +4,6 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' import { PaperlessTag } from 'src/app/data/paperless-tag' import { TagService } from 'src/app/services/rest/tag.service' -import { ToastService } from 'src/app/services/toast.service' import { randomColor } from 'src/app/utils/color' import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model' @@ -14,12 +13,8 @@ import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model' styleUrls: ['./tag-edit-dialog.component.scss'], }) export class TagEditDialogComponent extends EditDialogComponent { - constructor( - service: TagService, - activeModal: NgbActiveModal, - toastService: ToastService - ) { - super(service, activeModal, toastService) + constructor(service: TagService, activeModal: NgbActiveModal) { + super(service, activeModal) } getCreateTitle() { diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html new file mode 100644 index 000000000..f182a1339 --- /dev/null +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html @@ -0,0 +1,34 @@ +
+ + + +
diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.scss b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts new file mode 100644 index 000000000..c136da2a3 --- /dev/null +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts @@ -0,0 +1,51 @@ +import { Component } from '@angular/core' +import { FormControl, FormGroup } from '@angular/forms' +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' +import { first } from 'rxjs' +import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' +import { PaperlessGroup } from 'src/app/data/paperless-group' +import { PaperlessUser } from 'src/app/data/paperless-user' +import { GroupService } from 'src/app/services/rest/group.service' +import { UserService } from 'src/app/services/rest/user.service' + +@Component({ + selector: 'app-user-edit-dialog', + templateUrl: './user-edit-dialog.component.html', + styleUrls: ['./user-edit-dialog.component.scss'], +}) +export class UserEditDialogComponent extends EditDialogComponent { + groups: PaperlessGroup[] + + constructor( + service: UserService, + activeModal: NgbActiveModal, + groupsService: GroupService + ) { + super(service, activeModal) + + groupsService + .listAll() + .pipe(first()) + .subscribe((result) => (this.groups = result.results)) + } + + getCreateTitle() { + return $localize`Create new user account` + } + + getEditTitle() { + return $localize`Edit user account` + } + + getForm(): FormGroup { + return new FormGroup({ + username: new FormControl(''), + first_name: new FormControl(''), + last_name: new FormControl(''), + is_active: new FormControl(''), + is_superuser: new FormControl(''), + groups: new FormControl(''), + permissions: new FormControl(''), + }) + } +} diff --git a/src-ui/src/app/components/common/input/select/select.component.html b/src-ui/src/app/components/common/input/select/select.component.html index b111e1656..83e642bed 100644 --- a/src-ui/src/app/components/common/input/select/select.component.html +++ b/src-ui/src/app/components/common/input/select/select.component.html @@ -11,6 +11,7 @@ addTagText="Add item" i18n-addTagText="Used for both types, correspondents, storage paths" [placeholder]="placeholder" + [multiple]="multiple" bindLabel="name" bindValue="id" (change)="onChange(value)" diff --git a/src-ui/src/app/components/common/input/select/select.component.ts b/src-ui/src/app/components/common/input/select/select.component.ts index d1a3d6e5c..9ae361387 100644 --- a/src-ui/src/app/components/common/input/select/select.component.ts +++ b/src-ui/src/app/components/common/input/select/select.component.ts @@ -44,6 +44,9 @@ export class SelectComponent extends AbstractInputComponent { @Input() placeholder: string + @Input() + multiple: boolean = false + @Output() createNew = new EventEmitter() diff --git a/src-ui/src/app/components/common/permissions-select/permissions-select.component.html b/src-ui/src/app/components/common/permissions-select/permissions-select.component.html new file mode 100644 index 000000000..93c326eae --- /dev/null +++ b/src-ui/src/app/components/common/permissions-select/permissions-select.component.html @@ -0,0 +1,18 @@ +
+ +
    +
  • + {{type.key}}: + +
    + + +
    + +
    + + +
    +
  • +
+
diff --git a/src-ui/src/app/components/common/permissions-select/permissions-select.component.scss b/src-ui/src/app/components/common/permissions-select/permissions-select.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src-ui/src/app/components/common/permissions-select/permissions-select.component.ts b/src-ui/src/app/components/common/permissions-select/permissions-select.component.ts new file mode 100644 index 000000000..1aa9f3f81 --- /dev/null +++ b/src-ui/src/app/components/common/permissions-select/permissions-select.component.ts @@ -0,0 +1,79 @@ +import { Component, forwardRef, Input, OnInit } from '@angular/core' +import { + ControlValueAccessor, + FormControl, + FormGroup, + NG_VALUE_ACCESSOR, +} from '@angular/forms' +import { + PermissionAction, + PermissionsService, + PermissionType, +} from 'src/app/services/permissions.service' +import { AbstractInputComponent } from '../input/abstract-input' + +@Component({ + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => PermissionsSelectComponent), + multi: true, + }, + ], + selector: 'app-permissions-select', + templateUrl: './permissions-select.component.html', + styleUrls: ['./permissions-select.component.scss'], +}) +export class PermissionsSelectComponent + implements OnInit, ControlValueAccessor +{ + PermissionType = PermissionType + PermissionAction = PermissionAction + + @Input() + title: string = 'Permissions' + + permissions: string[] + + form = new FormGroup({}) + + constructor(private readonly permissionsService: PermissionsService) { + for (const type in PermissionType) { + const control = new FormGroup({}) + control.addControl('all', new FormControl(null)) + for (const action in PermissionAction) { + control.addControl(action, new FormControl(null)) + } + this.form.addControl(type, control) + } + } + + writeValue(permissions: string[]): void { + this.permissions = permissions + this.permissions.forEach((permissionStr) => { + const { actionKey, typeKey } = + this.permissionsService.getPermissionKeys(permissionStr) + + if (actionKey && typeKey) { + if (this.form.get(typeKey)?.get(actionKey)) { + this.form.get(typeKey).get(actionKey).setValue(true) + } + } + }) + } + registerOnChange(fn: any): void { + throw new Error('Method not implemented.') + } + registerOnTouched(fn: any): void { + throw new Error('Method not implemented.') + } + setDisabledState?(isDisabled: boolean): void { + throw new Error('Method not implemented.') + } + + ngOnInit(): void {} + + isAll(key: string): boolean { + return this.form.get(key).get('all').value == true + } +} diff --git a/src-ui/src/app/components/manage/settings/settings.component.html b/src-ui/src/app/components/manage/settings/settings.component.html index 873933b02..51f44b666 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.html +++ b/src-ui/src/app/components/manage/settings/settings.component.html @@ -221,6 +221,82 @@ + +
  • + Users & Groups + + + +

    + Users + +

    +
      + +
    • +
      +
      Username
      +
      Name
      +
      Groups
      +
      Actions
      +
      +
    • + +
    • +
      +
      +
      {{user.first_name}} {{user.last_name}}
      +
      {{user.groups}}
      +
      +
      + + +
      +
      +
      +
    • +
    + +

    + Groups + +

    +
      + +
    • +
      +
      Name
      +
      +
      +
      Actions
      +
      +
    • + +
    • +
      +
      +
      +
      +
      +
      + + +
      +
      +
      +
    • +
    + +
    No groups defined
    +
    + +
    +
    +
    Loading...
    +
    + +
    +
  • diff --git a/src-ui/src/app/components/manage/settings/settings.component.ts b/src-ui/src/app/components/manage/settings/settings.component.ts index 8b24fa38a..b1cb33baf 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -30,8 +30,15 @@ import { ActivatedRoute } from '@angular/router' import { ViewportScroller } from '@angular/common' import { TourService } from 'ngx-ui-tour-ng-bootstrap' import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component' -import { NgbNavChangeEvent } from '@ng-bootstrap/ng-bootstrap' +import { NgbModal, NgbNavChangeEvent } from '@ng-bootstrap/ng-bootstrap' import { Results } from 'src/app/data/results' +import { UserService } from 'src/app/services/rest/user.service' +import { GroupService } from 'src/app/services/rest/group.service' +import { PaperlessUser } from 'src/app/data/paperless-user' +import { PaperlessGroup } from 'src/app/data/paperless-group' +import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component' +import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component' +import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component' enum SettingsNavIDs { General = 1, @@ -54,6 +61,8 @@ export class SettingsComponent activeNavID: number savedViewGroup = new FormGroup({}) + usersGroup = new FormGroup({}) + groupsGroup = new FormGroup({}) settingsForm = new FormGroup({ bulkEditConfirmationDialogs: new FormControl(null), @@ -75,6 +84,8 @@ export class SettingsComponent notificationsConsumerSuppressOnDashboard: new FormControl(null), commentsEnabled: new FormControl(null), updateCheckingEnabled: new FormControl(null), + usersGroup: this.usersGroup, + groupsGroup: this.groupsGroup, }) savedViews: PaperlessSavedView[] @@ -86,6 +97,9 @@ export class SettingsComponent unsubscribeNotifier: Subject = new Subject() savePending: boolean = false + users: PaperlessUser[] + groups: PaperlessGroup[] + get computedDateLocale(): string { return ( this.settingsForm.value.dateLocale || @@ -102,7 +116,10 @@ export class SettingsComponent @Inject(LOCALE_ID) public currentLocale: string, private viewportScroller: ViewportScroller, private activatedRoute: ActivatedRoute, - public readonly tourService: TourService + public readonly tourService: TourService, + private usersService: UserService, + private groupsService: GroupService, + private modalService: NgbModal ) { super() this.settings.settingsSaved.subscribe(() => { @@ -159,6 +176,8 @@ export class SettingsComponent updateCheckingEnabled: this.settings.get( SETTINGS_KEYS.UPDATE_CHECKING_ENABLED ), + usersGroup: {}, + groupsGroup: {}, } } @@ -176,6 +195,18 @@ export class SettingsComponent this.savedViews = r.results this.initialize() }) + } else if ( + (navID == SettingsNavIDs.UsersGroups && !this.users) || + !this.groups + ) { + this.usersService.listAll().subscribe((r) => { + this.users = r.results + + this.groupsService.listAll().subscribe((r) => { + this.groups = r.results + this.initialize() + }) + }) } } @@ -204,6 +235,50 @@ export class SettingsComponent } } + if (this.users && this.groups) { + for (let user of this.users) { + storeData.usersGroup[user.id.toString()] = { + id: user.id, + username: user.username, + first_name: user.first_name, + last_name: user.last_name, + is_active: user.is_active, + is_superuser: user.is_superuser, + groups: user.groups, + permissions: user.permissions, + } + this.usersGroup.addControl( + user.id.toString(), + new FormGroup({ + id: new FormControl(null), + username: new FormControl(null), + first_name: new FormControl(null), + last_name: new FormControl(null), + is_active: new FormControl(null), + is_superuser: new FormControl(null), + groups: new FormControl(null), + permissions: new FormControl(null), + }) + ) + } + + for (let group of this.groups) { + storeData.groupsGroup[group.id.toString()] = { + id: group.id, + name: group.name, + permissions: group.permissions, + } + this.groupsGroup.addControl( + group.id.toString(), + new FormGroup({ + id: new FormControl(null), + name: new FormControl(null), + permissions: new FormControl(null), + }) + ) + } + } + this.store = new BehaviorSubject(storeData) this.storeSub = this.store.asObservable().subscribe((state) => { @@ -400,4 +475,86 @@ export class SettingsComponent clearThemeColor() { this.settingsForm.get('themeColor').patchValue('') } + + editUser(user: PaperlessUser) { + var modal = this.modalService.open(UserEditDialogComponent, { + backdrop: 'static', + size: 'xl', + }) + modal.componentInstance.dialogMode = user ? 'edit' : 'create' + modal.componentInstance.object = user + modal.componentInstance.success + .pipe(takeUntil(this.unsubscribeNotifier)) + .subscribe({ + next: (newUser) => { + this.toastService.showInfo( + $localize`Saved user "${newUser.username}".` + ) + this.usersService.listAll().subscribe((r) => { + this.users = r.results + this.initialize() + }) + }, + error: (e) => { + this.toastService.showError( + $localize`Error saving user: ${e.toString()}.` + ) + }, + }) + } + + deleteUser(user: PaperlessUser) { + let modal = this.modalService.open(ConfirmDialogComponent, { + backdrop: 'static', + }) + modal.componentInstance.title = $localize`Confirm delete user account` + modal.componentInstance.messageBold = $localize`This operation will permanently this user account.` + modal.componentInstance.message = $localize`This operation cannot be undone.` + modal.componentInstance.btnClass = 'btn-danger' + modal.componentInstance.btnCaption = $localize`Proceed` + modal.componentInstance.confirmClicked.subscribe(() => { + modal.componentInstance.buttonsEnabled = false + this.usersService.delete(user) + }) + } + + editGroup(group: PaperlessGroup) { + var modal = this.modalService.open(GroupEditDialogComponent, { + backdrop: 'static', + size: 'lg', + }) + modal.componentInstance.dialogMode = group ? 'edit' : 'create' + modal.componentInstance.object = group + modal.componentInstance.success + .pipe(takeUntil(this.unsubscribeNotifier)) + .subscribe({ + next: (newGroup) => { + this.toastService.showInfo($localize`Saved group "${newGroup.name}".`) + this.groupsService.listAll().subscribe((r) => { + this.groups = r.results + this.initialize() + }) + }, + error: (e) => { + this.toastService.showError( + $localize`Error saving group: ${e.toString()}.` + ) + }, + }) + } + + deleteGroup(group: PaperlessGroup) { + let modal = this.modalService.open(ConfirmDialogComponent, { + backdrop: 'static', + }) + modal.componentInstance.title = $localize`Confirm delete user group` + modal.componentInstance.messageBold = $localize`This operation will permanently this user group.` + modal.componentInstance.message = $localize`This operation cannot be undone.` + modal.componentInstance.btnClass = 'btn-danger' + modal.componentInstance.btnCaption = $localize`Proceed` + modal.componentInstance.confirmClicked.subscribe(() => { + modal.componentInstance.buttonsEnabled = false + this.groupsService.delete(group) + }) + } } diff --git a/src-ui/src/app/services/permissions.service.ts b/src-ui/src/app/services/permissions.service.ts index 94c8ef828..23fbb47ca 100644 --- a/src-ui/src/app/services/permissions.service.ts +++ b/src-ui/src/app/services/permissions.service.ts @@ -20,7 +20,7 @@ export enum PermissionType { Log = 'admin.%s_logentry', MailAccount = 'paperless_mail.%s_mailaccount', MailRule = 'paperless_mail.%s_mailrule', - Auth = 'auth.%s_user', + User = 'auth.%s_user', Admin = 'admin.%s_logentry', } @@ -46,4 +46,30 @@ export class PermissionsService { private getPermissionCode(permission: PaperlessPermission): string { return permission.type.replace('%s', permission.action) } + + public getPermissionKeys(permissionStr: string): { + actionKey: string + typeKey: string + } { + const matches = permissionStr.match(/\.(.+)_/) + let typeKey + let actionKey + if (matches?.length > 0) { + const action = matches[1] + const actionIndex = Object.values(PermissionAction).indexOf( + action as PermissionAction + ) + if (actionIndex > -1) { + actionKey = Object.keys(PermissionAction)[actionIndex] + } + const typeIndex = Object.values(PermissionType).indexOf( + permissionStr.replace(action, '%s') as PermissionType + ) + if (typeIndex > -1) { + typeKey = Object.keys(PermissionType)[typeIndex] + } + } + + return { actionKey, typeKey } + } } diff --git a/src/paperless/views.py b/src/paperless/views.py index 9894c3006..f116385ba 100644 --- a/src/paperless/views.py +++ b/src/paperless/views.py @@ -38,7 +38,7 @@ class FaviconView(View): class UserViewSet(ModelViewSet): model = User - queryset = User.objects.order_by(Lower("username")) + queryset = User.objects.exclude(username="consumer").order_by(Lower("username")) serializer_class = UserSerializer pagination_class = StandardPagination