add share to c/dt/t/sp, refactor share input, ifOwner directive

This commit is contained in:
Michael Shamoon 2022-12-07 14:55:40 -08:00
parent 32da039d5f
commit c73688d167
18 changed files with 192 additions and 18 deletions

View File

@ -104,6 +104,8 @@ import localeSr from '@angular/common/locales/sr'
import localeSv from '@angular/common/locales/sv' import localeSv from '@angular/common/locales/sv'
import localeTr from '@angular/common/locales/tr' import localeTr from '@angular/common/locales/tr'
import localeZh from '@angular/common/locales/zh' import localeZh from '@angular/common/locales/zh'
import { ShareUserComponent } from './components/common/input/share-user/share-user.component'
import { IfOwnerDirective } from './directives/if-owner.directive'
registerLocaleData(localeBe) registerLocaleData(localeBe)
registerLocaleData(localeCs) registerLocaleData(localeCs)
@ -195,6 +197,8 @@ function initializeApp(settings: SettingsService) {
PermissionsSelectComponent, PermissionsSelectComponent,
MailAccountEditDialogComponent, MailAccountEditDialogComponent,
MailRuleEditDialogComponent, MailRuleEditDialogComponent,
ShareUserComponent,
IfOwnerDirective,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -5,10 +5,20 @@
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text> <app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text>
<app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> <app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text> <app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></app-input-check> <app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></app-input-check>
<div *ifOwner="object.owner">
<h5 i18n>Permissions</h5>
<div formGroupName="set_permissions">
<app-share-user type="view" formControlName="view"></app-share-user>
<app-share-user type="change" formControlName="change"></app-share-user>
</div>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button> <button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>

View File

@ -30,6 +30,10 @@ export class CorrespondentEditDialogComponent extends EditDialogComponent<Paperl
matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM), matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM),
match: new FormControl(''), match: new FormControl(''),
is_insensitive: new FormControl(true), is_insensitive: new FormControl(true),
set_permissions: new FormGroup({
view: new FormControl(null),
change: new FormControl(null),
}),
}) })
} }
} }

View File

@ -11,6 +11,14 @@
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text> <app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check> <app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
<div *ifOwner="object.owner">
<h5 i18n>Permissions</h5>
<div formGroupName="set_permissions">
<app-share-user type="view" formControlName="view"></app-share-user>
<app-share-user type="change" formControlName="change"></app-share-user>
</div>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button> <button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>

View File

@ -30,6 +30,10 @@ export class DocumentTypeEditDialogComponent extends EditDialogComponent<Paperle
matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM), matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM),
match: new FormControl(''), match: new FormControl(''),
is_insensitive: new FormControl(true), is_insensitive: new FormControl(true),
set_permissions: new FormGroup({
view: new FormControl(null),
change: new FormControl(null),
}),
}) })
} }
} }

View File

@ -4,11 +4,13 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model' import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model'
import { ObjectWithId } from 'src/app/data/object-with-id' import { ObjectWithId } from 'src/app/data/object-with-id'
import { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service' import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'
@Directive() @Directive()
export abstract class EditDialogComponent<T extends ObjectWithId> export abstract class EditDialogComponent<
implements OnInit T extends ObjectWithPermissions | ObjectWithId
> implements OnInit
{ {
constructor( constructor(
private service: AbstractPaperlessService<T>, private service: AbstractPaperlessService<T>,
@ -36,6 +38,16 @@ export abstract class EditDialogComponent<T extends ObjectWithId>
ngOnInit(): void { ngOnInit(): void {
if (this.object != null) { if (this.object != null) {
if (this.object['permissions']) {
this.object['set_permissions'] = {
view: (this.object as ObjectWithPermissions).permissions
.filter((p) => (p[1] as string).includes('view'))
.map((p) => p[0]),
change: (this.object as ObjectWithPermissions).permissions
.filter((p) => (p[1] as string).includes('change'))
.map((p) => p[0]),
}
}
this.objectForm.patchValue(this.object) this.objectForm.patchValue(this.object)
} }

View File

@ -16,6 +16,14 @@
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text> <app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check> <app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
<div *ifOwner="object.owner">
<h5 i18n>Permissions</h5>
<div formGroupName="set_permissions">
<app-share-user type="view" formControlName="view"></app-share-user>
<app-share-user type="change" formControlName="change"></app-share-user>
</div>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button> <button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>

View File

@ -41,6 +41,10 @@ export class StoragePathEditDialogComponent extends EditDialogComponent<Paperles
matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM), matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM),
match: new FormControl(''), match: new FormControl(''),
is_insensitive: new FormControl(true), is_insensitive: new FormControl(true),
set_permissions: new FormGroup({
view: new FormControl(null),
change: new FormControl(null),
}),
}) })
} }
} }

