Refactor permissions check code

Directly check permissions and no subscription (uisettings is always initialized on frontend startup)
update permission directive to accept single string
add explicit management permission name
This commit is contained in:
Michael Shamoon 2022-11-11 14:32:18 -08:00
parent 4603813896
commit 59e359ff98
25 changed files with 74 additions and 125 deletions

View File

@ -74,12 +74,7 @@ export class AppComponent implements OnInit, OnDestroy {
if (
this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS)
) {
// TODO - Is this the only way to allow/disallow from permissions?
var canOpenDocuments = false
this.settings.permissions().subscribe((perm) => {
canOpenDocuments = perm.includes('documents.view_document')
})
if (canOpenDocuments)
if (this.settings.currentUserCan('documents.view_document')) {
this.toastService.show({
title: $localize`Document added`,
delay: 10000,
@ -89,13 +84,14 @@ export class AppComponent implements OnInit, OnDestroy {
this.router.navigate(['documents', status.documentId])
},
})
else
} else {
this.toastService.show({
title: $localize`Document added`,
delay: 10000,
content: $localize`Document ${status.filename} was added to paperless.`,
})
}
}
})
this.failedSubscription = this.consumerStatusService
@ -211,12 +207,10 @@ export class AppComponent implements OnInit, OnDestroy {
}
public get dragDropEnabled(): boolean {
// TODO - Is this the only way to allow/disallow from permissions?
var canAddDocuments = false
this.settings.permissions().subscribe((perm) => {
canAddDocuments = perm.includes('documents.add_document')
})
return !this.router.url.includes('dashboard') && canAddDocuments
return (
!this.router.url.includes('dashboard') &&
this.settings.currentUserCan('documents.add_document')
)
}
public fileOver() {

View File

@ -10,7 +10,7 @@
</svg>
<span class="ms-2" [class.visually-hidden]="slimSidebarEnabled" i18n="app title">Paperless-ngx</span>
</a>
<div class="search-form-container flex-grow-1 py-2 pb-3 pb-sm-2 px-3 ps-md-4 me-sm-auto order-3 order-sm-1" *ifPermissions='["documents.view_document"]'>
<div class="search-form-container flex-grow-1 py-2 pb-3 pb-sm-2 px-3 ps-md-4 me-sm-auto order-3 order-sm-1" *ifPermissions="'documents.view_document'">
<form (ngSubmit)="search()" class="form-inline flex-grow-1">
<svg width="1em" height="1em" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#search"/>
@ -39,7 +39,7 @@
<p class="small mb-0 px-3 text-muted" i18n>Logged in as {{this.settingsService.displayName}}</p>
<div class="dropdown-divider"></div>
</div>
<a ngbDropdownItem class="nav-link" routerLink="settings" (click)="closeMenu()" *ifPermissions='["documents.view_uisettings"]'>
<a ngbDropdownItem class="nav-link" routerLink="settings" (click)="closeMenu()" *ifPermissions="'documents.view_uisettings'">
<svg class="sidebaricon me-2" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#gear"/>
</svg><ng-container i18n>Settings</ng-container>
@ -72,7 +72,7 @@
</svg><span>&nbsp;<ng-container i18n>Dashboard</ng-container></span>
</a>
</li>
<li class="nav-item" *ifPermissions='["documents.view_document"]'>
<li class="nav-item" *ifPermissions="'documents.view_document'">
<a class="nav-link" routerLink="documents" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Documents" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#files"/>
@ -80,7 +80,7 @@
</a>
</li>
</ul>
<div *ifPermissions='["documents.view_savedview"]'>
<div *ifPermissions="'documents.view_savedview'">
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted" *ngIf='savedViewService.loading || savedViewService.sidebarViews.length > 0'>
<span i18n>Saved views</span>
<div *ngIf="savedViewService.loading" class="spinner-border spinner-border-sm fw-normal ms-2" role="status"></div>
@ -96,7 +96,7 @@
</ul>
</div>
<div *ifPermissions='["documents.view_document"]'>
<div *ifPermissions="'documents.view_document'">
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted" *ngIf='openDocuments.length > 0'>
<span i18n>Open documents</span>
</h6>
@ -127,35 +127,35 @@
<span i18n>Manage</span>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item" *ifPermissions='["documents.view_correspondent"]'>
<li class="nav-item" *ifPermissions="'documents.view_correspondent'">
<a class="nav-link" routerLink="correspondents" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Correspondents" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#person"/>
</svg><span>&nbsp;<ng-container i18n>Correspondents</ng-container></span>
</a>
</li>
<li class="nav-item" *ifPermissions='["documents.view_tag"]' tourAnchor="tour.tags">
<li class="nav-item" *ifPermissions="'documents.view_tag'" tourAnchor="tour.tags">
<a class="nav-link" routerLink="tags" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Tags" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#tags"/>
</svg><span>&nbsp;<ng-container i18n>Tags</ng-container></span>
</a>
</li>
<li class="nav-item" *ifPermissions='["documents.view_documenttype"]'>
<li class="nav-item" *ifPermissions="'documents.view_documenttype'">
<a class="nav-link" routerLink="documenttypes" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Document types" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#hash"/>
</svg><span>&nbsp;<ng-container i18n>Document types</ng-container></span>
</a>
</li>
<li class="nav-item" *ifPermissions='["documents.view_storagepath"]'>
<li class="nav-item" *ifPermissions="'documents.view_storagepath'">
<a class="nav-link" routerLink="storagepaths" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Storage paths" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#folder"/>
</svg><span>&nbsp;<ng-container i18n>Storage paths</ng-container></span>
</a>
</li>
<li class="nav-item" *ifPermissions='["documents.view_paperlesstask"]' tourAnchor="tour.file-tasks">
<li class="nav-item" *ifPermissions="'documents.view_paperlesstask'" tourAnchor="tour.file-tasks">
<a class="nav-link" routerLink="tasks" routerLinkActive="active" (click)="closeMenu()" ngbPopover="File Tasks" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<span *ngIf="tasksService.failedFileTasks.length > 0 && slimSidebarEnabled" class="badge bg-danger position-absolute top-0 end-0">{{tasksService.failedFileTasks.length}}</span>
<svg class="sidebaricon" fill="currentColor">
@ -163,21 +163,21 @@
</svg><span>&nbsp;<ng-container i18n>File Tasks<span *ngIf="tasksService.failedFileTasks.length > 0"><span class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span></span></ng-container></span>
</a>
</li>
<li class="nav-item" *ifPermissions='["documents.view_log"]'>
<li class="nav-item" *ifPermissions="'documents.view_log'">
<a class="nav-link" routerLink="logs" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Logs" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#text-left"/>
</svg><span>&nbsp;<ng-container i18n>Logs</ng-container></span>
</a>
</li>
<li class="nav-item" *ifPermissions='["documents.view_uisettings"]' tourAnchor="tour.settings">
<li class="nav-item" *ifPermissions="'documents.view_uisettings'" tourAnchor="tour.settings">
<a class="nav-link" routerLink="settings" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Settings" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#gear"/>
</svg><span>&nbsp;<ng-container i18n>Settings</ng-container></span>
</a>
</li>
<li class="nav-item" *ifPermissions='["admin.view_logentry"]' tourAnchor="tour.admin">
<li class="nav-item" *ifPermissions="'admin.view_logentry'" tourAnchor="tour.admin">
<a class="nav-link" href="admin/" ngbPopover="Admin" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#toggles"/>

View File

@ -28,7 +28,7 @@
<app-welcome-widget *ngIf="settingsService.offerTour()" tourAnchor="tour.dashboard"></app-welcome-widget>
<div *ifPermissions='["documents.view_savedview"]'>
<div *ifPermissions="'documents.view_savedview'">
<ng-container *ngFor="let v of savedViewService.dashboardViews; first as isFirst">
<app-saved-view-widget *ngIf="isFirst; else noTour" [savedView]="v" tourAnchor="tour.dashboard"></app-saved-view-widget>
<ng-template #noTour>

View File

@ -1,6 +1,6 @@
<app-widget-frame [title]="savedView.name" [loading]="loading">
<a class="btn-link" header-buttons [routerLink]="[]" (click)="showAll()" *ifPermissions='["documents.view_document"]' i18n>Show all</a>
<a class="btn-link" header-buttons [routerLink]="[]" (click)="showAll()" *ifPermissions="'documents.view_document'" i18n>Show all</a>
<table content class="table table-sm table-hover table-borderless mb-0">
@ -10,7 +10,7 @@
<th scope="col" i18n>Title</th>
</tr>
</thead>
<tbody *ifPermissions='["documents.view_document"]'>
<tbody *ifPermissions="'documents.view_document'">
<tr *ngFor="let doc of documents" (click)="openDocumentsService.openDocument(doc)">
<td>{{doc.created_date | customDate}}</td>
<td>{{doc.title | documentTitle}}<app-tag [tag]="t" *ngFor="let t of doc.tags$ | async" class="ms-1" (click)="clickTag(t); $event.stopPropagation();"></app-tag></td>

View File

@ -9,7 +9,7 @@
</a>
</div>
<div content tourAnchor="tour.upload-widget">
<form *ifPermissions='["documents.add_document"]'>
<form *ifPermissions="'documents.add_document'">
<ngx-file-drop dropZoneLabel="Drop documents here or" browseBtnLabel="Browse files" (onFileDrop)="dropped($event)"
(onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)" dropZoneClassName="bg-light card"
multiple="true" contentClassName="justify-content-center d-flex align-items-center py-5 px-2" [showBrowseBtn]=true
@ -40,7 +40,7 @@
<h6 class="alert-heading">{{status.filename}}</h6>
<p class="mb-0 pb-1" *ngIf="!isFinished(status) || (isFinished(status) && !status.documentId)">{{status.message}}</p>
<ngb-progressbar [value]="status.getProgress()" [max]="1" [type]="getStatusColor(status)"></ngb-progressbar>
<div *ifPermissions='["documents.view_document"]'>
<div *ifPermissions="'documents.view_document'">
<div *ngIf="isFinished(status)">
<button *ngIf="status.documentId" class="btn btn-sm btn-outline-primary btn-open" routerLink="/documents/{{status.documentId}}" (click)="dismiss(status)">
<small i18n>Open document</small>

View File

@ -1,5 +1,5 @@
<div *ngIf="comments">
<form [formGroup]="commentForm" class="needs-validation mt-3" *ifPermissions='["documents.add_comment"]' novalidate>
<form [formGroup]="commentForm" class="needs-validation mt-3" *ifPermissions="'documents.add_comment'" novalidate>
<div class="form-group">
<textarea class="form-control form-control-sm" [class.is-invalid]="newCommentError" rows="3" formControlName="newComment" placeholder="Enter comment" i18n-placeholder required></textarea>
<div class="invalid-feedback" i18n>
@ -18,7 +18,7 @@
</div>
<div class="d-flex card-footer small bg-light text-primary justify-content-between align-items-center">
<span>{{displayName(comment)}} - {{ comment.created | customDate}}</span>
<button type="button" class="btn btn-link btn-sm p-0 fade" (click)="deleteComment(comment.id)" *ifPermissions='["documents.delete_comment"]'>
<button type="button" class="btn btn-link btn-sm p-0 fade" (click)="deleteComment(comment.id)" *ifPermissions="'documents.delete_comment'">
<svg width="13" height="13" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#trash" />
</svg>

View File

@ -5,7 +5,7 @@
<div class="input-group-text" i18n>of {{previewNumPages}}</div>
</div>
<button type="button" class="btn btn-sm btn-outline-danger me-2 ms-auto" (click)="delete()" *ifPermissions='["documents.delete_document"]'>
<button type="button" class="btn btn-sm btn-outline-danger me-2 ms-auto" (click)="delete()" *ifPermissions="'documents.delete_document'">
<svg class="buttonicon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#trash" />
</svg><span class="d-none d-lg-inline ps-1" i18n>Delete</span>
@ -182,7 +182,7 @@
<button type="button" class="btn btn-outline-secondary" (click)="discard()" i18n [disabled]="networkActive || !(isDirty$ | async)">Discard</button>&nbsp;
<button type="button" class="btn btn-outline-primary" (click)="saveEditNext()" *ngIf="hasNext()" i18n [disabled]="networkActive || !(isDirty$ | async) || error">Save & next</button>&nbsp;
<button type="submit" class="btn btn-primary" *ifPermissions='["documents.change_document"]' i18n [disabled]="networkActive || !(isDirty$ | async) || error">Save</button>&nbsp;
<button type="submit" class="btn btn-primary" *ifPermissions="'documents.change_document'" i18n [disabled]="networkActive || !(isDirty$ | async) || error">Save</button>&nbsp;
</form>
</div>

View File

@ -553,11 +553,9 @@ export class DocumentDetailComponent
}
get commentsEnabled(): boolean {
// TODO - Is this the only way to allow/disallow from permissions?
var canViewComments = false
this.settings.permissions().subscribe((perm) => {
canViewComments = perm.includes('documents.view_comment')
})
return this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED) && canViewComments
return (
this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED) &&
this.settings.currentUserCan('documents.view_comment')
)
}
}

