</div>
<div class="col-auto ms-auto mb-2 mb-xl-0 d-flex">
<div class="btn-group btn-group-sm me-2">
-
<div ngbDropdown class="me-2 d-flex">
<button class="btn btn-sm btn-outline-primary" id="dropdownSelect" ngbDropdownToggle>
<svg class="toolbaricon" fill="currentColor">
<div class="d-none d-sm-inline"> <ng-container i18n>Actions</ng-container></div>
</button>
<div ngbDropdownMenu aria-labelledby="dropdownSelect" class="shadow">
- <button ngbDropdownItem [disabled]="awaitingDownload" (click)="downloadSelected()" i18n>
- Download
- <div *ngIf="awaitingDownload" class="spinner-border spinner-border-sm" role="status">
- <span class="visually-hidden">Preparing download...</span>
+ <button ngbDropdownItem (click)="redoOcrSelected()" i18n>Redo OCR</button>
+ </div>
+ </div>
+ </div>
+
+ <div class="btn-group btn-group-sm me-2">
+ <button class="btn btn-sm btn-outline-primary" [disabled]="awaitingDownload" (click)="downloadSelected()">
+ <svg *ngIf="!awaitingDownload" class="toolbaricon" fill="currentColor">
+ <use xlink:href="assets/bootstrap-icons.svg#arrow-down" />
+ </svg>
+ <div *ngIf="awaitingDownload" class="spinner-border spinner-border-sm" role="status">
+ <span class="visually-hidden">Preparing download...</span>
+ </div>
+ <div class="d-none d-sm-inline"> <ng-container i18n>Download</ng-container></div>
+ </button>
+ <div ngbDropdown class="me-2 d-flex btn-group" role="group">
+ <button type="button" class="btn btn-sm btn-outline-primary dropdown-toggle-split rounded-end" ngbDropdownToggle></button>
+ <div ngbDropdownMenu aria-labelledby="dropdownSelect" class="shadow">
+ <form [formGroup]="downloadForm" class="px-3 py-1">
+ <p class="mb-1" i18n>Include:</p>
+ <div class="form-group ps-3 mb-2">
+ <div class="form-check">
+ <input type="checkbox" class="form-check-input" id="downloadFileType_archive" formControlName="downloadFileTypeArchive" />
+ <label class="form-check-label" for="downloadFileType_archive" i18n>
+ Archived files
+ </label>
+ </div>
+ <div class="form-check">
+ <input type="checkbox" class="form-check-input" id="downloadFileType_originals" formControlName="downloadFileTypeOriginals" />
+ <label class="form-check-label" for="downloadFileType_originals" i18n>
+ Original files
+ </label>
+ </div>
</div>
- </button>
- <button ngbDropdownItem [disabled]="awaitingDownload" (click)="downloadSelected('originals')" i18n>
- Download originals
- <div *ngIf="awaitingDownload" class="spinner-border spinner-border-sm" role="status">
- <span class="visually-hidden">Preparing download...</span>
+ <div class="form-check">
+ <input type="checkbox" class="form-check-input" id="downloadUseFormatting" formControlName="downloadUseFormatting" />
+ <label class="form-check-label" for="downloadUseFormatting" i18n>
+ Use formatted filename
+ </label>
</div>
- </button>
- <button ngbDropdownItem (click)="redoOcrSelected()" i18n>Redo OCR</button>
+ </form>
</div>
</div>
+ </div>
- <button type="button" class="btn btn-sm btn-outline-danger" (click)="applyDelete()">
- <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
- <use xlink:href="assets/bootstrap-icons.svg#trash" />
- </svg> <ng-container i18n>Delete</ng-container>
- </button>
+ <div class="btn-group btn-group-sm me-2">
+ <button type="button" class="btn btn-sm btn-outline-danger" (click)="applyDelete()">
+ <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
+ <use xlink:href="assets/bootstrap-icons.svg#trash" />
+ </svg> <ng-container i18n>Delete</ng-container>
+ </button>
+ </div>
</div>
</div>
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
+import { FormControl, FormGroup } from '@angular/forms'
+import { first, Subject, takeUntil } from 'rxjs'
@Component({
selector: 'app-bulk-editor',
storagePathsSelectionModel = new FilterableDropdownSelectionModel()
awaitingDownload: boolean
+ unsubscribeNotifier: Subject<any> = new Subject()
+
+ downloadForm = new FormGroup({
+ downloadFileTypeArchive: new FormControl(true),
+ downloadFileTypeOriginals: new FormControl(false),
+ downloadUseFormatting: new FormControl(false),
+ })
+
constructor(
private documentTypeService: DocumentTypeService,
private tagService: TagService,
ngOnInit() {
this.tagService
.listAll()
+ .pipe(first())
.subscribe((result) => (this.tags = result.results))
this.correspondentService
.listAll()
+ .pipe(first())
.subscribe((result) => (this.correspondents = result.results))
this.documentTypeService
.listAll()
+ .pipe(first())
.subscribe((result) => (this.documentTypes = result.results))
this.storagePathService
.listAll()
+ .pipe(first())
.subscribe((result) => (this.storagePaths = result.results))
+
+ this.downloadForm
+ .get('downloadFileTypeArchive')
+ .valueChanges.pipe(takeUntil(this.unsubscribeNotifier))
+ .subscribe((newValue) => {
+ if (!newValue) {
+ this.downloadForm
+ .get('downloadFileTypeOriginals')
+ .patchValue(true, { emitEvent: false })
+ }
+ })
+ this.downloadForm
+ .get('downloadFileTypeOriginals')
+ .valueChanges.pipe(takeUntil(this.unsubscribeNotifier))
+ .subscribe((newValue) => {
+ if (!newValue) {
+ this.downloadForm
+ .get('downloadFileTypeArchive')
+ .patchValue(true, { emitEvent: false })
+ }
+ })
+ }
+
+ ngOnDestroy(): void {
+ this.unsubscribeNotifier.next(this)
+ this.unsubscribeNotifier.complete()
}
private executeBulkOperation(modal, method: string, args) {
}
this.documentService
.bulkEdit(Array.from(this.list.selected), method, args)
- .subscribe(
- (response) => {
+ .pipe(first())
+ .subscribe({
+ next: () => {
this.list.reload()
this.list.reduceSelectionToFilter()
this.list.selected.forEach((id) => {
modal.close()
}
},
- (error) => {
+ error: (error) => {
if (modal) {
modal.componentInstance.buttonsEnabled = true
}
error.error
)}`
)
- }
- )
+ },
+ })
}
private applySelectionData(
openTagsDropdown() {
this.documentService
.getSelectionData(Array.from(this.list.selected))
+ .pipe(first())
.subscribe((s) => {
this.applySelectionData(s.selected_tags, this.tagSelectionModel)
})
openDocumentTypeDropdown() {
this.documentService
.getSelectionData(Array.from(this.list.selected))
+ .pipe(first())
.subscribe((s) => {
this.applySelectionData(
s.selected_document_types,
openCorrespondentDropdown() {
this.documentService
.getSelectionData(Array.from(this.list.selected))
+ .pipe(first())
.subscribe((s) => {
this.applySelectionData(
s.selected_correspondents,
openStoragePathDropdown() {
this.documentService
.getSelectionData(Array.from(this.list.selected))
+ .pipe(first())
.subscribe((s) => {
this.applySelectionData(
s.selected_storage_paths,
modal.componentInstance.btnClass = 'btn-warning'
modal.componentInstance.btnCaption = $localize`Confirm`
- modal.componentInstance.confirmClicked.subscribe(() => {
- this.executeBulkOperation(modal, 'modify_tags', {
- add_tags: changedTags.itemsToAdd.map((t) => t.id),
- remove_tags: changedTags.itemsToRemove.map((t) => t.id),
+ modal.componentInstance.confirmClicked
+ .pipe(takeUntil(this.unsubscribeNotifier))
+ .subscribe(() => {
+ this.executeBulkOperation(modal, 'modify_tags', {
+ add_tags: changedTags.itemsToAdd.map((t) => t.id),
+ remove_tags: changedTags.itemsToRemove.map((t) => t.id),
+ })
})
- })
} else {
this.executeBulkOperation(null, 'modify_tags', {
add_tags: changedTags.itemsToAdd.map((t) => t.id),
}
modal.componentInstance.btnClass = 'btn-warning'
modal.componentInstance.btnCaption = $localize`Confirm`
- modal.componentInstance.confirmClicked.subscribe(() => {
- this.executeBulkOperation(modal, 'set_correspondent', {
- correspondent: correspondent ? correspondent.id : null,
+ modal.componentInstance.confirmClicked
+ .pipe(takeUntil(this.unsubscribeNotifier))
+ .subscribe(() => {
+ this.executeBulkOperation(modal, 'set_correspondent', {
+ correspondent: correspondent ? correspondent.id : null,
+ })
})
- })
} else {
this.executeBulkOperation(null, 'set_correspondent', {
correspondent: correspondent ? correspondent.id : null,
}
modal.componentInstance.btnClass = 'btn-warning'
modal.componentInstance.btnCaption = $localize`Confirm`
- modal.componentInstance.confirmClicked.subscribe(() => {
- this.executeBulkOperation(modal, 'set_document_type', {
- document_type: documentType ? documentType.id : null,
+ modal.componentInstance.confirmClicked
+ .pipe(takeUntil(this.unsubscribeNotifier))
+ .subscribe(() => {
+ this.executeBulkOperation(modal, 'set_document_type', {
+ document_type: documentType ? documentType.id : null,
+ })
})
- })
} else {
this.executeBulkOperation(null, 'set_document_type', {
document_type: documentType ? documentType.id : null,
}
modal.componentInstance.btnClass = 'btn-warning'
modal.componentInstance.btnCaption = $localize`Confirm`
- modal.componentInstance.confirmClicked.subscribe(() => {
- this.executeBulkOperation(modal, 'set_storage_path', {
- storage_path: storagePath ? storagePath.id : null,
+ modal.componentInstance.confirmClicked
+ .pipe(takeUntil(this.unsubscribeNotifier))
+ .subscribe(() => {
+ this.executeBulkOperation(modal, 'set_storage_path', {
+ storage_path: storagePath ? storagePath.id : null,
+ })
})
- })
} else {
this.executeBulkOperation(null, 'set_storage_path', {
storage_path: storagePath ? storagePath.id : null,
modal.componentInstance.message = $localize`This operation cannot be undone.`
modal.componentInstance.btnClass = 'btn-danger'
modal.componentInstance.btnCaption = $localize`Delete document(s)`
- modal.componentInstance.confirmClicked.subscribe(() => {
- modal.componentInstance.buttonsEnabled = false
- this.executeBulkOperation(modal, 'delete', {})
- })
+ modal.componentInstance.confirmClicked
+ .pipe(takeUntil(this.unsubscribeNotifier))
+ .subscribe(() => {
+ modal.componentInstance.buttonsEnabled = false
+ this.executeBulkOperation(modal, 'delete', {})
+ })
}
- downloadSelected(content = 'archive') {
+ downloadSelected() {
this.awaitingDownload = true
+ let downloadFileType: string =
+ this.downloadForm.get('downloadFileTypeArchive').value &&
+ this.downloadForm.get('downloadFileTypeOriginals').value
+ ? 'both'
+ : this.downloadForm.get('downloadFileTypeArchive').value
+ ? 'archive'
+ : 'originals'
this.documentService
- .bulkDownload(Array.from(this.list.selected), content)
+ .bulkDownload(
+ Array.from(this.list.selected),
+ downloadFileType,
+ this.downloadForm.get('downloadUseFormatting').value
+ )
+ .pipe(first())
.subscribe((result: any) => {
saveAs(result, 'documents.zip')
this.awaitingDownload = false
modal.componentInstance.message = $localize`This operation cannot be undone.`
modal.componentInstance.btnClass = 'btn-danger'
modal.componentInstance.btnCaption = $localize`Proceed`
- modal.componentInstance.confirmClicked.subscribe(() => {
- modal.componentInstance.buttonsEnabled = false
- this.executeBulkOperation(modal, 'redo_ocr', {})
- })
+ modal.componentInstance.confirmClicked
+ .pipe(takeUntil(this.unsubscribeNotifier))
+ .subscribe(() => {
+ modal.componentInstance.buttonsEnabled = false
+ this.executeBulkOperation(modal, 'redo_ocr', {})
+ })
}
}