From 6c5f9bbabbf6e56515a5ceacbf4172325a8f7172 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Mon, 25 Jan 2021 19:23:03 -0800 Subject: [PATCH 01/26] Settings dirty check for saving --- src-ui/package-lock.json | 5 ++ src-ui/package.json | 1 + .../manage/settings/settings.component.html | 2 +- .../manage/settings/settings.component.ts | 68 +++++++++++++++---- 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index c3827f80c..cc43532d0 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -2070,6 +2070,11 @@ "tslib": "^2.0.0" } }, + "@ngneat/dirty-check-forms": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ngneat/dirty-check-forms/-/dirty-check-forms-1.0.1.tgz", + "integrity": "sha512-3rgThvjpZIxLS3sANYvma4J4VLITeAsnHrWOe9dtEYauCI4Vl5wDLB2aPNJkKu6EQD/X0QLs96rZvFqe3A3paw==" + }, "@ngtools/webpack": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-10.2.0.tgz", diff --git a/src-ui/package.json b/src-ui/package.json index 14d828483..c7965ff04 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -22,6 +22,7 @@ "@angular/router": "~10.1.5", "@ng-bootstrap/ng-bootstrap": "^8.0.0", "@ng-select/ng-select": "^5.0.9", + "@ngneat/dirty-check-forms": "^1.0.1", "bootstrap": "^4.5.0", "ng-bootstrap": "^1.6.3", "ng2-pdf-viewer": "^6.3.2", 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 13ffb4517..688100100 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.html +++ b/src-ui/src/app/components/manage/settings/settings.component.html @@ -150,5 +150,5 @@
- + 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 3c4de59b6..231a5a45d 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -1,35 +1,41 @@ -import { Component, OnInit, Renderer2 } from '@angular/core'; +import { Component, OnInit, OnDestroy, Renderer2 } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { SavedViewService } from 'src/app/services/rest/saved-view.service'; import { LanguageOption, SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; import { ToastService } from 'src/app/services/toast.service'; +import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'; +import { Observable, Subscription, BehaviorSubject } from 'rxjs'; @Component({ selector: 'app-settings', templateUrl: './settings.component.html', styleUrls: ['./settings.component.scss'] }) -export class SettingsComponent implements OnInit { +export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { savedViewGroup = new FormGroup({}) settingsForm = new FormGroup({ - '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)), - 'darkModeUseSystem': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM)), - 'darkModeEnabled': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED)), - 'useNativePdfViewer': new FormControl(this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)), + 'bulkEditConfirmationDialogs': new FormControl(null), + 'bulkEditApplyOnClose': new FormControl(null), + 'documentListItemPerPage': new FormControl(null), + 'darkModeUseSystem': new FormControl(null), + 'darkModeEnabled': new FormControl(null), + 'useNativePdfViewer': new FormControl(null), 'savedViews': this.savedViewGroup, - 'displayLanguage': new FormControl(this.settings.getLanguage()), - 'dateLocale': new FormControl(this.settings.get(SETTINGS_KEYS.DATE_LOCALE)), - 'dateFormat': new FormControl(this.settings.get(SETTINGS_KEYS.DATE_FORMAT)), + 'displayLanguage': new FormControl(null), + 'dateLocale': new FormControl(null), + 'dateFormat': new FormControl(null), }) savedViews: PaperlessSavedView[] + store: BehaviorSubject; + storeSub: Subscription; + isDirty$: Observable; + get computedDateLocale(): string { return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage } @@ -44,17 +50,49 @@ export class SettingsComponent implements OnInit { ngOnInit() { this.savedViewService.listAll().subscribe(r => { this.savedViews = r.results + let storeData = { + 'bulkEditConfirmationDialogs': this.settings.get(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS), + 'bulkEditApplyOnClose': this.settings.get(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE), + 'documentListItemPerPage': this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE), + 'darkModeUseSystem': this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM), + 'darkModeEnabled': this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED), + 'useNativePdfViewer': this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER), + 'savedViews': {}, + 'displayLanguage': this.settings.getLanguage(), + 'dateLocale': this.settings.get(SETTINGS_KEYS.DATE_LOCALE), + 'dateFormat': this.settings.get(SETTINGS_KEYS.DATE_FORMAT), + } + for (let view of this.savedViews) { + storeData.savedViews[view.id.toString()] = { + "id": view.id, + "name": view.name, + "show_on_dashboard": view.show_on_dashboard, + "show_in_sidebar": view.show_in_sidebar + } this.savedViewGroup.addControl(view.id.toString(), new FormGroup({ - "id": new FormControl(view.id), - "name": new FormControl(view.name), - "show_on_dashboard": new FormControl(view.show_on_dashboard), - "show_in_sidebar": new FormControl(view.show_in_sidebar) + "id": new FormControl(null), + "name": new FormControl(null), + "show_on_dashboard": new FormControl(null), + "show_in_sidebar": new FormControl(null) })) } + + this.store = new BehaviorSubject(storeData) + + this.storeSub = this.store.asObservable().subscribe(state => { + this.settingsForm.patchValue(state, { emitEvent: false }) + }); + + // Initialize dirtyCheck + this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable()); }) } + ngOnDestroy() { + this.storeSub && this.storeSub.unsubscribe(); + } + deleteSavedView(savedView: PaperlessSavedView) { this.savedViewService.delete(savedView).subscribe(() => { this.savedViewGroup.removeControl(savedView.id.toString()) From b0fa0f231996e251b46e2b56f9f93dc7571e3617 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Mon, 25 Jan 2021 22:44:26 -0800 Subject: [PATCH 02/26] Settings navigation warning dialog --- src-ui/src/app/app-routing.module.ts | 7 +++-- .../confirm-dialog.component.html | 6 ++-- .../confirm-dialog.component.ts | 21 +++++++++++--- .../manage/settings/settings.component.ts | 10 +++---- src-ui/src/app/guards/dirty-form.guard.ts | 29 +++++++++++++++++++ 5 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 src-ui/src/app/guards/dirty-form.guard.ts diff --git a/src-ui/src/app/app-routing.module.ts b/src-ui/src/app/app-routing.module.ts index 27f0629b4..8af68221b 100644 --- a/src-ui/src/app/app-routing.module.ts +++ b/src-ui/src/app/app-routing.module.ts @@ -11,6 +11,7 @@ import { SettingsComponent } from './components/manage/settings/settings.compone import { TagListComponent } from './components/manage/tag-list/tag-list.component'; import { NotFoundComponent } from './components/not-found/not-found.component'; import { SearchComponent } from './components/search/search.component'; +import { FormDirtyGuard } from './guards/dirty-form.guard'; const routes: Routes = [ {path: '', redirectTo: 'dashboard', pathMatch: 'full'}, @@ -20,13 +21,13 @@ const routes: Routes = [ {path: 'view/:id', component: DocumentListComponent }, {path: 'search', component: SearchComponent }, {path: 'documents/:id', component: DocumentDetailComponent }, - + {path: 'tags', component: TagListComponent }, {path: 'documenttypes', component: DocumentTypeListComponent }, {path: 'correspondents', component: CorrespondentListComponent }, {path: 'logs', component: LogsComponent }, - {path: 'settings', component: SettingsComponent }, - ]}, + {path: 'settings', component: SettingsComponent, canDeactivate: [FormDirtyGuard] }, + ]}, {path: '404', component: NotFoundComponent}, {path: '**', redirectTo: '/404', pathMatch: 'full'} diff --git a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html index 8a8a297d9..780ae6891 100644 --- a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html +++ b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html @@ -1,6 +1,6 @@ @@ -9,8 +9,8 @@