View File

@ -23,7 +23,7 @@
</div>
<div class="w-100 d-xl-none"></div>
<div class="col-auto mb-2 mb-xl-0">
<div class="d-flex" *ifPermissions='["documents.change_document"]'>
<div class="d-flex" *ifPermissions="'documents.change_document'">
<label class="ms-auto mt-1 mb-0 me-2" i18n>Edit:</label>
<app-filterable-dropdown class="me-2 me-md-3" title="Tags" icon="tag-fill" i18n-title
filterPlaceholder="Filter tags" i18n-filterPlaceholder
@ -91,7 +91,7 @@
</div>
</div>
<button type="button" class="btn btn-sm btn-outline-danger" (click)="applyDelete()" *ifPermissions='["documents.delete_document"]'>
<button type="button" class="btn btn-sm btn-outline-danger" (click)="applyDelete()" *ifPermissions="'documents.delete_document'">
<svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#trash" />
</svg>&nbsp;<ng-container i18n>Delete</ng-container>

View File

@ -37,7 +37,7 @@
<use xlink:href="assets/bootstrap-icons.svg#diagram-3"/>
</svg>&nbsp;<span class="d-none d-md-inline" i18n>More like this</span>
</a>
<a (click)="openDocumentsService.openDocument(document)" class="btn btn-sm btn-outline-secondary" *ifPermissions='["documents.change_document"]'>
<a (click)="openDocumentsService.openDocument(document)" class="btn btn-sm btn-outline-secondary" *ifPermissions="'documents.change_document'">
<svg class="sidebaricon" fill="currentColor" class="sidebaricon">
<use xlink:href="assets/bootstrap-icons.svg#pencil"/>
</svg>&nbsp;<span class="d-none d-md-inline" i18n>Edit</span>