View File

@ -13,6 +13,15 @@
<app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> <app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text> <app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check> <app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
<div *ifOwner="object.owner">
<h5 i18n>Permissions</h5>
<div formGroupName="set_permissions">
<app-share-user type="view" formControlName="view"></app-share-user>
<app-share-user type="change" formControlName="change"></app-share-user>
</div>
</div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button> <button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>

View File

@ -33,6 +33,10 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM), matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM),
match: new FormControl(''), match: new FormControl(''),
is_insensitive: new FormControl(true), is_insensitive: new FormControl(true),
set_permissions: new FormGroup({
view: new FormControl(null),
change: new FormControl(null),
}),
}) })
} }
} }

View File

@ -0,0 +1,15 @@
<div class="mb-3 paperless-input-select">
<label class="form-label" [for]="inputId">{{title}}</label>
<div>
<ng-select name="inputId" [(ngModel)]="value"
[disabled]="disabled"
clearable="true"
[items]="users"
multiple="true"
bindLabel="username"
bindValue="id"
(change)="onChange(value)">
</ng-select>
</div>
<small *ngIf="hint" class="form-text text-muted">{{hint}}</small>
</div>

View File

@ -0,0 +1,47 @@
import { Component, forwardRef, Input, OnInit } from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { first } from 'rxjs/operators'
import { PaperlessUser } from 'src/app/data/paperless-user'
import { UserService } from 'src/app/services/rest/user.service'
import { AbstractInputComponent } from '../abstract-input'
@Component({
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ShareUserComponent),
multi: true,
},
],
selector: 'app-share-user',
templateUrl: './share-user.component.html',
styleUrls: ['./share-user.component.scss'],
})
export class ShareUserComponent
extends AbstractInputComponent<PaperlessUser>
implements OnInit
{
users: PaperlessUser[]
@Input()
type: string
constructor(userService: UserService) {
super()
userService
.listAll()
.pipe(first())
.subscribe((result) => (this.users = result.results))
}
ngOnInit(): void {
if (this.type == 'view') {
this.title = $localize`Users can view`
} else if (this.type == 'change') {
this.title = $localize`Users can edit`
this.hint = $localize`Edit permissions also grant viewing permissions`
}
super.ngOnInit()
}
}

View File

@ -178,12 +178,12 @@
</ng-template> </ng-template>
</li> </li>
<li [ngbNavItem]="6"> <li [ngbNavItem]="6" *ifOwner="document?.owner">
<a ngbNavLink i18n>Permissions</a> <a ngbNavLink i18n>Permissions</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
<div formGroupName="set_permissions"> <div formGroupName="set_permissions">
<app-input-select i18n-title title="Users can view" [items]="users" [bindLabel]="'username'" multiple="true" formControlName="view"></app-input-select> <app-share-user type="view" formControlName="view"></app-share-user>
<app-input-select i18n-title title="Users can edit" [items]="users" [bindLabel]="'username'" multiple="true" formControlName="change"></app-input-select> <app-share-user type="change" formControlName="change"></app-share-user>
</div> </div>
</ng-template> </ng-template>
</li> </li>

View File

