Merge branch 'dev' into feature-consumption-templates

This commit is contained in:
shamoon 2023-09-21 19:57:36 -07:00
commit 05b1a4fc01
17 changed files with 475 additions and 162 deletions

View File

@ -247,6 +247,13 @@ do not have an owner set.
Note that superusers have access to all objects. Note that superusers have access to all objects.
### Default permissions
Default permissions for documents can be set using consumption templates.
For objects created via the web UI (tags, doc types, etc.) the default is to set the current user
as owner and no extra permissions, but you explicitly set these under Settings > Permissions.
### Users and Groups ### Users and Groups
Paperless-ngx versions after 1.14.0 allow creating and editing users and groups via the 'frontend' UI. Paperless-ngx versions after 1.14.0 allow creating and editing users and groups via the 'frontend' UI.

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 { 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 { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component' import { SettingsService } from 'src/app/services/settings.service'
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 { 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', () => { describe('CorrespondentEditDialogComponent', () => {
let component: CorrespondentEditDialogComponent let component: CorrespondentEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<CorrespondentEditDialogComponent> let fixture: ComponentFixture<CorrespondentEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
@ -36,6 +38,8 @@ describe('CorrespondentEditDialogComponent', () => {
}).compileComponents() }).compileComponents()
fixture = TestBed.createComponent(CorrespondentEditDialogComponent) fixture = TestBed.createComponent(CorrespondentEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance component = fixture.componentInstance
fixture.detectChanges() 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 { 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 { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component' import { SettingsService } from 'src/app/services/settings.service'
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 { 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', () => { describe('DocumentTypeEditDialogComponent', () => {
let component: DocumentTypeEditDialogComponent let component: DocumentTypeEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<DocumentTypeEditDialogComponent> let fixture: ComponentFixture<DocumentTypeEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
@ -36,6 +38,8 @@ describe('DocumentTypeEditDialogComponent', () => {
}).compileComponents() }).compileComponents()
fixture = TestBed.createComponent(DocumentTypeEditDialogComponent) fixture = TestBed.createComponent(DocumentTypeEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance component = fixture.componentInstance
fixture.detectChanges() fixture.detectChanges()

View File

@ -1,6 +1,6 @@
import { import {
HttpClientTestingModule,
HttpTestingController, HttpTestingController,
HttpClientTestingModule,
} from '@angular/common/http/testing' } from '@angular/common/http/testing'
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { import {
@ -16,19 +16,20 @@ import {
ReactiveFormsModule, ReactiveFormsModule,
} from '@angular/forms' } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' 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 { 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 { TagService } from 'src/app/services/rest/tag.service'
import { UserService } from 'src/app/services/rest/user.service' import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.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 { environment } from 'src/environments/environment'
import { EditDialogComponent, EditDialogMode } from './edit-dialog.component'
@Component({ @Component({
template: ` template: `
@ -88,6 +89,7 @@ describe('EditDialogComponent', () => {
let component: TestComponent let component: TestComponent
let fixture: ComponentFixture<TestComponent> let fixture: ComponentFixture<TestComponent>
let tagService: TagService let tagService: TagService
let settingsService: SettingsService
let activeModal: NgbActiveModal let activeModal: NgbActiveModal
let httpTestingController: HttpTestingController let httpTestingController: HttpTestingController
@ -110,18 +112,15 @@ describe('EditDialogComponent', () => {
}), }),
}, },
}, },
{ SettingsService,
provide: SettingsService,
useValue: {
currentUser,
},
},
TagService, TagService,
], ],
imports: [HttpClientTestingModule, FormsModule, ReactiveFormsModule], imports: [HttpClientTestingModule, FormsModule, ReactiveFormsModule],
}).compileComponents() }).compileComponents()
tagService = TestBed.inject(TagService) tagService = TestBed.inject(TagService)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = currentUser
activeModal = TestBed.inject(NgbActiveModal) activeModal = TestBed.inject(NgbActiveModal)
httpTestingController = TestBed.inject(HttpTestingController) httpTestingController = TestBed.inject(HttpTestingController)
@ -149,7 +148,7 @@ describe('EditDialogComponent', () => {
expect(component.closeEnabled).toBeTruthy() 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.dialogMode = EditDialogMode.CREATE
component.ngOnInit() component.ngOnInit()
expect(component.objectForm.get('permissions_form').value.owner).toEqual( expect(component.objectForm.get('permissions_form').value.owner).toEqual(
@ -160,6 +159,32 @@ describe('EditDialogComponent', () => {
component.ngOnInit() 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', () => { it('should detect if pattern required', () => {
expect(component.patternRequired).toBeFalsy() expect(component.patternRequired).toBeFalsy()
component.objectForm.get('matching_algorithm').setValue(MATCH_AUTO) 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 { UserService } from 'src/app/services/rest/user.service'
import { PermissionsFormObject } from '../input/permissions/permissions-form/permissions-form.component' import { PermissionsFormObject } from '../input/permissions/permissions-form/permissions-form.component'
import { SettingsService } from 'src/app/services/settings.service' import { SettingsService } from 'src/app/services/settings.service'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
export enum EditDialogMode { export enum EditDialogMode {
CREATE = 0, CREATE = 0,
@ -67,6 +68,31 @@ export abstract class EditDialogComponent<
set_permissions: (this.object as ObjectWithPermissions).permissions, set_permissions: (this.object as ObjectWithPermissions).permissions,
} }
this.objectForm.patchValue(this.object) 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 // 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.userService.listAll().subscribe((r) => {
this.users = r.results 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 { 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 { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component' import { SettingsService } from 'src/app/services/settings.service'
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 { 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 { PermissionsSelectComponent } from '../../permissions-select/permissions-select.component'
import { EditDialogMode } from '../edit-dialog.component'
import { GroupEditDialogComponent } from './group-edit-dialog.component'
describe('GroupEditDialogComponent', () => { describe('GroupEditDialogComponent', () => {
let component: GroupEditDialogComponent let component: GroupEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<GroupEditDialogComponent> let fixture: ComponentFixture<GroupEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
@ -38,6 +40,8 @@ describe('GroupEditDialogComponent', () => {
}).compileComponents() }).compileComponents()
fixture = TestBed.createComponent(GroupEditDialogComponent) fixture = TestBed.createComponent(GroupEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance component = fixture.componentInstance
fixture.detectChanges() fixture.detectChanges()

View File

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

View File

@ -1,30 +1,32 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientTestingModule } from '@angular/common/http/testing' import { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component' import { ComponentFixture, TestBed } from '@angular/core/testing'
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 { 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 { 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 { of } from 'rxjs'
import { import {
MailAction,
MailMetadataCorrespondentOption, MailMetadataCorrespondentOption,
MailAction,
} from 'src/app/data/paperless-mail-rule' } 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 { CheckComponent } from '../../input/check/check.component' import { CheckComponent } from '../../input/check/check.component'
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', () => { describe('MailRuleEditDialogComponent', () => {
let component: MailRuleEditDialogComponent let component: MailRuleEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<MailRuleEditDialogComponent> let fixture: ComponentFixture<MailRuleEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
@ -72,6 +74,8 @@ describe('MailRuleEditDialogComponent', () => {
}).compileComponents() }).compileComponents()
fixture = TestBed.createComponent(MailRuleEditDialogComponent) fixture = TestBed.createComponent(MailRuleEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance component = fixture.componentInstance
fixture.detectChanges() 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 { 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 { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.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 { 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', () => { describe('StoragePathEditDialogComponent', () => {
let component: StoragePathEditDialogComponent let component: StoragePathEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<StoragePathEditDialogComponent> let fixture: ComponentFixture<StoragePathEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
@ -38,6 +40,8 @@ describe('StoragePathEditDialogComponent', () => {
}).compileComponents() }).compileComponents()
fixture = TestBed.createComponent(StoragePathEditDialogComponent) fixture = TestBed.createComponent(StoragePathEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance component = fixture.componentInstance
fixture.detectChanges() 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 { 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 { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SelectComponent } from '../../input/select/select.component' import { SettingsService } from 'src/app/services/settings.service'
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 { CheckComponent } from '../../input/check/check.component' 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', () => { describe('TagEditDialogComponent', () => {
let component: TagEditDialogComponent let component: TagEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<TagEditDialogComponent> let fixture: ComponentFixture<TagEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
@ -29,7 +31,7 @@ describe('TagEditDialogComponent', () => {
ColorComponent, ColorComponent,
CheckComponent, CheckComponent,
], ],
providers: [NgbActiveModal], providers: [NgbActiveModal, SettingsService],
imports: [ imports: [
HttpClientTestingModule, HttpClientTestingModule,
FormsModule, FormsModule,
@ -40,6 +42,8 @@ describe('TagEditDialogComponent', () => {
}).compileComponents() }).compileComponents()
fixture = TestBed.createComponent(TagEditDialogComponent) fixture = TestBed.createComponent(TagEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance component = fixture.componentInstance
fixture.detectChanges() 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 { HttpClientTestingModule } from '@angular/common/http/testing'
import { EditDialogMode } from '../edit-dialog.component' import { ComponentFixture, TestBed } from '@angular/core/testing'
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 { import {
AbstractControl,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
AbstractControl,
} from '@angular/forms' } 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 { 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 { 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', () => { describe('UserEditDialogComponent', () => {
let component: UserEditDialogComponent let component: UserEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<UserEditDialogComponent> let fixture: ComponentFixture<UserEditDialogComponent>
beforeEach(async () => { beforeEach(async () => {
@ -51,6 +53,7 @@ describe('UserEditDialogComponent', () => {
}), }),
}, },
}, },
SettingsService,
], ],
imports: [ imports: [
HttpClientTestingModule, HttpClientTestingModule,
@ -62,6 +65,8 @@ describe('UserEditDialogComponent', () => {
}).compileComponents() }).compileComponents()
fixture = TestBed.createComponent(UserEditDialogComponent) fixture = TestBed.createComponent(UserEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance component = fixture.componentInstance
fixture.detectChanges() fixture.detectChanges()

View File

@ -11,14 +11,14 @@
<form [formGroup]="settingsForm" (ngSubmit)="saveSettings()"> <form [formGroup]="settingsForm" (ngSubmit)="saveSettings()">
<ul ngbNav #nav="ngbNav" (navChange)="onNavChange($event)" [(activeId)]="activeNavID" class="nav-tabs"> <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> <a ngbNavLink i18n>General</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
<h4 i18n>Appearance</h4> <h4 i18n>Appearance</h4>
<div class="row mb-3"> <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> <span i18n>Display language</span>
</div> </div>
<div class="col"> <div class="col">
@ -33,7 +33,7 @@
</div> </div>
<div class="row mb-3"> <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> <span i18n>Date display</span>
</div> </div>
<div class="col"> <div class="col">
@ -46,7 +46,7 @@
</div> </div>
<div class="row mb-3"> <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> <span i18n>Date format</span>
</div> </div>
<div class="col"> <div class="col">
@ -68,7 +68,7 @@
</div> </div>
<div class="row mb-3"> <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> <span i18n>Items per page</span>
</div> </div>
<div class="col"> <div class="col">
@ -84,7 +84,7 @@
</div> </div>
<div class="row mb-3"> <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> <span i18n>Document editor</span>
</div> </div>
<div class="col"> <div class="col">
@ -95,7 +95,7 @@
</div> </div>
<div class="row mb-3"> <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> <span i18n>Sidebar</span>
</div> </div>
<div class="col"> <div class="col">
@ -106,7 +106,7 @@
</div> </div>
<div class="row mb-3"> <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> <span i18n>Dark mode</span>
</div> </div>
<div class="col"> <div class="col">
@ -117,7 +117,7 @@
</div> </div>
<div class="row mb-3"> <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> <span i18n>Theme Color</span>
</div> </div>
<div class="col col-md-3"> <div class="col col-md-3">
@ -132,6 +132,82 @@
</div> </div>
</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> <h4 class="mt-4" id="update-checking" i18n>Update checking</h4>
<div class="row mb-3"> <div class="row mb-3">

View File

@ -13,10 +13,11 @@ import { RouterTestingModule } from '@angular/router/testing'
import { import {
NgbModal, NgbModal,
NgbModule, NgbModule,
NgbAlertModule,
NgbNavLink, NgbNavLink,
NgbModalRef, NgbModalRef,
NgbAlertModule,
} from '@ng-bootstrap/ng-bootstrap' } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { of, throwError } from 'rxjs' import { of, throwError } from 'rxjs'
import { routes } from 'src/app/app-routing.module' import { routes } from 'src/app/app-routing.module'
import { PaperlessMailAccount } from 'src/app/data/paperless-mail-account' 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 { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe' 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 { PermissionsService } from 'src/app/services/permissions.service'
import { GroupService } from 'src/app/services/rest/group.service' import { GroupService } from 'src/app/services/rest/group.service'
import { MailAccountService } from 'src/app/services/rest/mail-account.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 { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
import { CheckComponent } from '../../common/input/check/check.component' import { CheckComponent } from '../../common/input/check/check.component'
import { ColorComponent } from '../../common/input/color/color.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 { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { SettingsComponent } from './settings.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 = [ const savedViews = [
{ id: 1, name: 'view1' }, { id: 1, name: 'view1' },
@ -106,6 +108,8 @@ describe('SettingsComponent', () => {
TagsComponent, TagsComponent,
MailAccountEditDialogComponent, MailAccountEditDialogComponent,
MailRuleEditDialogComponent, MailRuleEditDialogComponent,
PermissionsUserComponent,
PermissionsGroupComponent,
], ],
providers: [CustomDatePipe, DatePipe, PermissionsGuard], providers: [CustomDatePipe, DatePipe, PermissionsGuard],
imports: [ imports: [
@ -125,6 +129,7 @@ describe('SettingsComponent', () => {
viewportScroller = TestBed.inject(ViewportScroller) viewportScroller = TestBed.inject(ViewportScroller)
toastService = TestBed.inject(ToastService) toastService = TestBed.inject(ToastService)
settingsService = TestBed.inject(SettingsService) settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
userService = TestBed.inject(UserService) userService = TestBed.inject(UserService)
permissionsService = TestBed.inject(PermissionsService) permissionsService = TestBed.inject(PermissionsService)
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
@ -247,11 +252,11 @@ describe('SettingsComponent', () => {
) )
expect(component.mailAccounts).not.toBeUndefined() expect(component.mailAccounts).not.toBeUndefined()
expect(component.users).toBeUndefined() expect(component.groups).toBeUndefined()
tabButtons[4].nativeElement.dispatchEvent( tabButtons[4].nativeElement.dispatchEvent(
new MouseEvent('mouseover', { bubbles: true }) new MouseEvent('mouseover', { bubbles: true })
) )
expect(component.users).not.toBeUndefined() expect(component.groups).not.toBeUndefined()
}) })
it('should support save saved views, show error', () => { it('should support save saved views, show error', () => {
@ -301,7 +306,7 @@ describe('SettingsComponent', () => {
expect(toastErrorSpy).toHaveBeenCalled() expect(toastErrorSpy).toHaveBeenCalled()
expect(storeSpy).toHaveBeenCalled() expect(storeSpy).toHaveBeenCalled()
expect(appearanceSettingsSpy).not.toHaveBeenCalled() expect(appearanceSettingsSpy).not.toHaveBeenCalled()
expect(setSpy).toHaveBeenCalledTimes(19) expect(setSpy).toHaveBeenCalledTimes(24)
// succeed // succeed
storeSpy.mockReturnValueOnce(of(true)) 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 { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
import { import {
PermissionAction, PermissionAction,
PermissionType,
PermissionsService, PermissionsService,
} from 'src/app/services/permissions.service' } from 'src/app/services/permissions.service'
@ -93,6 +94,11 @@ export class SettingsComponent
dateFormat: new FormControl(null), dateFormat: new FormControl(null),
notesEnabled: new FormControl(null), notesEnabled: new FormControl(null),
updateCheckingEnabled: 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), notificationsConsumerNewDocument: new FormControl(null),
notificationsConsumerSuccess: new FormControl(null), notificationsConsumerSuccess: new FormControl(null),
@ -159,6 +165,16 @@ export class SettingsComponent
this.activatedRoute.paramMap.subscribe((paramMap) => { this.activatedRoute.paramMap.subscribe((paramMap) => {
const section = paramMap.get('section') const section = paramMap.get('section')
if (section === null) {
if (
this.permissionsService.currentUserCan(
PermissionAction.View,
PermissionType.User
)
) {
this.getUsers()
}
}
if (section) { if (section) {
const navIDKey: string = Object.keys(SettingsNavIDs).find( const navIDKey: string = Object.keys(SettingsNavIDs).find(
(navID) => navID.toLowerCase() == section (navID) => navID.toLowerCase() == section
@ -222,6 +238,19 @@ export class SettingsComponent
savedViewsWarnOnUnsavedChange: this.settings.get( savedViewsWarnOnUnsavedChange: this.settings.get(
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE 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: {}, usersGroup: {},
groupsGroup: {}, groupsGroup: {},
savedViews: {}, savedViews: {},
@ -256,33 +285,21 @@ export class SettingsComponent
this.initialize(false) this.initialize(false)
}) })
} else if ( } else if (
navID == SettingsNavIDs.UsersGroups && (navID == SettingsNavIDs.UsersGroups ||
navID == SettingsNavIDs.General) &&
(!this.users || !this.groups) (!this.users || !this.groups)
) { ) {
this.usersService if (!this.users) this.getUsers()
this.groupsService
.listAll() .listAll()
.pipe(first()) .pipe(first())
.subscribe({ .subscribe({
next: (r) => { next: (r) => {
this.users = r.results this.groups = r.results
this.groupsService this.initialize(false)
.listAll()
.pipe(first())
.subscribe({
next: (r) => {
this.groups = r.results
this.initialize(false)
},
error: (e) => {
this.toastService.showError(
$localize`Error retrieving groups`,
e
)
},
})
}, },
error: (e) => { error: (e) => {
this.toastService.showError($localize`Error retrieving users`, e) this.toastService.showError($localize`Error retrieving groups`, e)
}, },
}) })
} else if ( } 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) { initialize(resetSettings: boolean = true) {
this.unsubscribeNotifier.next(true) this.unsubscribeNotifier.next(true)
@ -611,6 +642,26 @@ export class SettingsComponent
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE, SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE,
this.settingsForm.value.savedViewsWarnOnUnsavedChange 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.setLanguage(this.settingsForm.value.displayLanguage)
this.settings this.settings
.storeSettings() .storeSettings()

View File

@ -42,6 +42,11 @@ export const SETTINGS_KEYS = {
SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE: SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE:
'general-settings:saved-views:warn-on-unsaved-change', 'general-settings:saved-views:warn-on-unsaved-change',
TOUR_COMPLETE: 'general-settings:tour-complete', 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[] = [ export const SETTINGS: PaperlessUiSetting[] = [
@ -150,4 +155,29 @@ export const SETTINGS: PaperlessUiSetting[] = [
type: 'boolean', type: 'boolean',
default: false, 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 { import {
HttpClientTestingModule,
HttpTestingController, HttpTestingController,
HttpClientTestingModule,
} from '@angular/common/http/testing' } from '@angular/common/http/testing'
import { RouterTestingModule } from '@angular/router/testing' import { TestBed } from '@angular/core/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 { FormsModule, ReactiveFormsModule } from '@angular/forms' 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 { AppModule } from '../app.module'
import {
PaperlessUiSettings,
SETTINGS_KEYS,
} from '../data/paperless-uisettings'
import { SettingsService } from './settings.service'
describe('SettingsService', () => { describe('SettingsService', () => {
let httpTestingController: HttpTestingController let httpTestingController: HttpTestingController
let settingsService: SettingsService let settingsService: SettingsService
let cookieService: CookieService
let subscription: Subscription let subscription: Subscription
const ui_settings: PaperlessUiSettings = { const ui_settings: PaperlessUiSettings = {
@ -46,6 +50,13 @@ describe('SettingsService', () => {
saved_views: { warn_on_unsaved_change: true }, saved_views: { warn_on_unsaved_change: true },
notes_enabled: true, notes_enabled: true,
tour_complete: false, tour_complete: false,
permissions: {
default_owner: null,
default_view_users: [1],
default_view_groups: [2],
default_edit_users: [3],
default_edit_groups: [4],
},
}, },
permissions: [], permissions: [],
} }
@ -53,7 +64,7 @@ describe('SettingsService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [], declarations: [],
providers: [SettingsService], providers: [SettingsService, CookieService],
imports: [ imports: [
HttpClientTestingModule, HttpClientTestingModule,
RouterTestingModule, RouterTestingModule,
@ -65,6 +76,7 @@ describe('SettingsService', () => {
}) })
httpTestingController = TestBed.inject(HttpTestingController) httpTestingController = TestBed.inject(HttpTestingController)
cookieService = TestBed.inject(CookieService)
settingsService = TestBed.inject(SettingsService) settingsService = TestBed.inject(SettingsService)
}) })
@ -136,7 +148,52 @@ describe('SettingsService', () => {
expect(settingsService.get(SETTINGS_KEYS.THEME_COLOR)).toEqual('#000000') 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( const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}ui_settings/` `${environment.apiBaseUrl}ui_settings/`
) )

View File

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