View File

@ -67,7 +67,7 @@
</div>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group w-100">
<a (click)="openDocumentsService.openDocument(document)" class="btn btn-sm btn-outline-secondary" title="Edit" *ifPermissions='["documents.change_document"]' i18n-title>
<a (click)="openDocumentsService.openDocument(document)" class="btn btn-sm btn-outline-secondary" title="Edit" *ifPermissions="'documents.change_document'" i18n-title>
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
</svg>

View File

@ -59,7 +59,7 @@
</div>
</div>
<div class="btn-group ms-2 flex-fill" *ifPermissions='["documents.view_savedview"]' ngbDropdown role="group">
<div class="btn-group ms-2 flex-fill" *ifPermissions="'documents.view_savedview'" ngbDropdown role="group">
<button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" tourAnchor="tour.documents-views" ngbDropdownToggle>
<ng-container i18n>Views</ng-container>
<div *ngIf="savedViewIsModified" class="position-absolute top-0 start-100 p-2 translate-middle badge bg-secondary border border-light rounded-circle">
@ -72,10 +72,10 @@
<div class="dropdown-divider" *ngIf="savedViewService.allViews.length > 0"></div>
</ng-container>
<div *ifPermissions='["documents.change_savedviewfilterrule"]'>
<div *ifPermissions="'documents.change_savedviewfilterrule'">
<button ngbDropdownItem (click)="saveViewConfig()" *ngIf="list.activeSavedViewId" [disabled]="!savedViewIsModified" i18n>Save "{{list.activeSavedViewTitle}}"</button>
</div>
<button ngbDropdownItem (click)="saveViewConfigAs()" *ifPermissions='["documents.add_savedview"]' i18n>Save as...</button>
<button ngbDropdownItem (click)="saveViewConfigAs()" *ifPermissions="'documents.add_savedview'" i18n>Save as...</button>
</div>
</div>

