Support default permissions for object creation via frontend (#4233)

This commit is contained in:
shamoon
2023-09-21 16:28:22 -07:00
committed by GitHub
parent 9d72d1fc81
commit 02bf0349ca
18 changed files with 653 additions and 284 deletions

View File

@@ -1,18 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog.component'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { SettingsService } from 'src/app/services/settings.service'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
import { EditDialogMode } from '../edit-dialog.component'
import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog.component'
describe('CorrespondentEditDialogComponent', () => {
let component: CorrespondentEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<CorrespondentEditDialogComponent>
beforeEach(async () => {
@@ -36,6 +38,8 @@ describe('CorrespondentEditDialogComponent', () => {
}).compileComponents()
fixture = TestBed.createComponent(CorrespondentEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance
fixture.detectChanges()

View File

@@ -1,18 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog.component'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { SettingsService } from 'src/app/services/settings.service'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
import { EditDialogMode } from '../edit-dialog.component'
import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog.component'
describe('DocumentTypeEditDialogComponent', () => {
let component: DocumentTypeEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<DocumentTypeEditDialogComponent>
beforeEach(async () => {
@@ -36,6 +38,8 @@ describe('DocumentTypeEditDialogComponent', () => {
}).compileComponents()
fixture = TestBed.createComponent(DocumentTypeEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance
fixture.detectChanges()

View File

@@ -1,6 +1,6 @@
import {
HttpClientTestingModule,
HttpTestingController,
HttpClientTestingModule,
} from '@angular/common/http/testing'
import { Component } from '@angular/core'
import {
@@ -16,19 +16,20 @@ import {
ReactiveFormsModule,
} from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { of } from 'rxjs'
import {
DEFAULT_MATCHING_ALGORITHM,
MATCH_AUTO,
MATCH_NONE,
MATCH_ALL,
} from 'src/app/data/matching-model'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { TagService } from 'src/app/services/rest/tag.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
import { EditDialogComponent, EditDialogMode } from './edit-dialog.component'
import {
DEFAULT_MATCHING_ALGORITHM,
MATCH_ALL,
MATCH_AUTO,
MATCH_NONE,
} from 'src/app/data/matching-model'
import { of } from 'rxjs'
import { environment } from 'src/environments/environment'
import { EditDialogComponent, EditDialogMode } from './edit-dialog.component'
@Component({
template: `
@@ -88,6 +89,7 @@ describe('EditDialogComponent', () => {
let component: TestComponent
let fixture: ComponentFixture<TestComponent>
let tagService: TagService
let settingsService: SettingsService
let activeModal: NgbActiveModal
let httpTestingController: HttpTestingController
@@ -110,18 +112,15 @@ describe('EditDialogComponent', () => {
}),
},
},
{
provide: SettingsService,
useValue: {
currentUser,
},
},
SettingsService,
TagService,
],
imports: [HttpClientTestingModule, FormsModule, ReactiveFormsModule],
}).compileComponents()
tagService = TestBed.inject(TagService)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = currentUser
activeModal = TestBed.inject(NgbActiveModal)
httpTestingController = TestBed.inject(HttpTestingController)
@@ -149,7 +148,7 @@ describe('EditDialogComponent', () => {
expect(component.closeEnabled).toBeTruthy()
}))
it('should set default owner when in create mode', () => {
it('should set default owner when in create mode if unset', () => {
component.dialogMode = EditDialogMode.CREATE
component.ngOnInit()
expect(component.objectForm.get('permissions_form').value.owner).toEqual(
@@ -160,6 +159,32 @@ describe('EditDialogComponent', () => {
component.ngOnInit()
})
it('should set default perms when in create mode if set', () => {
component.dialogMode = EditDialogMode.CREATE
settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_OWNER, 11)
settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS, [1, 2])
settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_VIEW_GROUPS, [3])
settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_EDIT_USERS, [4])
settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS, [5])
component.ngOnInit()
expect(component.objectForm.get('permissions_form').value.owner).toEqual(11)
expect(
component.objectForm.get('permissions_form').value.set_permissions
).toEqual({
view: {
users: [1, 2],
groups: [3],
},
change: {
users: [4],
groups: [5],
},
})
// cover optional chaining
component.objectForm.removeControl('permissions_form')
component.ngOnInit()
})
it('should detect if pattern required', () => {
expect(component.patternRequired).toBeFalsy()
component.objectForm.get('matching_algorithm').setValue(MATCH_AUTO)

View File

@@ -14,6 +14,7 @@ import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperle
import { UserService } from 'src/app/services/rest/user.service'
import { PermissionsFormObject } from '../input/permissions/permissions-form/permissions-form.component'
import { SettingsService } from 'src/app/services/settings.service'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
export enum EditDialogMode {
CREATE = 0,
@@ -67,6 +68,31 @@ export abstract class EditDialogComponent<
set_permissions: (this.object as ObjectWithPermissions).permissions,
}
this.objectForm.patchValue(this.object)
} else {
// defaults from settings
this.objectForm.patchValue({
permissions_form: {
owner: this.settingsService.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER),
set_permissions: {
view: {
users: this.settingsService.get(
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS
),
groups: this.settingsService.get(
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_GROUPS
),
},
change: {
users: this.settingsService.get(
SETTINGS_KEYS.DEFAULT_PERMS_EDIT_USERS
),
groups: this.settingsService.get(
SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS
),
},
},
},
})
}
// wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
@@ -76,11 +102,6 @@ export abstract class EditDialogComponent<
this.userService.listAll().subscribe((r) => {
this.users = r.results
if (this.dialogMode === EditDialogMode.CREATE) {
this.objectForm.get('permissions_form')?.setValue({
owner: this.settingsService.currentUser.id,
})
}
})
}

View File

@@ -1,19 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { SettingsService } from 'src/app/services/settings.service'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { GroupEditDialogComponent } from './group-edit-dialog.component'
import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
import { PermissionsSelectComponent } from '../../permissions-select/permissions-select.component'
import { EditDialogMode } from '../edit-dialog.component'
import { GroupEditDialogComponent } from './group-edit-dialog.component'
describe('GroupEditDialogComponent', () => {
let component: GroupEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<GroupEditDialogComponent>
beforeEach(async () => {
@@ -38,6 +40,8 @@ describe('GroupEditDialogComponent', () => {
}).compileComponents()
fixture = TestBed.createComponent(GroupEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance
fixture.detectChanges()

View File

@@ -1,30 +1,32 @@
import {
HttpTestingController,
HttpClientTestingModule,
} from '@angular/common/http/testing'
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import {
HttpClientTestingModule,
HttpTestingController,
} from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { IMAPSecurity } from 'src/app/data/paperless-mail-account'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { MailAccountEditDialogComponent } from './mail-account-edit-dialog.component'
import { PasswordComponent } from '../../input/password/password.component'
import { CheckComponent } from '../../input/check/check.component'
import { IMAPSecurity } from 'src/app/data/paperless-mail-account'
import { SettingsService } from 'src/app/services/settings.service'
import { environment } from 'src/environments/environment'
import { CheckComponent } from '../../input/check/check.component'
import { PasswordComponent } from '../../input/password/password.component'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
import { EditDialogMode } from '../edit-dialog.component'
import { MailAccountEditDialogComponent } from './mail-account-edit-dialog.component'
describe('MailAccountEditDialogComponent', () => {
let component: MailAccountEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<MailAccountEditDialogComponent>
let httpController: HttpTestingController
@@ -53,6 +55,8 @@ describe('MailAccountEditDialogComponent', () => {
httpController = TestBed.inject(HttpTestingController)
fixture = TestBed.createComponent(MailAccountEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance
fixture.detectChanges()

View File

@@ -1,29 +1,31 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { MailRuleEditDialogComponent } from './mail-rule-edit-dialog.component'
import { NumberComponent } from '../../input/number/number.component'
import { TagsComponent } from '../../input/tags/tags.component'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { of } from 'rxjs'
import {
MailAction,
MailMetadataCorrespondentOption,
MailAction,
} from 'src/app/data/paperless-mail-rule'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
import { SettingsService } from 'src/app/services/settings.service'
import { NumberComponent } from '../../input/number/number.component'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../../input/select/select.component'
import { TagsComponent } from '../../input/tags/tags.component'
import { TextComponent } from '../../input/text/text.component'
import { EditDialogMode } from '../edit-dialog.component'
import { MailRuleEditDialogComponent } from './mail-rule-edit-dialog.component'
describe('MailRuleEditDialogComponent', () => {
let component: MailRuleEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<MailRuleEditDialogComponent>
let accountService: MailAccountService
let correspondentService: CorrespondentService
@@ -73,6 +75,8 @@ describe('MailRuleEditDialogComponent', () => {
}).compileComponents()
fixture = TestBed.createComponent(MailRuleEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance
fixture.detectChanges()

View File

@@ -1,19 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { StoragePathEditDialogComponent } from './storage-path-edit-dialog.component'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { SettingsService } from 'src/app/services/settings.service'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
import { EditDialogMode } from '../edit-dialog.component'
import { StoragePathEditDialogComponent } from './storage-path-edit-dialog.component'
describe('StoragePathEditDialogComponent', () => {
let component: StoragePathEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<StoragePathEditDialogComponent>
beforeEach(async () => {
@@ -38,6 +40,8 @@ describe('StoragePathEditDialogComponent', () => {
}).compileComponents()
fixture = TestBed.createComponent(StoragePathEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance
fixture.detectChanges()

View File

@@ -1,20 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { TagEditDialogComponent } from './tag-edit-dialog.component'
import { ColorComponent } from '../../input/color/color.component'
import { SettingsService } from 'src/app/services/settings.service'
import { CheckComponent } from '../../input/check/check.component'
import { ColorComponent } from '../../input/color/color.component'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
import { EditDialogMode } from '../edit-dialog.component'
import { TagEditDialogComponent } from './tag-edit-dialog.component'
describe('TagEditDialogComponent', () => {
let component: TagEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<TagEditDialogComponent>
beforeEach(async () => {
@@ -29,7 +31,7 @@ describe('TagEditDialogComponent', () => {
ColorComponent,
CheckComponent,
],
providers: [NgbActiveModal],
providers: [NgbActiveModal, SettingsService],
imports: [
HttpClientTestingModule,
FormsModule,
@@ -40,6 +42,8 @@ describe('TagEditDialogComponent', () => {
}).compileComponents()
fixture = TestBed.createComponent(TagEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance
fixture.detectChanges()

View File

@@ -1,26 +1,28 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
AbstractControl,
FormsModule,
ReactiveFormsModule,
AbstractControl,
} from '@angular/forms'
import { TextComponent } from '../../input/text/text.component'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { UserEditDialogComponent } from './user-edit-dialog.component'
import { PasswordComponent } from '../../input/password/password.component'
import { PermissionsSelectComponent } from '../../permissions-select/permissions-select.component'
import { GroupService } from 'src/app/services/rest/group.service'
import { of } from 'rxjs'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { GroupService } from 'src/app/services/rest/group.service'
import { SettingsService } from 'src/app/services/settings.service'
import { PasswordComponent } from '../../input/password/password.component'
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
import { PermissionsSelectComponent } from '../../permissions-select/permissions-select.component'
import { EditDialogMode } from '../edit-dialog.component'
import { UserEditDialogComponent } from './user-edit-dialog.component'
describe('UserEditDialogComponent', () => {
let component: UserEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<UserEditDialogComponent>
beforeEach(async () => {
@@ -51,6 +53,7 @@ describe('UserEditDialogComponent', () => {
}),
},
},
SettingsService,
],
imports: [
HttpClientTestingModule,
@@ -62,6 +65,8 @@ describe('UserEditDialogComponent', () => {
}).compileComponents()
fixture = TestBed.createComponent(UserEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance
fixture.detectChanges()

View File

@@ -11,14 +11,14 @@
<form [formGroup]="settingsForm" (ngSubmit)="saveSettings()">
<ul ngbNav #nav="ngbNav" (navChange)="onNavChange($event)" [(activeId)]="activeNavID" class="nav-tabs">
<li [ngbNavItem]="SettingsNavIDs.General">
<li [ngbNavItem]="SettingsNavIDs.General" (mouseover)="maybeInitializeTab(SettingsNavIDs.General)">
<a ngbNavLink i18n>General</a>
<ng-template ngbNavContent>
<h4 i18n>Appearance</h4>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Display language</span>
</div>
<div class="col">
@@ -33,7 +33,7 @@
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Date display</span>
</div>
<div class="col">
@@ -46,7 +46,7 @@
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Date format</span>
</div>
<div class="col">
@@ -68,7 +68,7 @@
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Items per page</span>
</div>
<div class="col">
@@ -84,7 +84,7 @@
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Document editor</span>
</div>
<div class="col">
@@ -95,7 +95,7 @@
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Sidebar</span>
</div>
<div class="col">
@@ -106,7 +106,7 @@
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Dark mode</span>
</div>
<div class="col">
@@ -117,7 +117,7 @@
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Theme Color</span>
</div>
<div class="col col-md-3">
@@ -132,6 +132,82 @@
</div>
</div>
<h4 i18n>Permissions</h4>
<div class="row mb-3">
<div class="offset-md-3 col">
<p i18n>
Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI
</p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Default Owner</span>
</div>
<div class="col-md-4">
<pngx-input-select [items]="users" bindLabel="username" formControlName="defaultPermsOwner" [allowNull]="true"></pngx-input-select>
<small class="form-text text-muted text-end d-block mt-n2" i18n>Objects without an owner can be viewed and edited by all users</small>
</div>
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Default View Permissions</span>
</div>
<div class="col-md-4">
<div class="row">
<div class="col-2">
<span class="d-block pt-1" i18n>Users:</span>
</div>
<div class="col">
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.User }">
<pngx-permissions-user type="view" formControlName="defaultPermsViewUsers"></pngx-permissions-user>
</ng-container>
</div>
</div>
<div class="row">
<div class="col-2">
<span class="d-block pt-1" i18n>Groups:</span>
</div>
<div class="col">
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Group }">
<pngx-permissions-group type="view" formControlName="defaultPermsViewGroups"></pngx-permissions-group>
</ng-container>
</div>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Default Edit Permissions</span>
</div>
<div class="col-md-4">
<div class="row">
<div class="col-2">
<span class="d-block pt-1" i18n>Users:</span>
</div>
<div class="col">
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.User }">
<pngx-permissions-user type="view" formControlName="defaultPermsEditUsers"></pngx-permissions-user>
</ng-container>
</div>
</div>
<div class="row">
<div class="col-2">
<span class="d-block pt-1" i18n>Groups:</span>
</div>
<div class="col">
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Group }">
<pngx-permissions-group type="view" formControlName="defaultPermsEditGroups"></pngx-permissions-group>
</ng-container>
</div>
</div>
<div class="row">
<small class="form-text text-muted text-end d-block" i18n>Edit permissions also grant viewing permissions</small>
</div>
</div>
</div>
<h4 class="mt-4" id="update-checking" i18n>Update checking</h4>
<div class="row mb-3">

View File

@@ -13,10 +13,11 @@ import { RouterTestingModule } from '@angular/router/testing'
import {
NgbModal,
NgbModule,
NgbAlertModule,
NgbNavLink,
NgbModalRef,
NgbAlertModule,
} from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { of, throwError } from 'rxjs'
import { routes } from 'src/app/app-routing.module'
import { PaperlessMailAccount } from 'src/app/data/paperless-mail-account'
@@ -26,6 +27,7 @@ import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { PermissionsService } from 'src/app/services/permissions.service'
import { GroupService } from 'src/app/services/rest/group.service'
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
@@ -41,15 +43,15 @@ import { MailRuleEditDialogComponent } from '../../common/edit-dialog/mail-rule-
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
import { CheckComponent } from '../../common/input/check/check.component'
import { ColorComponent } from '../../common/input/color/color.component'
import { NumberComponent } from '../../common/input/number/number.component'
import { PasswordComponent } from '../../common/input/password/password.component'
import { PermissionsGroupComponent } from '../../common/input/permissions/permissions-group/permissions-group.component'
import { PermissionsUserComponent } from '../../common/input/permissions/permissions-user/permissions-user.component'
import { SelectComponent } from '../../common/input/select/select.component'
import { TagsComponent } from '../../common/input/tags/tags.component'
import { TextComponent } from '../../common/input/text/text.component'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { SettingsComponent } from './settings.component'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { SelectComponent } from '../../common/input/select/select.component'
import { TextComponent } from '../../common/input/text/text.component'
import { PasswordComponent } from '../../common/input/password/password.component'
import { NumberComponent } from '../../common/input/number/number.component'
import { TagsComponent } from '../../common/input/tags/tags.component'
import { NgSelectModule } from '@ng-select/ng-select'
const savedViews = [
{ id: 1, name: 'view1' },
@@ -106,6 +108,8 @@ describe('SettingsComponent', () => {
TagsComponent,
MailAccountEditDialogComponent,
MailRuleEditDialogComponent,
PermissionsUserComponent,
PermissionsGroupComponent,
],
providers: [CustomDatePipe, DatePipe, PermissionsGuard],
imports: [
@@ -125,6 +129,7 @@ describe('SettingsComponent', () => {
viewportScroller = TestBed.inject(ViewportScroller)
toastService = TestBed.inject(ToastService)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
userService = TestBed.inject(UserService)
permissionsService = TestBed.inject(PermissionsService)
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
@@ -247,11 +252,11 @@ describe('SettingsComponent', () => {
)
expect(component.mailAccounts).not.toBeUndefined()
expect(component.users).toBeUndefined()
expect(component.groups).toBeUndefined()
tabButtons[4].nativeElement.dispatchEvent(
new MouseEvent('mouseover', { bubbles: true })
)
expect(component.users).not.toBeUndefined()
expect(component.groups).not.toBeUndefined()
})
it('should support save saved views, show error', () => {
@@ -301,7 +306,7 @@ describe('SettingsComponent', () => {
expect(toastErrorSpy).toHaveBeenCalled()
expect(storeSpy).toHaveBeenCalled()
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
expect(setSpy).toHaveBeenCalledTimes(19)
expect(setSpy).toHaveBeenCalledTimes(24)
// succeed
storeSpy.mockReturnValueOnce(of(true))

View File

@@ -48,6 +48,7 @@ import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
import { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
import {
PermissionAction,
PermissionType,
PermissionsService,
} from 'src/app/services/permissions.service'
@@ -93,6 +94,11 @@ export class SettingsComponent
dateFormat: new FormControl(null),
notesEnabled: new FormControl(null),
updateCheckingEnabled: new FormControl(null),
defaultPermsOwner: new FormControl(null),
defaultPermsViewUsers: new FormControl(null),
defaultPermsViewGroups: new FormControl(null),
defaultPermsEditUsers: new FormControl(null),
defaultPermsEditGroups: new FormControl(null),
notificationsConsumerNewDocument: new FormControl(null),
notificationsConsumerSuccess: new FormControl(null),
@@ -159,6 +165,16 @@ export class SettingsComponent
this.activatedRoute.paramMap.subscribe((paramMap) => {
const section = paramMap.get('section')
if (section === null) {
if (
this.permissionsService.currentUserCan(
PermissionAction.View,
PermissionType.User
)
) {
this.getUsers()
}
}
if (section) {
const navIDKey: string = Object.keys(SettingsNavIDs).find(
(navID) => navID.toLowerCase() == section
@@ -222,6 +238,19 @@ export class SettingsComponent
savedViewsWarnOnUnsavedChange: this.settings.get(
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE
),
defaultPermsOwner: this.settings.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER),
defaultPermsViewUsers: this.settings.get(
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS
),
defaultPermsViewGroups: this.settings.get(
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_GROUPS
),
defaultPermsEditUsers: this.settings.get(
SETTINGS_KEYS.DEFAULT_PERMS_EDIT_USERS
),
defaultPermsEditGroups: this.settings.get(
SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS
),
usersGroup: {},
groupsGroup: {},
savedViews: {},
@@ -256,33 +285,21 @@ export class SettingsComponent
this.initialize(false)
})
} else if (
navID == SettingsNavIDs.UsersGroups &&
(navID == SettingsNavIDs.UsersGroups ||
navID == SettingsNavIDs.General) &&
(!this.users || !this.groups)
) {
this.usersService
if (!this.users) this.getUsers()
this.groupsService
.listAll()
.pipe(first())
.subscribe({
next: (r) => {
this.users = r.results
this.groupsService
.listAll()
.pipe(first())
.subscribe({
next: (r) => {
this.groups = r.results
this.initialize(false)
},
error: (e) => {
this.toastService.showError(
$localize`Error retrieving groups`,
e
)
},
})
this.groups = r.results
this.initialize(false)
},
error: (e) => {
this.toastService.showError($localize`Error retrieving users`, e)
this.toastService.showError($localize`Error retrieving groups`, e)
},
})
} else if (
@@ -322,6 +339,20 @@ export class SettingsComponent
}
}
private getUsers() {
this.usersService
.listAll()
.pipe(first())
.subscribe({
next: (r) => {
this.users = r.results
},
error: (e) => {
this.toastService.showError($localize`Error retrieving users`, e)
},
})
}
initialize(resetSettings: boolean = true) {
this.unsubscribeNotifier.next(true)
@@ -611,6 +642,26 @@ export class SettingsComponent
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE,
this.settingsForm.value.savedViewsWarnOnUnsavedChange
)
this.settings.set(
SETTINGS_KEYS.DEFAULT_PERMS_OWNER,
this.settingsForm.value.defaultPermsOwner
)
this.settings.set(
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS,
this.settingsForm.value.defaultPermsViewUsers
)
this.settings.set(
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_GROUPS,
this.settingsForm.value.defaultPermsViewGroups
)
this.settings.set(
SETTINGS_KEYS.DEFAULT_PERMS_EDIT_USERS,
this.settingsForm.value.defaultPermsEditUsers
)
this.settings.set(
SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS,
this.settingsForm.value.defaultPermsEditGroups
)
this.settings.setLanguage(this.settingsForm.value.displayLanguage)
this.settings
.storeSettings()

View File

@@ -42,6 +42,11 @@ export const SETTINGS_KEYS = {
SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE:
'general-settings:saved-views:warn-on-unsaved-change',
TOUR_COMPLETE: 'general-settings:tour-complete',
DEFAULT_PERMS_OWNER: 'general-settings:permissions:default-owner',
DEFAULT_PERMS_VIEW_USERS: 'general-settings:permissions:default-view-users',
DEFAULT_PERMS_VIEW_GROUPS: 'general-settings:permissions:default-view-groups',
DEFAULT_PERMS_EDIT_USERS: 'general-settings:permissions:default-edit-users',
DEFAULT_PERMS_EDIT_GROUPS: 'general-settings:permissions:default-edit-groups',
}
export const SETTINGS: PaperlessUiSetting[] = [
@@ -150,4 +155,29 @@ export const SETTINGS: PaperlessUiSetting[] = [
type: 'boolean',
default: false,
},
{
key: SETTINGS_KEYS.DEFAULT_PERMS_OWNER,
type: 'number',
default: undefined,
},
{
key: SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS,
type: 'array',
default: [],
},
{
key: SETTINGS_KEYS.DEFAULT_PERMS_VIEW_GROUPS,
type: 'array',
default: [],
},
{
key: SETTINGS_KEYS.DEFAULT_PERMS_EDIT_USERS,
type: 'array',
default: [],
},
{
key: SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS,
type: 'array',
default: [],
},
]

View File

@@ -1,21 +1,25 @@
import { TestBed } from '@angular/core/testing'
import { SettingsService } from './settings.service'
import {
HttpClientTestingModule,
HttpTestingController,
HttpClientTestingModule,
} from '@angular/common/http/testing'
import { RouterTestingModule } from '@angular/router/testing'
import { environment } from 'src/environments/environment'
import { Subscription } from 'rxjs'
import { PaperlessUiSettings } from '../data/paperless-uisettings'
import { SETTINGS_KEYS } from '../data/paperless-uisettings'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { RouterTestingModule } from '@angular/router/testing'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { CookieService } from 'ngx-cookie-service'
import { Subscription } from 'rxjs'
import { environment } from 'src/environments/environment'
import { AppModule } from '../app.module'
import {
PaperlessUiSettings,
SETTINGS_KEYS,
} from '../data/paperless-uisettings'
import { SettingsService } from './settings.service'
describe('SettingsService', () => {
let httpTestingController: HttpTestingController
let settingsService: SettingsService
let cookieService: CookieService
let subscription: Subscription
const ui_settings: PaperlessUiSettings = {
@@ -46,6 +50,13 @@ describe('SettingsService', () => {
saved_views: { warn_on_unsaved_change: true },
notes_enabled: true,
tour_complete: false,
permissions: {
default_owner: null,
default_view_users: [1],
default_view_groups: [2],
default_edit_users: [3],
default_edit_groups: [4],
},
},
permissions: [],
}
@@ -53,7 +64,7 @@ describe('SettingsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [],
providers: [SettingsService],
providers: [SettingsService, CookieService],
imports: [
HttpClientTestingModule,
RouterTestingModule,
@@ -65,6 +76,7 @@ describe('SettingsService', () => {
})
httpTestingController = TestBed.inject(HttpTestingController)
cookieService = TestBed.inject(CookieService)
settingsService = TestBed.inject(SettingsService)
})
@@ -136,7 +148,52 @@ describe('SettingsService', () => {
expect(settingsService.get(SETTINGS_KEYS.THEME_COLOR)).toEqual('#000000')
})
it('updates appearnce settings', () => {
it('sets django cookie for languages', () => {
httpTestingController
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
.flush(ui_settings)
const cookieSetSpy = jest.spyOn(cookieService, 'set')
settingsService.initializeSettings().subscribe(() => {})
const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}ui_settings/`
)
ui_settings.settings['language'] = 'foobar'
req.flush(ui_settings)
expect(cookieSetSpy).toHaveBeenCalledWith('django_language', 'foobar')
const cookieDeleteSpy = jest.spyOn(cookieService, 'delete')
settingsService.setLanguage('')
expect(cookieDeleteSpy).toHaveBeenCalled()
})
it('should support null values for settings if set, undefined if not', () => {
httpTestingController
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
.flush(ui_settings)
expect(settingsService.get('foo')).toEqual(undefined)
expect(settingsService.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER)).toEqual(null)
})
it('should support array values', () => {
httpTestingController
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
.flush(ui_settings)
expect(settingsService.get(SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS)).toEqual(
[1]
)
})
it('should support default permissions values', () => {
delete ui_settings.settings['permissions']
httpTestingController
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
.flush(ui_settings)
expect(settingsService.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER)).toEqual(1)
expect(settingsService.get(SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS)).toEqual(
[]
)
})
it('updates appearance settings', () => {
const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}ui_settings/`
)

View File

@@ -372,7 +372,7 @@ export class SettingsService {
}
private getSettingRawValue(key: string): any {
let value = null
let value = undefined
// parse key:key:key into nested object
const keys = key.replace('general-settings:', '').split(':')
let settingObj = this.settings
@@ -389,12 +389,20 @@ export class SettingsService {
let setting = SETTINGS.find((s) => s.key == key)
if (!setting) {
return null
return undefined
}
let value = this.getSettingRawValue(key)
if (value != null) {
// special case to fallback
if (key === SETTINGS_KEYS.DEFAULT_PERMS_OWNER && value === undefined) {
return this.currentUser.id
}
if (value !== undefined) {
if (value === null) {
return null
}
switch (setting.type) {
case 'boolean':
return JSON.parse(value)
@@ -424,7 +432,7 @@ export class SettingsService {
private settingIsSet(key: string): boolean {
let value = this.getSettingRawValue(key)
return value != null
return value != undefined
}
storeSettings(): Observable<any> {