{{message}}

@@ -145,6 +145,6 @@ - + diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index aa2308eac..317d1104c 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -19,13 +19,15 @@ import { PDFDocumentProxy } from 'ng2-pdf-viewer'; import { ToastService } from 'src/app/services/toast.service'; import { TextComponent } from '../common/input/text/text.component'; import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; +import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'; +import { Observable, Subscription, BehaviorSubject } from 'rxjs'; @Component({ selector: 'app-document-detail', templateUrl: './document-detail.component.html', styleUrls: ['./document-detail.component.scss'] }) -export class DocumentDetailComponent implements OnInit { +export class DocumentDetailComponent implements OnInit, DirtyComponent { @ViewChild("inputTitle") titleInput: TextComponent @@ -61,6 +63,10 @@ export class DocumentDetailComponent implements OnInit { previewCurrentPage: number = 1 previewNumPages: number = 1 + store: BehaviorSubject + storeSub: Subscription + isDirty$: Observable + constructor( private documentsService: DocumentService, private route: ActivatedRoute, @@ -113,7 +119,23 @@ export class DocumentDetailComponent implements OnInit { this.metadata = result }) this.title = this.documentTitlePipe.transform(doc.title) - this.documentForm.patchValue(doc) + + this.store = new BehaviorSubject({ + title: doc.title, + content: doc.content, + created: doc.created, + correspondent: doc.correspondent, + document_type: doc.document_type, + archive_serial_number: doc.archive_serial_number, + tags: doc.tags + }) + + this.storeSub = this.store.asObservable().subscribe(state => { + this.documentForm.patchValue(state, { emitEvent: false }) + }) + + // Initialize dirtyCheck + this.isDirty$ = dirtyCheck(this.documentForm, this.store.asObservable()) } createDocumentType() { From bc02dc30b14630bd399b04f08493e276a18dc9ea Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Mon, 25 Jan 2021 23:06:25 -0800 Subject: [PATCH 04/26] Prevent confirmation warning after save --- .../app/components/document-detail/document-detail.component.ts | 2 ++ src-ui/src/app/components/manage/settings/settings.component.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 317d1104c..753317937 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -170,6 +170,7 @@ export class DocumentDetailComponent implements OnInit, DirtyComponent { save() { this.networkActive = true + this.store.next(this.documentForm.value) this.documentsService.update(this.document).subscribe(result => { this.close() this.networkActive = false @@ -182,6 +183,7 @@ export class DocumentDetailComponent implements OnInit, DirtyComponent { saveEditNext() { this.networkActive = true + this.store.next(this.documentForm.value) this.documentsService.update(this.document).subscribe(result => { this.error = null this.documentListViewService.getNext(this.document.id).subscribe(nextDocId => { 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 5ca206b19..5089c880f 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -111,6 +111,7 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { this.settings.set(SETTINGS_KEYS.DATE_LOCALE, this.settingsForm.value.dateLocale) this.settings.set(SETTINGS_KEYS.DATE_FORMAT, this.settingsForm.value.dateFormat) this.settings.setLanguage(this.settingsForm.value.displayLanguage) + this.store.next(this.settingsForm.value) this.documentListViewService.updatePageSize() this.settings.updateDarkModeSettings() this.toastService.showInfo($localize`Settings saved successfully.`) From 9c427f202d502cec425293e4bb6397cdc61ba91f Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Mon, 25 Jan 2021 23:09:56 -0800 Subject: [PATCH 05/26] Disable discard button also if not dirty --- .../components/document-detail/document-detail.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 348fb477f..c0fc4af6d 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 @@ -127,7 +127,7 @@
-   +       From 3f73cd5bfced4f2e11fc1fbe3d2a849bb7fa759b Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Mon, 25 Jan 2021 23:16:11 -0800 Subject: [PATCH 06/26] Change warning title --- src-ui/src/app/guards/dirty-form.guard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/src/app/guards/dirty-form.guard.ts b/src-ui/src/app/guards/dirty-form.guard.ts index 4983e6732..b0b50ac7f 100644 --- a/src-ui/src/app/guards/dirty-form.guard.ts +++ b/src-ui/src/app/guards/dirty-form.guard.ts @@ -13,7 +13,7 @@ export class FormDirtyGuard extends DirtyCheckGuard { confirmChanges(): Observable { let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) - modal.componentInstance.title = $localize`Confirm` + modal.componentInstance.title = $localize`Unsaved Changes` modal.componentInstance.messageBold = $localize`You have unsaved changes.` modal.componentInstance.message = $localize`Are you sure you want to leave?` modal.componentInstance.btnClass = "btn-warning" From 2a7bb27a78a9dc808e14ff66e528e5189484d35a Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Mon, 25 Jan 2021 23:16:18 -0800 Subject: [PATCH 07/26] Merge remote-tracking branch 'upstream/dev' into feature/unsaved-changes --- crowdin.yml | 5 ++ docker/docker-entrypoint.sh | 4 ++ docs/administration.rst | 20 ++++++- docs/changelog.rst | 4 +- src-ui/messages.xlf | 14 ++--- .../document-card-large.component.html | 4 +- .../document-card-small.component.html | 4 +- .../document-list.component.html | 2 +- .../manage/settings/settings.component.html | 4 +- .../manage/settings/settings.component.ts | 5 +- src-ui/src/app/services/settings.service.ts | 2 +- src-ui/src/locale/messages.de.xlf | 58 +++++++++---------- src-ui/src/locale/messages.fr.xlf | 58 +++++++++---------- src-ui/src/locale/messages.nl_NL.xlf | 58 +++++++++---------- .../management/commands/document_consumer.py | 43 ++------------ src/documents/models.py | 6 -- src/documents/tests/test_api.py | 53 +++++++++++++++++ .../tests/test_management_consumer.py | 27 +-------- src/documents/tests/test_parsers.py | 1 + src/documents/views.py | 3 + 20 files changed, 198 insertions(+), 177 deletions(-) create mode 100644 crowdin.yml diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 000000000..f5a7765dd --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,5 @@ +files: + - source: /src/locale/en-us/LC_MESSAGES/django.po + translation: /src/locale/%two_letters_code%/LC_MESSAGES/django.po + - source: /src-ui/messages.xlf + translation: /src-ui/src/locale/messages.%two_letters_code%.xlf diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index 8e1a21757..5919b14aa 100644 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -79,7 +79,11 @@ initialize() { fi done + echo "creating directory /tmp/paperless" + mkdir -p /tmp/paperless + chown -R paperless:paperless ../ + chown -R paperless:paperless /tmp/paperless migrations diff --git a/docs/administration.rst b/docs/administration.rst index bb543e3db..14b986e82 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -90,10 +90,28 @@ B. If you built the image yourself, do the following: $ docker-compose build $ docker-compose up -Running `docker-compose up` will also apply any new database migrations. +Running ``docker-compose up`` will also apply any new database migrations. If you see everything working, press CTRL+C once to gracefully stop paperless. Then you can start paperless-ng with ``-d`` to have it run in the background. + .. note:: + + In version 0.9.14, the update process was changed. In 0.9.13 and earlier, the + docker-compose files specified exact versions and pull won't automatically + update to newer versions. In order to enable updates as described above, either + get the new ``docker-compose.yml`` file from `here `_ + or edit the ``docker-compose.yml`` file, find the line that says + + .. code:: + + image: jonaswinkler/paperless-ng:0.9.x + + and replace the version with ``latest``: + + .. code:: + + image: jonaswinkler/paperless-ng:latest + Bare Metal Route ================ diff --git a/docs/changelog.rst b/docs/changelog.rst index 721e9520e..28bcc00e6 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,7 +5,6 @@ Changelog ********* - paperless-ng 1.0.0 ################## @@ -25,7 +24,8 @@ Nothing special about this release, but since there are relatively few bug repor * Added a language selector to the settings. * Added date format options to the settings. * Range selection with shift clicking is now possible in the document list. - + * Filtering correspondent, type and tag management pages by name. + * Focus "Name" field in dialogs by default. paperless-ng 0.9.14 diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 1e89a9408..6175cf700 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -482,35 +482,35 @@ Saved view "" deleted. src/app/components/manage/settings/settings.component.ts - 62 + 63 Settings saved successfully. src/app/components/manage/settings/settings.component.ts - 78 + 79 Use system language src/app/components/manage/settings/settings.component.ts - 82 + 83 Use date format of display language src/app/components/manage/settings/settings.component.ts - 86 + 87 Error while storing settings on server: src/app/components/manage/settings/settings.component.ts - 102 + 103 @@ -1463,8 +1463,8 @@ 12 - - English + + English (US) src/app/services/settings.service.ts 74 diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html index 85964f528..d26c17159 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html @@ -1,6 +1,6 @@ -
+
-
+
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html index a0d0299a5..882de71ed 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html @@ -1,6 +1,6 @@
-
-
+
+
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 a29e146bf..cfc2e655d 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 @@ -152,7 +152,7 @@ {{d.title | documentTitle}} - + 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 688100100..367b4ea20 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.html +++ b/src-ui/src/app/components/manage/settings/settings.component.html @@ -19,7 +19,7 @@
You need to reload the page after applying a new language. @@ -34,7 +34,7 @@
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 5089c880f..f9a84e53b 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, Renderer2 } from '@angular/core'; +import { Component, Inject, LOCALE_ID, OnInit, Renderer2 } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'; @@ -44,7 +44,8 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent { public savedViewService: SavedViewService, private documentListViewService: DocumentListViewService, private toastService: ToastService, - private settings: SettingsService + private settings: SettingsService, + @Inject(LOCALE_ID) public currentLocale: string ) { } ngOnInit() { diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index d2a190c1f..041fb51ca 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -71,7 +71,7 @@ export class SettingsService { getLanguageOptions(): LanguageOption[] { return [ - {code: "en-us", name: $localize`English`, englishName: "English"}, + {code: "en-US", name: $localize`English (US)`, englishName: "English (US)"}, {code: "de", name: $localize`German`, englishName: "German"}, {code: "nl", name: $localize`Dutch`, englishName: "Dutch"}, {code: "fr", name: $localize`French`, englishName: "French"} diff --git a/src-ui/src/locale/messages.de.xlf b/src-ui/src/locale/messages.de.xlf index 80cfefab9..a898e4630 100644 --- a/src-ui/src/locale/messages.de.xlf +++ b/src-ui/src/locale/messages.de.xlf @@ -449,12 +449,20 @@ 2 + + Filter by: + Filtern nach: + + src/app/components/manage/tag-list/tag-list.component.html + 8 + + Name Name src/app/components/manage/tag-list/tag-list.component.html - 13 + 9 @@ -462,7 +470,7 @@ Farbe src/app/components/manage/tag-list/tag-list.component.html - 14 + 20 @@ -470,7 +478,7 @@ Zuweisung src/app/components/manage/tag-list/tag-list.component.html - 15 + 21 @@ -478,7 +486,7 @@ Anzahl Dokumente src/app/components/manage/tag-list/tag-list.component.html - 16 + 22 @@ -486,7 +494,7 @@ Aktionen src/app/components/manage/tag-list/tag-list.component.html - 17 + 23 @@ -494,7 +502,7 @@ Dokumente src/app/components/manage/tag-list/tag-list.component.html - 32 + 38 @@ -502,7 +510,7 @@ Bearbeiten src/app/components/manage/tag-list/tag-list.component.html - 37 + 43 @@ -542,7 +550,7 @@ Gespeicherte Ansicht "" gelöscht. src/app/components/manage/settings/settings.component.ts - 62 + 63 @@ -550,7 +558,7 @@ Einstellungen erfolgreich gespeichert. src/app/components/manage/settings/settings.component.ts - 78 + 79 @@ -558,7 +566,7 @@ Benutze Systemsprache src/app/components/manage/settings/settings.component.ts - 82 + 83 @@ -566,7 +574,7 @@ Benutze Datumsformat der Anzeigesprache src/app/components/manage/settings/settings.component.ts - 86 + 87 @@ -574,7 +582,7 @@ Fehler beim Speichern der Einstellungen auf dem Server: src/app/components/manage/settings/settings.component.ts - 102 + 103 @@ -814,7 +822,7 @@ Letzter Kontakt src/app/components/manage/correspondent-list/correspondent-list.component.html - 15 + 22 @@ -1114,14 +1122,6 @@ 46 - - Filter by: - Filtern nach: - - src/app/components/document-list/filter-editor/filter-editor.component.html - 4 - - Filter tags Tags filtern @@ -1670,9 +1670,9 @@ 12 - - English - Englisch + + English (US) + Englisch (US) src/app/services/settings.service.ts 74 @@ -1907,7 +1907,7 @@ Automatisch src/app/components/manage/generic-list/generic-list.component.ts - 33 + 39 @@ -1915,7 +1915,7 @@ Möchten Sie dieses Element wirklich löschen? src/app/components/manage/generic-list/generic-list.component.ts - 76 + 97 @@ -1923,7 +1923,7 @@ Assoziierte Dokumente werden nicht gelöscht. src/app/components/manage/generic-list/generic-list.component.ts - 83 + 104 @@ -1931,7 +1931,7 @@ Löschen src/app/components/manage/generic-list/generic-list.component.ts - 85 + 106 @@ -1939,7 +1939,7 @@ Fehler beim Löschen des Elements: src/app/components/manage/generic-list/generic-list.component.ts - 93 + 114 diff --git a/src-ui/src/locale/messages.fr.xlf b/src-ui/src/locale/messages.fr.xlf index ee97f7ce0..4f09eab72 100644 --- a/src-ui/src/locale/messages.fr.xlf +++ b/src-ui/src/locale/messages.fr.xlf @@ -449,12 +449,20 @@ 2 + + Filter by: + Filtrer par : + + src/app/components/manage/tag-list/tag-list.component.html + 8 + + Name Nom src/app/components/manage/tag-list/tag-list.component.html - 13 + 9 @@ -462,7 +470,7 @@ Couleur src/app/components/manage/tag-list/tag-list.component.html - 14 + 20 @@ -470,7 +478,7 @@ Rapprochement src/app/components/manage/tag-list/tag-list.component.html - 15 + 21 @@ -478,7 +486,7 @@ Nombre de documents src/app/components/manage/tag-list/tag-list.component.html - 16 + 22 @@ -486,7 +494,7 @@ Actions src/app/components/manage/tag-list/tag-list.component.html - 17 + 23 @@ -494,7 +502,7 @@ Documents src/app/components/manage/tag-list/tag-list.component.html - 32 + 38 @@ -502,7 +510,7 @@ Éditer src/app/components/manage/tag-list/tag-list.component.html - 37 + 43 @@ -542,7 +550,7 @@ Vue "" supprimée. src/app/components/manage/settings/settings.component.ts - 62 + 63 @@ -550,7 +558,7 @@ Paramètres enregistrés avec succès. src/app/components/manage/settings/settings.component.ts - 78 + 79 @@ -558,7 +566,7 @@ Utiliser la langue du système src/app/components/manage/settings/settings.component.ts - 82 + 83 @@ -566,7 +574,7 @@ Utiliser le format de date de la langue d'affichage src/app/components/manage/settings/settings.component.ts - 86 + 87 @@ -574,7 +582,7 @@ Une erreur s'est produite lors de l'enregistrement des paramètres sur le serveur : src/app/components/manage/settings/settings.component.ts - 102 + 103 @@ -814,7 +822,7 @@ Dernière correspondance src/app/components/manage/correspondent-list/correspondent-list.component.html - 15 + 22 @@ -1114,14 +1122,6 @@ 46 - - Filter by: - Filtrer par : - - src/app/components/document-list/filter-editor/filter-editor.component.html - 4 - - Filter tags Filtrer les étiquettes @@ -1670,9 +1670,9 @@ 12 - - English - Anglais + + English (US) + Anglais (US) src/app/services/settings.service.ts 74 @@ -1907,7 +1907,7 @@ Automatique src/app/components/manage/generic-list/generic-list.component.ts - 33 + 39 @@ -1915,7 +1915,7 @@ Voulez-vous vraiment supprimer cet élément ? src/app/components/manage/generic-list/generic-list.component.ts - 76 + 97 @@ -1923,7 +1923,7 @@ Les documents associés ne seront pas supprimés. src/app/components/manage/generic-list/generic-list.component.ts - 83 + 104 @@ -1931,7 +1931,7 @@ Supprimer src/app/components/manage/generic-list/generic-list.component.ts - 85 + 106 @@ -1939,7 +1939,7 @@ Une erreur s'est produite lors de la suppression de l'élément : src/app/components/manage/generic-list/generic-list.component.ts - 93 + 114 diff --git a/src-ui/src/locale/messages.nl_NL.xlf b/src-ui/src/locale/messages.nl_NL.xlf index 39545c0e2..3753bc92c 100644 --- a/src-ui/src/locale/messages.nl_NL.xlf +++ b/src-ui/src/locale/messages.nl_NL.xlf @@ -449,12 +449,20 @@ 2 + + Filter by: + Filter op: + + src/app/components/manage/tag-list/tag-list.component.html + 8 + + Name Naam src/app/components/manage/tag-list/tag-list.component.html - 13 + 9 @@ -462,7 +470,7 @@ Kleur src/app/components/manage/tag-list/tag-list.component.html - 14 + 20 @@ -470,7 +478,7 @@ Overeenkomend src/app/components/manage/tag-list/tag-list.component.html - 15 + 21 @@ -478,7 +486,7 @@ Aantal documenten src/app/components/manage/tag-list/tag-list.component.html - 16 + 22 @@ -486,7 +494,7 @@ Acties src/app/components/manage/tag-list/tag-list.component.html - 17 + 23 @@ -494,7 +502,7 @@ Documenten src/app/components/manage/tag-list/tag-list.component.html - 32 + 38 @@ -502,7 +510,7 @@ Bewerk src/app/components/manage/tag-list/tag-list.component.html - 37 + 43 @@ -542,7 +550,7 @@ Opgeslagen view "" verwijderd. src/app/components/manage/settings/settings.component.ts - 62 + 63 @@ -550,7 +558,7 @@ Instellingen succesvol opgeslagen. src/app/components/manage/settings/settings.component.ts - 78 + 79 @@ -558,7 +566,7 @@ Gebruik de systeemtaal src/app/components/manage/settings/settings.component.ts - 82 + 83 @@ -566,7 +574,7 @@ Datumopmaak van weergavetaal gebruiken src/app/components/manage/settings/settings.component.ts - 86 + 87 @@ -574,7 +582,7 @@ Fout bij het opslaan van de instellingen: src/app/components/manage/settings/settings.component.ts - 102 + 103 @@ -814,7 +822,7 @@ Laatste correspondentie src/app/components/manage/correspondent-list/correspondent-list.component.html - 15 + 22 @@ -1114,14 +1122,6 @@ 46 - - Filter by: - Filter op: - - src/app/components/document-list/filter-editor/filter-editor.component.html - 4 - - Filter tags Etiketten filteren @@ -1670,9 +1670,9 @@ 12 - - English - Engels + + English (US) + Engels (US) src/app/services/settings.service.ts 74 @@ -1907,7 +1907,7 @@ Automatisch src/app/components/manage/generic-list/generic-list.component.ts - 33 + 39 @@ -1915,7 +1915,7 @@ Wil je dit element echt verwijderen? src/app/components/manage/generic-list/generic-list.component.ts - 76 + 97 @@ -1923,7 +1923,7 @@ Geassocieerde documenten zullen niet verwijderd worden. src/app/components/manage/generic-list/generic-list.component.ts - 83 + 104 @@ -1931,7 +1931,7 @@ Verwijderen src/app/components/manage/generic-list/generic-list.component.ts - 85 + 106 @@ -1939,7 +1939,7 @@ Fout bij het verwijderen van het element: src/app/components/manage/generic-list/generic-list.component.ts - 93 + 114 diff --git a/src/documents/management/commands/document_consumer.py b/src/documents/management/commands/document_consumer.py index 595bd39cd..9ba1b1a87 100644 --- a/src/documents/management/commands/document_consumer.py +++ b/src/documents/management/commands/document_consumer.py @@ -70,31 +70,6 @@ def _consume(filepath): "Error while consuming document: {}".format(e)) -def _test_inotify(directory): - if not INotify: - return False - - test_file = os.path.join(directory, "__inotify_test_file__") - inotify = INotify() - descriptor = None - try: - inotify_flags = flags.CLOSE_WRITE | flags.MOVED_TO - descriptor = inotify.add_watch(directory, inotify_flags) - Path(test_file).touch() - events = inotify.read(timeout=1000) - return len(events) == 1 - except Exception as e: - logger.warning( - f"Error while checking inotify availability: {str(e)}") - return False - finally: - if descriptor: - inotify.rm_watch(descriptor) - inotify.close() - if os.path.isfile(test_file): - os.unlink(test_file) - - def _consume_wait_unmodified(file, num_tries=20, wait_time=1): mtime = -1 current_try = 0 @@ -178,25 +153,17 @@ class Command(BaseCommand): if options["oneshot"]: return - if settings.CONSUMER_POLLING == 0: - if _test_inotify(directory): - self.handle_inotify(directory, recursive) - else: - logger.warning( - f"Inotify notifications are not available on {directory}, " - f"falling back to polling every 10 seconds") - self.handle_polling( - directory, recursive, 10) + if settings.CONSUMER_POLLING == 0 and INotify: + self.handle_inotify(directory, recursive) else: - self.handle_polling( - directory, recursive, settings.CONSUMER_POLLING) + self.handle_polling(directory, recursive) logger.debug("Consumer exiting.") - def handle_polling(self, directory, recursive, timeout): + def handle_polling(self, directory, recursive): logging.getLogger(__name__).info( f"Polling directory for changes: {directory}") - self.observer = PollingObserver(timeout=timeout) + self.observer = PollingObserver(timeout=settings.CONSUMER_POLLING) self.observer.schedule(Handler(), directory, recursive=recursive) self.observer.start() try: diff --git a/src/documents/models.py b/src/documents/models.py index 928b8aa4d..86878dd7e 100755 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -63,12 +63,6 @@ class MatchingModel(models.Model): def __str__(self): return self.name - def save(self, *args, **kwargs): - - self.match = self.match.lower() - - models.Model.save(self, *args, **kwargs) - class Correspondent(MatchingModel): diff --git a/src/documents/tests/test_api.py b/src/documents/tests/test_api.py index 5f81b83b6..2b332a873 100644 --- a/src/documents/tests/test_api.py +++ b/src/documents/tests/test_api.py @@ -228,6 +228,12 @@ class TestDocumentApi(DirectoriesMixin, APITestCase): self.assertEqual(len(results), 2) self.assertCountEqual([results[0]['id'], results[1]['id']], [doc1.id, doc3.id]) + response = self.client.get("/api/documents/?tags__id__in={},{}".format(tag_2.id, tag_3.id)) + self.assertEqual(response.status_code, 200) + results = response.data['results'] + self.assertEqual(len(results), 2) + self.assertCountEqual([results[0]['id'], results[1]['id']], [doc2.id, doc3.id]) + response = self.client.get("/api/documents/?tags__id__all={},{}".format(tag_2.id, tag_3.id)) self.assertEqual(response.status_code, 200) results = response.data['results'] @@ -923,6 +929,14 @@ class TestBulkEdit(DirectoriesMixin, APITestCase): doc2 = Document.objects.get(id=self.doc2.id) self.assertEqual(doc2.correspondent, self.c1) + def test_api_no_correspondent(self): + response = self.client.post("/api/documents/bulk_edit/", json.dumps({ + "documents": [self.doc2.id], + "method": "set_correspondent", + "parameters": {} + }), content_type='application/json') + self.assertEqual(response.status_code, 400) + def test_api_invalid_document_type(self): self.assertEqual(self.doc2.document_type, self.dt1) response = self.client.post("/api/documents/bulk_edit/", json.dumps({ @@ -935,6 +949,14 @@ class TestBulkEdit(DirectoriesMixin, APITestCase): doc2 = Document.objects.get(id=self.doc2.id) self.assertEqual(doc2.document_type, self.dt1) + def test_api_no_document_type(self): + response = self.client.post("/api/documents/bulk_edit/", json.dumps({ + "documents": [self.doc2.id], + "method": "set_document_type", + "parameters": {} + }), content_type='application/json') + self.assertEqual(response.status_code, 400) + def test_api_add_invalid_tag(self): self.assertEqual(list(self.doc2.tags.all()), [self.t1]) response = self.client.post("/api/documents/bulk_edit/", json.dumps({ @@ -946,6 +968,14 @@ class TestBulkEdit(DirectoriesMixin, APITestCase): self.assertEqual(list(self.doc2.tags.all()), [self.t1]) + def test_api_add_tag_no_tag(self): + response = self.client.post("/api/documents/bulk_edit/", json.dumps({ + "documents": [self.doc2.id], + "method": "add_tag", + "parameters": {} + }), content_type='application/json') + self.assertEqual(response.status_code, 400) + def test_api_delete_invalid_tag(self): self.assertEqual(list(self.doc2.tags.all()), [self.t1]) response = self.client.post("/api/documents/bulk_edit/", json.dumps({ @@ -957,6 +987,14 @@ class TestBulkEdit(DirectoriesMixin, APITestCase): self.assertEqual(list(self.doc2.tags.all()), [self.t1]) + def test_api_delete_tag_no_tag(self): + response = self.client.post("/api/documents/bulk_edit/", json.dumps({ + "documents": [self.doc2.id], + "method": "remove_tag", + "parameters": {} + }), content_type='application/json') + self.assertEqual(response.status_code, 400) + 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({ @@ -966,6 +1004,21 @@ class TestBulkEdit(DirectoriesMixin, APITestCase): }), content_type='application/json') self.assertEqual(response.status_code, 400) + def test_api_modify_tags_no_tags(self): + response = self.client.post("/api/documents/bulk_edit/", json.dumps({ + "documents": [self.doc2.id], + "method": "modify_tags", + "parameters": {"remove_tags": [1123123]} + }), content_type='application/json') + self.assertEqual(response.status_code, 400) + + response = self.client.post("/api/documents/bulk_edit/", json.dumps({ + "documents": [self.doc2.id], + "method": "modify_tags", + "parameters": {'add_tags': [self.t2.id, 1657]} + }), 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": [] diff --git a/src/documents/tests/test_management_consumer.py b/src/documents/tests/test_management_consumer.py index 0680e7f56..b6a61a167 100644 --- a/src/documents/tests/test_management_consumer.py +++ b/src/documents/tests/test_management_consumer.py @@ -7,9 +7,8 @@ from unittest import mock from django.conf import settings from django.core.management import call_command, CommandError -from django.test import override_settings, TransactionTestCase, TestCase +from django.test import override_settings, TransactionTestCase -from documents.management.commands.document_consumer import _test_inotify from documents.models import Tag from documents.consumer import ConsumerError from documents.management.commands import document_consumer @@ -261,27 +260,3 @@ class TestConsumerTags(DirectoriesMixin, ConsumerMixin, TransactionTestCase): @override_settings(CONSUMER_POLLING=1) def test_consume_file_with_path_tags_polling(self): self.test_consume_file_with_path_tags() - - -class TestInotify(DirectoriesMixin, TestCase): - - def test_inotify(self): - self.assertTrue(_test_inotify(self.dirs.consumption_dir)) - - @mock.patch("documents.management.commands.document_consumer.Path.touch") - def test_inotify_error(self, m): - m.side_effect = OSError("Permission error") - self.assertFalse(_test_inotify(self.dirs.consumption_dir)) - - @mock.patch("documents.management.commands.document_consumer.Command.handle_polling") - @mock.patch("documents.management.commands.document_consumer.Command.handle_inotify") - @mock.patch("documents.management.commands.document_consumer._test_inotify") - def test_polling_fallback(self, test_inotify, handle_inotify, handle_polling): - test_inotify.return_value = False - - cmd = document_consumer.Command() - cmd.handle(directory=settings.CONSUMPTION_DIR, oneshot=False) - - test_inotify.assert_called_once() - handle_polling.assert_called_once() - handle_inotify.assert_not_called() diff --git a/src/documents/tests/test_parsers.py b/src/documents/tests/test_parsers.py index 805e4beac..392c0504f 100644 --- a/src/documents/tests/test_parsers.py +++ b/src/documents/tests/test_parsers.py @@ -120,3 +120,4 @@ class TestParserAvailability(TestCase): self.assertTrue(is_file_ext_supported('.pdf')) self.assertFalse(is_file_ext_supported('.hsdfh')) + self.assertFalse(is_file_ext_supported('')) diff --git a/src/documents/views.py b/src/documents/views.py index eb9078f75..b99bf11c7 100755 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -159,6 +159,9 @@ class DocumentViewSet(RetrieveModelMixin, "added", "archive_serial_number") + def get_queryset(self): + return Document.objects.distinct() + def get_serializer(self, *args, **kwargs): fields_param = self.request.query_params.get('fields', None) if fields_param: From 96a572b7a681bd9a9bd5c3c323054711e5feb531 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Mon, 25 Jan 2021 23:29:16 -0800 Subject: [PATCH 08/26] Refactor, missed import --- src-ui/src/app/app-routing.module.ts | 6 +++--- .../app/components/manage/settings/settings.component.ts | 2 +- src-ui/src/app/guards/dirty-form.guard.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src-ui/src/app/app-routing.module.ts b/src-ui/src/app/app-routing.module.ts index d85026081..f49c5cd71 100644 --- a/src-ui/src/app/app-routing.module.ts +++ b/src-ui/src/app/app-routing.module.ts @@ -11,7 +11,7 @@ import { SettingsComponent } from './components/manage/settings/settings.compone import { TagListComponent } from './components/manage/tag-list/tag-list.component'; import { NotFoundComponent } from './components/not-found/not-found.component'; import { SearchComponent } from './components/search/search.component'; -import { FormDirtyGuard } from './guards/dirty-form.guard'; +import { DirtyFormGuard } from './guards/dirty-form.guard'; const routes: Routes = [ {path: '', redirectTo: 'dashboard', pathMatch: 'full'}, @@ -20,13 +20,13 @@ const routes: Routes = [ {path: 'documents', component: DocumentListComponent }, {path: 'view/:id', component: DocumentListComponent }, {path: 'search', component: SearchComponent }, - {path: 'documents/:id', component: DocumentDetailComponent, canDeactivate: [FormDirtyGuard] }, + {path: 'documents/:id', component: DocumentDetailComponent, canDeactivate: [DirtyFormGuard] }, {path: 'tags', component: TagListComponent }, {path: 'documenttypes', component: DocumentTypeListComponent }, {path: 'correspondents', component: CorrespondentListComponent }, {path: 'logs', component: LogsComponent }, - {path: 'settings', component: SettingsComponent, canDeactivate: [FormDirtyGuard] }, + {path: 'settings', component: SettingsComponent, canDeactivate: [DirtyFormGuard] }, ]}, {path: '404', component: NotFoundComponent}, 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 f9a84e53b..aa73969f0 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, LOCALE_ID, OnInit, Renderer2 } from '@angular/core'; +import { Component, Inject, LOCALE_ID, OnInit, OnDestroy, Renderer2 } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'; import { DocumentListViewService } from 'src/app/services/document-list-view.service'; diff --git a/src-ui/src/app/guards/dirty-form.guard.ts b/src-ui/src/app/guards/dirty-form.guard.ts index b0b50ac7f..2b9f1b4a2 100644 --- a/src-ui/src/app/guards/dirty-form.guard.ts +++ b/src-ui/src/app/guards/dirty-form.guard.ts @@ -6,7 +6,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component'; @Injectable({ providedIn: 'root' }) -export class FormDirtyGuard extends DirtyCheckGuard { +export class DirtyFormGuard extends DirtyCheckGuard { constructor(private modalService: NgbModal) { super(); } From d672455ad80fe30c71699ee8d6207cf471fbd904 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Mon, 25 Jan 2021 23:32:02 -0800 Subject: [PATCH 09/26] Cleanup --- .../common/confirm-dialog/confirm-dialog.component.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts index 4287b66d0..454c9a3d4 100644 --- a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts +++ b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts @@ -9,6 +9,8 @@ import { Subject } from 'rxjs'; }) export class ConfirmDialogComponent implements OnInit { + constructor(public activeModal: NgbActiveModal) { } + @Output() public confirmClicked = new EventEmitter() @@ -35,10 +37,6 @@ export class ConfirmDialogComponent implements OnInit { subject: Subject - constructor(public activeModal: NgbActiveModal) { - this.confirmClicked.asObservable() - } - delayConfirm(seconds: number) { this.confirmButtonEnabled = false this.seconds = seconds @@ -61,8 +59,8 @@ export class ConfirmDialogComponent implements OnInit { } confirm() { + this.confirmClicked.emit() this.subject?.next(true) this.subject?.complete() - this.confirmClicked.emit() } } From 07faba3983ba2133e9d35e62a1b56ea8843be7d3 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Mon, 25 Jan 2021 23:39:16 -0800 Subject: [PATCH 10/26] Confirm button text --- src-ui/src/app/guards/dirty-form.guard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/src/app/guards/dirty-form.guard.ts b/src-ui/src/app/guards/dirty-form.guard.ts index 2b9f1b4a2..79cb34d6c 100644 --- a/src-ui/src/app/guards/dirty-form.guard.ts +++ b/src-ui/src/app/guards/dirty-form.guard.ts @@ -17,7 +17,7 @@ export class DirtyFormGuard extends DirtyCheckGuard { modal.componentInstance.messageBold = $localize`You have unsaved changes.` modal.componentInstance.message = $localize`Are you sure you want to leave?` modal.componentInstance.btnClass = "btn-warning" - modal.componentInstance.btnCaption = $localize`Ok` + modal.componentInstance.btnCaption = $localize`Leave page` modal.componentInstance.confirmClicked.subscribe(() => { modal.componentInstance.buttonsEnabled = false modal.close() From 1576e07011e589fca8010585e131ecb6a046ae28 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Tue, 26 Jan 2021 20:46:28 -0800 Subject: [PATCH 11/26] Dont warn on navigate for documents --- src-ui/src/app/app-routing.module.ts | 2 +- .../document-detail.component.html | 2 +- .../document-detail.component.ts | 20 +++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src-ui/src/app/app-routing.module.ts b/src-ui/src/app/app-routing.module.ts index f49c5cd71..ee75b3312 100644 --- a/src-ui/src/app/app-routing.module.ts +++ b/src-ui/src/app/app-routing.module.ts @@ -20,7 +20,7 @@ const routes: Routes = [ {path: 'documents', component: DocumentListComponent }, {path: 'view/:id', component: DocumentListComponent }, {path: 'search', component: SearchComponent }, - {path: 'documents/:id', component: DocumentDetailComponent, canDeactivate: [DirtyFormGuard] }, + {path: 'documents/:id', component: DocumentDetailComponent }, {path: 'tags', component: TagListComponent }, {path: 'documenttypes', component: DocumentTypeListComponent }, 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 c0fc4af6d..d5c8c862e 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 @@ -38,7 +38,7 @@  More like this - -
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index f03270547..4effb6179 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -75,8 +75,7 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen @ViewChild('nav') nav: NgbNav @ViewChild('pdfPreview') set pdfPreview(element) { // this gets called when compontent added or removed from DOM - if (element && element.nativeElement.offsetParent !== null) { // its visible - + if (element && element.nativeElement.offsetParent !== null && this.nav?.activeId == 4) { // its visible setTimeout(()=> this.nav?.select(1)); } } From 8c853e56e4c376f5db45a84ac0d123643922c742 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 16 Feb 2022 13:43:25 -0800 Subject: [PATCH 23/26] Reset networkActive & error after switchmap pipes complete --- .../components/document-detail/document-detail.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 4effb6179..5f6e7f369 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -222,13 +222,14 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen this.networkActive = true this.store.next(this.documentForm.value) this.documentsService.update(this.document).pipe(switchMap(updateResult => { - this.error = null return this.documentListViewService.getNext(this.documentId).pipe(map(nextDocId => ({nextDocId, updateResult}))) })).pipe(switchMap(({nextDocId, updateResult}) => { if (nextDocId) return this.openDocumentService.closeDocument(this.document, true).pipe(map(closeResult => ({updateResult, nextDocId, closeResult}))) })) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(({updateResult, nextDocId, closeResult}) => { + this.error = null + this.networkActive = false if (closeResult) { this.router.navigate(['documents', nextDocId]) this.titleInput?.focus() From 9bc48fed73ecda6921b7fa322170fdb2550bbe09 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 16 Feb 2022 14:34:19 -0800 Subject: [PATCH 24/26] Check for live changes on document detail title --- .../document-detail.component.html | 2 +- .../document-detail.component.ts | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 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 52710aca2..4acd7a904 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 @@ -56,7 +56,7 @@ Details - + = new Subject() previewUrl: string downloadUrl: string downloadOriginalUrl: string @@ -91,7 +92,19 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen private documentListViewService: DocumentListViewService, private documentTitlePipe: DocumentTitlePipe, private toastService: ToastService, - private settings: SettingsService) { } + private settings: SettingsService) { + this.titleSubject.pipe( + debounceTime(200), + distinctUntilChanged(), + takeUntil(this.unsubscribeNotifier) + ).subscribe(titleValue => { + this.documentForm.patchValue({'title': titleValue}) + }) + } + + titleKeyUp(event) { + this.titleSubject.next(event.target?.value) + } get useNativePdfViewer(): boolean { return this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER) From 66b9a99e7170235e6fc5b0a2833a6f140f5703a5 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 17 Feb 2022 23:04:38 -0800 Subject: [PATCH 25/26] Correct runtime issue(s) with saveEditNext --- .../document-detail.component.ts | 36 +++++++++---------- .../app/services/open-documents.service.ts | 4 +-- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index 8037cf44b..85b221307 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -124,14 +124,18 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen this.route.paramMap.pipe(switchMap(paramMap => { const documentId = +paramMap.get('id') - return this.documentsService.get(documentId).pipe(map(doc => ({doc, documentId}))) - })).pipe(switchMap(({doc, documentId}) => { - this.previewUrl = this.documentsService.getPreviewUrl(documentId) - this.downloadUrl = this.documentsService.getDownloadUrl(documentId) - this.downloadOriginalUrl = this.documentsService.getDownloadUrl(documentId, true) + return this.documentsService.get(documentId) + })).pipe(switchMap((doc) => { + this.documentId = doc.id + this.previewUrl = this.documentsService.getPreviewUrl(this.documentId) + this.downloadUrl = this.documentsService.getDownloadUrl(this.documentId) + this.downloadOriginalUrl = this.documentsService.getDownloadUrl(this.documentId, true) this.suggestions = null - if (this.openDocumentService.getOpenDocument(documentId)) { - this.updateComponent(this.openDocumentService.getOpenDocument(documentId)) + if (this.openDocumentService.getOpenDocument(this.documentId)) { + this.updateComponent(this.openDocumentService.getOpenDocument(this.documentId)) + } else { + this.openDocumentService.openDocument(doc) + this.updateComponent(doc) } // Initialize dirtyCheck @@ -147,17 +151,11 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen this.isDirty$ = dirtyCheck(this.documentForm, this.store.asObservable()) - return this.isDirty$.pipe(map(dirty => ({doc, documentId, dirty}))) + return this.isDirty$.pipe(map(dirty => ({doc, dirty}))) })) .pipe(takeUntil(this.unsubscribeNotifier)) - .subscribe(({doc, documentId, dirty}) => { - this.documentId = documentId - this.openDocumentService.setDirty(documentId, dirty) - - if (!this.openDocumentService.getOpenDocument(documentId)) { - this.openDocumentService.openDocument(doc) - this.updateComponent(doc) - } + .subscribe(({doc, dirty}) => { + this.openDocumentService.setDirty(doc.id, dirty) }, error => {this.router.navigate(['404'])}) } @@ -237,13 +235,13 @@ export class DocumentDetailComponent implements OnInit, OnDestroy, DirtyComponen this.documentsService.update(this.document).pipe(switchMap(updateResult => { return this.documentListViewService.getNext(this.documentId).pipe(map(nextDocId => ({nextDocId, updateResult}))) })).pipe(switchMap(({nextDocId, updateResult}) => { - if (nextDocId) return this.openDocumentService.closeDocument(this.document, true).pipe(map(closeResult => ({updateResult, nextDocId, closeResult}))) + if (nextDocId && updateResult) return this.openDocumentService.closeDocument(this.document).pipe(map(closeResult => ({updateResult, nextDocId, closeResult}))) })) - .pipe(takeUntil(this.unsubscribeNotifier)) + .pipe(first()) .subscribe(({updateResult, nextDocId, closeResult}) => { this.error = null this.networkActive = false - if (closeResult) { + if (closeResult && updateResult && nextDocId) { this.router.navigate(['documents', nextDocId]) this.titleInput?.focus() } diff --git a/src-ui/src/app/services/open-documents.service.ts b/src-ui/src/app/services/open-documents.service.ts index ad811da86..c44f4cd75 100644 --- a/src-ui/src/app/services/open-documents.service.ts +++ b/src-ui/src/app/services/open-documents.service.ts @@ -63,10 +63,10 @@ export class OpenDocumentsService { else this.dirtyDocuments.delete(documentId) } - closeDocument(doc: PaperlessDocument, force: boolean = false): Observable { + closeDocument(doc: PaperlessDocument): Observable { let index = this.openDocuments.findIndex(d => d.id == doc.id) if (index == -1) return of(true); - if (force || !this.dirtyDocuments.has(doc.id)) { + if (!this.dirtyDocuments.has(doc.id)) { this.openDocuments.splice(index, 1) this.save() return of(true) From c9baa76afdcca09de60140ac562ee5d29cf379e5 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 17 Feb 2022 23:05:14 -0800 Subject: [PATCH 26/26] clear 'dirty' documents when closing all --- src-ui/src/app/services/open-documents.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src-ui/src/app/services/open-documents.service.ts b/src-ui/src/app/services/open-documents.service.ts index c44f4cd75..92802c765 100644 --- a/src-ui/src/app/services/open-documents.service.ts +++ b/src-ui/src/app/services/open-documents.service.ts @@ -102,6 +102,7 @@ export class OpenDocumentsService { modal.componentInstance.buttonsEnabled = false modal.close() this.openDocuments.splice(0, this.openDocuments.length) + this.dirtyDocuments.clear() this.save() }) const subject = new Subject() @@ -109,6 +110,7 @@ export class OpenDocumentsService { return subject.asObservable() } else { this.openDocuments.splice(0, this.openDocuments.length) + this.dirtyDocuments.clear() this.save() return of(true) }