View File

@ -32,6 +32,7 @@ export class CorrespondentListComponent extends ManagementListComponent<Paperles
FILTER_CORRESPONDENT,
$localize`correspondent`,
$localize`correspondents`,
'correspondent',
[
{
key: 'last_correspondence',

View File

@ -29,6 +29,7 @@ export class DocumentTypeListComponent extends ManagementListComponent<Paperless
FILTER_DOCUMENT_TYPE,
$localize`document type`,
$localize`document types`,
'documenttype',
[]
)
}

View File

@ -1,5 +1,5 @@
<app-page-header title="{{ typeNamePlural | titlecase }}">
<button type="button" class="btn btn-sm btn-outline-primary" (click)="openCreateDialog()" *ifPermissions='["documents.add_" + typeNameWithoutWhitespace()]' i18n>Create</button>
<button type="button" class="btn btn-sm btn-outline-primary" (click)="openCreateDialog()" *ifPermissions="'documents.add_' + permissionName" i18n>Create</button>
</app-page-header>
<div class="row">
@ -41,24 +41,24 @@
</svg>
</button>
<div ngbDropdownMenu aria-labelledby="actionsMenuMobile">
<button (click)="filterDocuments(object)" *ifPermissions='["documents.view_document"]' ngbDropdownItem i18n>Filter Documents</button>
<button (click)="openEditDialog(object)" *ifPermissions='["documents.change_" + typeNameWithoutWhitespace()]' ngbDropdownItem i18n>Edit</button>
<button class="text-danger" (click)="openDeleteDialog(object)" *ifPermissions='["documents.delete_" + typeNameWithoutWhitespace()]' ngbDropdownItem i18n>Delete</button>
<button (click)="filterDocuments(object)" *ifPermissions="'documents.view_document'" ngbDropdownItem i18n>Filter Documents</button>
<button (click)="openEditDialog(object)" *ifPermissions="'documents.change_' + permissionName" ngbDropdownItem i18n>Edit</button>
<button class="text-danger" (click)="openDeleteDialog(object)" *ifPermissions="'documents.delete_' + permissionName" ngbDropdownItem i18n>Delete</button>
</div>
</div>
</div>
<div class="btn-group d-none d-sm-block">
<button class="btn btn-sm btn-outline-secondary" (click)="filterDocuments(object)" *ifPermissions='["documents.view_document"]'>
<button class="btn btn-sm btn-outline-secondary" (click)="filterDocuments(object)" *ifPermissions="'documents.view_document'">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-funnel" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M1.5 1.5A.5.5 0 0 1 2 1h12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.128.334L10 8.692V13.5a.5.5 0 0 1-.342.474l-3 1A.5.5 0 0 1 6 14.5V8.692L1.628 3.834A.5.5 0 0 1 1.5 3.5v-2zm1 .5v1.308l4.372 4.858A.5.5 0 0 1 7 8.5v5.306l2-.666V8.5a.5.5 0 0 1 .128-.334L13.5 3.308V2h-11z"/>
</svg>&nbsp;<ng-container i18n>Documents</ng-container>
</button>
<button class="btn btn-sm btn-outline-secondary" (click)="openEditDialog(object)" *ifPermissions='["documents.change_" + typeNameWithoutWhitespace()]'>
<button class="btn btn-sm btn-outline-secondary" (click)="openEditDialog(object)" *ifPermissions="'documents.change_' + permissionName">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
</svg>&nbsp;<ng-container i18n>Edit</ng-container>
</button>
<button class="btn btn-sm btn-outline-danger" (click)="openDeleteDialog(object)" *ifPermissions='["documents.delete_" + typeNameWithoutWhitespace()]'>
<button class="btn btn-sm btn-outline-danger" (click)="openDeleteDialog(object)" *ifPermissions="'documents.delete_' + permissionName">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash" viewBox="0 0 16 16">
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>

