From 6834c70baee677aae1bdcbcfaa68ee81622c8d53 Mon Sep 17 00:00:00 2001 From: Jo Vandeginste Date: Mon, 28 Dec 2020 22:19:30 +0100 Subject: [PATCH 01/20] Allow extending INSTALLED_APPS via environment This allows a user to add "apps" (aka parsers) through the environment. Especially useful when using Docker, and adding a test-parser. Usage: ```yaml services: webserver: environment: PAPERLESS_APPS: paperless_tika.apps.PaperlessTikaConfig ``` You can add more by separating them with a `,`: ```yaml PAPERLESS_APPS: app1,app2 ``` --- src/paperless/settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index c6f7c9357..d7cae943b 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -69,6 +69,8 @@ SCRATCH_DIR = os.getenv("PAPERLESS_SCRATCH_DIR", "/tmp/paperless") # Application Definition # ############################################################################### +env_apps = os.getenv("PAPERLESS_APPS") if os.getenv("PAPERLESS_APPS") else [] + INSTALLED_APPS = [ "whitenoise.runserver_nostatic", @@ -95,7 +97,7 @@ INSTALLED_APPS = [ "django_q", -] +] + env_apps REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ From 755da317eaf8dab2e5894b0953ffd230268dc0c0 Mon Sep 17 00:00:00 2001 From: Jo Vandeginste Date: Mon, 28 Dec 2020 22:37:53 +0100 Subject: [PATCH 02/20] Update settings.py --- src/paperless/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index d7cae943b..0aa162739 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -69,7 +69,7 @@ SCRATCH_DIR = os.getenv("PAPERLESS_SCRATCH_DIR", "/tmp/paperless") # Application Definition # ############################################################################### -env_apps = os.getenv("PAPERLESS_APPS") if os.getenv("PAPERLESS_APPS") else [] +env_apps = os.getenv("PAPERLESS_APPS").split(",") if os.getenv("PAPERLESS_APPS") else [] INSTALLED_APPS = [ "whitenoise.runserver_nostatic", From 1dd2386fa507a491b18fbbfa1b7f94ae1a1258e3 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Mon, 28 Dec 2020 23:53:18 +0100 Subject: [PATCH 03/20] messed up, restore functionality --- .../document-list/bulk-editor/bulk-editor.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 1e2e8496f..f8520b84d 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -147,7 +147,7 @@ export class BulkEditorComponent { modal.componentInstance.btnClass = "btn-warning" modal.componentInstance.btnCaption = $localize`Confirm` modal.componentInstance.confirmClicked.subscribe(() => { - this.executeBulkOperation('set_correspondent', {"correspondent": correspondent?.id}).subscribe( + this.executeBulkOperation('set_correspondent', {"correspondent": correspondent ? correspondent.id : null}).subscribe( response => { this.correspondentService.clearCache() modal.close() @@ -170,7 +170,7 @@ export class BulkEditorComponent { modal.componentInstance.btnClass = "btn-warning" modal.componentInstance.btnCaption = $localize`Confirm` modal.componentInstance.confirmClicked.subscribe(() => { - this.executeBulkOperation('set_document_type', {"document_type": documentType?.id}).subscribe( + this.executeBulkOperation('set_document_type', {"document_type": documentType ? documentType.id : null}).subscribe( response => { this.documentService.clearCache() modal.close() From d6285cc85147e7dfd9ee9bb9c9457971e9211392 Mon Sep 17 00:00:00 2001 From: Marco Aceti Date: Tue, 29 Dec 2020 01:57:46 +0100 Subject: [PATCH 04/20] Add libxslt-dev library to Dockerfile build --- docker/local/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/local/Dockerfile b/docker/local/Dockerfile index 9fc71abc7..4c35eec3a 100644 --- a/docker/local/Dockerfile +++ b/docker/local/Dockerfile @@ -22,6 +22,7 @@ RUN apt-get update \ libpq-dev \ libqpdf-dev \ libxml2 \ + libxslt-dev \ optipng \ pngquant \ qpdf \ From 39d1c051cf7bddf515ea08d08c6de66ac77ecc46 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 02:25:39 +0100 Subject: [PATCH 05/20] use the actual name of the package #205 --- docker/local/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/local/Dockerfile b/docker/local/Dockerfile index 4c35eec3a..1fd5e24fa 100644 --- a/docker/local/Dockerfile +++ b/docker/local/Dockerfile @@ -22,7 +22,7 @@ RUN apt-get update \ libpq-dev \ libqpdf-dev \ libxml2 \ - libxslt-dev \ + libxslt1-dev \ optipng \ pngquant \ qpdf \ From f964dd59353ae30f1c3e940a2938833211e8b6cb Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 12:26:41 +0100 Subject: [PATCH 06/20] added configuration option for the font #197 #207 --- docs/configuration.rst | 9 +++++++++ paperless.conf.example | 1 + src/paperless/settings.py | 2 ++ src/paperless_text/parsers.py | 6 +++--- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/configuration.rst b/docs/configuration.rst index efc1a9db1..5ccb80b3a 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -400,6 +400,15 @@ PAPERLESS_FILENAME_DATE_ORDER= Defaults to none, which disables this feature. +PAPERLESS_THUMBNAIL_FONT_NAME= + Paperless creates thumbnails for plain text files by rendering the content + of the file on an image and uses a predefined font for that. This + font can be changed here. + + Note that this won't have any effect on already generated thumbnails. + + Defaults to ``/usr/share/fonts/liberation/LiberationSerif-Regular.ttf``. + Binaries ######## diff --git a/paperless.conf.example b/paperless.conf.example index 910fc22a0..139453cf3 100644 --- a/paperless.conf.example +++ b/paperless.conf.example @@ -54,6 +54,7 @@ #PAPERLESS_POST_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh #PAPERLESS_FILENAME_DATE_ORDER=YMD #PAPERLESS_FILENAME_PARSE_TRANSFORMS=[] +#PAPERLESS_THUMBNAIL_FONT_NAME= # Binaries diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 0aa162739..5af1be85e 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -422,3 +422,5 @@ for t in json.loads(os.getenv("PAPERLESS_FILENAME_PARSE_TRANSFORMS", "[]")): # TODO: this should not have a prefix. # Specify the filename format for out files PAPERLESS_FILENAME_FORMAT = os.getenv("PAPERLESS_FILENAME_FORMAT") + +THUMBNAIL_FONT_NAME = os.getenv("PAPERLESS_THUMBNAIL_FONT_NAME", "/usr/share/fonts/liberation/LiberationSerif-Regular.ttf") diff --git a/src/paperless_text/parsers.py b/src/paperless_text/parsers.py index 030c2c2c2..a38bd7a91 100644 --- a/src/paperless_text/parsers.py +++ b/src/paperless_text/parsers.py @@ -1,10 +1,9 @@ import os -import subprocess from PIL import ImageDraw, ImageFont, Image from django.conf import settings -from documents.parsers import DocumentParser, ParseError +from documents.parsers import DocumentParser class TextDocumentParser(DocumentParser): @@ -23,7 +22,8 @@ class TextDocumentParser(DocumentParser): img = Image.new("RGB", (500, 700), color="white") draw = ImageDraw.Draw(img) font = ImageFont.truetype( - "/usr/share/fonts/liberation/LiberationSerif-Regular.ttf", 20, + font=settings.THUMBNAIL_FONT_NAME, + size=20, layout_engine=ImageFont.LAYOUT_BASIC) draw.text((5, 5), read_text(), font=font, fill="black") From b2327d6fde161b0a22f245c795273f1689572361 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 17:09:07 +0100 Subject: [PATCH 07/20] more settings --- .../filterable-dropdown.component.ts | 10 +- .../bulk-editor/bulk-editor.component.html | 3 + .../bulk-editor/bulk-editor.component.ts | 134 +++++++++++------- .../manage/settings/settings.component.html | 7 + .../manage/settings/settings.component.ts | 13 +- src-ui/src/app/data/storage-keys.ts | 5 - .../services/document-list-view.service.ts | 9 +- .../src/app/services/settings.service.spec.ts | 16 +++ src-ui/src/app/services/settings.service.ts | 60 ++++++++ 9 files changed, 194 insertions(+), 63 deletions(-) create mode 100644 src-ui/src/app/services/settings.service.spec.ts create mode 100644 src-ui/src/app/services/settings.service.ts diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts index 06b2333d5..0e075ff19 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts @@ -195,6 +195,9 @@ export class FilterableDropdownComponent { @Input() editing = false + @Input() + applyOnClose = false + @Output() apply = new EventEmitter() @@ -208,7 +211,9 @@ export class FilterableDropdownComponent { applyClicked() { if (this.selectionModel.isDirty()) { this.dropdown.close() - this.apply.emit(this.selectionModel.diff()) + if (!this.applyOnClose) { + this.apply.emit(this.selectionModel.diff()) + } } } @@ -223,6 +228,9 @@ export class FilterableDropdownComponent { this.open.next() } else { this.filterText = '' + if (this.applyOnClose && this.selectionModel.isDirty()) { + this.apply.emit(this.selectionModel.diff()) + } } } diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html index e08d50c47..62a2bb95d 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.html @@ -30,6 +30,7 @@ [items]="tags" [editing]="true" [multiple]="true" + [applyOnClose]="applyOnClose" (open)="openTagsDropdown()" [(selectionModel)]="tagSelectionModel" (apply)="setTags($event)"> @@ -37,6 +38,7 @@ @@ -44,6 +46,7 @@ diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index f8520b84d..64b84b47a 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -15,6 +15,7 @@ import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog import { ChangedItems, FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component'; import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'; import { MatchingModel } from 'src/app/data/matching-model'; +import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; @Component({ selector: 'app-bulk-editor', @@ -38,9 +39,13 @@ export class BulkEditorComponent { public list: DocumentListViewService, private documentService: DocumentService, private modalService: NgbModal, - private openDocumentService: OpenDocumentsService + private openDocumentService: OpenDocumentsService, + private settings: SettingsService ) { } + applyOnClose: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE) + showConfirmationDialogs: boolean = this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS) + ngOnInit() { this.tagService.listAll().subscribe(result => this.tags = result.results) this.correspondentService.listAll().subscribe(result => this.correspondents = result.results) @@ -54,7 +59,6 @@ export class BulkEditorComponent { this.list.selected.forEach(id => { this.openDocumentService.refreshDocument(id) }) - this.list.selectNone() }) ) } @@ -105,30 +109,40 @@ export class BulkEditorComponent { setTags(changedTags: ChangedItems) { if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 0) return - let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) - modal.componentInstance.title = $localize`Confirm tags assignment` - if (changedTags.itemsToAdd.length == 1 && changedTags.itemsToRemove.length == 0) { - let tag = changedTags.itemsToAdd[0] - modal.componentInstance.message = $localize`This operation will add the tag ${tag.name} to all ${this.list.selected.size} selected document(s).` - } else if (changedTags.itemsToAdd.length > 1 && changedTags.itemsToRemove.length == 0) { - modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} to all ${this.list.selected.size} selected document(s).` - } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 1) { - let tag = changedTags.itemsToAdd[0] - modal.componentInstance.message = $localize`This operation will remove the tag ${tag.name} from all ${this.list.selected.size} selected document(s).` - } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length > 1) { - modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(changedTags.itemsToRemove)} from all ${this.list.selected.size} selected document(s).` + if (this.showConfirmationDialogs) { + let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) + modal.componentInstance.title = $localize`Confirm tags assignment` + if (changedTags.itemsToAdd.length == 1 && changedTags.itemsToRemove.length == 0) { + let tag = changedTags.itemsToAdd[0] + modal.componentInstance.message = $localize`This operation will add the tag ${tag.name} to all ${this.list.selected.size} selected document(s).` + } else if (changedTags.itemsToAdd.length > 1 && changedTags.itemsToRemove.length == 0) { + modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} to all ${this.list.selected.size} selected document(s).` + } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 1) { + let tag = changedTags.itemsToAdd[0] + modal.componentInstance.message = $localize`This operation will remove the tag ${tag.name} from all ${this.list.selected.size} selected document(s).` + } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length > 1) { + modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(changedTags.itemsToRemove)} from all ${this.list.selected.size} selected document(s).` + } else { + modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} and remove the tags ${this._localizeList(changedTags.itemsToRemove)} on all ${this.list.selected.size} selected document(s).` + } + + modal.componentInstance.btnClass = "btn-warning" + modal.componentInstance.btnCaption = $localize`Confirm` + modal.componentInstance.confirmClicked.subscribe(() => { + this.performSetTags(modal, changedTags) + }) } else { - modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} and remove the tags ${this._localizeList(changedTags.itemsToRemove)} on all ${this.list.selected.size} selected document(s).` + this.performSetTags(null, changedTags) } - - modal.componentInstance.btnClass = "btn-warning" - modal.componentInstance.btnCaption = $localize`Confirm` - modal.componentInstance.confirmClicked.subscribe(() => { - this.executeBulkOperation('modify_tags', {"add_tags": changedTags.itemsToAdd.map(t => t.id), "remove_tags": changedTags.itemsToRemove.map(t => t.id)}).subscribe( - response => { - this.tagService.clearCache() + } + + private performSetTags(modal, changedTags: ChangedItems) { + this.executeBulkOperation('modify_tags', {"add_tags": changedTags.itemsToAdd.map(t => t.id), "remove_tags": changedTags.itemsToRemove.map(t => t.id)}).subscribe( + response => { + this.tagService.clearCache() + if (modal) { modal.close() - }) + } } ) } @@ -136,47 +150,69 @@ export class BulkEditorComponent { setCorrespondents(changedCorrespondents: ChangedItems) { if (changedCorrespondents.itemsToAdd.length == 0 && changedCorrespondents.itemsToRemove.length == 0) return - let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) - modal.componentInstance.title = $localize`Confirm correspondent assignment` let correspondent = changedCorrespondents.itemsToAdd.length > 0 ? changedCorrespondents.itemsToAdd[0] : null - if (correspondent) { - modal.componentInstance.message = $localize`This operation will assign the correspondent ${correspondent.name} to all ${this.list.selected.size} selected document(s).` + + if (this.showConfirmationDialogs) { + let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) + modal.componentInstance.title = $localize`Confirm correspondent assignment` + if (correspondent) { + modal.componentInstance.message = $localize`This operation will assign the correspondent ${correspondent.name} to all ${this.list.selected.size} selected document(s).` + } else { + modal.componentInstance.message = $localize`This operation will remove the correspondent from all ${this.list.selected.size} selected document(s).` + } + modal.componentInstance.btnClass = "btn-warning" + modal.componentInstance.btnCaption = $localize`Confirm` + modal.componentInstance.confirmClicked.subscribe(() => { + this.performSetCorrespondents(modal, correspondent) + }) } else { - modal.componentInstance.message = $localize`This operation will remove the correspondent from all ${this.list.selected.size} selected document(s).` + this.performSetCorrespondents(null, correspondent) } - modal.componentInstance.btnClass = "btn-warning" - modal.componentInstance.btnCaption = $localize`Confirm` - modal.componentInstance.confirmClicked.subscribe(() => { - this.executeBulkOperation('set_correspondent', {"correspondent": correspondent ? correspondent.id : null}).subscribe( - response => { - this.correspondentService.clearCache() + } + + private performSetCorrespondents(modal, correspondent: MatchingModel) { + this.executeBulkOperation('set_correspondent', {"correspondent": correspondent ? correspondent.id : null}).subscribe( + response => { + this.correspondentService.clearCache() + if (modal) { modal.close() } - ) - }) + } + ) } setDocumentTypes(changedDocumentTypes: ChangedItems) { if (changedDocumentTypes.itemsToAdd.length == 0 && changedDocumentTypes.itemsToRemove.length == 0) return - let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) - modal.componentInstance.title = $localize`Confirm document type assignment` let documentType = changedDocumentTypes.itemsToAdd.length > 0 ? changedDocumentTypes.itemsToAdd[0] : null - if (documentType) { - modal.componentInstance.message = $localize`This operation will assign the document type ${documentType.name} to all ${this.list.selected.size} selected document(s).` + + if (this.showConfirmationDialogs) { + let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) + modal.componentInstance.title = $localize`Confirm document type assignment` + if (documentType) { + modal.componentInstance.message = $localize`This operation will assign the document type ${documentType.name} to all ${this.list.selected.size} selected document(s).` + } else { + modal.componentInstance.message = $localize`This operation will remove the document type from all ${this.list.selected.size} selected document(s).` + } + modal.componentInstance.btnClass = "btn-warning" + modal.componentInstance.btnCaption = $localize`Confirm` + modal.componentInstance.confirmClicked.subscribe(() => { + this.performSetDocumentTypes(modal, documentType) + }) } else { - modal.componentInstance.message = $localize`This operation will remove the document type from all ${this.list.selected.size} selected document(s).` + this.performSetDocumentTypes(null, documentType) } - modal.componentInstance.btnClass = "btn-warning" - modal.componentInstance.btnCaption = $localize`Confirm` - modal.componentInstance.confirmClicked.subscribe(() => { - this.executeBulkOperation('set_document_type', {"document_type": documentType ? documentType.id : null}).subscribe( - response => { - this.documentService.clearCache() + } + + private performSetDocumentTypes(modal, documentType) { + this.executeBulkOperation('set_document_type', {"document_type": documentType ? documentType.id : null}).subscribe( + response => { + this.documentTypeService.clearCache() + if (modal) { modal.close() } - ) - }) + } + ) } applyDelete() { diff --git a/src-ui/src/app/components/manage/settings/settings.component.html b/src-ui/src/app/components/manage/settings/settings.component.html index 6c16cfaa8..1f5374dba 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.html +++ b/src-ui/src/app/components/manage/settings/settings.component.html @@ -26,8 +26,15 @@ + + +

