mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Bulk editor enabling/disabling by permissions
This commit is contained in:
		@@ -1,5 +1,5 @@
 | 
				
			|||||||
<div class="btn-group w-100" ngbDropdown role="group" (openChange)="dropdownOpenChange($event)" #dropdown="ngbDropdown">
 | 
					<div class="btn-group w-100" ngbDropdown role="group" (openChange)="dropdownOpenChange($event)" #dropdown="ngbDropdown">
 | 
				
			||||||
  <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="!editing && selectionModel.selectionSize() > 0 ? 'btn-primary' : 'btn-outline-primary'">
 | 
					  <button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="!editing && selectionModel.selectionSize() > 0 ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled">
 | 
				
			||||||
    <svg class="toolbaricon" fill="currentColor">
 | 
					    <svg class="toolbaricon" fill="currentColor">
 | 
				
			||||||
      <use attr.xlink:href="assets/bootstrap-icons.svg#{{icon}}" />
 | 
					      <use attr.xlink:href="assets/bootstrap-icons.svg#{{icon}}" />
 | 
				
			||||||
    </svg>
 | 
					    </svg>
 | 
				
			||||||
@@ -25,10 +25,10 @@
 | 
				
			|||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div *ngIf="selectionModel.items" class="items">
 | 
					      <div *ngIf="selectionModel.items" class="items">
 | 
				
			||||||
        <ng-container *ngFor="let item of selectionModel.itemsSorted | filter: filterText">
 | 
					        <ng-container *ngFor="let item of selectionModel.itemsSorted | filter: filterText">
 | 
				
			||||||
          <app-toggleable-dropdown-button *ngIf="allowSelectNone || item.id" [item]="item" [state]="selectionModel.get(item.id)" (toggle)="selectionModel.toggle(item.id)" (exclude)="excludeClicked(item.id)"></app-toggleable-dropdown-button>
 | 
					          <app-toggleable-dropdown-button *ngIf="allowSelectNone || item.id" [item]="item" [state]="selectionModel.get(item.id)" (toggle)="selectionModel.toggle(item.id)" (exclude)="excludeClicked(item.id)" [disabled]="disabled"></app-toggleable-dropdown-button>
 | 
				
			||||||
        </ng-container>
 | 
					        </ng-container>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <button *ngIf="editing" class="list-group-item list-group-item-action bg-light" (click)="applyClicked()" [disabled]="!modelIsDirty">
 | 
					      <button *ngIf="editing" class="list-group-item list-group-item-action bg-light" (click)="applyClicked()" [disabled]="!modelIsDirty || disabled">
 | 
				
			||||||
        <small class="ms-2" [ngClass]="{'fw-bold': modelIsDirty}" i18n>Apply</small>
 | 
					        <small class="ms-2" [ngClass]="{'fw-bold': modelIsDirty}" i18n>Apply</small>
 | 
				
			||||||
        <svg width="1.5em" height="1em" viewBox="0 0 16 16" fill="currentColor">
 | 
					        <svg width="1.5em" height="1em" viewBox="0 0 16 16" fill="currentColor">
 | 
				
			||||||
          <use xlink:href="assets/bootstrap-icons.svg#arrow-right" />
 | 
					          <use xlink:href="assets/bootstrap-icons.svg#arrow-right" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -317,6 +317,9 @@ export class FilterableDropdownComponent {
 | 
				
			|||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  applyOnClose = false
 | 
					  applyOnClose = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Input()
 | 
				
			||||||
 | 
					  disabled = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Output()
 | 
					  @Output()
 | 
				
			||||||
  apply = new EventEmitter<ChangedItems>()
 | 
					  apply = new EventEmitter<ChangedItems>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
<button class="list-group-item list-group-item-action d-flex align-items-center p-2 border-top-0 border-start-0 border-end-0 border-bottom" role="menuitem" (click)="toggleItem($event)">
 | 
					<button class="list-group-item list-group-item-action d-flex align-items-center p-2 border-top-0 border-start-0 border-end-0 border-bottom" role="menuitem" (click)="toggleItem($event)" [disabled]="disabled">
 | 
				
			||||||
  <div class="selected-icon me-1">
 | 
					  <div class="selected-icon me-1">
 | 
				
			||||||
    <ng-container *ngIf="isChecked()">
 | 
					    <ng-container *ngIf="isChecked()">
 | 
				
			||||||
      <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
 | 
					      <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,6 +23,9 @@ export class ToggleableDropdownButtonComponent {
 | 
				
			|||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  count: number
 | 
					  count: number
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Input()
 | 
				
			||||||
 | 
					  disabled: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Output()
 | 
					  @Output()
 | 
				
			||||||
  toggle = new EventEmitter()
 | 
					  toggle = new EventEmitter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,7 @@
 | 
				
			|||||||
      <app-filterable-dropdown class="me-2 me-md-3" title="Tags" icon="tag-fill" i18n-title
 | 
					      <app-filterable-dropdown class="me-2 me-md-3" title="Tags" icon="tag-fill" i18n-title
 | 
				
			||||||
        filterPlaceholder="Filter tags" i18n-filterPlaceholder
 | 
					        filterPlaceholder="Filter tags" i18n-filterPlaceholder
 | 
				
			||||||
        [items]="tags"
 | 
					        [items]="tags"
 | 
				
			||||||
 | 
					        [disabled]="!userCanEditAll"
 | 
				
			||||||
        [editing]="true"
 | 
					        [editing]="true"
 | 
				
			||||||
        [multiple]="true"
 | 
					        [multiple]="true"
 | 
				
			||||||
        [applyOnClose]="applyOnClose"
 | 
					        [applyOnClose]="applyOnClose"
 | 
				
			||||||
@@ -38,6 +39,7 @@
 | 
				
			|||||||
      <app-filterable-dropdown class="me-2 me-md-3" title="Correspondent" icon="person-fill" i18n-title
 | 
					      <app-filterable-dropdown class="me-2 me-md-3" title="Correspondent" icon="person-fill" i18n-title
 | 
				
			||||||
        filterPlaceholder="Filter correspondents" i18n-filterPlaceholder
 | 
					        filterPlaceholder="Filter correspondents" i18n-filterPlaceholder
 | 
				
			||||||
        [items]="correspondents"
 | 
					        [items]="correspondents"
 | 
				
			||||||
 | 
					        [disabled]="!userCanEditAll"
 | 
				
			||||||
        [editing]="true"
 | 
					        [editing]="true"
 | 
				
			||||||
        [applyOnClose]="applyOnClose"
 | 
					        [applyOnClose]="applyOnClose"
 | 
				
			||||||
        (open)="openCorrespondentDropdown()"
 | 
					        (open)="openCorrespondentDropdown()"
 | 
				
			||||||
@@ -47,6 +49,7 @@
 | 
				
			|||||||
      <app-filterable-dropdown class="me-2 me-md-3" title="Document type" icon="file-earmark-fill" i18n-title
 | 
					      <app-filterable-dropdown class="me-2 me-md-3" title="Document type" icon="file-earmark-fill" i18n-title
 | 
				
			||||||
        filterPlaceholder="Filter document types" i18n-filterPlaceholder
 | 
					        filterPlaceholder="Filter document types" i18n-filterPlaceholder
 | 
				
			||||||
        [items]="documentTypes"
 | 
					        [items]="documentTypes"
 | 
				
			||||||
 | 
					        [disabled]="!userCanEditAll"
 | 
				
			||||||
        [editing]="true"
 | 
					        [editing]="true"
 | 
				
			||||||
        [applyOnClose]="applyOnClose"
 | 
					        [applyOnClose]="applyOnClose"
 | 
				
			||||||
        (open)="openDocumentTypeDropdown()"
 | 
					        (open)="openDocumentTypeDropdown()"
 | 
				
			||||||
@@ -56,6 +59,7 @@
 | 
				
			|||||||
      <app-filterable-dropdown class="me-2 me-md-3" title="Storage path" icon="folder-fill" i18n-title
 | 
					      <app-filterable-dropdown class="me-2 me-md-3" title="Storage path" icon="folder-fill" i18n-title
 | 
				
			||||||
        filterPlaceholder="Filter storage paths" i18n-filterPlaceholder
 | 
					        filterPlaceholder="Filter storage paths" i18n-filterPlaceholder
 | 
				
			||||||
        [items]="storagePaths"
 | 
					        [items]="storagePaths"
 | 
				
			||||||
 | 
					        [disabled]="!userCanEditAll"
 | 
				
			||||||
        [editing]="true"
 | 
					        [editing]="true"
 | 
				
			||||||
        [applyOnClose]="applyOnClose"
 | 
					        [applyOnClose]="applyOnClose"
 | 
				
			||||||
        (open)="openStoragePathDropdown()"
 | 
					        (open)="openStoragePathDropdown()"
 | 
				
			||||||
@@ -67,7 +71,7 @@
 | 
				
			|||||||
  <div class="col-auto ms-auto mb-2 mb-xl-0 d-flex">
 | 
					  <div class="col-auto ms-auto mb-2 mb-xl-0 d-flex">
 | 
				
			||||||
    <div class="btn-toolbar me-2">
 | 
					    <div class="btn-toolbar me-2">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <button type="button" class="btn btn-sm btn-outline-primary me-2" (click)="setPermissions()">
 | 
					      <button type="button" class="btn btn-sm btn-outline-primary me-2" (click)="setPermissions()" [disabled]="!userOwnsAll">
 | 
				
			||||||
        <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
 | 
					        <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
 | 
				
			||||||
          <use xlink:href="assets/bootstrap-icons.svg#person-fill-lock" />
 | 
					          <use xlink:href="assets/bootstrap-icons.svg#person-fill-lock" />
 | 
				
			||||||
        </svg> <ng-container i18n>Permissions</ng-container>
 | 
					        </svg> <ng-container i18n>Permissions</ng-container>
 | 
				
			||||||
@@ -93,11 +97,11 @@
 | 
				
			|||||||
              <span class="visually-hidden">Preparing download...</span>
 | 
					              <span class="visually-hidden">Preparing download...</span>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </button>
 | 
					          </button>
 | 
				
			||||||
          <button ngbDropdownItem (click)="redoOcrSelected()" i18n>Redo OCR</button>
 | 
					          <button ngbDropdownItem (click)="redoOcrSelected()" [disabled]="!userCanEditAll" i18n>Redo OCR</button>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <button type="button" class="btn btn-sm btn-outline-danger" (click)="applyDelete()" *ifPermissions="{ action: PermissionAction.Delete, type: PermissionType.Document }">
 | 
					    <button type="button" class="btn btn-sm btn-outline-danger" (click)="applyDelete()" *ifPermissions="{ action: PermissionAction.Delete, type: PermissionType.Document }" [disabled]="!userOwnsAll">
 | 
				
			||||||
      <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
 | 
					      <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
 | 
				
			||||||
        <use xlink:href="assets/bootstrap-icons.svg#trash" />
 | 
					        <use xlink:href="assets/bootstrap-icons.svg#trash" />
 | 
				
			||||||
      </svg> <ng-container i18n>Delete</ng-container>
 | 
					      </svg> <ng-container i18n>Delete</ng-container>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
 | 
				
			|||||||
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
 | 
					import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
 | 
				
			||||||
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
 | 
					import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
 | 
				
			||||||
import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
 | 
					import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component'
 | 
				
			||||||
 | 
					import { PermissionsService } from 'src/app/services/permissions.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-bulk-editor',
 | 
					  selector: 'app-bulk-editor',
 | 
				
			||||||
@@ -55,7 +56,8 @@ export class BulkEditorComponent extends ComponentWithPermissions {
 | 
				
			|||||||
    private openDocumentService: OpenDocumentsService,
 | 
					    private openDocumentService: OpenDocumentsService,
 | 
				
			||||||
    private settings: SettingsService,
 | 
					    private settings: SettingsService,
 | 
				
			||||||
    private toastService: ToastService,
 | 
					    private toastService: ToastService,
 | 
				
			||||||
    private storagePathService: StoragePathService
 | 
					    private storagePathService: StoragePathService,
 | 
				
			||||||
 | 
					    private permissionService: PermissionsService
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -67,6 +69,25 @@ export class BulkEditorComponent extends ComponentWithPermissions {
 | 
				
			|||||||
    SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
 | 
					    SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get userCanEditAll(): boolean {
 | 
				
			||||||
 | 
					    let canEdit: boolean = true
 | 
				
			||||||
 | 
					    const docs = this.list.documents.filter((d) => this.list.selected.has(d.id))
 | 
				
			||||||
 | 
					    canEdit = docs.every((d) =>
 | 
				
			||||||
 | 
					      this.permissionService.currentUserHasObjectPermissions(
 | 
				
			||||||
 | 
					        this.PermissionAction.Change,
 | 
				
			||||||
 | 
					        d
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    return canEdit
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get userOwnsAll(): boolean {
 | 
				
			||||||
 | 
					    let ownsAll: boolean = true
 | 
				
			||||||
 | 
					    const docs = this.list.documents.filter((d) => this.list.selected.has(d.id))
 | 
				
			||||||
 | 
					    ownsAll = docs.every((d) => this.permissionService.currentUserOwnsObject(d))
 | 
				
			||||||
 | 
					    return ownsAll
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit() {
 | 
					  ngOnInit() {
 | 
				
			||||||
    this.tagService
 | 
					    this.tagService
 | 
				
			||||||
      .listAll()
 | 
					      .listAll()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user