View File

@ -46,6 +46,7 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
protected filterRuleType: number,
public typeName: string,
public typeNamePlural: string,
public permissionName: string,
public extraColumns: ManagementListColumn[]
) {}
@ -60,12 +61,6 @@ export abstract class ManagementListComponent<T extends ObjectWithId>
public sortField: string
public sortReverse: boolean
// TODO - Getter used to automatically build a permission name from typeName
// Will basically break if permission name is different than typeName
public typeNameWithoutWhitespace(): string {
return this.typeName.replace(/\s/g, '')
}
private nameFilterDebounce: Subject<string>
private subscription: Subscription
private _nameFilter: string

View File

@ -206,7 +206,7 @@
<div class="mb-2 col-auto">
<label class="form-label" for="name_{{view.id}}" i18n>Actions</label>
<button type="button" class="btn btn-sm btn-outline-danger form-control" (click)="deleteSavedView(view)" *ifPermissions='["documents.delete_savedview"]' i18n>Delete</button>
<button type="button" class="btn btn-sm btn-outline-danger form-control" (click)="deleteSavedView(view)" *ifPermissions="'documents.delete_savedview'" i18n>Delete</button>
</div>
</div>
@ -220,5 +220,5 @@
<div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div>
<button type="submit" class="btn btn-primary mb-2" *ifPermissions='["documents.change_uisettings"]' [disabled]="!(isDirty$ | async)" i18n>Save</button>
<button type="submit" class="btn btn-primary mb-2" *ifPermissions="'documents.change_uisettings'" [disabled]="!(isDirty$ | async)" i18n>Save</button>
</form>