Bulk editing

+ + + +
  • diff --git a/src-ui/src/app/components/manage/settings/settings.component.ts b/src-ui/src/app/components/manage/settings/settings.component.ts index a90966bb9..c26c63384 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; -import { GENERAL_SETTINGS } from 'src/app/data/storage-keys'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { SavedViewService } from 'src/app/services/rest/saved-view.service'; +import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; import { ToastService } from 'src/app/services/toast.service'; @Component({ @@ -16,14 +16,17 @@ export class SettingsComponent implements OnInit { savedViewGroup = new FormGroup({}) settingsForm = new FormGroup({ - 'documentListItemPerPage': new FormControl(+localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT), + 'bulkEditConfirmationDialogs': new FormControl(this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS)), + 'bulkEditApplyOnClose': new FormControl(this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE)), + 'documentListItemPerPage': new FormControl(this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)), 'savedViews': this.savedViewGroup }) constructor( public savedViewService: SavedViewService, private documentListViewService: DocumentListViewService, - private toastService: ToastService + private toastService: ToastService, + private settings: SettingsService ) { } savedViews: PaperlessSavedView[] @@ -51,7 +54,9 @@ export class SettingsComponent implements OnInit { } private saveLocalSettings() { - localStorage.setItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage) + this.settings.set(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, this.settingsForm.value.bulkEditApplyOnClose) + this.settings.set(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS, this.settingsForm.value.bulkEditConfirmationDialogs) + this.settings.set(SETTINGS_KEYS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage) this.documentListViewService.updatePageSize() this.toastService.showInfo($localize`Settings saved successfully.`) } diff --git a/src-ui/src/app/data/storage-keys.ts b/src-ui/src/app/data/storage-keys.ts index 13b41d4a7..ec91c06ac 100644 --- a/src-ui/src/app/data/storage-keys.ts +++ b/src-ui/src/app/data/storage-keys.ts @@ -5,8 +5,3 @@ export const OPEN_DOCUMENT_SERVICE = { export const DOCUMENT_LIST_SERVICE = { CURRENT_VIEW_CONFIG: 'document-list-service:currentViewConfig' } - -export const GENERAL_SETTINGS = { - DOCUMENT_LIST_SIZE: 'general-settings:documentListSize', - DOCUMENT_LIST_SIZE_DEFAULT: 50 -} \ No newline at end of file diff --git a/src-ui/src/app/services/document-list-view.service.ts b/src-ui/src/app/services/document-list-view.service.ts index b148d4087..ab78d78b1 100644 --- a/src-ui/src/app/services/document-list-view.service.ts +++ b/src-ui/src/app/services/document-list-view.service.ts @@ -3,8 +3,9 @@ import { Observable } from 'rxjs'; import { cloneFilterRules, FilterRule } from '../data/filter-rule'; import { PaperlessDocument } from '../data/paperless-document'; import { PaperlessSavedView } from '../data/paperless-saved-view'; -import { DOCUMENT_LIST_SERVICE, GENERAL_SETTINGS } from '../data/storage-keys'; +import { DOCUMENT_LIST_SERVICE } from '../data/storage-keys'; import { DocumentService } from './rest/document.service'; +import { SettingsService, SETTINGS_KEYS } from './settings.service'; /** @@ -23,7 +24,7 @@ export class DocumentListViewService { isReloading: boolean = false documents: PaperlessDocument[] = [] currentPage = 1 - currentPageSize: number = +localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT + currentPageSize: number = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE) collectionSize: number /** @@ -190,7 +191,7 @@ export class DocumentListViewService { } updatePageSize() { - let newPageSize = +localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT + let newPageSize = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE) if (newPageSize != this.currentPageSize) { this.currentPageSize = newPageSize } @@ -239,7 +240,7 @@ export class DocumentListViewService { } } - constructor(private documentService: DocumentService) { + constructor(private documentService: DocumentService, private settings: SettingsService) { let documentListViewConfigJson = sessionStorage.getItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG) if (documentListViewConfigJson) { try { diff --git a/src-ui/src/app/services/settings.service.spec.ts b/src-ui/src/app/services/settings.service.spec.ts new file mode 100644 index 000000000..359cb6b7a --- /dev/null +++ b/src-ui/src/app/services/settings.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { SettingsService } from './settings.service'; + +describe('SettingsService', () => { + let service: SettingsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(SettingsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts new file mode 100644 index 000000000..00e6ff639 --- /dev/null +++ b/src-ui/src/app/services/settings.service.ts @@ -0,0 +1,60 @@ +import { Injectable } from '@angular/core'; + +export interface PaperlessSettings { + key: string + type: string + default: any +} + +export const SETTINGS_KEYS = { + BULK_EDIT_CONFIRMATION_DIALOGS: 'general-settings:bulk-edit:confirmation-dialogs', + BULK_EDIT_APPLY_ON_CLOSE: 'general-settings:bulk-edit:apply-on-close', + DOCUMENT_LIST_SIZE: 'general-settings:documentListSize', +} + +const SETTINGS: PaperlessSettings[] = [ + {key: SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS, type: "boolean", default: true}, + {key: SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, type: "boolean", default: false}, + {key: SETTINGS_KEYS.DOCUMENT_LIST_SIZE, type: "number", default: 50} +] + +@Injectable({ + providedIn: 'root' +}) +export class SettingsService { + + constructor() { } + + get(key: string): any { + let setting = SETTINGS.find(s => s.key == key) + + if (!setting) { + return null + } + + let value = localStorage.getItem(key) + + if (value != null) { + switch (setting.type) { + case "boolean": + return JSON.parse(value) + case "number": + return +value + case "string": + return value + default: + return value + } + } else { + return setting.default + } + } + + set(key: string, value: any) { + localStorage.setItem(key, value.toString()) + } + + unset(key: string) { + localStorage.removeItem(key) + } +} From 7f4cfc0b76496ab60da4bf85ef2b1462e4197095 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 17:20:45 +0100 Subject: [PATCH 08/20] fix a couple issues with the bulk editor --- .../document-list/bulk-editor/bulk-editor.component.ts | 6 ++---- src-ui/src/app/services/document-list-view.service.ts | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 64b84b47a..88efcee06 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -56,6 +56,7 @@ export class BulkEditorComponent { return this.documentService.bulkEdit(Array.from(this.list.selected), method, args).pipe( tap(() => { this.list.reload() + this.list.reduceSelectionToFilter() this.list.selected.forEach(id => { this.openDocumentService.refreshDocument(id) }) @@ -118,7 +119,7 @@ export class BulkEditorComponent { } else if (changedTags.itemsToAdd.length > 1 && changedTags.itemsToRemove.length == 0) { modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} to all ${this.list.selected.size} selected document(s).` } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 1) { - let tag = changedTags.itemsToAdd[0] + let tag = changedTags.itemsToRemove[0] modal.componentInstance.message = $localize`This operation will remove the tag ${tag.name} from all ${this.list.selected.size} selected document(s).` } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length > 1) { modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(changedTags.itemsToRemove)} from all ${this.list.selected.size} selected document(s).` @@ -139,7 +140,6 @@ export class BulkEditorComponent { private performSetTags(modal, changedTags: ChangedItems) { this.executeBulkOperation('modify_tags', {"add_tags": changedTags.itemsToAdd.map(t => t.id), "remove_tags": changedTags.itemsToRemove.map(t => t.id)}).subscribe( response => { - this.tagService.clearCache() if (modal) { modal.close() } @@ -173,7 +173,6 @@ export class BulkEditorComponent { private performSetCorrespondents(modal, correspondent: MatchingModel) { this.executeBulkOperation('set_correspondent', {"correspondent": correspondent ? correspondent.id : null}).subscribe( response => { - this.correspondentService.clearCache() if (modal) { modal.close() } @@ -207,7 +206,6 @@ export class BulkEditorComponent { private performSetDocumentTypes(modal, documentType) { this.executeBulkOperation('set_document_type', {"document_type": documentType ? documentType.id : null}).subscribe( response => { - this.documentTypeService.clearCache() if (modal) { modal.close() } diff --git a/src-ui/src/app/services/document-list-view.service.ts b/src-ui/src/app/services/document-list-view.service.ts index ab78d78b1..dfcf9c0c5 100644 --- a/src-ui/src/app/services/document-list-view.service.ts +++ b/src-ui/src/app/services/document-list-view.service.ts @@ -203,7 +203,7 @@ export class DocumentListViewService { this.selected.clear() } - private reduceSelectionToFilter() { + reduceSelectionToFilter() { if (this.selected.size > 0) { this.documentService.listAllFilteredIds(this.filterRules).subscribe(ids => { let subset = new Set() From 2b341afcaef82accf97871f82302866e6931c1bf Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 17:32:56 +0100 Subject: [PATCH 09/20] fix some bugs with the filter editor --- .../filter-editor/filter-editor.component.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts index d4cac1a9d..de6d724bc 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts @@ -80,13 +80,16 @@ export class FilterEditorComponent implements OnInit, OnDestroy { this.dateAddedBefore = rule.value break case FILTER_HAS_TAG: - this.tagSelectionModel.set(+rule.value, ToggleableItemState.Selected, false) + this.tagSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) + break + case FILTER_HAS_ANY_TAG: + this.tagSelectionModel.set(null, ToggleableItemState.Selected, false) break case FILTER_CORRESPONDENT: - this.correspondentSelectionModel.set(+rule.value, ToggleableItemState.Selected, false) + this.correspondentSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) break case FILTER_DOCUMENT_TYPE: - this.documentTypeSelectionModel.set(+rule.value, ToggleableItemState.Selected, false) + this.documentTypeSelectionModel.set(rule.value ? +rule.value : null, ToggleableItemState.Selected, false) break } }) From 5c3ae44021c284f93a74cc41857a2dd8fb5710c6 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 18:23:08 +0100 Subject: [PATCH 10/20] fix up gunicorn conf --- docker/gunicorn.conf.py | 2 +- docker/supervisord.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/gunicorn.conf.py b/docker/gunicorn.conf.py index 88d881664..5c9c0eb37 100644 --- a/docker/gunicorn.conf.py +++ b/docker/gunicorn.conf.py @@ -1,4 +1,4 @@ -bind = '[::]:8000' +bind = ['[::]:8000', 'localhost:8000'] backlog = 2048 workers = 3 worker_class = 'sync' diff --git a/docker/supervisord.conf b/docker/supervisord.conf index ff3ed4311..9b97b6825 100644 --- a/docker/supervisord.conf +++ b/docker/supervisord.conf @@ -8,7 +8,7 @@ loglevel=info ; log level; default info; others: debug,warn,trace user=root [program:gunicorn] -command=gunicorn -c /usr/src/paperless/gunicorn.conf.py -b '[::]:8000' paperless.wsgi +command=gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.wsgi user=paperless stdout_logfile=/dev/stdout From 6581cff8dc241f0ba3bb01b1ed4dfa1e3a41eb2f Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 20:55:27 +0100 Subject: [PATCH 11/20] more tests --- src/documents/tests/test_api.py | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/documents/tests/test_api.py b/src/documents/tests/test_api.py index adc947a1a..dbe8f881c 100644 --- a/src/documents/tests/test_api.py +++ b/src/documents/tests/test_api.py @@ -847,6 +847,21 @@ class TestBulkEdit(DirectoriesMixin, APITestCase): self.assertEqual(args[0], [self.doc1.id]) self.assertEqual(kwargs['tag'], self.t1.id) + @mock.patch("documents.serialisers.bulk_edit.modify_tags") + def test_api_modify_tags(self, m): + m.return_value = "OK" + response = self.client.post("/api/documents/bulk_edit/", json.dumps({ + "documents": [self.doc1.id, self.doc3.id], + "method": "modify_tags", + "parameters": {"add_tags": [self.t1.id], "remove_tags": [self.t2.id]} + }), content_type='application/json') + self.assertEqual(response.status_code, 200) + m.assert_called_once() + args, kwargs = m.call_args + self.assertListEqual(args[0], [self.doc1.id, self.doc3.id]) + self.assertEqual(kwargs['add_tags'], [self.t1.id]) + self.assertEqual(kwargs['remove_tags'], [self.t2.id]) + @mock.patch("documents.serialisers.bulk_edit.delete") def test_api_delete(self, m): m.return_value = "OK" @@ -927,6 +942,38 @@ class TestBulkEdit(DirectoriesMixin, APITestCase): self.assertEqual(list(self.doc2.tags.all()), [self.t1]) + def test_api_modify_invalid_tags(self): + self.assertEqual(list(self.doc2.tags.all()), [self.t1]) + response = self.client.post("/api/documents/bulk_edit/", json.dumps({ + "documents": [self.doc2.id], + "method": "modify_tags", + "parameters": {'add_tags': [self.t2.id, 1657], "remove_tags": [1123123]} + }), content_type='application/json') + self.assertEqual(response.status_code, 400) + + def test_api_selection_data_empty(self): + response = self.client.post("/api/documents/selection_data/", json.dumps({ + "documents": [] + }), content_type='application/json') + self.assertEqual(response.status_code, 200) + for field, Entity in [('selected_correspondents', Correspondent), ('selected_tags', Tag), ('selected_document_types', DocumentType)]: + self.assertEqual(len(response.data[field]), Entity.objects.count()) + for correspondent in response.data[field]: + self.assertEqual(correspondent['document_count'], 0) + self.assertCountEqual( + map(lambda c: c['id'], response.data[field]), + map(lambda c: c['id'], Entity.objects.values('id'))) + + def test_api_selection_data(self): + response = self.client.post("/api/documents/selection_data/", json.dumps({ + "documents": [self.doc1.id, self.doc2.id, self.doc4.id, self.doc5.id] + }), content_type='application/json') + self.assertEqual(response.status_code, 200) + + self.assertCountEqual(response.data['selected_correspondents'], [{"id": self.c1.id, "document_count": 1}, {"id": self.c2.id, "document_count": 0}]) + self.assertCountEqual(response.data['selected_tags'], [{"id": self.t1.id, "document_count": 2}, {"id": self.t2.id, "document_count": 1}]) + self.assertCountEqual(response.data['selected_document_types'], [{"id": self.c1.id, "document_count": 1}, {"id": self.c2.id, "document_count": 0}]) + class TestApiAuth(APITestCase): @@ -951,3 +998,4 @@ class TestApiAuth(APITestCase): self.assertEqual(self.client.get("/api/search/").status_code, 401) self.assertEqual(self.client.get("/api/search/auto_complete/").status_code, 401) self.assertEqual(self.client.get("/api/documents/bulk_edit/").status_code, 401) + self.assertEqual(self.client.get("/api/documents/selection_data/").status_code, 401) From e24b40de2919febda07085a34e774ba59dcd3139 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 21:01:18 +0100 Subject: [PATCH 12/20] more tests --- src/documents/tests/test_tasks.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/documents/tests/test_tasks.py b/src/documents/tests/test_tasks.py index 6d04e58e1..653590707 100644 --- a/src/documents/tests/test_tasks.py +++ b/src/documents/tests/test_tasks.py @@ -1,10 +1,12 @@ from datetime import datetime +from unittest import mock from django.test import TestCase from django.utils import timezone from documents import tasks from documents.models import Document +from documents.sanity_checker import SanityError, SanityFailedError from documents.tests.utils import DirectoriesMixin @@ -22,3 +24,19 @@ class TestTasks(DirectoriesMixin, TestCase): def test_train_classifier(self): tasks.train_classifier() + + @mock.patch("documents.tasks.sanity_checker.check_sanity") + def test_sanity_check(self, m): + m.return_value = [] + tasks.sanity_check() + m.assert_called_once() + m.reset_mock() + m.return_value = [SanityError("")] + self.assertRaises(SanityFailedError, tasks.sanity_check) + m.assert_called_once() + + def test_culk_update_documents(self): + doc1 = Document.objects.create(title="test", content="my document", checksum="wow", added=timezone.now(), + created=timezone.now(), modified=timezone.now()) + + tasks.bulk_update_documents([doc1.pk]) From d690b34ee078a4d968eb906d12a1f7f4b33a21ba Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 21:02:45 +0100 Subject: [PATCH 13/20] added invalid PDF document with BOM marker --- src/documents/tests/samples/test_with_bom.pdf | Bin 0 -> 16109 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/documents/tests/samples/test_with_bom.pdf diff --git a/src/documents/tests/samples/test_with_bom.pdf b/src/documents/tests/samples/test_with_bom.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c4a46701cfe54cc12affb0e66f362daca6de5af0 GIT binary patch literal 16109 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 literal 0 HcmV?d00001 From fb09f67899343f8c5dd5816e72b86db6f3bde298 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 22:01:18 +0100 Subject: [PATCH 14/20] bugfixes --- .../filter-editor/filter-editor.component.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts index de6d724bc..4b62d6a51 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts @@ -62,6 +62,10 @@ export class FilterEditorComponent implements OnInit, OnDestroy { @Input() set filterRules (value: FilterRule[]) { + this.documentTypeSelectionModel.clear(false) + this.tagSelectionModel.clear(false) + this.correspondentSelectionModel.clear(false) + value.forEach(rule => { switch (rule.rule_type) { case FILTER_TITLE: @@ -95,10 +99,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy { }) } - @Output() - filterRulesChange = new EventEmitter() - - updateRules() { + get filterRules() { let filterRules: FilterRule[] = [] if (this._titleFilter) { filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter}) @@ -128,7 +129,14 @@ export class FilterEditorComponent implements OnInit, OnDestroy { if (this.dateAddedAfter) { filterRules.push({rule_type: FILTER_ADDED_AFTER, value: this.dateAddedAfter}) } - this.filterRulesChange.next(filterRules) + return filterRules + } + + @Output() + filterRulesChange = new EventEmitter() + + updateRules() { + this.filterRulesChange.next(this.filterRules) } hasFilters() { From 8139ecfd3987a91734fade2519cbf0366d9d0db8 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 22:01:37 +0100 Subject: [PATCH 15/20] version bump --- docker/hub/docker-compose.postgres.yml | 2 +- docker/hub/docker-compose.sqlite.yml | 2 +- src-ui/src/environments/environment.prod.ts | 2 +- src/paperless/version.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/hub/docker-compose.postgres.yml b/docker/hub/docker-compose.postgres.yml index e7a32bec0..c2b599e52 100644 --- a/docker/hub/docker-compose.postgres.yml +++ b/docker/hub/docker-compose.postgres.yml @@ -15,7 +15,7 @@ services: POSTGRES_PASSWORD: paperless webserver: - image: jonaswinkler/paperless-ng:0.9.9 + image: jonaswinkler/paperless-ng:0.9.10 restart: always depends_on: - db diff --git a/docker/hub/docker-compose.sqlite.yml b/docker/hub/docker-compose.sqlite.yml index 98b7d70a2..429d42c06 100644 --- a/docker/hub/docker-compose.sqlite.yml +++ b/docker/hub/docker-compose.sqlite.yml @@ -5,7 +5,7 @@ services: restart: always webserver: - image: jonaswinkler/paperless-ng:0.9.9 + image: jonaswinkler/paperless-ng:0.9.10 restart: always depends_on: - broker diff --git a/src-ui/src/environments/environment.prod.ts b/src-ui/src/environments/environment.prod.ts index ab6b07c73..7b707f014 100644 --- a/src-ui/src/environments/environment.prod.ts +++ b/src-ui/src/environments/environment.prod.ts @@ -2,5 +2,5 @@ export const environment = { production: true, apiBaseUrl: "/api/", appTitle: "Paperless-ng", - version: "0.9.9" + version: "0.9.10" }; diff --git a/src/paperless/version.py b/src/paperless/version.py index b1dfc590c..facb097fc 100644 --- a/src/paperless/version.py +++ b/src/paperless/version.py @@ -1 +1 @@ -__version__ = (0, 9, 9) +__version__ = (0, 9, 10) From 761a6a4264f8823ff56c4abfa6c2fce5c5654625 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 22:01:56 +0100 Subject: [PATCH 16/20] plural form fix --- .../app/components/document-list/document-list.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/src/app/components/document-list/document-list.component.html b/src-ui/src/app/components/document-list/document-list.component.html index 40a08c105..bc1047ba9 100644 --- a/src-ui/src/app/components/document-list/document-list.component.html +++ b/src-ui/src/app/components/document-list/document-list.component.html @@ -84,7 +84,7 @@

    Selected {{list.selected.size}} of {{list.collectionSize || 0}} {list.collectionSize, plural, =1 {document} other {documents}}

    -

    {{list.collectionSize || 0}} {list.collectionSize, plural, =1 {document} other {documents}}

    +

    {list.collectionSize, plural, =1 {1 document} other {{{list.collectionSize || 0}} documents}}

    From ef63ec40d91fa7560abc0ed85e866e70bfc0749a Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 22:02:03 +0100 Subject: [PATCH 17/20] changelog --- docs/changelog.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index e63c19d7d..4357a981b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,35 @@ Changelog ********* +paperless-ng 0.9.10 +################### + +* Bulk editing + + * Thanks to `Michael Shamoon`_, we've got a new interface for the bulk editor. + * There are some configuration options in the settings to alter the behavior. + +* Other changes and additions + + * The Paperless-ng logo now navigates to the dashboard. + * Filter for documents that don't have any correspondents, types or tags assigned. + * Tags, types and correspondents are now sorted case insensitive. + * Lots of preparation work for localization support. + +* Fixes + + * Added missing dependencies for Raspberry Pi builds. + * Fixed an issue with plain text file consumption: Thumbnail generation failed due to missing fonts. + * An issue with the search index reporting missing documents after bulk deletes was fixed. + +.. note:: + + The bulk delete operations did not update the search index. Therefore, documents that you deleted remained in the index and + caused the search to return messages about missing documents when searching. Further bulk operations will properly update + the index. + + However, this change is not retroactive: If you used the delete method of the bulk editor, you need to reindex your search index + by :ref:`running the management command document_index with the argument reindex `. paperless-ng 0.9.9 ################## From f9ab8d3b3504006f0ea899c5082749d95c23957a Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 23:34:04 +0100 Subject: [PATCH 18/20] more localization tags #123 --- .../save-view-config-dialog.component.html | 2 +- .../correspondent-edit-dialog.component.html | 4 ++-- .../document-type-edit-dialog.component.html | 4 ++-- .../document-type-list.component.html | 2 +- .../tag-edit-dialog/tag-edit-dialog.component.html | 14 +++++++------- .../manage/tag-list/tag-list.component.html | 6 ++---- .../app/components/search/search.component.html | 2 +- 7 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html index 9a2a37e36..9e28448a2 100644 --- a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html +++ b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html @@ -7,7 +7,7 @@ diff --git a/src-ui/src/app/components/manage/tag-list/tag-list.component.html b/src-ui/src/app/components/manage/tag-list/tag-list.component.html index bbe2c6dd2..83bf211a6 100644 --- a/src-ui/src/app/components/manage/tag-list/tag-list.component.html +++ b/src-ui/src/app/components/manage/tag-list/tag-list.component.html @@ -1,7 +1,5 @@ - - + +
    diff --git a/src-ui/src/app/components/search/search.component.html b/src-ui/src/app/components/search/search.component.html index 7047a3144..547c8a475 100644 --- a/src-ui/src/app/components/search/search.component.html +++ b/src-ui/src/app/components/search/search.component.html @@ -1,4 +1,4 @@ - +
    Invalid search query: {{errorMessage}}
    From 03c6a4e18eafe79fe579eff44700b4b28baee835 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 23:37:33 +0100 Subject: [PATCH 19/20] more localization #123 --- .../common/date-dropdown/date-dropdown.component.ts | 8 ++++---- .../filterable-dropdown/filterable-dropdown.component.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src-ui/src/app/components/common/date-dropdown/date-dropdown.component.ts b/src-ui/src/app/components/common/date-dropdown/date-dropdown.component.ts index 1bf5d0216..27472bdc7 100644 --- a/src-ui/src/app/components/common/date-dropdown/date-dropdown.component.ts +++ b/src-ui/src/app/components/common/date-dropdown/date-dropdown.component.ts @@ -21,10 +21,10 @@ const LAST_YEAR = 3 export class DateDropdownComponent implements OnInit, OnDestroy { quickFilters = [ - {id: LAST_7_DAYS, name: "Last 7 days"}, - {id: LAST_MONTH, name: "Last month"}, - {id: LAST_3_MONTHS, name: "Last 3 months"}, - {id: LAST_YEAR, name: "Last year"} + {id: LAST_7_DAYS, name: $localize`Last 7 days`}, + {id: LAST_MONTH, name: $localize`Last month`}, + {id: LAST_3_MONTHS, name: $localize`Last 3 months`}, + {id: LAST_YEAR, name: $localize`Last year`} ] @Input() diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts index 0e075ff19..915d10677 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts @@ -142,7 +142,7 @@ export class FilterableDropdownComponent { if (items) { this._selectionModel.items = Array.from(items) this._selectionModel.items.unshift({ - name: "None", + name: $localize`Not assigned`, id: null }) } From 2de3894d67744bbdb5c5c67dced25b6244e2cf2d Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 29 Dec 2020 23:41:59 +0100 Subject: [PATCH 20/20] more localization tags --- .../document-detail.component.html | 2 +- .../manage/tag-list/tag-list.component.html | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html index daf01249f..eae3367c1 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.html +++ b/src-ui/src/app/components/document-detail/document-detail.component.html @@ -132,7 +132,7 @@
      -   +    
    diff --git a/src-ui/src/app/components/manage/tag-list/tag-list.component.html b/src-ui/src/app/components/manage/tag-list/tag-list.component.html index 83bf211a6..43126f7b2 100644 --- a/src-ui/src/app/components/manage/tag-list/tag-list.component.html +++ b/src-ui/src/app/components/manage/tag-list/tag-list.component.html @@ -10,11 +10,11 @@ - - - - - + + + + + @@ -29,21 +29,18 @@
    NameColourMatchingDocument countActionsNameColorMatchingDocument countActions