@ -40,7 +40,6 @@ import {
PermissionsService, PermissionsService,
PermissionType, PermissionType,
} from 'src/app/services/permissions.service' } from 'src/app/services/permissions.service'
import { UserService } from 'src/app/services/rest/user.service'
import { PaperlessUser } from 'src/app/data/paperless-user' import { PaperlessUser } from 'src/app/data/paperless-user'
@Component({ @Component({
@ -75,7 +74,6 @@ export class DocumentDetailComponent
correspondents: PaperlessCorrespondent[] correspondents: PaperlessCorrespondent[]
documentTypes: PaperlessDocumentType[] documentTypes: PaperlessDocumentType[]
storagePaths: PaperlessStoragePath[] storagePaths: PaperlessStoragePath[]
users: PaperlessUser[]
documentForm: FormGroup = new FormGroup({ documentForm: FormGroup = new FormGroup({
title: new FormControl(''), title: new FormControl(''),
@ -134,8 +132,7 @@ export class DocumentDetailComponent
private toastService: ToastService, private toastService: ToastService,
private settings: SettingsService, private settings: SettingsService,
private storagePathService: StoragePathService, private storagePathService: StoragePathService,
private permissionsService: PermissionsService, private permissionsService: PermissionsService
private userService: UserService
) {} ) {}
titleKeyUp(event) { titleKeyUp(event) {
@ -175,11 +172,6 @@ export class DocumentDetailComponent
.pipe(first()) .pipe(first())
.subscribe((result) => (this.storagePaths = result.results)) .subscribe((result) => (this.storagePaths = result.results))
this.userService
.listAll()
.pipe(first())
.subscribe((result) => (this.users = result.results))
this.route.paramMap this.route.paramMap
.pipe( .pipe(
takeUntil(this.unsubscribeNotifier), takeUntil(this.unsubscribeNotifier),

View File

@ -2,7 +2,7 @@ import { ObjectWithId } from './object-with-id'
import { PaperlessUser } from './paperless-user' import { PaperlessUser } from './paperless-user'
export interface ObjectWithPermissions extends ObjectWithId { export interface ObjectWithPermissions extends ObjectWithId {
user?: PaperlessUser owner?: PaperlessUser
permissions?: Array<[number, string]> permissions?: Array<[number, string]>
} }

View File

@ -0,0 +1,42 @@
import {
Directive,
Input,
OnChanges,
OnInit,
TemplateRef,
ViewContainerRef,
} from '@angular/core'
import { PaperlessUser } from '../data/paperless-user'
import { SettingsService } from '../services/settings.service'
@Directive({
selector: '[ifOwner]',
})
export class IfOwnerDirective implements OnInit, OnChanges {
// The role the user must have
@Input()
ifOwner: PaperlessUser
/**
* @param {ViewContainerRef} viewContainerRef -- The location where we need to render the templateRef
* @param {TemplateRef<any>} templateRef -- The templateRef to be potentially rendered
* @param {PermissionsService} permissionsService -- Will give us access to the permissions a user has
*/
constructor(
private viewContainerRef: ViewContainerRef,
private templateRef: TemplateRef<any>,
private settings: SettingsService
) {}
public ngOnInit(): void {
if (!this.ifOwner || this.ifOwner?.id === this.settings.currentUser.id) {
this.viewContainerRef.createEmbeddedView(this.templateRef)
} else {
this.viewContainerRef.clear()
}
}
public ngOnChanges(): void {
this.ngOnInit()
}
}

View File

@ -23,6 +23,7 @@ import {
SETTINGS, SETTINGS,
SETTINGS_KEYS, SETTINGS_KEYS,
} from '../data/paperless-uisettings' } from '../data/paperless-uisettings'
import { PaperlessUser } from '../data/paperless-user'
import { PermissionsService } from './permissions.service' import { PermissionsService } from './permissions.service'
import { SavedViewService } from './rest/saved-view.service' import { SavedViewService } from './rest/saved-view.service'
import { ToastService } from './toast.service' import { ToastService } from './toast.service'
@ -46,8 +47,7 @@ export class SettingsService {
protected baseUrl: string = environment.apiBaseUrl + 'ui_settings/' protected baseUrl: string = environment.apiBaseUrl + 'ui_settings/'
private settings: Object = {} private settings: Object = {}
currentUser: PaperlessUser
public displayName: string
public settingsSaved: EventEmitter<any> = new EventEmitter() public settingsSaved: EventEmitter<any> = new EventEmitter()
@ -75,12 +75,23 @@ export class SettingsService {
// to update lang cookie // to update lang cookie
if (this.settings['language']?.length) if (this.settings['language']?.length)
this.setLanguage(this.settings['language']) this.setLanguage(this.settings['language'])
this.displayName = uisettings.display_name.trim() this.currentUser = {
id: uisettings['user_id'],
username: uisettings['username'],
}
this.permissionsService.initialize(uisettings.permissions) this.permissionsService.initialize(uisettings.permissions)
}) })
) )
} }
get displayName(): string {
return (
this.currentUser.first_name ??
this.currentUser.username ??
''
).trim()
}
public updateAppearanceSettings( public updateAppearanceSettings(
darkModeUseSystem = null, darkModeUseSystem = null,
darkModeEnabled = null, darkModeEnabled = null,