View File

@ -29,6 +29,7 @@ export class StoragePathListComponent extends ManagementListComponent<PaperlessS
FILTER_STORAGE_PATH,
$localize`storage path`,
$localize`storage paths`,
'storagepath',
[
{
key: 'path',

View File

@ -29,6 +29,7 @@ export class TagListComponent extends ManagementListComponent<PaperlessTag> {
FILTER_HAS_TAGS_ALL,
$localize`tag`,
$localize`tags`,
'tag',
[
{
key: 'color',

View File

@ -5,7 +5,7 @@
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>&nbsp;<ng-container i18n>Clear selection</ng-container>
</button>
<button class="btn btn-sm btn-outline-primary me-4" (click)="dismissTasks()" *ifPermissions='["django_q.delete_task"]' [disabled]="tasksService.total == 0">
<button class="btn btn-sm btn-outline-primary me-4" (click)="dismissTasks()" *ifPermissions="'django_q.delete_task'" [disabled]="tasksService.total == 0">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#check2-all"/>
</svg>&nbsp;<ng-container i18n>{{dismissButtonText}}</ng-container>
@ -75,12 +75,12 @@
</td>
<td scope="row">
<div class="btn-group" role="group">
<button class="btn btn-sm btn-outline-secondary" (click)="dismissTask(task); $event.stopPropagation();" *ifPermissions='["django_q.delete_task"]'>
<button class="btn btn-sm btn-outline-secondary" (click)="dismissTask(task); $event.stopPropagation();" *ifPermissions="'django_q.delete_task'">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#check"/>
</svg>&nbsp;<ng-container i18n>Dismiss</ng-container>
</button>
<div *ifPermissions='["documents.view_document"]'> <!-- TODO - This div breaks btn-group logic, may have to find a way to merge *ngIf and *ifPermissions -->
<div *ifPermissions="'documents.view_document'"> <!-- TODO - This div breaks btn-group logic, may have to find a way to merge *ngIf and *ifPermissions -->
<button *ngIf="task.related_document" class="btn btn-sm btn-outline-primary" (click)="dismissAndGo(task); $event.stopPropagation();">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#file-text"/>

View File

@ -1,9 +0,0 @@
import { IfPermissionsDirective } from './if-permissions.directive'
// TODO - Must be implemented
describe('IfPermissionsDirective', () => {
it('should create an instance', () => {
const directive = new IfPermissionsDirective()
expect(directive).toBeTruthy()
})
})

View File

@ -4,18 +4,15 @@ import {
Directive,
ViewContainerRef,
TemplateRef,
OnDestroy,
} from '@angular/core'
import { Subscription } from 'rxjs'
import { SettingsService } from '../services/settings.service'
@Directive({
selector: '[ifPermissions]',
})
export class IfPermissionsDirective implements OnInit, OnDestroy {
private subscription: Subscription[] = []
export class IfPermissionsDirective implements OnInit {
// The role the user must have
@Input() public ifPermissions: Array<string>
@Input() public ifPermissions: Array<string> | string
/**
* @param {ViewContainerRef} viewContainerRef -- The location where we need to render the templateRef
@ -29,32 +26,14 @@ export class IfPermissionsDirective implements OnInit, OnDestroy {
) {}
public ngOnInit(): void {
this.subscription.push(
this.settingsService.permissions().subscribe((permission) => {
if (!permission) {
// Remove element from DOM
this.viewContainerRef.clear()
}
// User permissions are checked by a permission mention in DOM
const idx = permission.findIndex(
(element) => this.ifPermissions.indexOf(element) !== -1
)
if (idx < 0) {
this.viewContainerRef.clear()
} else {
// Appends the ref element to DOM
if (
[]
.concat(this.ifPermissions)
.every((perm) => this.settingsService.currentUserCan(perm))
) {
this.viewContainerRef.createEmbeddedView(this.templateRef)
}
})
)
}
/**
* On destroy cancels the API if its fetching.
*/
public ngOnDestroy(): void {
this.subscription.forEach((subscription: Subscription) =>
subscription.unsubscribe()
)
} else {
this.viewContainerRef.clear()
}
}
}

View File

@ -1,8 +0,0 @@
import { SortableDirective } from './sortable.directive'
describe('SortableDirective', () => {
it('should create an instance', () => {
const directive = new SortableDirective()
expect(directive).toBeTruthy()
})
})

View File

@ -14,10 +14,6 @@ export class AuthGard implements CanActivate {
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean {
var canActivate = false
this.settingsService.permissions().subscribe((perm) => {
canActivate = perm.includes(route.data.requiredPermission)
})
return canActivate
return this.settingsService.currentUserCan(route.data.requiredPermission)
}
}

View File

@ -11,7 +11,7 @@ import {
} from '@angular/core'
import { Meta } from '@angular/platform-browser'
import { CookieService } from 'ngx-cookie-service'
import { first, Observable, of, tap } from 'rxjs'
import { first, Observable, tap } from 'rxjs'
import {
BRIGHTNESS,
estimateBrightnessForColor,
@ -45,7 +45,7 @@ export class SettingsService {
protected baseUrl: string = environment.apiBaseUrl + 'ui_settings/'
private settings: Object = {}
private _permissions: string[]
private permissions: string[]
public displayName: string
@ -75,7 +75,7 @@ export class SettingsService {
if (this.settings['language']?.length)
this.setLanguage(this.settings['language'])
this.displayName = uisettings.display_name.trim()
this._permissions = uisettings.permissions
this.permissions = uisettings.permissions
})
)
}
@ -458,7 +458,7 @@ export class SettingsService {
)
}
public permissions(): Observable<string[]> {
return of(this._permissions)
currentUserCan(permission: string): boolean {
return this.permissions.includes(permission)
}
}