diff --git a/Pipfile b/Pipfile index 48759307..f6301b98 100644 --- a/Pipfile +++ b/Pipfile @@ -42,6 +42,7 @@ whoosh="~=2.7.4" inotifyrecursive = "~=0.3.4" ocrmypdf = "*" tqdm = "*" +tika = "*" [dev-packages] coveralls = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 1cfccb8f..c6621b54 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3d576f289958226a7583e4c471c7f8c11bff6933bf093185f623cfb381a92412" + "sha256": "993e362c31af6b8094693075f614270a820cf0b557369d66d674e1a107b7bd31" }, "pipfile-spec": 6, "requires": { @@ -44,6 +44,13 @@ ], "version": "==1.17.12" }, + "certifi": { + "hashes": [ + "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", + "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" + ], + "version": "==2020.12.5" + }, "cffi": { "hashes": [ "sha256:00a1ba5e2e95684448de9b89888ccd02c98d512064b4cb987d48f4b40aa0421e", @@ -229,6 +236,15 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==9.0" }, + "idna": { + "hashes": [ + "sha256:4a57a6379512ade94fa99e2fa46d3cd0f2f553040548d0e2958c6ed90ee48226", + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.10" + }, "imap-tools": { "hashes": [ "sha256:72bf46dc135b039a5d5b59f4e079242ac15eac02a30038e8cb2dec7b153cab65", @@ -683,6 +699,14 @@ ], "version": "==3.5.56" }, + "requests": { + "hashes": [ + "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", + "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==2.25.1" + }, "scikit-learn": { "hashes": [ "sha256:090bbf144fd5823c1f2efa3e1a9bf180295b24294ca8f478e75b40ed54f8036e", @@ -769,6 +793,14 @@ "markers": "python_version >= '3.5'", "version": "==2.1.0" }, + "tika": { + "hashes": [ + "sha256:c2c50f405622f74531841104f9e85c17511aede11de8e5385eab1a29a31f191b", + "sha256:d1f2eddb93caa9a2857569486aa2bc0320d0bf1796cdbe03066954cbc4b4bf62" + ], + "index": "pypi", + "version": "==1.24" + }, "tqdm": { "hashes": [ "sha256:38b658a3e4ecf9b4f6f8ff75ca16221ae3378b2e175d846b6b33ea3a20852cf5", @@ -777,6 +809,15 @@ "index": "pypi", "version": "==4.54.1" }, + "typing-extensions": { + "hashes": [ + "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", + "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", + "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" + ], + "markers": "python_version < '3.8'", + "version": "==3.7.4.3" + }, "tzlocal": { "hashes": [ "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44", @@ -784,6 +825,14 @@ ], "version": "==2.1" }, + "urllib3": { + "hashes": [ + "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08", + "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.26.2" + }, "watchdog": { "hashes": [ "sha256:3caefdcc8f06a57fdc5ef2d22aa7c0bfda4f55e71a0bee74cbf3176d97536ef3", @@ -1197,11 +1246,11 @@ }, "requests": { "hashes": [ - "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8", - "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998" + "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", + "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.25.0" + "version": "==2.25.1" }, "six": { "hashes": [ diff --git a/README.md b/README.md index eea41ce0..5c5fa4a7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/jonaswinkler/paperless-ng.svg?branch=master)](https://travis-ci.org/jonaswinkler/paperless-ng) +[![Build Status](https://travis-ci.com/jonaswinkler/paperless-ng.svg?branch=master)](https://travis-ci.com/jonaswinkler/paperless-ng) [![Documentation Status](https://readthedocs.org/projects/paperless-ng/badge/?version=latest)](https://paperless-ng.readthedocs.io/en/latest/?badge=latest) [![Gitter](https://badges.gitter.im/paperless-ng/community.svg)](https://gitter.im/paperless-ng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/jonaswinkler/paperless-ng.svg)](https://hub.docker.com/r/jonaswinkler/paperless-ng) diff --git a/docker/gunicorn.conf.py b/docker/gunicorn.conf.py index 5c9c0eb3..edfb362d 100644 --- a/docker/gunicorn.conf.py +++ b/docker/gunicorn.conf.py @@ -1,4 +1,4 @@ -bind = ['[::]:8000', 'localhost:8000'] +bind = '0.0.0.0:8000' backlog = 2048 workers = 3 worker_class = 'sync' diff --git a/docker/hub/docker-compose.postgres.yml b/docker/hub/docker-compose.postgres.yml index c2b599e5..0779cea2 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.10 + image: jonaswinkler/paperless-ng:0.9.11 restart: always depends_on: - db diff --git a/docker/hub/docker-compose.sqlite.yml b/docker/hub/docker-compose.sqlite.yml index 429d42c0..3eed96cc 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.10 + image: jonaswinkler/paperless-ng:0.9.11 restart: always depends_on: - broker diff --git a/docker/hub/docker-compose.tika.yml b/docker/hub/docker-compose.tika.yml new file mode 100644 index 00000000..af8f575a --- /dev/null +++ b/docker/hub/docker-compose.tika.yml @@ -0,0 +1,43 @@ +version: "3.4" +services: + broker: + image: redis:6.0 + restart: always + + webserver: + image: jonaswinkler/paperless-ng:0.9.9 + restart: always + depends_on: + - broker + ports: + - 8000:8000 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000"] + interval: 30s + timeout: 10s + retries: 5 + volumes: + - data:/usr/src/paperless/data + - media:/usr/src/paperless/media + - ./export:/usr/src/paperless/export + - ./consume:/usr/src/paperless/consume + env_file: docker-compose.env + environment: + PAPERLESS_REDIS: redis://broker:6379 + PAPERLESS_TIKA_ENABLED: 1 + PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000 + PAPERLESS_TIKA_ENDPOINT: http://tika:9998 + + gotenberg: + image: thecodingmachine/gotenberg + restart: unless-stopped + environment: + DISABLE_GOOGLE_CHROME: 1 + + tika: + image: apache/tika + restart: unless-stopped + +volumes: + data: + media: diff --git a/docker/local/Dockerfile b/docker/local/Dockerfile index 1fd5e24f..1160cc9a 100644 --- a/docker/local/Dockerfile +++ b/docker/local/Dockerfile @@ -63,6 +63,8 @@ WORKDIR /usr/src/paperless/src/ RUN sudo -HEu paperless python3 manage.py collectstatic --clear --no-input +RUN sudo -HEu paperless python3 manage.py compilemessages + VOLUME ["/usr/src/paperless/data", "/usr/src/paperless/media", "/usr/src/paperless/consume", "/usr/src/paperless/export"] ENTRYPOINT ["/sbin/docker-entrypoint.sh"] EXPOSE 8000 diff --git a/docker/local/docker-compose.tika.yml b/docker/local/docker-compose.tika.yml new file mode 100644 index 00000000..88971390 --- /dev/null +++ b/docker/local/docker-compose.tika.yml @@ -0,0 +1,43 @@ +version: "3.4" +services: + broker: + image: redis:6.0 + restart: always + + webserver: + build: . + restart: always + depends_on: + - broker + ports: + - 8000:8000 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000"] + interval: 30s + timeout: 10s + retries: 5 + volumes: + - data:/usr/src/paperless/data + - media:/usr/src/paperless/media + - ./export:/usr/src/paperless/export + - ./consume:/usr/src/paperless/consume + env_file: docker-compose.env + environment: + PAPERLESS_REDIS: redis://broker:6379 + PAPERLESS_TIKA_ENABLED: 1 + PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000 + PAPERLESS_TIKA_ENDPOINT: http://tika:9998 + + gotenberg: + image: thecodingmachine/gotenberg + restart: unless-stopped + environment: + DISABLE_GOOGLE_CHROME: 1 + + tika: + image: apache/tika + restart: unless-stopped + +volumes: + data: + media: diff --git a/docs/administration.rst b/docs/administration.rst index 8885b732..727d9a9d 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -148,7 +148,13 @@ After grabbing the new release and unpacking the contents, do the following: $ cd src $ pipenv run python3 manage.py migrate + +5. Update translation files. + .. code:: shell-session + + $ cd src + $ pipenv run python3 manage.py compilemessages Management utilities #################### diff --git a/docs/changelog.rst b/docs/changelog.rst index 4357a981..70f5cf68 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,13 @@ Changelog ********* + +paperless-ng 0.9.11 +################### + +* Fixed an issue with the docker image not starting at all due to a configuration change of the web server. + + paperless-ng 0.9.10 ################### @@ -15,6 +22,7 @@ paperless-ng 0.9.10 * Other changes and additions + * Thanks to `zjean`_, paperless now publishes a webmanifest, which is useful for adding the application to home screens on mobile devices. * 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. @@ -25,6 +33,8 @@ paperless-ng 0.9.10 * 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. + * Issue with the tag selector not clearing input correctly. + * The consumer used to stop working when encountering an incomplete classifier model file. .. note:: @@ -956,6 +966,7 @@ bulk of the work on this big change. * Initial release +.. _zjean: https://github.com/zjean .. _rYR79435: https://github.com/rYR79435 .. _Michael Shamoon: https://github.com/shamoon .. _jayme-github: http://github.com/jayme-github diff --git a/docs/configuration.rst b/docs/configuration.rst index c7202757..0271df27 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -283,6 +283,35 @@ PAPERLESS_OCR_USER_ARG= {"deskew": true, "optimize": 3, "unpaper_args": "--pre-rotate 90"} +.. _configuration-tika: + +Tika settings +############# + +Paperless can make use of `Tika `_ and +`Gotenberg `_ for parsing and +converting "Office" documents (such as ".doc", ".xlsx" and ".odt"). If you +wish to use this, you must provide a Tika server and a Gotenberg server, +configure their endpoints, and enable the feature. + +If you run paperless on docker, you can add those services to the docker-compose +file (see the examples provided). + +PAPERLESS_TIKA_ENABLED= + Enable (or disable) the Tika parser. + + Defaults to false. + +PAPERLESS_TIKA_ENDPOINT= + Set the endpoint URL were Paperless can reach your Tika server. + + Defaults to "http://localhost:9998". + +PAPERLESS_TIKA_GOTENBERG_ENDPOINT= + Set the endpoint URL were Paperless can reach your Gotenberg server. + + Defaults to "http://localhost:3000". + Software tweaks ############### diff --git a/docs/setup.rst b/docs/setup.rst index 43740919..e51c4c87 100644 --- a/docs/setup.rst +++ b/docs/setup.rst @@ -292,6 +292,9 @@ writing. Windows is not and will never be supported. # This creates the database schema. python3 manage.py migrate + + # This creates the translation files for paperless. + python3 manage.py compilemessages # This creates your first paperless user python3 manage.py createsuperuser diff --git a/paperless.conf.example b/paperless.conf.example index c55b7f5f..b74a1aea 100644 --- a/paperless.conf.example +++ b/paperless.conf.example @@ -40,7 +40,7 @@ #PAPERLESS_OCR_OUTPUT_TYPE=pdfa #PAPERLESS_OCR_PAGES=1 #PAPERLESS_OCR_IMAGE_DPI=300 -#PAPERLESS_OCR_USER_ARG={} +#PAPERLESS_OCR_USER_ARGS={} #PAPERLESS_CONVERT_MEMORY_LIMIT=0 #PAPERLESS_CONVERT_TMPDIR=/var/tmp/paperless @@ -57,6 +57,12 @@ #PAPERLESS_FILENAME_PARSE_TRANSFORMS=[] #PAPERLESS_THUMBNAIL_FONT_NAME= +# Tika settings + +#PAPERLESS_TIKA_ENABLED=false +#PAPERLESS_TIKA_ENDPOINT=http://localhost:9998 +#PAPERLESS_TIKA_GOTENBERG_ENDPOINT=http://localhost:3000 + # Binaries #PAPERLESS_CONVERT_BINARY=/usr/bin/convert diff --git a/scripts/start_services.sh b/scripts/start_services.sh index e566f59b..cbab7ac9 100755 --- a/scripts/start_services.sh +++ b/scripts/start_services.sh @@ -1,2 +1,4 @@ docker run -p 5432:5432 -v paperless_pgdata:/var/lib/postgresql/data -d postgres:13 docker run -d -p 6379:6379 redis:latest +docker run -p 3000:3000 -d thecodingmachine/gotenberg +docker run -p 9998:9998 -d apache/tika diff --git a/src-ui/angular.json b/src-ui/angular.json index 79233eed..df53224c 100644 --- a/src-ui/angular.json +++ b/src-ui/angular.json @@ -13,6 +13,12 @@ "root": "", "sourceRoot": "src", "prefix": "app", + "i18n": { + "sourceLocale": "en-US", + "locales": { + "de": "src/locale/messages.de.xlf" + } + }, "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", @@ -23,10 +29,16 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", + "localize": true, "aot": true, "assets": [ "src/favicon.ico", - "src/assets" + "src/assets", + "src/manifest.webmanifest", { + "glob": "pdf.worker.min.js", + "input": "node_modules/pdfjs-dist/build/", + "output": "/assets/js/" + } ], "styles": [ "src/styles.scss" @@ -64,13 +76,16 @@ "maximumError": "10kb" } ] + }, + "en-US": { + "localize": ["en-US"] } } }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "paperless-ui:build" + "browserTarget": "paperless-ui:build:en-US" }, "configurations": { "production": { @@ -93,7 +108,8 @@ "karmaConfig": "karma.conf.js", "assets": [ "src/favicon.ico", - "src/assets" + "src/assets", + "src/manifest.webmanifest" ], "styles": [ "src/styles.scss" diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 6270f537..4a84cfd6 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -79,22 +79,15 @@ 71 - - {VAR_PLURAL, plural, =1 {document} other {documents}} + + {VAR_PLURAL, plural, =1 {Selected of one document} other {Selected of documents}} src/app/components/document-list/document-list.component.html 86 - - Selected of - - src/app/components/document-list/document-list.component.html - 86 - - - - {VAR_PLURAL, plural, =1 {1 document} other { documents}} + + {VAR_PLURAL, plural, =1 {One document} other { documents}} src/app/components/document-list/document-list.component.html 87 @@ -149,8 +142,8 @@ 161 - - Do you really want to delete document ''? + + Do you really want to delete document ""? src/app/components/document-detail/document-detail.component.ts 162 @@ -373,11 +366,11 @@ 1 - - Do you really want to delete the tag ? + + Do you really want to delete the tag ""? src/app/components/manage/tag-list/tag-list.component.ts - 30 + 28 @@ -436,11 +429,11 @@ 37 - - Do you really want to delete the document type ? + + Do you really want to delete the document type ""? src/app/components/manage/document-type-list/document-type-list.component.ts - 26 + 24 @@ -464,25 +457,32 @@ 7 - - Saved view " deleted. + + Saved view "" deleted. src/app/components/manage/settings/settings.component.ts - 52 + 54 Settings saved successfully. src/app/components/manage/settings/settings.component.ts - 61 + 74 Error while storing settings on server: src/app/components/manage/settings/settings.component.ts - 73 + 86 + + + + Settings + + src/app/components/manage/settings/settings.component.html + 1 @@ -496,11 +496,11 @@ Saved views src/app/components/manage/settings/settings.component.html - 41 + 56 - - Document list + + Appearance src/app/components/manage/settings/settings.component.html 13 @@ -513,60 +513,74 @@ 17 + + Dark mode + + src/app/components/manage/settings/settings.component.html + 33 + + + + Use system settings + + src/app/components/manage/settings/settings.component.html + 36 + + Bulk editing src/app/components/manage/settings/settings.component.html - 33 + 44 Show confirmation dialogs src/app/components/manage/settings/settings.component.html - 35 + 48 Deleting documents will always ask for confirmation. src/app/components/manage/settings/settings.component.html - 35 + 48 Apply on close src/app/components/manage/settings/settings.component.html - 36 + 49 Appears on src/app/components/manage/settings/settings.component.html - 53 + 68 Show on dashboard src/app/components/manage/settings/settings.component.html - 56 + 71 Show in sidebar src/app/components/manage/settings/settings.component.html - 60 + 75 No saved views defined. src/app/components/manage/settings/settings.component.html - 70 + 85 @@ -576,11 +590,11 @@ 7 - - Do you really want to delete the correspondent ? + + Do you really want to delete the correspondent ""? src/app/components/manage/correspondent-list/correspondent-list.component.ts - 26 + 24 @@ -639,8 +653,8 @@ 11 - - Match + + Matching pattern src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html 12 @@ -776,26 +790,26 @@ Paperless-ng src/app/components/app-frame/app-frame.component.html - 4 + 11 app title - - Search for documents + + Search documents src/app/components/app-frame/app-frame.component.html - 12 + 15 + + + + Logout + + src/app/components/app-frame/app-frame.component.html + 45 Manage - - src/app/components/app-frame/app-frame.component.html - 77 - - - - Settings src/app/components/app-frame/app-frame.component.html 112 @@ -805,70 +819,91 @@ Admin src/app/components/app-frame/app-frame.component.html - 119 + 147 Misc src/app/components/app-frame/app-frame.component.html - 125 + 153 Documentation src/app/components/app-frame/app-frame.component.html - 132 + 160 GitHub src/app/components/app-frame/app-frame.component.html - 139 + 167 - - Logout + + Logged in as src/app/components/app-frame/app-frame.component.html - 146 + 34 Open documents src/app/components/app-frame/app-frame.component.html - 57 + 92 Close all src/app/components/app-frame/app-frame.component.html - 71 + 106 Correspondent: src/app/components/document-list/filter-editor/filter-editor.component.ts - 28 + 29 + + + + Without correspondent + + src/app/components/document-list/filter-editor/filter-editor.component.ts + 31 Type: src/app/components/document-list/filter-editor/filter-editor.component.ts - 31 + 36 + + + + Without document type + + src/app/components/document-list/filter-editor/filter-editor.component.ts + 38 Tag: src/app/components/document-list/filter-editor/filter-editor.component.ts - 34 + 42 + + + + Without any tag + + src/app/components/document-list/filter-editor/filter-editor.component.ts + 46 @@ -878,19 +913,41 @@ 4 + + Filter tags + + src/app/components/document-list/filter-editor/filter-editor.component.html + 12 + + + + Filter correspondents + + src/app/components/document-list/filter-editor/filter-editor.component.html + 19 + + + + Filter document types + + src/app/components/document-list/filter-editor/filter-editor.component.html + 25 + + Clear all filters src/app/components/document-list/filter-editor/filter-editor.component.html - 23 + 47 Not assigned src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts - 145 + 161 + Filter drop down element to filter for documents with no correspondent/type/tag assigned Apply @@ -955,13 +1012,6 @@ 50 - - Score: - - src/app/components/document-list/document-card-large/document-card-large.component.html - 61 - - Created: @@ -983,6 +1033,13 @@ 24 + + Score: + + src/app/components/document-list/document-card-large/document-card-large.component.html + 61 + + View in browser @@ -990,12 +1047,20 @@ 40 - - and + + "" and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts 103 + This is for messages like 'modify "tag1" and "tag2"' + + + "" + + src/app/components/document-list/bulk-editor/bulk-editor.component.ts + 105 + , @@ -1003,6 +1068,15 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts 105 + this is used to separate enumerations and should probably be a comma and a whitespace in most languages + + + and "" + + src/app/components/document-list/bulk-editor/bulk-editor.component.ts + 106 + + this is for messages like 'modify "tag1", "tag2" and "tag3"' Confirm tags assignment @@ -1011,36 +1085,36 @@ 115 - - This operation will add the tag to all selected document(s). + + This operation will add the tag "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 118 - - This operation will add the tags to all selected document(s). + + This operation will add the tags to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 120 - - This operation will remove the tag from all selected document(s). + + This operation will remove the tag "" from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 123 - - This operation will remove the tags from all selected document(s). + + This operation will remove the tags from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 125 - - This operation will add the tags and remove the tags on all selected document(s). + + This operation will add the tags and remove the tags on selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 127 @@ -1053,15 +1127,15 @@ 157 - - This operation will assign the correspondent to all selected document(s). + + This operation will assign the correspondent "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 159 - - This operation will remove the correspondent from all selected document(s). + + This operation will remove the correspondent from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 161 @@ -1074,15 +1148,15 @@ 190 - - This operation will assign the document type to all selected document(s). + + This operation will assign the document type "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 192 - - This operation will remove the document type from all selected document(s). + + This operation will remove the document type from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 194 @@ -1095,8 +1169,8 @@ 219 - - This operation will permanently delete all selected document(s). + + This operation will permanently delete selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts 220 @@ -1214,8 +1288,8 @@ 5 - - Uploading file(s) + + {VAR_PLURAL, plural, =1 {Uploading file...} =other {Uploading files...}} src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html 13 @@ -1235,15 +1309,15 @@ 5 - - You can start uploading documents by dropping them in the file upload box to the right or by dropping them in the configured consumption folder and they'll start showing up in the documents list. After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and have them displayed on the dashboard instead of this message. + + You can start uploading documents by dropping them in the file upload box to the right or by dropping them in the configured consumption folder and they'll start showing up in the documents list. After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and they will appear on the dashboard instead of this message. src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html 6,7 - - Paperless offers some more features that try to make your life easier, such as: + + Paperless offers some more features that try to make your life easier: src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html 8 @@ -1515,22 +1589,43 @@ 97 - - Any + + Any word src/app/data/matching-model.ts 12 - - All + + Any: Document contains any of these words (space separated) + + src/app/data/matching-model.ts + 12 + + + + All words src/app/data/matching-model.ts 13 - - Literal + + All: Document contains all of these words (space separated) + + src/app/data/matching-model.ts + 13 + + + + Exact match + + src/app/data/matching-model.ts + 14 + + + + Exact: Document contains this string src/app/data/matching-model.ts 14 @@ -1543,15 +1638,29 @@ 15 - - Fuzzy match + + Regular expression: Document matches this regular expression + + src/app/data/matching-model.ts + 15 + + + + Fuzzy word src/app/data/matching-model.ts 16 - - Auto + + Fuzzy: Document contains a word similar to this word + + src/app/data/matching-model.ts + 16 + + + + Auto: Learn matching automatically src/app/data/matching-model.ts 17 diff --git a/src-ui/src/app/app.component.ts b/src-ui/src/app/app.component.ts index 84c173a1..73c5fc86 100644 --- a/src-ui/src/app/app.component.ts +++ b/src-ui/src/app/app.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { SettingsService } from './services/settings.service'; @Component({ selector: 'app-root', @@ -6,9 +7,11 @@ import { Component } from '@angular/core'; styleUrls: ['./app.component.scss'] }) export class AppComponent { - - constructor () { + + constructor (private settings: SettingsService) { + let anyWindow = (window as any) + anyWindow.pdfWorkerSrc = '/assets/js/pdf.worker.min.js'; + this.settings.updateDarkModeSettings() } - } diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html index d191ec0d..42273ca3 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.html +++ b/src-ui/src/app/components/app-frame/app-frame.component.html @@ -1,17 +1,52 @@ diff --git a/src-ui/src/app/components/app-frame/app-frame.component.scss b/src-ui/src/app/components/app-frame/app-frame.component.scss index 5ace8a2f..6809875e 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.scss +++ b/src-ui/src/app/components/app-frame/app-frame.component.scss @@ -1,36 +1,30 @@ - @import "/src/theme"; - - /* +/* * Sidebar */ - - .sidebar { +.sidebar { position: fixed; top: 0; bottom: 0; left: 0; z-index: 100; /* Behind the navbar */ - padding: 48px 0 0; /* Height of navbar */ + padding: 50px 0 0; /* Height of navbar */ box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); } - @media (max-width: 767.98px) { .sidebar { - top: 3rem; + top: 3.5rem; } } .sidebar-sticky { position: relative; top: 0; - /* height: calc(100vh - 48px); */ height: 100%; - padding-top: .5rem; + padding-top: 0.5rem; overflow-x: hidden; overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ } - @supports ((position: -webkit-sticky) or (position: sticky)) { .sidebar-sticky { position: -webkit-sticky; @@ -53,36 +47,85 @@ font-weight: bold; } -.sidebar .nav-link:hover .sidebaricon, -.sidebar .nav-link.active .sidebaricon { +.sidebar .nav-link.active .sidebaricon, +.sidebar .nav-link:hover .sidebaricon { color: inherit; } .sidebar-heading { - font-size: .75rem; + font-size: 0.75rem; text-transform: uppercase; } - /* * Navbar */ - .navbar-brand { - padding-top: .75rem; - padding-bottom: .75rem; +.navbar-brand { + padding-top: 0.75rem; + padding-bottom: 0.75rem; font-size: 1rem; - background-color: rgba(0, 0, 0, .25); - box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25); } -.navbar .navbar-toggler { - top: .25rem; - right: 1rem; +.dropdown.show .dropdown-toggle, +.dropdown-toggle:hover { + opacity: 0.7; } -.navbar .form-control { - padding: .75rem 1rem; - border-width: 0; - border-radius: 0; +.dropdown-toggle::after { + margin-left: 0.4em; + vertical-align: 0.155em; +} + +.navbar .dropdown-menu { + font-size: 0.875rem; // body size + + a svg { + opacity: 0.6; + } +} + +.navbar .search-form-container { + max-width: 550px; + + form { + position: relative; + } + + svg { + position: absolute; + left: 0.6rem; + color: rgba(255, 255, 255, 0.6); + } + + &:focus-within { + svg { + display: none; + } + + .form-control::placeholder { + color: rgba(255, 255, 255, 0); + } + } + + .form-control { + color: rgba(255, 255, 255, 0.3); + background-color: rgba(0, 0, 0, 0.15); + padding-left: 1.8rem; + border-color: rgba(255, 255, 255, 0.2); + transition: flex 0.3s ease; + max-width: 600px; + min-width: 300px; // 1/2 max + + &::placeholder { + color: rgba(255, 255, 255, 0.4); + } + + &:focus { + background-color: #fff; + color: #212529; + flex-grow: 1; + padding-left: 0.5rem; + } + } } diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts index c4c00843..ad4460f1 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.ts @@ -9,7 +9,8 @@ import { SavedViewService } from 'src/app/services/rest/saved-view.service'; import { SearchService } from 'src/app/services/rest/search.service'; import { environment } from 'src/environments/environment'; import { DocumentDetailComponent } from '../document-detail/document-detail.component'; - +import { Meta } from '@angular/platform-browser'; + @Component({ selector: 'app-app-frame', templateUrl: './app-frame.component.html', @@ -22,8 +23,10 @@ export class AppFrameComponent implements OnInit, OnDestroy { private activatedRoute: ActivatedRoute, private openDocumentsService: OpenDocumentsService, private searchService: SearchService, - public savedViewService: SavedViewService + public savedViewService: SavedViewService, + private meta: Meta ) { + } versionString = `${environment.appTitle} ${environment.version}` @@ -55,7 +58,7 @@ export class AppFrameComponent implements OnInit, OnDestroy { term.length < 2 ? from([[]]) : this.searchService.autocomplete(term) ) ) - + itemSelected(event) { event.preventDefault() let currentSearch: string = this.searchField.value @@ -98,4 +101,17 @@ export class AppFrameComponent implements OnInit, OnDestroy { } } + get displayName() { + // TODO: taken from dashboard component, is this the best way to pass around username? + let tagFullName = this.meta.getTag('name=full_name') + let tagUsername = this.meta.getTag('name=username') + if (tagFullName && tagFullName.content) { + return tagFullName.content + } else if (tagUsername && tagUsername.content) { + return tagUsername.content + } else { + return null + } + } + } diff --git a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts index ea96be90..8f7af141 100644 --- a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts @@ -2,7 +2,7 @@ import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Observable } from 'rxjs'; -import { MATCHING_ALGORITHMS } from 'src/app/data/matching-model'; +import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model'; import { ObjectWithId } from 'src/app/data/object-with-id'; import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'; import { ToastService } from 'src/app/services/toast.service'; @@ -61,6 +61,10 @@ export abstract class EditDialogComponent implements OnI return MATCHING_ALGORITHMS } + get patternRequired(): boolean { + return this.objectForm?.value.matching_algorithm !== MATCH_AUTO + } + save() { var newObject = Object.assign(Object.assign({}, this.object), this.objectForm.value) var serverResponse: Observable diff --git a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html index 7215faa7..a0e833c9 100644 --- a/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html +++ b/src-ui/src/app/components/common/filterable-dropdown/filterable-dropdown.component.html @@ -16,11 +16,11 @@
- +
- +
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 915d1067..b51923c2 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 @@ -18,6 +18,18 @@ export class FilterableDropdownSelectionModel { items: MatchingModel[] = [] + get itemsSorted(): MatchingModel[] { + return this.items.sort((a,b) => { + if (this.getNonTemporary(a.id) == ToggleableItemState.NotSelected && this.getNonTemporary(b.id) != ToggleableItemState.NotSelected) { + return 1 + } else if (this.getNonTemporary(a.id) != ToggleableItemState.NotSelected && this.getNonTemporary(b.id) == ToggleableItemState.NotSelected) { + return -1 + } else { + return a.name.localeCompare(b.name) + } + }) + } + private selectionStates = new Map() private temporarySelectionStates = new Map() @@ -69,6 +81,10 @@ export class FilterableDropdownSelectionModel { } + private getNonTemporary(id: number) { + return this.selectionStates.get(id) || ToggleableItemState.NotSelected + } + get(id: number) { return this.temporarySelectionStates.get(id) || ToggleableItemState.NotSelected } @@ -142,7 +158,7 @@ export class FilterableDropdownComponent { if (items) { this._selectionModel.items = Array.from(items) this._selectionModel.items.unshift({ - name: $localize`Not assigned`, + name: $localize`:Filter drop down element to filter for documents with no correspondent/type/tag assigned:Not assigned`, id: null }) } @@ -186,6 +202,9 @@ export class FilterableDropdownComponent { @Input() title: string + @Input() + filterPlaceholder: string = "" + @Input() icon: string diff --git a/src-ui/src/app/components/common/input/tags/tags.component.html b/src-ui/src/app/components/common/input/tags/tags.component.html index 8a5dbc4f..01b3fe75 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.html +++ b/src-ui/src/app/components/common/input/tags/tags.component.html @@ -5,7 +5,9 @@ diff --git a/src-ui/src/app/components/dashboard/dashboard.component.html b/src-ui/src/app/components/dashboard/dashboard.component.html index 5b76dd24..50bd44a4 100644 --- a/src-ui/src/app/components/dashboard/dashboard.component.html +++ b/src-ui/src/app/components/dashboard/dashboard.component.html @@ -1,5 +1,8 @@ - +
diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss index e69de29b..40d1c836 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss @@ -0,0 +1,7 @@ +table { + overflow-wrap: anywhere; +} + +th:first-child { + min-width: 5rem; +} diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html index 91fff4e8..aa317bd5 100644 --- a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html +++ b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html @@ -10,7 +10,7 @@
-

Uploading {{uploadStatus.length}} file(s)

+

{uploadStatus.length, plural, =1 {Uploading file...} =other {Uploading {{uploadStatus.length}} files...}}

diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html index 6c9c34d5..6320189c 100644 --- a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html +++ b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html @@ -4,8 +4,8 @@

Paperless is running! :)

You can start uploading documents by dropping them in the file upload box to the right or by dropping them in the configured consumption folder and they'll start showing up in the documents list. - After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and have them displayed on the dashboard instead of this message.

-

Paperless offers some more features that try to make your life easier, such as:

+ After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and they will appear on the dashboard instead of this message.

+

Paperless offers some more features that try to make your life easier:

  • Once you've got a couple documents in paperless and added metadata to them, paperless can assign that metadata to new documents automatically.
  • You can configure paperless to read your mails and add documents from attached files.
  • 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 2efd32f2..053258f3 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 @@ -159,7 +159,7 @@ export class DocumentDetailComponent implements OnInit { delete() { let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) modal.componentInstance.title = $localize`Confirm delete` - modal.componentInstance.messageBold = $localize`Do you really want to delete document '${this.document.title}'?` + modal.componentInstance.messageBold = $localize`Do you really want to delete document "${this.document.title}"?` modal.componentInstance.message = $localize`The files for this document will be deleted permanently. This operation cannot be undone.` modal.componentInstance.btnClass = "btn-danger" modal.componentInstance.btnCaption = $localize`Delete document` diff --git a/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.html b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.html index e8fda1d0..ac42860c 100644 --- a/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.html +++ b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.html @@ -16,7 +16,7 @@ {{m.prefix}}:{{m.key}} - {{m.value}} + {{m.value}} diff --git a/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.scss b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.scss index e69de29b..b946da14 100644 --- a/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.scss +++ b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.scss @@ -0,0 +1,3 @@ +.metadata-column { + overflow-wrap: anywhere; +} \ No newline at end of file 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 62a2bb95..b9912ec1 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 @@ -26,7 +26,8 @@
    - - - i.name).join($localize`, `) - return $localize`${list} and ${items[items.length - 1].name}` + let list = items.slice(0, items.length - 1).map(i => $localize`"${i.name}"`).join($localize`:this is used to separate enumerations and should probably be a comma and a whitespace in most languages:, `) + return $localize`:this is for messages like 'modify "tag1", "tag2" and "tag3"':${list} and "${items[items.length - 1].name}"` } } @@ -115,16 +115,16 @@ export class BulkEditorComponent { 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).` + modal.componentInstance.message = $localize`This operation will add the tag "${tag.name}" to ${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).` + modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} to ${this.list.selected.size} selected document(s).` } else if (changedTags.itemsToAdd.length == 0 && changedTags.itemsToRemove.length == 1) { 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).` + modal.componentInstance.message = $localize`This operation will remove the tag "${tag.name}" from ${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).` + modal.componentInstance.message = $localize`This operation will remove the tags ${this._localizeList(changedTags.itemsToRemove)} from ${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.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} and remove the tags ${this._localizeList(changedTags.itemsToRemove)} on ${this.list.selected.size} selected document(s).` } modal.componentInstance.btnClass = "btn-warning" @@ -156,9 +156,9 @@ export class BulkEditorComponent { 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).` + modal.componentInstance.message = $localize`This operation will assign the correspondent "${correspondent.name}" to ${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.message = $localize`This operation will remove the correspondent from ${this.list.selected.size} selected document(s).` } modal.componentInstance.btnClass = "btn-warning" modal.componentInstance.btnCaption = $localize`Confirm` @@ -189,9 +189,9 @@ export class BulkEditorComponent { 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).` + modal.componentInstance.message = $localize`This operation will assign the document type "${documentType.name}" to ${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.message = $localize`This operation will remove the document type from ${this.list.selected.size} selected document(s).` } modal.componentInstance.btnClass = "btn-warning" modal.componentInstance.btnCaption = $localize`Confirm` @@ -217,7 +217,7 @@ export class BulkEditorComponent { let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'}) modal.componentInstance.delayConfirm(5) modal.componentInstance.title = $localize`Delete confirm` - modal.componentInstance.messageBold = $localize`This operation will permanently delete all ${this.list.selected.size} selected document(s).` + modal.componentInstance.messageBold = $localize`This operation will permanently delete ${this.list.selected.size} selected document(s).` modal.componentInstance.message = $localize`This operation cannot be undone.` modal.componentInstance.btnClass = "btn-danger" modal.componentInstance.btnCaption = $localize`Delete document(s)` 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 c1757eb3..d2f5ebba 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,7 +1,7 @@ -
    +
    -
    - +
    +
    @@ -12,7 +12,7 @@
    -
    +
    @@ -55,16 +55,16 @@  Download - +
    - Score: + Score: - - Created: {{document.created | date}} + + Created: {{document.created | date}}
    - +
    diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss index eb744d2a..0b1604d9 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss @@ -30,10 +30,6 @@ border-color: $primary; } -.doc-img-background { - background-color: white; -} - .doc-img-background-selected { background-color: $primaryFaded; -} \ No newline at end of file +} 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 e0d8d28e..83ba8c27 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,7 +1,7 @@
    -
    -
    - +
    +
    +
    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 bc1047ba..e4d7256e 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 @@ -78,13 +78,13 @@
    - - + +
    -

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

    -

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

    +

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

    +

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

    diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html index f0c83ae7..efbf6ce7 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html @@ -6,13 +6,37 @@
    -
    -
    - - - - - +
    +
    + + + + +
    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 4b62d6a5..e6565bea 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 @@ -25,13 +25,26 @@ export class FilterEditorComponent implements OnInit, OnDestroy { switch(this.filterRules[0].rule_type) { case FILTER_CORRESPONDENT: - return $localize`Correspondent: ${this.correspondents.find(c => c.id == +rule.value)?.name}` + if (rule.value) { + return $localize`Correspondent: ${this.correspondents.find(c => c.id == +rule.value)?.name}` + } else { + return $localize`Without correspondent` + } case FILTER_DOCUMENT_TYPE: - return $localize`Type: ${this.documentTypes.find(dt => dt.id == +rule.value)?.name}` + if (rule.value) { + return $localize`Type: ${this.documentTypes.find(dt => dt.id == +rule.value)?.name}` + } else { + return $localize`Without document type` + } case FILTER_HAS_TAG: return $localize`Tag: ${this.tags.find(t => t.id == +rule.value)?.name}` + + case FILTER_HAS_ANY_TAG: + if (rule.value == "false") { + return $localize`Without any tag` + } } } @@ -65,6 +78,11 @@ export class FilterEditorComponent implements OnInit, OnDestroy { this.documentTypeSelectionModel.clear(false) this.tagSelectionModel.clear(false) this.correspondentSelectionModel.clear(false) + this._titleFilter = null + this.dateAddedBefore = null + this.dateAddedAfter = null + this.dateCreatedBefore = null + this.dateCreatedAfter = null value.forEach(rule => { switch (rule.rule_type) { diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html b/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html index e35c57e2..48477b22 100644 --- a/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html +++ b/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html @@ -6,14 +6,14 @@
    - \ No newline at end of file + diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.ts b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.ts index bc3bf7b0..37b6fa66 100644 --- a/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.ts +++ b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.ts @@ -1,5 +1,4 @@ -import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; +import { Component } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type'; import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'; @@ -16,20 +15,16 @@ import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog/co export class CorrespondentListComponent extends GenericListComponent { constructor(correspondentsService: CorrespondentService, modalService: NgbModal, - private router: Router, private list: DocumentListViewService ) { super(correspondentsService,modalService,CorrespondentEditDialogComponent) } getDeleteMessage(object: PaperlessCorrespondent) { - return $localize`Do you really want to delete the correspondent ${object.name}?` + return $localize`Do you really want to delete the correspondent "${object.name}"?` } filterDocuments(object: PaperlessCorrespondent) { - this.list.documentListView.filter_rules = [ - {rule_type: FILTER_CORRESPONDENT, value: object.id.toString()} - ] - this.router.navigate(["documents"]) + this.list.quickFilter([{rule_type: FILTER_CORRESPONDENT, value: object.id.toString()}]) } } diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.html b/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.html index 458fbb4b..ba017faf 100644 --- a/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.html +++ b/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.html @@ -6,15 +6,15 @@
    - \ No newline at end of file + diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-list.component.ts b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.ts index b376b257..68c8b6f9 100644 --- a/src-ui/src/app/components/manage/document-type-list/document-type-list.component.ts +++ b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.ts @@ -1,5 +1,4 @@ import { Component } from '@angular/core'; -import { Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { FILTER_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type'; import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'; @@ -16,21 +15,17 @@ import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog/doc export class DocumentTypeListComponent extends GenericListComponent { constructor(service: DocumentTypeService, modalService: NgbModal, - private router: Router, private list: DocumentListViewService ) { super(service, modalService, DocumentTypeEditDialogComponent) } getDeleteMessage(object: PaperlessDocumentType) { - return $localize`Do you really want to delete the document type ${object.name}?` + return $localize`Do you really want to delete the document type "${object.name}"?` } filterDocuments(object: PaperlessDocumentType) { - this.list.documentListView.filter_rules = [ - {rule_type: FILTER_DOCUMENT_TYPE, value: object.id.toString()} - ] - this.router.navigate(["documents"]) + this.list.quickFilter([{rule_type: FILTER_DOCUMENT_TYPE, value: object.id.toString()}]) } } diff --git a/src-ui/src/app/components/manage/generic-list/generic-list.component.ts b/src-ui/src/app/components/manage/generic-list/generic-list.component.ts index 1f9cc65f..b31231bc 100644 --- a/src-ui/src/app/components/manage/generic-list/generic-list.component.ts +++ b/src-ui/src/app/components/manage/generic-list/generic-list.component.ts @@ -30,7 +30,7 @@ export abstract class GenericListComponent implements On if (o.matching_algorithm == MATCH_AUTO) { return $localize`Automatic` } else if (o.match && o.match.length > 0) { - return `${o.match} (${MATCHING_ALGORITHMS.find(a => a.id == o.matching_algorithm).name})` + return `${MATCHING_ALGORITHMS.find(a => a.id == o.matching_algorithm).shortName}: ${o.match}` } else { return "-" } 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 1f5374db..103b5bb6 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.html +++ b/src-ui/src/app/components/manage/settings/settings.component.html @@ -1,4 +1,4 @@ - + @@ -10,30 +10,45 @@ General settings -

    Document list

    - +

    Appearance

    +
    Items per page
    - + - -
    - +
    -

    Bulk editing

    +
    +
    + Dark mode +
    +
    + +
    + + +
    +
    +
    - - +

    Bulk editing

    + +
    +
    + + +
    +
    @@ -42,7 +57,7 @@
    - +
    @@ -68,7 +83,7 @@
    No saved views defined.
    - +
    @@ -78,4 +93,4 @@
    - \ No newline at end of file + 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 c26c6338..559ffe00 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 } from '@angular/core'; +import { Component, 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'; @@ -19,9 +19,13 @@ export class SettingsComponent implements OnInit { '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)), 'savedViews': this.savedViewGroup }) + savedViews: PaperlessSavedView[] + constructor( public savedViewService: SavedViewService, private documentListViewService: DocumentListViewService, @@ -29,8 +33,6 @@ export class SettingsComponent implements OnInit { private settings: SettingsService ) { } - savedViews: PaperlessSavedView[] - ngOnInit() { this.savedViewService.listAll().subscribe(r => { this.savedViews = r.results @@ -49,15 +51,26 @@ export class SettingsComponent implements OnInit { this.savedViewService.delete(savedView).subscribe(() => { this.savedViewGroup.removeControl(savedView.id.toString()) this.savedViews.splice(this.savedViews.indexOf(savedView), 1) - this.toastService.showInfo($localize`Saved view "${savedView.name} deleted.`) + this.toastService.showInfo($localize`Saved view "${savedView.name}" deleted.`) }) } + toggleDarkModeSetting() { + if (this.settingsForm.value.darkModeUseSystem) { + (this.settingsForm.controls.darkModeEnabled as FormControl).disable() + } else { + (this.settingsForm.controls.darkModeEnabled as FormControl).enable() + } + } + private saveLocalSettings() { 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.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem) + this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString()) this.documentListViewService.updatePageSize() + this.settings.updateDarkModeSettings() this.toastService.showInfo($localize`Settings saved successfully.`) } diff --git a/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html index bbb96fb6..9929b54d 100644 --- a/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html +++ b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html @@ -17,11 +17,11 @@
    - + - - + +