From 48b042bfb0af66ef495c9af095d5cdb8697e04db Mon Sep 17 00:00:00 2001 From: Jonas Winkler Date: Mon, 1 Feb 2021 20:14:29 +0100 Subject: [PATCH 001/225] troubleshooting for #216 --- docs/troubleshooting.rst | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index f55d57af5..ae7fe14c7 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -60,7 +60,8 @@ required so that the user running paperless inside docker has write permissions to these folders. This happens when pointing these directories to NFS shares, for example. -Ensure that `chown` is possible on these directories. +Ensure that ``chown`` is possible on these directories. + Classifier error: No training data available ############################################ @@ -73,6 +74,7 @@ This may have two reasons: with Inbox tags. Verify that there are documents in your archive without inbox tags. The algorithm will only learn from documents not in your inbox. + UserWarning in sklearn on every single document ############################################### @@ -91,6 +93,7 @@ in most cases. This warning will disappear automatically when paperless updates If you want to get rid of the warning or actually experience issues with automatic matching, delete the file ``classification_model.pickle`` in the data directory and let paperless recreate it. + Permission denied errors in the consumption directory ##################################################### @@ -106,6 +109,38 @@ different from ``1000``. See :ref:`setup-docker_hub`. Also ensure that you are able to read and write to the consumption directory on the host. + +OSError: [Errno 19] No such device when consuming files +####################################################### + +If you experience errors such as: + +.. code:: shell-session + + File "/usr/local/lib/python3.7/site-packages/whoosh/codec/base.py", line 570, in open_compound_file + return CompoundStorage(dbfile, use_mmap=storage.supports_mmap) + File "/usr/local/lib/python3.7/site-packages/whoosh/filedb/compound.py", line 75, in __init__ + self._source = mmap.mmap(fileno, 0, access=mmap.ACCESS_READ) + OSError: [Errno 19] No such device + + During handling of the above exception, another exception occurred: + + Traceback (most recent call last): + File "/usr/local/lib/python3.7/site-packages/django_q/cluster.py", line 436, in worker + res = f(*task["args"], **task["kwargs"]) + File "/usr/src/paperless/src/documents/tasks.py", line 73, in consume_file + override_tag_ids=override_tag_ids) + File "/usr/src/paperless/src/documents/consumer.py", line 271, in try_consume_file + raise ConsumerError(e) + +Paperless uses a search index to provide better and faster full text searching. This search index is stored inside +the ``data`` folder. The search index uses memory-mapped files (mmap). The above error indicates that paperless +was unable to create and open these files. + +This happens when you're trying to store the data directory on certain file systems (mostly network shares) +that don't support memory-mapped files. + + Web-UI stuck at "Loading..." ############################ @@ -148,4 +183,4 @@ This might have multiple reasons. SENDFILE=0 - to your `docker-compose.env` file. \ No newline at end of file + to your `docker-compose.env` file. From 0a28ccf62fac76d1fef0e18434d81ce1c3597ca2 Mon Sep 17 00:00:00 2001 From: Jonas Winkler Date: Mon, 1 Feb 2021 21:43:50 +0100 Subject: [PATCH 002/225] adjustments to setup --- docs/setup.rst | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/setup.rst b/docs/setup.rst index afb3784d6..a86c4ce27 100644 --- a/docs/setup.rst +++ b/docs/setup.rst @@ -151,13 +151,16 @@ Install Paperless from Docker Hub .. code:: - /home/jonaswinkler/paperless-inbox:/usr/src/paperless/consume - + Don't change the part after the colon or paperless wont find your documents. 5. Modify ``docker-compose.env``, following the comments in the file. The most important change is to set ``USERMAP_UID`` and ``USERMAP_GID`` - to the uid and gid of your user on the host system. This ensures that + to the uid and gid of your user on the host system. Use ``id -u`` and + ``id -g`` to get these. + + This ensures that both the docker container and you on the host machine have write access to the consumption directory. If your UID and GID on the host system is 1000 (the default for the first normal user on most systems), it will @@ -175,15 +178,11 @@ Install Paperless from Docker Hub on such a file system, paperless will not pick up new files with the default configuration. You will need to use ``PAPERLESS_CONSUMER_POLLING``, which will disable inotify. See :ref:`here `. - -6. Now head over to: https://hub.docker.com/r/jonaswinkler/paperless-ng and choose your preferred - image and copy the link. To download this image do a `docker pull` followed by the link. Do this within the directory with the .yml files. - Depending on your network connection and CPU this will take a while. You have time to get a beverage. -7. Run ``docker-compose up -d``. This will create and start the necessary - containers, but your are not done yet! +6. Run ``docker-compose up -d``. This will pull the image, create and start the necessary + containers. -8. To be able to login, you will need a super user. To create it, execute the +7. To be able to login, you will need a super user. To create it, execute the following command: .. code-block:: shell-session @@ -193,8 +192,8 @@ Install Paperless from Docker Hub This will prompt you to set a username, an optional e-mail address and finally a password (at least 8 characters). -9. The default ``docker-compose.yml`` exports the webserver on your local port - 8000. If you haven't adapted this, you should now be able to visit your +8. The default ``docker-compose.yml`` exports the webserver on your local port + 8000. If you did not change this, you should now be able to visit your Paperless instance at ``http://127.0.0.1:8000`` or your servers IP-Address:8000. Use the login credentials you have created with the previous step. From 2efb99192a67fb28269ec513ef1f2f3acddb511c Mon Sep 17 00:00:00 2001 From: Jonas Winkler Date: Mon, 1 Feb 2021 21:45:45 +0100 Subject: [PATCH 003/225] add pull to docker setup --- docs/setup.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/setup.rst b/docs/setup.rst index a86c4ce27..5c3c9c361 100644 --- a/docs/setup.rst +++ b/docs/setup.rst @@ -179,8 +179,8 @@ Install Paperless from Docker Hub with the default configuration. You will need to use ``PAPERLESS_CONSUMER_POLLING``, which will disable inotify. See :ref:`here `. -6. Run ``docker-compose up -d``. This will pull the image, create and start the necessary - containers. +6. Run ``docker-compose pull``, followed by ``docker-compose up -d``. + This will pull the image, create and start the necessary containers. 7. To be able to login, you will need a super user. To create it, execute the following command: From c78d54e71e96386a3b6a28545925a60521ae27e3 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 2 Feb 2021 19:54:56 +0100 Subject: [PATCH 004/225] update the easy install script and documentation --- docs/setup.rst | 19 ++++++++ install-paperless-ng.sh | 96 +++++++++++++++++++++++++++++++++++------ 2 files changed, 103 insertions(+), 12 deletions(-) diff --git a/docs/setup.rst b/docs/setup.rst index eda03550e..5e0e3e4a6 100644 --- a/docs/setup.rst +++ b/docs/setup.rst @@ -81,6 +81,7 @@ Installation You can go multiple routes to setup and run Paperless: +* :ref:`Use the easy install docker script ` * :ref:`Pull the image from Docker Hub ` * :ref:`Build the Docker image yourself ` * :ref:`Install Paperless directly on your system manually (bare metal) ` @@ -101,6 +102,24 @@ it includes the same sensible defaults, and it simultaneously provides the flexi .. _CLI Basics: https://sehn.tech/post/devops-with-docker/ .. _idempotent: https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#Idempotency +.. _setup-docker_script: + +Install Paperless from Docker Hub using the installation script +=============================================================== + +Paperless provides an interactive installation script. This script will ask you +for a couple configuration options, download and create the necessary configuration files, pull the docker image, start paperless and create your user account. This script essentially +performs all the steps described in :ref:`setup-docker_hub` automatically. + +1. Make sure that docker and docker-compose are installed. +2. Download and run the installation script: + + .. code:: shell-session + + $ wget https://raw.githubusercontent.com/jonaswinkler/paperless-ng/master/install-paperless-ng.sh + $ chmod +x install-paperless-ng.sh + $ ./install-paperless-ng.sh + .. _setup-docker_hub: Install Paperless from Docker Hub diff --git a/install-paperless-ng.sh b/install-paperless-ng.sh index ad992fe8c..8fb44e222 100755 --- a/install-paperless-ng.sh +++ b/install-paperless-ng.sh @@ -15,7 +15,7 @@ ask() { fi array=$3 if [[ -z $3 || " ${array[@]} " =~ " ${result} " ]]; then - ask_result=$result + ask_result=$result return else echo "Invalid option: $result" @@ -23,6 +23,47 @@ ask() { done } +ask_docker_folder() { + while true ; do + + read -p "$1 [$2]: " result + + if [[ -z $result ]]; then + ask_result=$2 + return + fi + + if [[ $result == /* || $result == ./* ]]; then + ask_result=$result + return + else + echo "Invalid folder: $result" + fi + + + done +} + +if [[ $(id -u) == "0" ]] ; then + echo "Do not run this script as root." + exit 1 +fi + +if [[ -z $(which wget) ]] ; then + echo "wget executable not found. Is wget installed?" + exit 1 +fi + +if [[ -z $(which docker) ]] ; then + echo "docker executable not found. Is docker installed?" + exit 1 +fi + +if [[ -z $(which docker-compose) ]] ; then + echo "docker-compose executable not found. Is docker-compose installed?" + exit 1 +fi + echo "" echo "############################################" echo "### Paperless-ng docker installation ###" @@ -48,14 +89,13 @@ echo "The consume folder is where paperles will search for new documents." echo "Point this to a folder where your scanner is able to put your scanned" echo "documents." echo "" -echo "HINT: If paperless is unable to pick up any files from this directory after" -echo "installation, you might need to configure PAPERLESS_CONSUMER_POLLING." -echo "See the documentation for details." -echo "" -echo "CAUTION: You must specify an absolute path starting with /" +echo "CAUTION: You must specify an absolute path starting with / or a relative " +echo "path starting with ./ here. Examples:" +echo " /mnt/consume" +echo " ./consume" echo "" -ask "Consume folder" "$TARGET_FOLDER/consume" +ask_docker_folder "Consume folder" "$TARGET_FOLDER/consume" CONSUME_FOLDER=$ask_result echo "" @@ -64,9 +104,10 @@ echo "Leave empty and docker will manage this folder for you." echo "Docker usually stores managed folders in /var/lib/docker/volumes." echo "" echo "CAUTION: If specified, you must specify an absolute path starting with /" +echo "or a relative path starting with ./ here." echo "" -ask "Media folder" "" +ask_docker_folder "Media folder" "" MEDIA_FOLDER=$ask_result echo "" @@ -74,8 +115,11 @@ echo "The data folder is where paperless stores other data, such as your" echo "SQLite database (if used), the search index and other data." echo "As with the media folder, leave empty to have this managed by docker." echo "" +echo "CAUTION: If specified, you must specify an absolute path starting with /" +echo "or a relative path starting with ./ here." +echo "" -ask "Data folder" "" +ask_docker_folder "Data folder" "" DATA_FOLDER=$ask_result echo "" @@ -166,8 +210,36 @@ done ask "Email" "$USERNAME@localhost" EMAIL=$ask_result -echo "Done collecting data. Press any key to install." -read +echo "" +echo "Summary" +echo "=======" +echo "" + +echo "Target folder: $TARGET_FOLDER" +echo "Consume folder: $CONSUME_FOLDER" +if [[ -z $MEDIA_FOLDER ]] ; then + echo "Media folder: Managed by docker" +else + echo "Media folder: $MEDIA_FOLDER" +fi +if [[ -z $DATA_FOLDER ]] ; then + echo "Data folder: Managed by docker" +else + echo "Data folder: $DATA_FOLDER" +fi +echo "" +echo "Port: $PORT" +echo "Database: $DATABASE_BACKEND" +echo "Tika enabled: $TIKA_ENABLED" +echo "OCR language: $OCR_LANGUAGE" +echo "User id: $USERMAP_UID" +echo "Group id: $USERMAP_GID" +echo "" +echo "Paperless username: $USERNAME" +echo "Paperless email: $EMAIL" + +echo "" +read -p "Press any key to install." echo "" echo "Installing paperless..." @@ -209,7 +281,7 @@ sed -i "s/- 8000:8000/- $PORT:8000/g" docker-compose.yml sed -i "s#- \./consume:/usr/src/paperless/consume#- $CONSUME_FOLDER:/usr/src/paperless/consume#g" docker-compose.yml if [[ -n $MEDIA_FOLDER ]] ; then - sed -i "s#- data:/usr/src/paperless/media#- $MEDIA_FOLDER:/usr/src/paperless/media#g" docker-compose.yml + sed -i "s#- media:/usr/src/paperless/media#- $MEDIA_FOLDER:/usr/src/paperless/media#g" docker-compose.yml fi if [[ -n $DATA_FOLDER ]] ; then From 82730ab491cb8d7ea093c5aa3564829da5d86e32 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 2 Feb 2021 20:31:25 +0100 Subject: [PATCH 005/225] update dependencies --- Pipfile | 4 +- Pipfile.lock | 121 +++++++++++++++++++++++++++-------------------- requirements.txt | 10 ++-- 3 files changed, 78 insertions(+), 57 deletions(-) diff --git a/Pipfile b/Pipfile index cbe5c5103..e5e13ccac 100644 --- a/Pipfile +++ b/Pipfile @@ -25,7 +25,7 @@ pdftotext = "*" pathvalidate = "*" # pinned to 8.1.0, since aarch64 wheels might not be available beyond that https://github.com/python-pillow/Pillow/issues/5202 pillow = "==8.1.0" -pikepdf = "~=2.2.5" +pikepdf = "~=2.5.0" python-gnupg = "*" python-dotenv = "*" python-dateutil = "*" @@ -41,7 +41,7 @@ whitenoise = "~=5.2.0" watchdog = "*" whoosh="~=2.7.4" inotifyrecursive = "~=0.3.4" -ocrmypdf = "~=11.4.5" +ocrmypdf = "~=11.6" tqdm = "*" tika = "*" # TODO: This will sadly also install daphne+dependencies, diff --git a/Pipfile.lock b/Pipfile.lock index ff9e96717..6d200ce4b 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "d80d2539a4528a8fd9e848875c2e2d5bcdb3e98154f45b612706094b84ecaaea" + "sha256": "3e5b28740397136ffd0a397889e232192414eca2e2e6e9284f452d60cbbceda8" }, "pipfile-spec": 6, "requires": {}, @@ -107,6 +107,7 @@ "sha256:6bc25fc545a6b3d57b5f8618e59fc13d3a3a68431e8ca5fd4c13241cd70d0009", "sha256:798caa2a2384b1cbe8a2a139d80734c9db54f9cc155c99d7cc92441a23871c03", "sha256:7c6b1dece89874d9541fc974917b631406233ea0440d0bdfbb8e03bf39a49b3b", + "sha256:7ef7d4ced6b325e92eb4d3502946c78c5367bc416398d387b39591532536734e", "sha256:840793c68105fe031f34d6a086eaea153a0cd5c491cde82a74b420edd0a2b909", "sha256:8d6603078baf4e11edc4168a514c5ce5b3ba6e3e9c374298cb88437957960a53", "sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35", @@ -218,11 +219,11 @@ }, "django": { "hashes": [ - "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7", - "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9" + "sha256:169e2e7b4839a7910b393eec127fd7cbae62e80fa55f89c6510426abf673fe5f", + "sha256:c6c0462b8b361f8691171af1fb87eceb4442da28477e12200c40420176206ba7" ], "index": "pypi", - "version": "==3.1.5" + "version": "==3.1.6" }, "django-cors-headers": { "hashes": [ @@ -587,11 +588,11 @@ }, "ocrmypdf": { "hashes": [ - "sha256:416a9c4321bfc844f250694b8c68ebb538f60609bbc8686bd9f84a13c5127d68", - "sha256:f45fc7e844e6026d6080a623a2936be120fc077d99aaa599df022acf35fb31e6" + "sha256:a54634d017a2f44aa2115b0b6ae5aa41a7cec018f5c53d16ad3abec1e70b3db7", + "sha256:d0e2da48d4abd90f48f0937b2cd4ba57503b56c603f5e3aa91e20e3b21a036cd" ], "index": "pypi", - "version": "==11.4.5" + "version": "==11.6.0" }, "pathvalidate": { "hashes": [ @@ -618,30 +619,32 @@ }, "pikepdf": { "hashes": [ - "sha256:0e67e5beeeed5422b3b8e862e4777fed5a4cd3c72e711e2a449a65d9ee641448", - "sha256:138155ae1f71634cd6eca79f5517f77b2067ef0bd5b627ea9414e308fe868dc5", - "sha256:15cf648dd760a47c55a4106b601b92bb653ae98155b10f04310553629c6695dd", - "sha256:1d6a011ae4c501c78509caf19cbe152c2e3cb5c267f7b47bc3db8cd3436585a7", - "sha256:211f529313953e44ae42eb896c2b688668385e6e8f9d04d21484bddb3c42b34c", - "sha256:22049ad288d603a7fc68e90a0722770d307886788373ddfe71fbf614ced0f5b2", - "sha256:24f7c371f6ecbee8f0ae30030992fc75cd32cd575dcfca8d466a03a8290377ca", - "sha256:26cdf561632866d584fedb6b1c1fce78cefa49b5cae54c65aa6a6ca5fe6de4ac", - "sha256:2c37afcd21a2eb1da1773687e853327fa8ec7d2c5cd90cdcd70180f55f0221e1", - "sha256:65b8ec6403814f51e1b9c7e18a8ff26087fcc7a199b1405583e5ff9eb931db56", - "sha256:66a03103aadb2e2738271cb18c89837ac3980fa0b4687195c4c150228b7e79de", - "sha256:6e8f0124354c53a66f83ec5a18111b760aeff1a64db3a86e7ee5fed8e8624707", - "sha256:70f2836cd468aa25bc8b09a2b9561364bd75d3e6ddb0e50a25d248d7da6cff25", - "sha256:82cebf68952cfb65c86d880eb782a0c558b37531cdae59f2e11fcd0f2bb4669c", - "sha256:84ad3e8fd5f3251fb5b534614da64b04a264ce9348f0fe35b781c0fb378b0f82", - "sha256:af13fbc022efa85d1ae161129d4cde66493479db52b9adb74d525b890a078208", - "sha256:c1d40fb8f8192c75f54f0e74a569ccf45e4e13bed8da78a78a5b488be29979bf", - "sha256:d147ec1ab58512871fdf40a161809f698eaa75720b4a230198e7e028582b20a1", - "sha256:dedad1f68d6b0b54000f7f99386351f1c6e19c8cf70a9700d8dd06b9809c54fb", - "sha256:e72c3f5b624b9c7341fd6a7e657926d4cf12a7ea453681ffd7332cabc3530c62", - "sha256:eb75f22e261b3bc69b6fc9a17b1d6966c95e79d3e792b7737a018a2bf6a2b07f" + "sha256:02815df9499d3a6dfac2e07e4d2fdbe25fcbefae208970e76bff90af4a402d49", + "sha256:06b0c3004cc9e9068ebc62bb59c3c3a54e7af13867f4a326690d79c69a1cf288", + "sha256:1a471c6ca288fbcd0e1b0e128ef12bb14c5e7db745786308ba292fc7cff30bb5", + "sha256:489ed0fd1281beb0343a34fe8b9d94407c440ed0419ab2e6f5ea297a41824a31", + "sha256:5106b27f7085ed449e057b9988f07c80a87292d2bf46c585a8635ac7a3ccf0d5", + "sha256:51acffba6f3d21674eea7a0432ce1adaf0743641d57844a5e3dc92b4a7e81c85", + "sha256:53d694d70dd072a47bd2dd71329dcef0f809dcd8084d1d11c31baf3b64cd345c", + "sha256:6a640fef52dc785abd354d6800a52ecc02656c98dbfc2ecde559323b001bd43f", + "sha256:7006ef95f847412605dea6e772019f637949eaeaf65363d5d6afd9aa96bf5623", + "sha256:81e13b62877dbc089095e7efa03c27834bdf6b49d404d064cb227b0e179ce049", + "sha256:8fc3e97b24dafbd4b1ac057c8f144c18467ce55d342a1a8c42688890ead58ef3", + "sha256:9158dc4d3ef4e2301fb1879d5825530fdb32143ced770d60fa8e5badeee70a35", + "sha256:961337a10b42bd656b59116ce1c574eafd515b45a513221df6ae1f11734bfb6c", + "sha256:994ccac972357a7b9b147217e1beea2f7688697944b862dbb2a3e64aa9a5ff14", + "sha256:a8e9abf7db0351357b55c3f935979e7dc14f3f259a25d15bcc86abce730955a7", + "sha256:b836eda7f70b9dd75ccdeaf4e78b38393118a66821a69a10054b1430f945d1fd", + "sha256:b859a225f6bd953472c50f4df612b4575df646e560189e3720310ad65b6805a0", + "sha256:cae106bd461cfad73c554c09f6db1d5f2c6a28f5b8cb0602b63046840f488226", + "sha256:cdc75b8fa5a650f4fc91214a315358fde7470e09b95a00981b73a7c4ac5ddb97", + "sha256:e2c28da4f37ad9a3efbedbbfc6f1084941bde43903d30dfdbb338d5ba94c9f50", + "sha256:e596bd8fdbf40bfb8dc8068cdbc7e5b72052188d1ad8ca84da9d6b77658a8b31", + "sha256:ee957b9c60b6def20cbdf656d35859ce211eec02dafa3abb9d5ca937d32a3c3b", + "sha256:f9428d4b1f70af4f4560be4dccbbc5ab5308c00c5b62ed2f1c44ce9e2591b3d2" ], "index": "pypi", - "version": "==2.2.5" + "version": "==2.5.2" }, "pillow": { "hashes": [ @@ -819,12 +822,12 @@ }, "python-levenshtein": { "hashes": [ - "sha256:108edd3c271f1afda8b21a8d9da81886414dfb6940a085fa7903c592e0f8f54b", - "sha256:1ff19b712c5974080b003fd26ef365cd93dfc1a5e690be621f79f3e63e00a7cc", - "sha256:554e273a88060d177e7b3c1e6ea9158dde11563bfae8f7f661f73f47e5ff0911" + "sha256:212db61934fcb819f5cb2fcb5ad5c0e2e43f3161f524eef4a135f7285dfb308a", + "sha256:d92fe5c3b10c8ad8f2d880499f5e96ed24dbc7cd0414a665c2e2505feaf4ec58", + "sha256:dc2395fbd148a1ab31090dd113c366695934b9e85fe5a4b2a032745efd0346f6" ], "index": "pypi", - "version": "==0.12.1" + "version": "==0.12.2" }, "python-magic": { "hashes": [ @@ -836,10 +839,10 @@ }, "pytz": { "hashes": [ - "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4", - "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5" + "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", + "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" ], - "version": "==2020.5" + "version": "==2021.1" }, "pyyaml": { "hashes": [ @@ -1558,12 +1561,11 @@ }, "jinja2": { "hashes": [ - "sha256:3f172970d5670703bd3812e8ca6459a9a7e069fa8e51b40195f83c81db191ec4", - "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", - "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" + "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", + "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.11.2" + "version": "==2.11.3" }, "markupsafe": { "hashes": [ @@ -1573,8 +1575,12 @@ "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", "sha256:19536834abffb3fa155017053c607cb835b2ecc6a3a2554a88043d991dffb736", + "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", + "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", + "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", "sha256:3d61f15e39611aacd91b7e71d903787da86d9e80896e683c0103fced9add7834", "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", @@ -1584,36 +1590,51 @@ "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", + "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:7952deddf24b85c88dab48f6ec366ac6e39d2761b5280f2f9594911e03fcd064", "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", + "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", + "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", + "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", + "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", + "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.1.1" }, "packaging": { "hashes": [ - "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858", - "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093" + "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", + "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.8" + "version": "==20.9" }, "pluggy": { "hashes": [ @@ -1722,10 +1743,10 @@ }, "pytz": { "hashes": [ - "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4", - "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5" + "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", + "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" ], - "version": "==2020.5" + "version": "==2021.1" }, "requests": { "hashes": [ @@ -1863,11 +1884,11 @@ }, "virtualenv": { "hashes": [ - "sha256:219ee956e38b08e32d5639289aaa5bd190cfbe7dafcb8fa65407fca08e808f9c", - "sha256:227a8fed626f2f20a6cdb0870054989f82dd27b2560a911935ba905a2a5e0034" + "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d", + "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==20.4.0" + "version": "==20.4.2" }, "zipp": { "hashes": [ diff --git a/requirements.txt b/requirements.txt index c3f32002b..536e84e3b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,7 +31,7 @@ django-extensions==3.1.0 django-filter==2.4.0 django-picklefield==3.0.1; python_version >= '3' django-q==1.3.4 -django==3.1.5 +django==3.1.6 djangorestframework==3.12.2 filelock==3.0.12 fuzzywuzzy==0.18.0 @@ -53,11 +53,11 @@ langdetect==1.0.8 lxml==4.6.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' msgpack==1.0.2 numpy==1.19.5; python_version >= '3.6' -ocrmypdf==11.4.5 +ocrmypdf==11.6.0 pathvalidate==2.3.2 pdfminer.six==20201018; python_version >= '3.4' pdftotext==2.1.5 -pikepdf==2.2.5 +pikepdf==2.5.2 pillow==8.1.0 pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' psycopg2-binary==2.8.6 @@ -69,9 +69,9 @@ pyopenssl==20.0.1; python_version >= '2.7' and python_version not in '3.0, 3.1, python-dateutil==2.8.1 python-dotenv==0.15.0 python-gnupg==0.4.6 -python-levenshtein==0.12.1 +python-levenshtein==0.12.2 python-magic==0.4.18 -pytz==2020.5 +pytz==2021.1 pyyaml==5.4.1 redis==3.5.3 regex==2020.11.13 From d198a89f22c5a72f93a8a7fe07331f33d903eda0 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 2 Feb 2021 20:43:07 +0100 Subject: [PATCH 006/225] update dependencies --- Pipfile | 3 +-- Pipfile.lock | 6 ++++-- requirements.txt | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Pipfile b/Pipfile index e5e13ccac..293d71603 100644 --- a/Pipfile +++ b/Pipfile @@ -17,7 +17,7 @@ django-filter = "~=2.4.0" django-q = "~=1.3.4" djangorestframework = "~=3.12.2" filelock = "*" -fuzzywuzzy = "*" +fuzzywuzzy = {extras = ["speedup"], version = "*"} gunicorn = "*" imap-tools = "*" langdetect = "*" @@ -29,7 +29,6 @@ pikepdf = "~=2.5.0" python-gnupg = "*" python-dotenv = "*" python-dateutil = "*" -python-Levenshtein = "*" python-magic = "*" psycopg2-binary = "*" redis = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 6d200ce4b..78cfecf95 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3e5b28740397136ffd0a397889e232192414eca2e2e6e9284f452d60cbbceda8" + "sha256": "658e2907c680baea81e77da9081ee063ff843b185d02c95d6483b747f0acd84d" }, "pipfile-spec": 6, "requires": {}, @@ -282,6 +282,9 @@ "version": "==3.0.12" }, "fuzzywuzzy": { + "extras": [ + "speedup" + ], "hashes": [ "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8", "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993" @@ -826,7 +829,6 @@ "sha256:d92fe5c3b10c8ad8f2d880499f5e96ed24dbc7cd0414a665c2e2505feaf4ec58", "sha256:dc2395fbd148a1ab31090dd113c366695934b9e85fe5a4b2a032745efd0346f6" ], - "index": "pypi", "version": "==0.12.2" }, "python-magic": { diff --git a/requirements.txt b/requirements.txt index 536e84e3b..e1b76a4a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -34,7 +34,7 @@ django-q==1.3.4 django==3.1.6 djangorestframework==3.12.2 filelock==3.0.12 -fuzzywuzzy==0.18.0 +fuzzywuzzy[speedup]==0.18.0 gunicorn==20.0.4 h11==0.12.0; python_version >= '3.6' hiredis==1.1.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' From 85f46424a812707f63360432107b69846c2384d3 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 2 Feb 2021 23:58:11 +0100 Subject: [PATCH 007/225] fixes layout issues #482 --- .../src/app/components/app-frame/app-frame.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 70329d537..34a978fe5 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 @@ -163,13 +163,13 @@ - -
+
diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index f3e437ada..a145ae5a7 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -80,6 +80,7 @@ export class SettingsService { getLanguageOptions(): LanguageOption[] { return [ {code: "en-US", name: $localize`English (US)`, englishName: "English (US)"}, + {code: "en-GB", name: $localize`English (GB)`, englishName: "English (GB)"}, {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.en_GB.xlf b/src-ui/src/locale/messages.en_GB.xlf index 4df278c69..49baa77ed 100644 --- a/src-ui/src/locale/messages.en_GB.xlf +++ b/src-ui/src/locale/messages.en_GB.xlf @@ -515,7 +515,7 @@ Color - Color + Colour src/app/components/manage/tag-list/tag-list.component.html 20 diff --git a/src/locale/en_GB/LC_MESSAGES/django.po b/src/locale/en_GB/LC_MESSAGES/django.po index 5f6901f7f..de13e5d34 100644 --- a/src/locale/en_GB/LC_MESSAGES/django.po +++ b/src/locale/en_GB/LC_MESSAGES/django.po @@ -2,10 +2,10 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# # Translators: # Ali Bates , 2021 -# +# #, fuzzy msgid "" msgstr "" @@ -76,7 +76,7 @@ msgstr "correspondents" #: documents/models.py:98 msgid "color" -msgstr "color" +msgstr "colour" #: documents/models.py:102 msgid "is inbox tag" diff --git a/src/locale/en-us/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po similarity index 81% rename from src/locale/en-us/LC_MESSAGES/django.po rename to src/locale/en_US/LC_MESSAGES/django.po index fdf3fd809..cb31bbc8c 100644 --- a/src/locale/en-us/LC_MESSAGES/django.po +++ b/src/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-28 22:02+0100\n" +"POT-Creation-Date: 2021-02-16 14:52+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -21,144 +21,144 @@ msgstr "" msgid "Documents" msgstr "" -#: documents/models.py:33 +#: documents/models.py:32 msgid "Any word" msgstr "" -#: documents/models.py:34 +#: documents/models.py:33 msgid "All words" msgstr "" -#: documents/models.py:35 +#: documents/models.py:34 msgid "Exact match" msgstr "" -#: documents/models.py:36 +#: documents/models.py:35 msgid "Regular expression" msgstr "" -#: documents/models.py:37 +#: documents/models.py:36 msgid "Fuzzy word" msgstr "" -#: documents/models.py:38 +#: documents/models.py:37 msgid "Automatic" msgstr "" -#: documents/models.py:42 documents/models.py:352 paperless_mail/models.py:25 +#: documents/models.py:41 documents/models.py:364 paperless_mail/models.py:25 #: paperless_mail/models.py:109 msgid "name" msgstr "" -#: documents/models.py:46 +#: documents/models.py:45 msgid "match" msgstr "" -#: documents/models.py:50 +#: documents/models.py:49 msgid "matching algorithm" msgstr "" -#: documents/models.py:56 +#: documents/models.py:55 msgid "is insensitive" msgstr "" -#: documents/models.py:75 documents/models.py:135 +#: documents/models.py:74 documents/models.py:134 msgid "correspondent" msgstr "" -#: documents/models.py:76 +#: documents/models.py:75 msgid "correspondents" msgstr "" -#: documents/models.py:98 +#: documents/models.py:97 msgid "color" msgstr "" -#: documents/models.py:102 +#: documents/models.py:101 msgid "is inbox tag" msgstr "" -#: documents/models.py:104 +#: documents/models.py:103 msgid "" "Marks this tag as an inbox tag: All newly consumed documents will be tagged " "with inbox tags." msgstr "" -#: documents/models.py:109 +#: documents/models.py:108 msgid "tag" msgstr "" -#: documents/models.py:110 documents/models.py:166 +#: documents/models.py:109 documents/models.py:165 msgid "tags" msgstr "" -#: documents/models.py:116 documents/models.py:148 +#: documents/models.py:115 documents/models.py:147 msgid "document type" msgstr "" -#: documents/models.py:117 +#: documents/models.py:116 msgid "document types" msgstr "" -#: documents/models.py:125 +#: documents/models.py:124 msgid "Unencrypted" msgstr "" -#: documents/models.py:126 +#: documents/models.py:125 msgid "Encrypted with GNU Privacy Guard" msgstr "" -#: documents/models.py:139 +#: documents/models.py:138 msgid "title" msgstr "" -#: documents/models.py:152 +#: documents/models.py:151 msgid "content" msgstr "" -#: documents/models.py:154 +#: documents/models.py:153 msgid "" "The raw, text-only data of the document. This field is primarily used for " "searching." msgstr "" -#: documents/models.py:159 +#: documents/models.py:158 msgid "mime type" msgstr "" -#: documents/models.py:170 +#: documents/models.py:169 msgid "checksum" msgstr "" -#: documents/models.py:174 +#: documents/models.py:173 msgid "The checksum of the original document." msgstr "" -#: documents/models.py:178 +#: documents/models.py:177 msgid "archive checksum" msgstr "" -#: documents/models.py:183 +#: documents/models.py:182 msgid "The checksum of the archived document." msgstr "" -#: documents/models.py:187 documents/models.py:330 +#: documents/models.py:186 documents/models.py:342 msgid "created" msgstr "" -#: documents/models.py:191 +#: documents/models.py:190 msgid "modified" msgstr "" -#: documents/models.py:195 +#: documents/models.py:194 msgid "storage type" msgstr "" -#: documents/models.py:203 +#: documents/models.py:202 msgid "added" msgstr "" -#: documents/models.py:207 +#: documents/models.py:206 msgid "filename" msgstr "" @@ -167,178 +167,186 @@ msgid "Current filename in storage" msgstr "" #: documents/models.py:216 +msgid "archive filename" +msgstr "" + +#: documents/models.py:222 +msgid "Current archive filename in storage" +msgstr "" + +#: documents/models.py:226 msgid "archive serial number" msgstr "" -#: documents/models.py:221 +#: documents/models.py:231 msgid "The position of this document in your physical document archive." msgstr "" -#: documents/models.py:227 +#: documents/models.py:237 msgid "document" msgstr "" -#: documents/models.py:228 +#: documents/models.py:238 msgid "documents" msgstr "" -#: documents/models.py:313 +#: documents/models.py:325 msgid "debug" msgstr "" -#: documents/models.py:314 +#: documents/models.py:326 msgid "information" msgstr "" -#: documents/models.py:315 +#: documents/models.py:327 msgid "warning" msgstr "" -#: documents/models.py:316 +#: documents/models.py:328 msgid "error" msgstr "" -#: documents/models.py:317 +#: documents/models.py:329 msgid "critical" msgstr "" -#: documents/models.py:321 +#: documents/models.py:333 msgid "group" msgstr "" -#: documents/models.py:324 +#: documents/models.py:336 msgid "message" msgstr "" -#: documents/models.py:327 +#: documents/models.py:339 msgid "level" msgstr "" -#: documents/models.py:334 +#: documents/models.py:346 msgid "log" msgstr "" -#: documents/models.py:335 +#: documents/models.py:347 msgid "logs" msgstr "" -#: documents/models.py:346 documents/models.py:396 +#: documents/models.py:358 documents/models.py:408 msgid "saved view" msgstr "" -#: documents/models.py:347 +#: documents/models.py:359 msgid "saved views" msgstr "" -#: documents/models.py:350 +#: documents/models.py:362 msgid "user" msgstr "" -#: documents/models.py:356 +#: documents/models.py:368 msgid "show on dashboard" msgstr "" -#: documents/models.py:359 +#: documents/models.py:371 msgid "show in sidebar" msgstr "" -#: documents/models.py:363 +#: documents/models.py:375 msgid "sort field" msgstr "" -#: documents/models.py:366 +#: documents/models.py:378 msgid "sort reverse" msgstr "" -#: documents/models.py:372 +#: documents/models.py:384 msgid "title contains" msgstr "" -#: documents/models.py:373 +#: documents/models.py:385 msgid "content contains" msgstr "" -#: documents/models.py:374 +#: documents/models.py:386 msgid "ASN is" msgstr "" -#: documents/models.py:375 +#: documents/models.py:387 msgid "correspondent is" msgstr "" -#: documents/models.py:376 +#: documents/models.py:388 msgid "document type is" msgstr "" -#: documents/models.py:377 +#: documents/models.py:389 msgid "is in inbox" msgstr "" -#: documents/models.py:378 +#: documents/models.py:390 msgid "has tag" msgstr "" -#: documents/models.py:379 +#: documents/models.py:391 msgid "has any tag" msgstr "" -#: documents/models.py:380 +#: documents/models.py:392 msgid "created before" msgstr "" -#: documents/models.py:381 +#: documents/models.py:393 msgid "created after" msgstr "" -#: documents/models.py:382 +#: documents/models.py:394 msgid "created year is" msgstr "" -#: documents/models.py:383 +#: documents/models.py:395 msgid "created month is" msgstr "" -#: documents/models.py:384 +#: documents/models.py:396 msgid "created day is" msgstr "" -#: documents/models.py:385 +#: documents/models.py:397 msgid "added before" msgstr "" -#: documents/models.py:386 +#: documents/models.py:398 msgid "added after" msgstr "" -#: documents/models.py:387 +#: documents/models.py:399 msgid "modified before" msgstr "" -#: documents/models.py:388 +#: documents/models.py:400 msgid "modified after" msgstr "" -#: documents/models.py:389 +#: documents/models.py:401 msgid "does not have tag" msgstr "" -#: documents/models.py:400 +#: documents/models.py:412 msgid "rule type" msgstr "" -#: documents/models.py:404 +#: documents/models.py:416 msgid "value" msgstr "" -#: documents/models.py:410 +#: documents/models.py:422 msgid "filter rule" msgstr "" -#: documents/models.py:411 +#: documents/models.py:423 msgid "filter rules" msgstr "" -#: documents/serialisers.py:383 +#: documents/serialisers.py:370 #, python-format msgid "File type %(type)s not supported" msgstr "" @@ -383,19 +391,23 @@ msgstr "" msgid "Sign in" msgstr "" -#: paperless/settings.py:286 -msgid "English" +#: paperless/settings.py:291 +msgid "English (US)" msgstr "" -#: paperless/settings.py:287 +#: paperless/settings.py:292 +msgid "English (GB)" +msgstr "" + +#: paperless/settings.py:293 msgid "German" msgstr "" -#: paperless/settings.py:288 +#: paperless/settings.py:294 msgid "Dutch" msgstr "" -#: paperless/settings.py:289 +#: paperless/settings.py:295 msgid "French" msgstr "" diff --git a/src/paperless/settings.py b/src/paperless/settings.py index 8d14262d1..42ddec88d 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -288,7 +288,8 @@ if os.getenv("PAPERLESS_DBHOST"): LANGUAGE_CODE = 'en-us' LANGUAGES = [ - ("en-us", _("English")), + ("en-us", _("English (US)")), + ("en-gb", _("English (GB)")), ("de", _("German")), ("nl-nl", _("Dutch")), ("fr", _("French")) From 1c81d8801355e149b1b6631e93ea7107f66561ed Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Wed, 17 Feb 2021 12:15:22 +0100 Subject: [PATCH 137/225] add support for iso 8601 date display --- .../manage/settings/settings.component.html | 2 +- .../manage/settings/settings.component.ts | 7 +++++-- src-ui/src/app/pipes/custom-date.pipe.ts | 17 ++++++++++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) 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 aa7ea05d3..3674bbcb9 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.html +++ b/src-ui/src/app/components/manage/settings/settings.component.html @@ -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 47c7b8d7b..15cfb8a19 100644 --- a/src-ui/src/app/components/manage/settings/settings.component.ts +++ b/src-ui/src/app/components/manage/settings/settings.component.ts @@ -35,7 +35,7 @@ export class SettingsComponent implements OnInit { savedViews: PaperlessSavedView[] get computedDateLocale(): string { - return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage + return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage || this.currentLocale } constructor( @@ -92,7 +92,10 @@ export class SettingsComponent implements OnInit { } get dateLocaleOptions(): LanguageOption[] { - return [{code: "", name: $localize`Use date format of display language`}].concat(this.settings.getLanguageOptions()) + return [ + {code: "", name: $localize`Use date format of display language`}, + {code: "iso-8601", name: $localize`ISO 8601`} + ].concat(this.settings.getLanguageOptions()) } get today() { diff --git a/src-ui/src/app/pipes/custom-date.pipe.ts b/src-ui/src/app/pipes/custom-date.pipe.ts index 19989fbdd..cf5a931cb 100644 --- a/src-ui/src/app/pipes/custom-date.pipe.ts +++ b/src-ui/src/app/pipes/custom-date.pipe.ts @@ -2,18 +2,29 @@ import { DatePipe } from '@angular/common'; import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core'; import { SettingsService, SETTINGS_KEYS } from '../services/settings.service'; +const FORMAT_TO_ISO_FORMAT = { + "longDate": "y-MM-dd", + "mediumDate": "yy-MM-dd", + "shortDate": "yy-MM-dd" +} + @Pipe({ name: 'customDate' }) export class CustomDatePipe extends DatePipe implements PipeTransform { constructor(@Inject(LOCALE_ID) locale: string, private settings: SettingsService) { - super(settings.get(SETTINGS_KEYS.DATE_LOCALE) || locale) - + super(locale) } transform(value: any, format?: string, timezone?: string, locale?: string): string | null { - return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, locale) + let l = locale || this.settings.get(SETTINGS_KEYS.DATE_LOCALE) + let f = format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT) + if (l == "iso-8601") { + return super.transform(value, FORMAT_TO_ISO_FORMAT[f], timezone) + } else { + return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, locale) + } } } From a1162d6d5ab8caaf9cc8d7318c40e4cdc08c3e4f Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Wed, 17 Feb 2021 12:25:34 +0100 Subject: [PATCH 138/225] update requirements --- Pipfile.lock | 18 +++++++++--------- requirements.txt | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index fee6359da..f75949d20 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -591,11 +591,11 @@ }, "ocrmypdf": { "hashes": [ - "sha256:a54634d017a2f44aa2115b0b6ae5aa41a7cec018f5c53d16ad3abec1e70b3db7", - "sha256:d0e2da48d4abd90f48f0937b2cd4ba57503b56c603f5e3aa91e20e3b21a036cd" + "sha256:0f624456a50be0b0bc8c0b59704d159f637616c093a1cabe8bb383706561bcf7", + "sha256:b829ad640a6160423162012e094ee2f7cd074ec99efadd7f7486954ec9182985" ], "index": "pypi", - "version": "==11.6.0" + "version": "==11.6.2" }, "pathvalidate": { "hashes": [ @@ -841,11 +841,11 @@ }, "python-magic": { "hashes": [ - "sha256:356efa93c8899047d1eb7d3eb91e871ba2f5b1376edbaf4cc305e3c872207355", - "sha256:b757db2a5289ea3f1ced9e60f072965243ea43a2221430048fd8cacab17be0ce" + "sha256:8551e804c09a3398790bd9e392acb26554ae2609f29c72abb0b9dee9a5571eae", + "sha256:ca884349f2c92ce830e3f498c5b7c7051fe2942c3ee4332f65213b8ebff15a62" ], "index": "pypi", - "version": "==0.4.18" + "version": "==0.4.22" }, "pytz": { "hashes": [ @@ -1837,11 +1837,11 @@ }, "tox": { "hashes": [ - "sha256:65d0e90ceb816638a50d64f4b47b11da767b284c0addda2294cb3cd69bd72425", - "sha256:cf7fef81a3a2434df4d7af2a6d1bf606d2970220addfbe7dea2615bd4bb2c252" + "sha256:89afa9c59c04beb55eda789c7a65feb1a70fde117f85f1bd1c27c66758456e60", + "sha256:ed1e650cf6368bcbc4a071eeeba363c480920e0ed8a9ad1793c7caaa5ad33d49" ], "index": "pypi", - "version": "==3.21.4" + "version": "==3.22.0" }, "urllib3": { "hashes": [ diff --git a/requirements.txt b/requirements.txt index 0a0ae75c0..7100ba953 100644 --- a/requirements.txt +++ b/requirements.txt @@ -53,7 +53,7 @@ langdetect==1.0.8 lxml==4.6.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' msgpack==1.0.2 numpy==1.19.5 -ocrmypdf==11.6.0 +ocrmypdf==11.6.2 pathvalidate==2.3.2 pdfminer.six==20201018; python_version >= '3.4' pdftotext==2.1.5 @@ -71,7 +71,7 @@ python-dateutil==2.8.1 python-dotenv==0.15.0 python-gnupg==0.4.6 python-levenshtein==0.12.2 -python-magic==0.4.18 +python-magic==0.4.22 pytz==2021.1 pyyaml==5.4.1 redis==3.5.3 From 08a44cf468631c029c5469205f860adc89139e40 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Wed, 17 Feb 2021 12:31:19 +0100 Subject: [PATCH 139/225] changelog and version --- docs/changelog.rst | 19 +++++++++++++------ src-ui/src/environments/environment.prod.ts | 2 +- src/paperless/version.py | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 8b87e642e..ad45becab 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,15 +5,12 @@ Changelog ********* -paperless-ng 1.1.3 +paperless-ng 1.1.4 ################## -* Added a docker-specific configuration option to adjust the number of - worker processes of the web server. See :ref:`configuration-docker`. +* Added English (GB) locale. -* Some more memory usage optimizations. - -* Don't show inbox statistics if no inbox tag is defined. +* Added ISO-8601 date display option. .. note:: @@ -26,6 +23,16 @@ paperless-ng 1.1.3 If using docker, this does not affect you. +paperless-ng 1.1.3 +################## + +* Added a docker-specific configuration option to adjust the number of + worker processes of the web server. See :ref:`configuration-docker`. + +* Some more memory usage optimizations. + +* Don't show inbox statistics if no inbox tag is defined. + paperless-ng 1.1.2 ################## diff --git a/src-ui/src/environments/environment.prod.ts b/src-ui/src/environments/environment.prod.ts index b2ffebc09..6df8ec565 100644 --- a/src-ui/src/environments/environment.prod.ts +++ b/src-ui/src/environments/environment.prod.ts @@ -2,7 +2,7 @@ export const environment = { production: true, apiBaseUrl: "/api/", appTitle: "Paperless-ng", - version: "1.1.3", + version: "1.1.4", webSocketHost: window.location.host, webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:") }; diff --git a/src/paperless/version.py b/src/paperless/version.py index 08f427169..f336d581e 100644 --- a/src/paperless/version.py +++ b/src/paperless/version.py @@ -1 +1 @@ -__version__ = (1, 1, 3) +__version__ = (1, 1, 4) From b7f1b9f8ad0f3a6e8ee11cbe4a20732353913245 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Wed, 17 Feb 2021 11:50:01 +0000 Subject: [PATCH 140/225] Apply translations in de translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po' on the 'de' language. --- src/locale/de/LC_MESSAGES/django.po | 182 +++++++++++++++------------- 1 file changed, 97 insertions(+), 85 deletions(-) diff --git a/src/locale/de/LC_MESSAGES/django.po b/src/locale/de/LC_MESSAGES/django.po index c53985275..358c9b721 100644 --- a/src/locale/de/LC_MESSAGES/django.po +++ b/src/locale/de/LC_MESSAGES/django.po @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-28 22:02+0100\n" -"PO-Revision-Date: 2020-12-30 19:27+0000\n" +"POT-Creation-Date: 2021-02-16 14:52+0100\n" +"PO-Revision-Date: 2021-02-16 18:37+0000\n" "Last-Translator: Jonas Winkler, 2021\n" "Language-Team: German (https://www.transifex.com/paperless/teams/115905/de/)\n" "MIME-Version: 1.0\n" @@ -25,64 +25,64 @@ msgstr "" msgid "Documents" msgstr "Dokumente" -#: documents/models.py:33 +#: documents/models.py:32 msgid "Any word" msgstr "Irgendein Wort" -#: documents/models.py:34 +#: documents/models.py:33 msgid "All words" msgstr "Alle Wörter" -#: documents/models.py:35 +#: documents/models.py:34 msgid "Exact match" msgstr "Exakte Übereinstimmung" -#: documents/models.py:36 +#: documents/models.py:35 msgid "Regular expression" msgstr "Regulärer Ausdruck" -#: documents/models.py:37 +#: documents/models.py:36 msgid "Fuzzy word" msgstr "Ungenaues Wort" -#: documents/models.py:38 +#: documents/models.py:37 msgid "Automatic" msgstr "Automatisch" -#: documents/models.py:42 documents/models.py:352 paperless_mail/models.py:25 +#: documents/models.py:41 documents/models.py:364 paperless_mail/models.py:25 #: paperless_mail/models.py:109 msgid "name" msgstr "Name" -#: documents/models.py:46 +#: documents/models.py:45 msgid "match" msgstr "Zuweisungsmuster" -#: documents/models.py:50 +#: documents/models.py:49 msgid "matching algorithm" msgstr "Zuweisungsalgorithmus" -#: documents/models.py:56 +#: documents/models.py:55 msgid "is insensitive" msgstr "Groß-/Kleinschreibung irrelevant" -#: documents/models.py:75 documents/models.py:135 +#: documents/models.py:74 documents/models.py:134 msgid "correspondent" msgstr "Korrespondent" -#: documents/models.py:76 +#: documents/models.py:75 msgid "correspondents" msgstr "Korrespondenten" -#: documents/models.py:98 +#: documents/models.py:97 msgid "color" msgstr "Farbe" -#: documents/models.py:102 +#: documents/models.py:101 msgid "is inbox tag" msgstr "Posteingangs-Tag" -#: documents/models.py:104 +#: documents/models.py:103 msgid "" "Marks this tag as an inbox tag: All newly consumed documents will be tagged " "with inbox tags." @@ -90,39 +90,39 @@ msgstr "" "Markiert das Tag als Posteingangs-Tag. Neue Dokumente werden immer mit " "diesem Tag versehen." -#: documents/models.py:109 +#: documents/models.py:108 msgid "tag" msgstr "Tag" -#: documents/models.py:110 documents/models.py:166 +#: documents/models.py:109 documents/models.py:165 msgid "tags" msgstr "Tags" -#: documents/models.py:116 documents/models.py:148 +#: documents/models.py:115 documents/models.py:147 msgid "document type" msgstr "Dokumenttyp" -#: documents/models.py:117 +#: documents/models.py:116 msgid "document types" msgstr "Dokumenttypen" -#: documents/models.py:125 +#: documents/models.py:124 msgid "Unencrypted" msgstr "Nicht verschlüsselt" -#: documents/models.py:126 +#: documents/models.py:125 msgid "Encrypted with GNU Privacy Guard" msgstr "Verschlüsselt mit GNU Privacy Guard" -#: documents/models.py:139 +#: documents/models.py:138 msgid "title" msgstr "Titel" -#: documents/models.py:152 +#: documents/models.py:151 msgid "content" msgstr "Inhalt" -#: documents/models.py:154 +#: documents/models.py:153 msgid "" "The raw, text-only data of the document. This field is primarily used for " "searching." @@ -130,43 +130,43 @@ msgstr "" "Der Inhalt des Dokuments in Textform. Dieses Feld wird primär für die Suche " "verwendet." -#: documents/models.py:159 +#: documents/models.py:158 msgid "mime type" msgstr "MIME-Typ" -#: documents/models.py:170 +#: documents/models.py:169 msgid "checksum" msgstr "Prüfsumme" -#: documents/models.py:174 +#: documents/models.py:173 msgid "The checksum of the original document." msgstr "Die Prüfsumme des originalen Dokuments." -#: documents/models.py:178 +#: documents/models.py:177 msgid "archive checksum" msgstr "Archiv-Prüfsumme" -#: documents/models.py:183 +#: documents/models.py:182 msgid "The checksum of the archived document." msgstr "Die Prüfsumme des archivierten Dokuments." -#: documents/models.py:187 documents/models.py:330 +#: documents/models.py:186 documents/models.py:342 msgid "created" msgstr "Ausgestellt" -#: documents/models.py:191 +#: documents/models.py:190 msgid "modified" msgstr "Geändert" -#: documents/models.py:195 +#: documents/models.py:194 msgid "storage type" msgstr "Speichertyp" -#: documents/models.py:203 +#: documents/models.py:202 msgid "added" msgstr "Hinzugefügt" -#: documents/models.py:207 +#: documents/models.py:206 msgid "filename" msgstr "Dateiname" @@ -175,178 +175,186 @@ msgid "Current filename in storage" msgstr "Aktueller Dateiname im Datenspeicher" #: documents/models.py:216 +msgid "archive filename" +msgstr "Archiv-Dateiname" + +#: documents/models.py:222 +msgid "Current archive filename in storage" +msgstr "Aktueller Dateiname im Archiv" + +#: documents/models.py:226 msgid "archive serial number" msgstr "Archiv-Seriennummer" -#: documents/models.py:221 +#: documents/models.py:231 msgid "The position of this document in your physical document archive." msgstr "Die Position dieses Dokuments in Ihrem physischen Dokumentenarchiv." -#: documents/models.py:227 +#: documents/models.py:237 msgid "document" msgstr "Dokument" -#: documents/models.py:228 +#: documents/models.py:238 msgid "documents" msgstr "Dokumente" -#: documents/models.py:313 +#: documents/models.py:325 msgid "debug" msgstr "Debug" -#: documents/models.py:314 +#: documents/models.py:326 msgid "information" msgstr "Information" -#: documents/models.py:315 +#: documents/models.py:327 msgid "warning" msgstr "Warnung" -#: documents/models.py:316 +#: documents/models.py:328 msgid "error" msgstr "Fehler" -#: documents/models.py:317 +#: documents/models.py:329 msgid "critical" msgstr "Kritisch" -#: documents/models.py:321 +#: documents/models.py:333 msgid "group" msgstr "Gruppe" -#: documents/models.py:324 +#: documents/models.py:336 msgid "message" msgstr "Nachricht" -#: documents/models.py:327 +#: documents/models.py:339 msgid "level" msgstr "Level" -#: documents/models.py:334 +#: documents/models.py:346 msgid "log" msgstr "Protokoll" -#: documents/models.py:335 +#: documents/models.py:347 msgid "logs" msgstr "Protokoll" -#: documents/models.py:346 documents/models.py:396 +#: documents/models.py:358 documents/models.py:408 msgid "saved view" msgstr "Gespeicherte Ansicht" -#: documents/models.py:347 +#: documents/models.py:359 msgid "saved views" msgstr "Gespeicherte Ansichten" -#: documents/models.py:350 +#: documents/models.py:362 msgid "user" msgstr "Benutzer" -#: documents/models.py:356 +#: documents/models.py:368 msgid "show on dashboard" msgstr "Auf Startseite zeigen" -#: documents/models.py:359 +#: documents/models.py:371 msgid "show in sidebar" msgstr "In Seitenleiste zeigen" -#: documents/models.py:363 +#: documents/models.py:375 msgid "sort field" msgstr "Sortierfeld" -#: documents/models.py:366 +#: documents/models.py:378 msgid "sort reverse" msgstr "Umgekehrte Sortierung" -#: documents/models.py:372 +#: documents/models.py:384 msgid "title contains" msgstr "Titel enthält" -#: documents/models.py:373 +#: documents/models.py:385 msgid "content contains" msgstr "Inhalt enthält" -#: documents/models.py:374 +#: documents/models.py:386 msgid "ASN is" msgstr "ASN ist" -#: documents/models.py:375 +#: documents/models.py:387 msgid "correspondent is" msgstr "Korrespondent ist" -#: documents/models.py:376 +#: documents/models.py:388 msgid "document type is" msgstr "Dokumenttyp ist" -#: documents/models.py:377 +#: documents/models.py:389 msgid "is in inbox" msgstr "Ist im Posteingang" -#: documents/models.py:378 +#: documents/models.py:390 msgid "has tag" msgstr "Hat Tag" -#: documents/models.py:379 +#: documents/models.py:391 msgid "has any tag" msgstr "Hat irgendein Tag" -#: documents/models.py:380 +#: documents/models.py:392 msgid "created before" msgstr "Ausgestellt vor" -#: documents/models.py:381 +#: documents/models.py:393 msgid "created after" msgstr "Ausgestellt nach" -#: documents/models.py:382 +#: documents/models.py:394 msgid "created year is" msgstr "Ausgestellt im Jahr" -#: documents/models.py:383 +#: documents/models.py:395 msgid "created month is" msgstr "Ausgestellt im Monat" -#: documents/models.py:384 +#: documents/models.py:396 msgid "created day is" msgstr "Ausgestellt am Tag" -#: documents/models.py:385 +#: documents/models.py:397 msgid "added before" msgstr "Hinzugefügt vor" -#: documents/models.py:386 +#: documents/models.py:398 msgid "added after" msgstr "Hinzugefügt nach" -#: documents/models.py:387 +#: documents/models.py:399 msgid "modified before" msgstr "Geändert vor" -#: documents/models.py:388 +#: documents/models.py:400 msgid "modified after" msgstr "Geändert nach" -#: documents/models.py:389 +#: documents/models.py:401 msgid "does not have tag" msgstr "Hat nicht folgendes Tag" -#: documents/models.py:400 +#: documents/models.py:412 msgid "rule type" msgstr "Regeltyp" -#: documents/models.py:404 +#: documents/models.py:416 msgid "value" msgstr "Wert" -#: documents/models.py:410 +#: documents/models.py:422 msgid "filter rule" msgstr "Filterregel" -#: documents/models.py:411 +#: documents/models.py:423 msgid "filter rules" msgstr "Filterregeln" -#: documents/serialisers.py:383 +#: documents/serialisers.py:370 #, python-format msgid "File type %(type)s not supported" msgstr "Dateityp %(type)s nicht unterstützt" @@ -393,19 +401,23 @@ msgstr "Passwort" msgid "Sign in" msgstr "Anmelden" -#: paperless/settings.py:286 -msgid "English" -msgstr "Englisch" +#: paperless/settings.py:291 +msgid "English (US)" +msgstr "Englisch (US)" -#: paperless/settings.py:287 +#: paperless/settings.py:292 +msgid "English (GB)" +msgstr "Englisch (UK)" + +#: paperless/settings.py:293 msgid "German" msgstr "Deutsch" -#: paperless/settings.py:288 +#: paperless/settings.py:294 msgid "Dutch" msgstr "Niederländisch" -#: paperless/settings.py:289 +#: paperless/settings.py:295 msgid "French" msgstr "Französisch" From 6ff99945f3de64859517c204f13fc0409cf69020 Mon Sep 17 00:00:00 2001 From: Jonas Winkler <17569239+jonaswinkler@users.noreply.github.com> Date: Wed, 17 Feb 2021 13:18:19 +0100 Subject: [PATCH 141/225] Update Crowdin configuration file --- crowdin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crowdin.yml b/crowdin.yml index f5a7765dd..a121e80b8 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,5 +1,5 @@ files: - - source: /src/locale/en-us/LC_MESSAGES/django.po + - 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 From 4e4d6e806cad6cd871ab732d9c50396f0aa3b567 Mon Sep 17 00:00:00 2001 From: Jonas Winkler <17569239+jonaswinkler@users.noreply.github.com> Date: Wed, 17 Feb 2021 13:22:45 +0100 Subject: [PATCH 142/225] Update Crowdin configuration file --- crowdin.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crowdin.yml b/crowdin.yml index a121e80b8..0e6a46a31 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,5 +1,5 @@ files: - source: /src/locale/en_US/LC_MESSAGES/django.po - translation: /src/locale/%two_letters_code%/LC_MESSAGES/django.po + translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po - source: /src-ui/messages.xlf - translation: /src-ui/src/locale/messages.%two_letters_code%.xlf + translation: /src-ui/src/locale/messages.%locale_with_underscore%.xlf From 58df3d5767105944937eb0792b72204cd137805e Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Wed, 17 Feb 2021 12:57:48 +0000 Subject: [PATCH 143/225] Apply translations in nl_NL translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po' on the 'nl_NL' language. --- src/locale/nl_NL/LC_MESSAGES/django.po | 186 +++++++++++++------------ 1 file changed, 99 insertions(+), 87 deletions(-) diff --git a/src/locale/nl_NL/LC_MESSAGES/django.po b/src/locale/nl_NL/LC_MESSAGES/django.po index 63391da15..eefba7fbb 100644 --- a/src/locale/nl_NL/LC_MESSAGES/django.po +++ b/src/locale/nl_NL/LC_MESSAGES/django.po @@ -4,17 +4,17 @@ # FIRST AUTHOR , YEAR. # # Translators: +# Jonas Winkler, 2021 # Jo Vandeginste , 2021 -# Ben , 2021 # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-28 22:02+0100\n" -"PO-Revision-Date: 2020-12-30 19:27+0000\n" -"Last-Translator: Ben , 2021\n" +"POT-Creation-Date: 2021-02-16 14:52+0100\n" +"PO-Revision-Date: 2021-02-16 18:37+0000\n" +"Last-Translator: Jo Vandeginste , 2021\n" "Language-Team: Dutch (Netherlands) (https://www.transifex.com/paperless/teams/115905/nl_NL/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -26,64 +26,64 @@ msgstr "" msgid "Documents" msgstr "Documenten" -#: documents/models.py:33 +#: documents/models.py:32 msgid "Any word" msgstr "Eender welk woord" -#: documents/models.py:34 +#: documents/models.py:33 msgid "All words" msgstr "Alle woorden" -#: documents/models.py:35 +#: documents/models.py:34 msgid "Exact match" msgstr "Exacte overeenkomst" -#: documents/models.py:36 +#: documents/models.py:35 msgid "Regular expression" msgstr "Reguliere expressie" -#: documents/models.py:37 +#: documents/models.py:36 msgid "Fuzzy word" msgstr "Gelijkaardig woord" -#: documents/models.py:38 +#: documents/models.py:37 msgid "Automatic" msgstr "Automatisch" -#: documents/models.py:42 documents/models.py:352 paperless_mail/models.py:25 +#: documents/models.py:41 documents/models.py:364 paperless_mail/models.py:25 #: paperless_mail/models.py:109 msgid "name" msgstr "naam" -#: documents/models.py:46 +#: documents/models.py:45 msgid "match" msgstr "Overeenkomst" -#: documents/models.py:50 +#: documents/models.py:49 msgid "matching algorithm" msgstr "Algoritme voor het bepalen van de overeenkomst" -#: documents/models.py:56 +#: documents/models.py:55 msgid "is insensitive" msgstr "is niet hoofdlettergevoelig" -#: documents/models.py:75 documents/models.py:135 +#: documents/models.py:74 documents/models.py:134 msgid "correspondent" msgstr "correspondent" -#: documents/models.py:76 +#: documents/models.py:75 msgid "correspondents" msgstr "correspondenten" -#: documents/models.py:98 +#: documents/models.py:97 msgid "color" msgstr "Kleur" -#: documents/models.py:102 +#: documents/models.py:101 msgid "is inbox tag" msgstr "is \"Postvak in\"-etiket" -#: documents/models.py:104 +#: documents/models.py:103 msgid "" "Marks this tag as an inbox tag: All newly consumed documents will be tagged " "with inbox tags." @@ -91,39 +91,39 @@ msgstr "" "Markeer dit etiket als een \"Postvak in\"-etiket: alle nieuw verwerkte " "documenten krijgen de \"Postvak in\"-etiketten." -#: documents/models.py:109 +#: documents/models.py:108 msgid "tag" msgstr "etiket" -#: documents/models.py:110 documents/models.py:166 +#: documents/models.py:109 documents/models.py:165 msgid "tags" msgstr "etiketten" -#: documents/models.py:116 documents/models.py:148 +#: documents/models.py:115 documents/models.py:147 msgid "document type" msgstr "documenttype" -#: documents/models.py:117 +#: documents/models.py:116 msgid "document types" msgstr "documenttypen" -#: documents/models.py:125 +#: documents/models.py:124 msgid "Unencrypted" msgstr "Niet versleuteld" -#: documents/models.py:126 +#: documents/models.py:125 msgid "Encrypted with GNU Privacy Guard" msgstr "Versleuteld met GNU Privacy Guard" -#: documents/models.py:139 +#: documents/models.py:138 msgid "title" msgstr "titel" -#: documents/models.py:152 +#: documents/models.py:151 msgid "content" msgstr "inhoud" -#: documents/models.py:154 +#: documents/models.py:153 msgid "" "The raw, text-only data of the document. This field is primarily used for " "searching." @@ -131,43 +131,43 @@ msgstr "" "De onbewerkte gegevens van het document. Dit veld wordt voornamelijk " "gebruikt om te zoeken." -#: documents/models.py:159 +#: documents/models.py:158 msgid "mime type" msgstr "mimetype" -#: documents/models.py:170 +#: documents/models.py:169 msgid "checksum" msgstr "checksum" -#: documents/models.py:174 +#: documents/models.py:173 msgid "The checksum of the original document." msgstr "Het controlecijfer van het originele document." -#: documents/models.py:178 +#: documents/models.py:177 msgid "archive checksum" msgstr "archief checksum" -#: documents/models.py:183 +#: documents/models.py:182 msgid "The checksum of the archived document." msgstr "De checksum van het gearchiveerde document." -#: documents/models.py:187 documents/models.py:330 +#: documents/models.py:186 documents/models.py:342 msgid "created" msgstr "aangemaakt" -#: documents/models.py:191 +#: documents/models.py:190 msgid "modified" msgstr "gewijzigd" -#: documents/models.py:195 +#: documents/models.py:194 msgid "storage type" msgstr "type opslag" -#: documents/models.py:203 +#: documents/models.py:202 msgid "added" msgstr "toegevoegd" -#: documents/models.py:207 +#: documents/models.py:206 msgid "filename" msgstr "bestandsnaam" @@ -176,178 +176,186 @@ msgid "Current filename in storage" msgstr "Huidige bestandsnaam in opslag" #: documents/models.py:216 +msgid "archive filename" +msgstr "Bestandsnaam in archief" + +#: documents/models.py:222 +msgid "Current archive filename in storage" +msgstr "Huidige bestandsnaam in archief" + +#: documents/models.py:226 msgid "archive serial number" msgstr "serienummer in archief" -#: documents/models.py:221 +#: documents/models.py:231 msgid "The position of this document in your physical document archive." msgstr "De positie van dit document in je fysieke documentenarchief." -#: documents/models.py:227 +#: documents/models.py:237 msgid "document" msgstr "document" -#: documents/models.py:228 +#: documents/models.py:238 msgid "documents" msgstr "documenten" -#: documents/models.py:313 +#: documents/models.py:325 msgid "debug" msgstr "debug" -#: documents/models.py:314 +#: documents/models.py:326 msgid "information" msgstr "informatie" -#: documents/models.py:315 +#: documents/models.py:327 msgid "warning" msgstr "waarschuwing" -#: documents/models.py:316 +#: documents/models.py:328 msgid "error" msgstr "fout" -#: documents/models.py:317 +#: documents/models.py:329 msgid "critical" msgstr "kritisch" -#: documents/models.py:321 +#: documents/models.py:333 msgid "group" msgstr "groep" -#: documents/models.py:324 +#: documents/models.py:336 msgid "message" msgstr "bericht" -#: documents/models.py:327 +#: documents/models.py:339 msgid "level" msgstr "niveau" -#: documents/models.py:334 +#: documents/models.py:346 msgid "log" msgstr "bericht" -#: documents/models.py:335 +#: documents/models.py:347 msgid "logs" msgstr "berichten" -#: documents/models.py:346 documents/models.py:396 +#: documents/models.py:358 documents/models.py:408 msgid "saved view" msgstr "opgeslagen view" -#: documents/models.py:347 +#: documents/models.py:359 msgid "saved views" msgstr "opgeslagen views" -#: documents/models.py:350 +#: documents/models.py:362 msgid "user" msgstr "gebruiker" -#: documents/models.py:356 +#: documents/models.py:368 msgid "show on dashboard" msgstr "weergeven op dashboard" -#: documents/models.py:359 +#: documents/models.py:371 msgid "show in sidebar" msgstr "weergeven in zijbalk" -#: documents/models.py:363 +#: documents/models.py:375 msgid "sort field" msgstr "sorteerveld" -#: documents/models.py:366 +#: documents/models.py:378 msgid "sort reverse" msgstr "omgekeerd sorteren" -#: documents/models.py:372 +#: documents/models.py:384 msgid "title contains" msgstr "titel bevat" -#: documents/models.py:373 +#: documents/models.py:385 msgid "content contains" msgstr "inhoud bevat" -#: documents/models.py:374 +#: documents/models.py:386 msgid "ASN is" msgstr "ASN is" -#: documents/models.py:375 +#: documents/models.py:387 msgid "correspondent is" msgstr "correspondent is" -#: documents/models.py:376 +#: documents/models.py:388 msgid "document type is" msgstr "documenttype is" -#: documents/models.py:377 +#: documents/models.py:389 msgid "is in inbox" msgstr "zit in \"Postvak in\"" -#: documents/models.py:378 +#: documents/models.py:390 msgid "has tag" msgstr "heeft etiket" -#: documents/models.py:379 +#: documents/models.py:391 msgid "has any tag" msgstr "heeft één van de etiketten" -#: documents/models.py:380 +#: documents/models.py:392 msgid "created before" msgstr "aangemaakt voor" -#: documents/models.py:381 +#: documents/models.py:393 msgid "created after" msgstr "aangemaakt na" -#: documents/models.py:382 +#: documents/models.py:394 msgid "created year is" msgstr "aangemaakt jaar is" -#: documents/models.py:383 +#: documents/models.py:395 msgid "created month is" msgstr "aangemaakte maand is" -#: documents/models.py:384 +#: documents/models.py:396 msgid "created day is" msgstr "aangemaakte dag is" -#: documents/models.py:385 +#: documents/models.py:397 msgid "added before" msgstr "toegevoegd voor" -#: documents/models.py:386 +#: documents/models.py:398 msgid "added after" msgstr "toegevoegd na" -#: documents/models.py:387 +#: documents/models.py:399 msgid "modified before" msgstr "gewijzigd voor" -#: documents/models.py:388 +#: documents/models.py:400 msgid "modified after" msgstr "gewijzigd na" -#: documents/models.py:389 +#: documents/models.py:401 msgid "does not have tag" msgstr "heeft geen etiket" -#: documents/models.py:400 +#: documents/models.py:412 msgid "rule type" msgstr "type regel" -#: documents/models.py:404 +#: documents/models.py:416 msgid "value" msgstr "waarde" -#: documents/models.py:410 +#: documents/models.py:422 msgid "filter rule" msgstr "filterregel" -#: documents/models.py:411 +#: documents/models.py:423 msgid "filter rules" msgstr "filterregels" -#: documents/serialisers.py:383 +#: documents/serialisers.py:370 #, python-format msgid "File type %(type)s not supported" msgstr "Bestandstype %(type)s niet ondersteund" @@ -392,19 +400,23 @@ msgstr "Wachtwoord" msgid "Sign in" msgstr "Aanmelden" -#: paperless/settings.py:286 -msgid "English" -msgstr "Engels" +#: paperless/settings.py:291 +msgid "English (US)" +msgstr "Engels (US)" -#: paperless/settings.py:287 +#: paperless/settings.py:292 +msgid "English (GB)" +msgstr "Engels (Brits)" + +#: paperless/settings.py:293 msgid "German" msgstr "Duits" -#: paperless/settings.py:288 +#: paperless/settings.py:294 msgid "Dutch" msgstr "Nederlands" -#: paperless/settings.py:289 +#: paperless/settings.py:295 msgid "French" msgstr "Frans" From 0d78e58d77907ec9037bd459608602f787fddcda Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Wed, 17 Feb 2021 14:26:06 +0100 Subject: [PATCH 144/225] fixed paperless not properly selecting en-gb --- src-ui/src/app/services/settings.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index a145ae5a7..d511fa65f 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -79,8 +79,8 @@ export class SettingsService { getLanguageOptions(): LanguageOption[] { return [ - {code: "en-US", name: $localize`English (US)`, englishName: "English (US)"}, - {code: "en-GB", name: $localize`English (GB)`, englishName: "English (GB)"}, + {code: "en-us", name: $localize`English (US)`, englishName: "English (US)"}, + {code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)"}, {code: "de", name: $localize`German`, englishName: "German"}, {code: "nl", name: $localize`Dutch`, englishName: "Dutch"}, {code: "fr", name: $localize`French`, englishName: "French"} From 7bc557a9999e86e6fdcb0627372b50721325f28a Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Wed, 17 Feb 2021 13:36:15 +0000 Subject: [PATCH 145/225] Apply translations in en_GB translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po' on the 'en_GB' language. --- src/locale/en_GB/LC_MESSAGES/django.po | 191 +++++++++++++------------ 1 file changed, 102 insertions(+), 89 deletions(-) diff --git a/src/locale/en_GB/LC_MESSAGES/django.po b/src/locale/en_GB/LC_MESSAGES/django.po index de13e5d34..f0c5b0381 100644 --- a/src/locale/en_GB/LC_MESSAGES/django.po +++ b/src/locale/en_GB/LC_MESSAGES/django.po @@ -2,18 +2,19 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# # Translators: -# Ali Bates , 2021 -# +# Ali Bates, 2021 +# Jonas Winkler, 2021 +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-28 22:02+0100\n" -"PO-Revision-Date: 2020-12-30 19:27+0000\n" -"Last-Translator: Ali Bates , 2021\n" +"POT-Creation-Date: 2021-02-16 14:52+0100\n" +"PO-Revision-Date: 2021-02-16 18:37+0000\n" +"Last-Translator: Jonas Winkler, 2021\n" "Language-Team: English (United Kingdom) (https://www.transifex.com/paperless/teams/115905/en_GB/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -25,64 +26,64 @@ msgstr "" msgid "Documents" msgstr "Documents" -#: documents/models.py:33 +#: documents/models.py:32 msgid "Any word" msgstr "Any word" -#: documents/models.py:34 +#: documents/models.py:33 msgid "All words" msgstr "All words" -#: documents/models.py:35 +#: documents/models.py:34 msgid "Exact match" msgstr "Exact match" -#: documents/models.py:36 +#: documents/models.py:35 msgid "Regular expression" msgstr "Regular expression" -#: documents/models.py:37 +#: documents/models.py:36 msgid "Fuzzy word" msgstr "Fuzzy word" -#: documents/models.py:38 +#: documents/models.py:37 msgid "Automatic" msgstr "Automatic" -#: documents/models.py:42 documents/models.py:352 paperless_mail/models.py:25 +#: documents/models.py:41 documents/models.py:364 paperless_mail/models.py:25 #: paperless_mail/models.py:109 msgid "name" msgstr "name" -#: documents/models.py:46 +#: documents/models.py:45 msgid "match" msgstr "match" -#: documents/models.py:50 +#: documents/models.py:49 msgid "matching algorithm" msgstr "matching algorithm" -#: documents/models.py:56 +#: documents/models.py:55 msgid "is insensitive" msgstr "is insensitive" -#: documents/models.py:75 documents/models.py:135 +#: documents/models.py:74 documents/models.py:134 msgid "correspondent" msgstr "correspondent" -#: documents/models.py:76 +#: documents/models.py:75 msgid "correspondents" msgstr "correspondents" -#: documents/models.py:98 +#: documents/models.py:97 msgid "color" msgstr "colour" -#: documents/models.py:102 +#: documents/models.py:101 msgid "is inbox tag" msgstr "is inbox tag" -#: documents/models.py:104 +#: documents/models.py:103 msgid "" "Marks this tag as an inbox tag: All newly consumed documents will be tagged " "with inbox tags." @@ -90,39 +91,39 @@ msgstr "" "Marks this tag as an inbox tag: All newly consumed documents will be tagged " "with inbox tags." -#: documents/models.py:109 +#: documents/models.py:108 msgid "tag" msgstr "tag" -#: documents/models.py:110 documents/models.py:166 +#: documents/models.py:109 documents/models.py:165 msgid "tags" msgstr "tags" -#: documents/models.py:116 documents/models.py:148 +#: documents/models.py:115 documents/models.py:147 msgid "document type" msgstr "document type" -#: documents/models.py:117 +#: documents/models.py:116 msgid "document types" msgstr "document types" -#: documents/models.py:125 +#: documents/models.py:124 msgid "Unencrypted" msgstr "Unencrypted" -#: documents/models.py:126 +#: documents/models.py:125 msgid "Encrypted with GNU Privacy Guard" msgstr "Encrypted with GNU Privacy Guard" -#: documents/models.py:139 +#: documents/models.py:138 msgid "title" msgstr "title" -#: documents/models.py:152 +#: documents/models.py:151 msgid "content" msgstr "content" -#: documents/models.py:154 +#: documents/models.py:153 msgid "" "The raw, text-only data of the document. This field is primarily used for " "searching." @@ -130,43 +131,43 @@ msgstr "" "The raw, text-only data of the document. This field is primarily used for " "searching." -#: documents/models.py:159 +#: documents/models.py:158 msgid "mime type" msgstr "mime type" -#: documents/models.py:170 +#: documents/models.py:169 msgid "checksum" msgstr "checksum" -#: documents/models.py:174 +#: documents/models.py:173 msgid "The checksum of the original document." msgstr "The checksum of the original document." -#: documents/models.py:178 +#: documents/models.py:177 msgid "archive checksum" msgstr "archive checksum" -#: documents/models.py:183 +#: documents/models.py:182 msgid "The checksum of the archived document." msgstr "The checksum of the archived document." -#: documents/models.py:187 documents/models.py:330 +#: documents/models.py:186 documents/models.py:342 msgid "created" msgstr "created" -#: documents/models.py:191 +#: documents/models.py:190 msgid "modified" msgstr "modified" -#: documents/models.py:195 +#: documents/models.py:194 msgid "storage type" msgstr "storage type" -#: documents/models.py:203 +#: documents/models.py:202 msgid "added" msgstr "added" -#: documents/models.py:207 +#: documents/models.py:206 msgid "filename" msgstr "filename" @@ -175,178 +176,186 @@ msgid "Current filename in storage" msgstr "Current filename in storage" #: documents/models.py:216 +msgid "archive filename" +msgstr "archive filename" + +#: documents/models.py:222 +msgid "Current archive filename in storage" +msgstr "Current archive filename in storage" + +#: documents/models.py:226 msgid "archive serial number" msgstr "archive serial number" -#: documents/models.py:221 +#: documents/models.py:231 msgid "The position of this document in your physical document archive." msgstr "The position of this document in your physical document archive." -#: documents/models.py:227 +#: documents/models.py:237 msgid "document" msgstr "document" -#: documents/models.py:228 +#: documents/models.py:238 msgid "documents" msgstr "documents" -#: documents/models.py:313 +#: documents/models.py:325 msgid "debug" msgstr "debug" -#: documents/models.py:314 +#: documents/models.py:326 msgid "information" msgstr "information" -#: documents/models.py:315 +#: documents/models.py:327 msgid "warning" msgstr "warning" -#: documents/models.py:316 +#: documents/models.py:328 msgid "error" msgstr "error" -#: documents/models.py:317 +#: documents/models.py:329 msgid "critical" msgstr "critical" -#: documents/models.py:321 +#: documents/models.py:333 msgid "group" msgstr "group" -#: documents/models.py:324 +#: documents/models.py:336 msgid "message" msgstr "message" -#: documents/models.py:327 +#: documents/models.py:339 msgid "level" msgstr "level" -#: documents/models.py:334 +#: documents/models.py:346 msgid "log" msgstr "log" -#: documents/models.py:335 +#: documents/models.py:347 msgid "logs" msgstr "logs" -#: documents/models.py:346 documents/models.py:396 +#: documents/models.py:358 documents/models.py:408 msgid "saved view" msgstr "saved view" -#: documents/models.py:347 +#: documents/models.py:359 msgid "saved views" msgstr "saved views" -#: documents/models.py:350 +#: documents/models.py:362 msgid "user" msgstr "user" -#: documents/models.py:356 +#: documents/models.py:368 msgid "show on dashboard" msgstr "show on dashboard" -#: documents/models.py:359 +#: documents/models.py:371 msgid "show in sidebar" msgstr "show in sidebar" -#: documents/models.py:363 +#: documents/models.py:375 msgid "sort field" msgstr "sort field" -#: documents/models.py:366 +#: documents/models.py:378 msgid "sort reverse" msgstr "sort reverse" -#: documents/models.py:372 +#: documents/models.py:384 msgid "title contains" msgstr "title contains" -#: documents/models.py:373 +#: documents/models.py:385 msgid "content contains" msgstr "content contains" -#: documents/models.py:374 +#: documents/models.py:386 msgid "ASN is" msgstr "ASN is" -#: documents/models.py:375 +#: documents/models.py:387 msgid "correspondent is" msgstr "correspondent is" -#: documents/models.py:376 +#: documents/models.py:388 msgid "document type is" msgstr "document type is" -#: documents/models.py:377 +#: documents/models.py:389 msgid "is in inbox" msgstr "is in inbox" -#: documents/models.py:378 +#: documents/models.py:390 msgid "has tag" msgstr "has tag" -#: documents/models.py:379 +#: documents/models.py:391 msgid "has any tag" msgstr "has any tag" -#: documents/models.py:380 +#: documents/models.py:392 msgid "created before" msgstr "created before" -#: documents/models.py:381 +#: documents/models.py:393 msgid "created after" msgstr "created after" -#: documents/models.py:382 +#: documents/models.py:394 msgid "created year is" msgstr "created year is" -#: documents/models.py:383 +#: documents/models.py:395 msgid "created month is" msgstr "created month is" -#: documents/models.py:384 +#: documents/models.py:396 msgid "created day is" msgstr "created day is" -#: documents/models.py:385 +#: documents/models.py:397 msgid "added before" msgstr "added before" -#: documents/models.py:386 +#: documents/models.py:398 msgid "added after" msgstr "added after" -#: documents/models.py:387 +#: documents/models.py:399 msgid "modified before" msgstr "modified before" -#: documents/models.py:388 +#: documents/models.py:400 msgid "modified after" msgstr "modified after" -#: documents/models.py:389 +#: documents/models.py:401 msgid "does not have tag" msgstr "does not have tag" -#: documents/models.py:400 +#: documents/models.py:412 msgid "rule type" msgstr "rule type" -#: documents/models.py:404 +#: documents/models.py:416 msgid "value" msgstr "value" -#: documents/models.py:410 +#: documents/models.py:422 msgid "filter rule" msgstr "filter rule" -#: documents/models.py:411 +#: documents/models.py:423 msgid "filter rules" msgstr "filter rules" -#: documents/serialisers.py:383 +#: documents/serialisers.py:370 #, python-format msgid "File type %(type)s not supported" msgstr "File type %(type)s not supported" @@ -391,19 +400,23 @@ msgstr "Password" msgid "Sign in" msgstr "Sign in" -#: paperless/settings.py:286 -msgid "English" -msgstr "English" +#: paperless/settings.py:291 +msgid "English (US)" +msgstr "English (US)" -#: paperless/settings.py:287 +#: paperless/settings.py:292 +msgid "English (GB)" +msgstr "English (GB)" + +#: paperless/settings.py:293 msgid "German" msgstr "German" -#: paperless/settings.py:288 +#: paperless/settings.py:294 msgid "Dutch" msgstr "Dutch" -#: paperless/settings.py:289 +#: paperless/settings.py:295 msgid "French" msgstr "French" From 0e237fa45908f2e60d9801422b06a40f3475946e Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Thu, 18 Feb 2021 17:11:47 +0100 Subject: [PATCH 146/225] messages --- src-ui/messages.xlf | 66 +++++++++++-------- .../filter-editor/filter-editor.component.ts | 4 +- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index a80bd082f..731fdff7f 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -52,17 +52,17 @@
- View "" saved successfully. + View "" saved successfully. src/app/components/document-list/document-list.component.ts - 109 + 115 View "" created successfully. src/app/components/document-list/document-list.component.ts - 130 + 136 @@ -114,8 +114,8 @@ 72 - - Save "" + + Save "" src/app/components/document-list/document-list.component.html 71 @@ -513,13 +513,6 @@ 1 - - Filter - - src/app/components/manage/logs/logs.component.html - 7 - - Saved view "" deleted. @@ -545,14 +538,21 @@ Use date format of display language src/app/components/manage/settings/settings.component.ts - 95 + 96 + + + + ISO 8601 + + src/app/components/manage/settings/settings.component.ts + 97 Error while storing settings on server: src/app/components/manage/settings/settings.component.ts - 111 + 114 @@ -1081,6 +1081,13 @@ 46 + + Title: + + src/app/components/document-list/filter-editor/filter-editor.component.ts + 50 + + Filter tags @@ -1414,20 +1421,20 @@ 1 - - Documents in inbox: - - src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html - 3 - - - - Total documents: + + Total documents: src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html 4 + + Documents in inbox: + + src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html + 3 + + Processing: @@ -1619,25 +1626,32 @@ 82 + + English (GB) + + src/app/services/settings.service.ts + 83 + + German src/app/services/settings.service.ts - 83 + 84 Dutch src/app/services/settings.service.ts - 84 + 85 French src/app/services/settings.service.ts - 85 + 86 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 a9111bc9f..a6506efde 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 @@ -46,6 +46,8 @@ export class FilterEditorComponent implements OnInit, OnDestroy { return $localize`Without any tag` } + case FILTER_TITLE: + return $localize`Title: ${rule.value}` } } @@ -117,7 +119,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy { }) } - get filterRules() { + get filterRules(): FilterRule[] { let filterRules: FilterRule[] = [] if (this._titleFilter) { filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter}) From 0f80eee54e7b7e3a29f9f03be6e8330f12a5eed8 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Thu, 18 Feb 2021 17:29:21 +0100 Subject: [PATCH 147/225] refactored most of the list view; fixes #147, much snappier UX when switching between views --- .../saved-view-widget.component.ts | 2 +- .../document-detail.component.ts | 4 +- .../bulk-editor/bulk-editor.component.ts | 2 +- .../document-list.component.html | 6 +- .../document-list/document-list.component.ts | 38 +-- .../services/document-list-view.service.ts | 236 ++++++++++-------- 6 files changed, 163 insertions(+), 125 deletions(-) diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts index 36feeea1e..20bc26cdc 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts @@ -48,7 +48,7 @@ export class SavedViewWidgetComponent implements OnInit, OnDestroy { if (this.savedView.show_in_sidebar) { this.router.navigate(['view', this.savedView.id]) } else { - this.list.load(this.savedView) + this.list.loadSavedView(this.savedView, true) this.router.navigate(["documents"]) } } 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 071c8e249..af98a6f7f 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 @@ -191,8 +191,8 @@ export class DocumentDetailComponent implements OnInit { close() { this.openDocumentService.closeDocument(this.document) - if (this.documentListViewService.savedViewId) { - this.router.navigate(['view', this.documentListViewService.savedViewId]) + if (this.documentListViewService.activeSavedViewId) { + this.router.navigate(['view', this.documentListViewService.activeSavedViewId]) } else { this.router.navigate(['documents']) } diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 04fc2a978..ebc7a8c11 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -137,7 +137,7 @@ export class BulkEditorComponent { } else { 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" modal.componentInstance.btnCaption = $localize`Confirm` modal.componentInstance.confirmClicked.subscribe(() => { 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 cfc2e655d..75a9804a2 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 @@ -63,12 +63,12 @@
@@ -86,7 +86,7 @@ {list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}} (filtered)

+ [rotate]="true" aria-label="Default pagination">
diff --git a/src-ui/src/app/components/document-list/document-list.component.ts b/src-ui/src/app/components/document-list/document-list.component.ts index 0d1562c24..cf7afb845 100644 --- a/src-ui/src/app/components/document-list/document-list.component.ts +++ b/src-ui/src/app/components/document-list/document-list.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; +import { Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { Subscription } from 'rxjs'; @@ -9,7 +9,7 @@ import { ConsumerStatusService } from 'src/app/services/consumer-status.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service'; import { DOCUMENT_SORT_FIELDS } from 'src/app/services/rest/document.service'; import { SavedViewService } from 'src/app/services/rest/saved-view.service'; -import { Toast, ToastService } from 'src/app/services/toast.service'; +import { ToastService } from 'src/app/services/toast.service'; import { FilterEditorComponent } from './filter-editor/filter-editor.component'; import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component'; @@ -46,7 +46,7 @@ export class DocumentListComponent implements OnInit, OnDestroy { } getTitle() { - return this.list.savedViewTitle || $localize`Documents` + return this.list.activeSavedViewTitle || $localize`Documents` } getSortFields() { @@ -73,19 +73,18 @@ export class DocumentListComponent implements OnInit, OnDestroy { this.list.reload() }) this.route.paramMap.subscribe(params => { - this.list.clear() if (params.has('id')) { this.savedViewService.getCached(+params.get('id')).subscribe(view => { if (!view) { this.router.navigate(["404"]) return } - this.list.savedView = view + this.list.activateSavedView(view) this.list.reload() this.rulesChanged() }) } else { - this.list.savedView = null + this.list.activateSavedView(null) this.list.reload() this.rulesChanged() } @@ -99,16 +98,23 @@ export class DocumentListComponent implements OnInit, OnDestroy { } loadViewConfig(view: PaperlessSavedView) { - this.list.load(view) + this.list.loadSavedView(view) this.list.reload() this.rulesChanged() } saveViewConfig() { - this.savedViewService.update(this.list.savedView).subscribe(result => { - this.toastService.showInfo($localize`View "${this.list.savedView.name}" saved successfully.`) - }) - + if (this.list.activeSavedViewId != null) { + let savedView: PaperlessSavedView = { + id: this.list.activeSavedViewId, + filter_rules: this.list.filterRules, + sort_field: this.list.sortField, + sort_reverse: this.list.sortReverse + } + this.savedViewService.patch(savedView).subscribe(result => { + this.toastService.showInfo($localize`View "${this.list.activeSavedViewTitle}" saved successfully.`) + }) + } } saveViewConfigAs() { @@ -116,7 +122,7 @@ export class DocumentListComponent implements OnInit, OnDestroy { modal.componentInstance.defaultName = this.filterEditor.generateFilterName() modal.componentInstance.saveClicked.subscribe(formValue => { modal.componentInstance.buttonsEnabled = false - let savedView = { + let savedView: PaperlessSavedView = { name: formValue.name, show_on_dashboard: formValue.showOnDashboard, show_in_sidebar: formValue.showInSideBar, @@ -137,8 +143,8 @@ export class DocumentListComponent implements OnInit, OnDestroy { resetFilters(): void { this.filterRulesModified = false - if (this.list.savedViewId) { - this.savedViewService.getCached(this.list.savedViewId).subscribe(viewUntouched => { + if (this.list.activeSavedViewId) { + this.savedViewService.getCached(this.list.activeSavedViewId).subscribe(viewUntouched => { this.list.filterRules = viewUntouched.filter_rules this.list.reload() }) @@ -150,11 +156,11 @@ export class DocumentListComponent implements OnInit, OnDestroy { rulesChanged() { let modified = false - if (this.list.savedView == null) { + if (this.list.activeSavedViewId == null) { modified = this.list.filterRules.length > 0 // documents list is modified if it has any filters } else { // compare savedView current filters vs original - this.savedViewService.getCached(this.list.savedViewId).subscribe(view => { + this.savedViewService.getCached(this.list.activeSavedViewId).subscribe(view => { let filterRulesInitial = view.filter_rules if (this.list.filterRules.length !== filterRulesInitial.length) modified = true diff --git a/src-ui/src/app/services/document-list-view.service.ts b/src-ui/src/app/services/document-list-view.service.ts index 4eb0c276c..334706a3c 100644 --- a/src-ui/src/app/services/document-list-view.service.ts +++ b/src-ui/src/app/services/document-list-view.service.ts @@ -8,6 +8,23 @@ import { DOCUMENT_LIST_SERVICE } from '../data/storage-keys'; import { DocumentService } from './rest/document.service'; import { SettingsService, SETTINGS_KEYS } from './settings.service'; +interface ListViewState { + + title?: string + + documents?: PaperlessDocument[] + + currentPage: number + collectionSize: number + + sortField: string + sortReverse: boolean + + filterRules: FilterRule[] + + selected?: Set + +} /** * This service manages the document list which is displayed using the document list view. @@ -20,156 +37,174 @@ import { SettingsService, SETTINGS_KEYS } from './settings.service'; }) export class DocumentListViewService { - static DEFAULT_SORT_FIELD = 'created' - isReloading: boolean = false - documents: PaperlessDocument[] = [] - currentPage = 1 - currentPageSize: number = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE) - collectionSize: number + rangeSelectionAnchorIndex: number lastRangeSelectionToIndex: number - /** - * This is the current config for the document list. The service will always remember the last settings used for the document list. - */ - private _documentListViewConfig: PaperlessSavedView - /** - * Optionally, this is the currently selected saved view, which might be null. - */ - private _savedViewConfig: PaperlessSavedView + currentPageSize: number = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE) - get savedView(): PaperlessSavedView { - return this._savedViewConfig + private listViewStates: Map = new Map() + + private _activeSavedViewId: number = null + + get activeSavedViewId() { + return this._activeSavedViewId } - set savedView(value: PaperlessSavedView) { - if (value && !this._savedViewConfig || value && value.id != this._savedViewConfig.id) { - //saved view inactive and should be active now, or saved view active, but a different view is requested - //this is here so that we don't modify value, which might be the actual instance of the saved view. - this.selectNone() - this._savedViewConfig = Object.assign({}, value) - } else if (this._savedViewConfig && !value) { - //saved view active, but document list requested - this.selectNone() - this._savedViewConfig = null + get activeSavedViewTitle() { + return this.activeListViewState.title + } + + private defaultListViewState(): ListViewState { + return { + title: null, + documents: [], + currentPage: 1, + collectionSize: null, + sortField: "created", + sortReverse: true, + filterRules: [], + selected: new Set() } } - get savedViewId() { - return this.savedView?.id + private get activeListViewState() { + if (!this.listViewStates.has(this._activeSavedViewId)) { + this.listViewStates.set(this._activeSavedViewId, this.defaultListViewState()) + } + return this.listViewStates.get(this._activeSavedViewId) } - get savedViewTitle() { - return this.savedView?.name - } - - get documentListView() { - return this._documentListViewConfig - } - - set documentListView(value) { - if (value) { - this._documentListViewConfig = Object.assign({}, value) - this.saveDocumentListView() + activateSavedView(view: PaperlessSavedView) { + this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null + if (view) { + this._activeSavedViewId = view.id + this.loadSavedView(view) + } else { + this._activeSavedViewId = null } } - /** - * This is what switches between the saved views and the document list view. Everything on the document list uses - * this property to determine the settings for the currently displayed document list. - */ - get view() { - return this.savedView || this.documentListView - } - - load(view: PaperlessSavedView) { - this.documentListView.filter_rules = cloneFilterRules(view.filter_rules) - this.documentListView.sort_reverse = view.sort_reverse - this.documentListView.sort_field = view.sort_field - this.saveDocumentListView() - } - - clear() { - this.collectionSize = null - this.documents = [] - this.currentPage = 1 + loadSavedView(view: PaperlessSavedView, closeCurrentView: boolean = false) { + if (closeCurrentView) { + this._activeSavedViewId = null + } + this.activeListViewState.filterRules = cloneFilterRules(view.filter_rules) + this.activeListViewState.sortField = view.sort_field + this.activeListViewState.sortReverse = view.sort_reverse + if (this._activeSavedViewId) { + this.activeListViewState.title = view.name + } + this.reduceSelectionToFilter() } reload(onFinish?) { this.isReloading = true + let activeListViewState = this.activeListViewState + this.documentService.listFiltered( - this.currentPage, + activeListViewState.currentPage, this.currentPageSize, - this.view.sort_field, - this.view.sort_reverse, - this.view.filter_rules).subscribe( + activeListViewState.sortField, + activeListViewState.sortReverse, + activeListViewState.filterRules).subscribe( result => { - this.collectionSize = result.count - this.documents = result.results + this.isReloading = false + activeListViewState.collectionSize = result.count + activeListViewState.documents = result.results if (onFinish) { onFinish() } this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null - this.isReloading = false }, error => { - if (this.currentPage != 1 && error.status == 404) { + this.isReloading = false + if (activeListViewState.currentPage != 1 && error.status == 404) { // this happens when applying a filter: the current page might not be available anymore due to the reduced result set. - this.currentPage = 1 + activeListViewState.currentPage = 1 this.reload() } - this.isReloading = false }) } set filterRules(filterRules: FilterRule[]) { - //we're going to clone the filterRules object, since we don't - //want changes in the filter editor to propagate into here right away. - this.view.filter_rules = filterRules + this.activeListViewState.filterRules = filterRules this.reload() this.reduceSelectionToFilter() this.saveDocumentListView() } get filterRules(): FilterRule[] { - return this.view.filter_rules + return this.activeListViewState.filterRules } set sortField(field: string) { - this.view.sort_field = field - this.saveDocumentListView() + this.activeListViewState.sortField = field this.reload() + this.saveDocumentListView() } get sortField(): string { - return this.view.sort_field + return this.activeListViewState.sortField } set sortReverse(reverse: boolean) { - this.view.sort_reverse = reverse - this.saveDocumentListView() + this.activeListViewState.sortReverse = reverse this.reload() + this.saveDocumentListView() } get sortReverse(): boolean { - return this.view.sort_reverse + return this.activeListViewState.sortReverse + } + + get collectionSize(): number { + return this.activeListViewState.collectionSize + } + + get currentPage(): number { + return this.activeListViewState.currentPage + } + + set currentPage(page: number) { + this.activeListViewState.currentPage = page + this.reload() + this.saveDocumentListView() + } + + get documents(): PaperlessDocument[] { + return this.activeListViewState.documents + } + + get selected(): Set { + return this.activeListViewState.selected } setSort(field: string, reverse: boolean) { - this.view.sort_field = field - this.view.sort_reverse = reverse - this.saveDocumentListView() + this.activeListViewState.sortField = field + this.activeListViewState.sortReverse = reverse this.reload() + this.saveDocumentListView() } private saveDocumentListView() { - sessionStorage.setItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG, JSON.stringify(this.documentListView)) + if (this._activeSavedViewId == null) { + let savedState: ListViewState = { + collectionSize: this.activeListViewState.collectionSize, + currentPage: this.activeListViewState.currentPage, + filterRules: this.activeListViewState.filterRules, + sortField: this.activeListViewState.sortField, + sortReverse: this.activeListViewState.sortReverse + } + sessionStorage.setItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG, JSON.stringify(savedState)) + } } quickFilter(filterRules: FilterRule[]) { - this.savedView = null - this.view.filter_rules = filterRules + this._activeSavedViewId = null + this.activeListViewState.filterRules = filterRules + this.activeListViewState.currentPage = 1 this.reduceSelectionToFilter() this.saveDocumentListView() this.router.navigate(["documents"]) @@ -217,8 +252,6 @@ export class DocumentListViewService { } } - selected = new Set() - selectNone() { this.selected.clear() this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null @@ -227,13 +260,11 @@ export class DocumentListViewService { reduceSelectionToFilter() { if (this.selected.size > 0) { this.documentService.listAllFilteredIds(this.filterRules).subscribe(ids => { - let subset = new Set() - for (let id of ids) { - if (this.selected.has(id)) { - subset.add(id) + for (let id of this.selected) { + if (!ids.includes(id)) { + this.selected.delete(id) } } - this.selected = subset }) } } @@ -287,20 +318,21 @@ export class DocumentListViewService { } constructor(private documentService: DocumentService, private settings: SettingsService, private router: Router) { - let documentListViewConfigJson = sessionStorage.getItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG) + let documentListViewConfigJson = sessionStorage.getItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG) if (documentListViewConfigJson) { try { - this.documentListView = JSON.parse(documentListViewConfigJson) + let savedState: ListViewState = JSON.parse(documentListViewConfigJson) + // Remove null elements from the restored state + Object.keys(savedState).forEach(k => { + if (savedState[k] == null) { + delete savedState[k] + } + }) + //only use restored state attributes instead of defaults if they are not null + let newState = Object.assign(this.defaultListViewState(), savedState) + this.listViewStates.set(null, newState) } catch (e) { sessionStorage.removeItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG) - this.documentListView = null - } - } - if (!this.documentListView || this.documentListView.filter_rules == null || this.documentListView.sort_reverse == null || this.documentListView.sort_field == null) { - this.documentListView = { - filter_rules: [], - sort_reverse: true, - sort_field: 'created' } } } From b5a8106a6ab1da778e0f2a0bf09e7f0257224bce Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Thu, 18 Feb 2021 16:43:41 +0000 Subject: [PATCH 148/225] Translate /src-ui/messages.xlf in de translation completed for the source file '/src-ui/messages.xlf' on the 'de' language. --- src-ui/src/locale/messages.de.xlf | 78 +++++++++++++++++++------------ 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/src-ui/src/locale/messages.de.xlf b/src-ui/src/locale/messages.de.xlf index cc5e1d68a..aa57afdab 100644 --- a/src-ui/src/locale/messages.de.xlf +++ b/src-ui/src/locale/messages.de.xlf @@ -58,11 +58,11 @@ - View "" saved successfully. - Ansicht "" erfolgreich gespeichert. + View "" saved successfully. + Ansicht "" erfolgreich gespeichert. src/app/components/document-list/document-list.component.ts - 109 + 115 @@ -70,7 +70,7 @@ Ansicht "" erfolgreich erstellt. src/app/components/document-list/document-list.component.ts - 130 + 136 @@ -129,9 +129,9 @@ 72 - - Save "" - "" speichern + + Save "" + "" speichern src/app/components/document-list/document-list.component.html 71 @@ -585,14 +585,6 @@ 1 - - Filter - Filtern - - src/app/components/manage/logs/logs.component.html - 7 - - Saved view "" deleted. Gespeicherte Ansicht "" gelöscht. @@ -622,7 +614,15 @@ Benutze Datumsformat der Anzeigesprache src/app/components/manage/settings/settings.component.ts - 95 + 96 + + + + ISO 8601 + ISO 8601 + + src/app/components/manage/settings/settings.component.ts + 97 @@ -630,7 +630,7 @@ Fehler beim Speichern der Einstellungen auf dem Server: src/app/components/manage/settings/settings.component.ts - 111 + 114 @@ -1234,6 +1234,14 @@ 46 + + Title: + Titel: + + src/app/components/document-list/filter-editor/filter-editor.component.ts + 50 + + Filter tags Tags filtern @@ -1614,22 +1622,22 @@ 1 - - Documents in inbox: - Dokumente im Posteingang: - - src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html - 3 - - - - Total documents: - Anzahl Dokumente gesamt: + + Total documents: + Anzahl Dokumente gesamt: src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html 4 + + Documents in inbox: + Dokumente im Posteingang: + + src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html + 3 + + Processing: Verarbeite: @@ -1848,12 +1856,20 @@ 82 + + English (GB) + Englisch (UK) + + src/app/services/settings.service.ts + 83 + + German Deutsch src/app/services/settings.service.ts - 83 + 84 @@ -1861,7 +1877,7 @@ Niederländisch src/app/services/settings.service.ts - 84 + 85 @@ -1869,7 +1885,7 @@ Französisch src/app/services/settings.service.ts - 85 + 86 From efb49af7ac44285fe260d698652f1af817dd5ce2 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Thu, 18 Feb 2021 16:44:55 +0000 Subject: [PATCH 149/225] Translate /src-ui/messages.xlf in en_GB translation completed for the source file '/src-ui/messages.xlf' on the 'en_GB' language. --- src-ui/src/locale/messages.en_GB.xlf | 78 +++++++++++++++++----------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/src-ui/src/locale/messages.en_GB.xlf b/src-ui/src/locale/messages.en_GB.xlf index 49baa77ed..14ab56659 100644 --- a/src-ui/src/locale/messages.en_GB.xlf +++ b/src-ui/src/locale/messages.en_GB.xlf @@ -58,11 +58,11 @@ - View "" saved successfully. - View &quot;&quot; saved successfully. + View "" saved successfully. + View &quot;&quot; saved successfully. src/app/components/document-list/document-list.component.ts - 109 + 115 @@ -70,7 +70,7 @@ View &quot;&quot; created successfully. src/app/components/document-list/document-list.component.ts - 130 + 136 @@ -129,9 +129,9 @@ 72 - - Save "" - Save &quot;&quot; + + Save "" + Save &quot;&quot; src/app/components/document-list/document-list.component.html 71 @@ -585,14 +585,6 @@ 1 - - Filter - Filter - - src/app/components/manage/logs/logs.component.html - 7 - - Saved view "" deleted. Saved view &quot;&quot; deleted. @@ -622,7 +614,15 @@ Use date format of display language src/app/components/manage/settings/settings.component.ts - 95 + 96 + + + + ISO 8601 + ISO 8601 + + src/app/components/manage/settings/settings.component.ts + 97 @@ -630,7 +630,7 @@ Error while storing settings on server: src/app/components/manage/settings/settings.component.ts - 111 + 114 @@ -1234,6 +1234,14 @@ 46 + + Title: + Title: + + src/app/components/document-list/filter-editor/filter-editor.component.ts + 50 + + Filter tags Filter tags @@ -1614,22 +1622,22 @@ 1 - - Documents in inbox: - Documents in inbox: - - src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html - 3 - - - - Total documents: - Total documents: + + Total documents: + Total documents: src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html 4 + + Documents in inbox: + Documents in inbox: + + src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html + 3 + + Processing: Processing: @@ -1848,12 +1856,20 @@ 82 + + English (GB) + English (GB) + + src/app/services/settings.service.ts + 83 + + German German src/app/services/settings.service.ts - 83 + 84 @@ -1861,7 +1877,7 @@ Dutch src/app/services/settings.service.ts - 84 + 85 @@ -1869,7 +1885,7 @@ French src/app/services/settings.service.ts - 85 + 86 From 1140a878b47066107436e0996f8966a3bc258ee7 Mon Sep 17 00:00:00 2001 From: Jonas Winkler <17569239+jonaswinkler@users.noreply.github.com> Date: Fri, 19 Feb 2021 11:22:43 +0100 Subject: [PATCH 150/225] Update README.md --- README.md | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c90be0912..ea47248dc 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,16 @@ # Paperless-ng -[Paperless](https://github.com/the-paperless-project/paperless) is an application by Daniel Quinn and contributors that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents. +[Paperless (click me)](https://github.com/the-paperless-project/paperless) is an application by Daniel Quinn and contributors that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents. -Paperless-ng is a fork of the original project, adding a new interface and many other changes under the hood. For a detailed list of changes, have a look at the [change log](https://paperless-ng.readthedocs.io/en/latest/changelog.html) in the documentation. +Paperless-ng is a fork of the original project, adding a new interface and many other changes under the hood. These key points should help you decide whether Paperless-ng is something you would prefer over Paperless: + +* Interface: The new front end is the main interface for paperless-ng, the old interface still exists but most customizations (such as thumbnails for the document list) have been removed. +* Encryption: Paperless-ng does not support GnuPG anymore, since storing your data on encrypted file systems (that you optionally mount on demand) achieves about the same result. +* Resource usage: Paperless-ng does use a bit more resources than Paperless. Running the web server requires about 300MB of RAM, depending on the configuration. It still runs on Pi (many users do that), but it has been generally geared to better use the resources of more powerful systems. +* API changes: If you rely on the REST API of paperless, some of its functionality has been changed. + +For a detailed list of changes, have a look at the [change log](https://paperless-ng.readthedocs.io/en/latest/changelog.html) in the documentation. # How it Works @@ -32,8 +39,8 @@ Here's what you get: * Performs OCR on your documents, adds selectable text to image only documents and adds tags, correspondents and document types to your documents. * Supports PDF documents, images, plain text files, and Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents). * Office document support is optional and provided by Apache Tika (see [configuration](https://paperless-ng.readthedocs.io/en/latest/configuration.html#tika-settings)) -* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and can be configured freely. -* Single page application front end. Should be pretty snappy. Will be mobile friendly in the future. +* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely. +* Single page application front end. * Includes a dashboard that shows basic statistics and has document upload. * Filtering by tags, correspondents, types, and more. * Customizable views can be saved and displayed on the dashboard. @@ -44,14 +51,13 @@ Here's what you get: * Searching for similar documents ("More like this") * Email processing: Paperless adds documents from your email accounts. * Configure multiple accounts and filters for each account. - * When adding documents from mails, paperless can move these mails to a new folder, mark them as read, flag them or delete them. + * When adding documents from mails, paperless can move these mails to a new folder, mark them as read, flag them as important or delete them. * Machine learning powered document matching. * Paperless learns from your documents and will be able to automatically assign tags, correspondents and types to documents once you've stored a few documents in paperless. -* A task processor that processes documents in parallel and also tells you when something goes wrong. On modern multi core systems, consumption is blazing fast. +* Optimized for multi core systems: Paperless-ng consumes multiple documents in parallel. +* The integrated sanity checker makes sure that your document archive is in good health. -If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html). However, some parts of the UI have changed since I took these. - -For a complete list of changes from paperless, check out the [changelog](https://paperless-ng.readthedocs.io/en/latest/changelog.html) +If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html). # Getting started @@ -103,4 +109,4 @@ These projects also exist, but their status and compatibility with paperless-ng # Important Note -Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. Everything is stored in the clear without encryption by default (it needs to be searchable, so if someone has ideas on how to do that on encrypted data, I'm all ears). This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home. +Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. Everything is stored in the clear without encryption. This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home. From 44936dc5f0663d086dfc45d0d3caa8ad3c0f4e05 Mon Sep 17 00:00:00 2001 From: Jonas Winkler <17569239+jonaswinkler@users.noreply.github.com> Date: Fri, 19 Feb 2021 11:24:21 +0100 Subject: [PATCH 151/225] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea47248dc..745c3b8f9 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Paperless-ng is a fork of the original project, adding a new interface and many * Interface: The new front end is the main interface for paperless-ng, the old interface still exists but most customizations (such as thumbnails for the document list) have been removed. * Encryption: Paperless-ng does not support GnuPG anymore, since storing your data on encrypted file systems (that you optionally mount on demand) achieves about the same result. -* Resource usage: Paperless-ng does use a bit more resources than Paperless. Running the web server requires about 300MB of RAM, depending on the configuration. It still runs on Pi (many users do that), but it has been generally geared to better use the resources of more powerful systems. +* Resource usage: Paperless-ng does use a bit more resources than Paperless. Running the web server requires about 300MB of RAM or more, depending on the configuration. It still runs on Pi (many users do that), but it has been generally geared to better use the resources of more powerful systems. * API changes: If you rely on the REST API of paperless, some of its functionality has been changed. For a detailed list of changes, have a look at the [change log](https://paperless-ng.readthedocs.io/en/latest/changelog.html) in the documentation. From 950bb468278856afd51c18d18f7f4c8cbf6a331b Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Fri, 19 Feb 2021 11:31:14 +0100 Subject: [PATCH 152/225] version bump --- src-ui/src/environments/environment.prod.ts | 2 +- src/paperless/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ui/src/environments/environment.prod.ts b/src-ui/src/environments/environment.prod.ts index 6df8ec565..51beb3fba 100644 --- a/src-ui/src/environments/environment.prod.ts +++ b/src-ui/src/environments/environment.prod.ts @@ -2,7 +2,7 @@ export const environment = { production: true, apiBaseUrl: "/api/", appTitle: "Paperless-ng", - version: "1.1.4", + version: "1.1.5", webSocketHost: window.location.host, webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:") }; diff --git a/src/paperless/version.py b/src/paperless/version.py index f336d581e..b21fef930 100644 --- a/src/paperless/version.py +++ b/src/paperless/version.py @@ -1 +1 @@ -__version__ = (1, 1, 4) +__version__ = (1, 1, 5) From 4de4789605469dfbf01fd5c274e4cb1e8e900185 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Fri, 19 Feb 2021 11:34:51 +0100 Subject: [PATCH 153/225] this took way too much time --- resources/logo.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 resources/logo.txt diff --git a/resources/logo.txt b/resources/logo.txt new file mode 100644 index 000000000..75a1d2c75 --- /dev/null +++ b/resources/logo.txt @@ -0,0 +1,16 @@ + 9w + {@@N + Q@@@@H + G@@@@@@@\ + SilN@@@@@@@ + *Q *@@@@@@@@S /= = = = = = = = = = = = = = = = = =\ + *@ B@@@@@@@@N || || + N R$ A@@@@@@@@@@ || PAPERLESS-NG || +x@@ $U B@@@@@@@@@R || || +N@@N^ @ N@@@@@@@@@* \= = = = = = = = = = = = = = = = = =/ +|@@@u @ E@@@@@@@@l + Q@@@ \ Px@@@@@@P + 1@@S` @@@o' + z$ ; + v + / From 0ae9aecdef1cd0c40a10f83ca02e3afb3d8b37f2 Mon Sep 17 00:00:00 2001 From: Jonas Winkler <17569239+jonaswinkler@users.noreply.github.com> Date: Fri, 19 Feb 2021 11:51:59 +0100 Subject: [PATCH 154/225] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 745c3b8f9..83ef1c1ed 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Paperless-ng is a fork of the original project, adding a new interface and many * Interface: The new front end is the main interface for paperless-ng, the old interface still exists but most customizations (such as thumbnails for the document list) have been removed. * Encryption: Paperless-ng does not support GnuPG anymore, since storing your data on encrypted file systems (that you optionally mount on demand) achieves about the same result. -* Resource usage: Paperless-ng does use a bit more resources than Paperless. Running the web server requires about 300MB of RAM or more, depending on the configuration. It still runs on Pi (many users do that), but it has been generally geared to better use the resources of more powerful systems. +* Resource usage: Paperless-ng does use a bit more resources than Paperless. Running the web server requires about 300MB of RAM or more, depending on the configuration. While adding documents, it requires about 300MB additional RAM, depending on the document. It still runs on Pi (many users do that), but it has been generally geared to better use the resources of more powerful systems. * API changes: If you rely on the REST API of paperless, some of its functionality has been changed. For a detailed list of changes, have a look at the [change log](https://paperless-ng.readthedocs.io/en/latest/changelog.html) in the documentation. From 31f03ef1d301a6e23c276408e88d53aadf6886f2 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Sat, 20 Feb 2021 16:09:29 +0100 Subject: [PATCH 155/225] API support for downloading compressed archives of multiple documents --- src/documents/bulk_download.py | 60 +++++++++++++++++ src/documents/serialisers.py | 65 ++++++++++++------- src/documents/tests/test_api.py | 111 ++++++++++++++++++++++++++++++++ src/documents/views.py | 58 ++++++++++++++++- src/paperless/urls.py | 6 +- 5 files changed, 274 insertions(+), 26 deletions(-) create mode 100644 src/documents/bulk_download.py diff --git a/src/documents/bulk_download.py b/src/documents/bulk_download.py new file mode 100644 index 000000000..8c675b4b5 --- /dev/null +++ b/src/documents/bulk_download.py @@ -0,0 +1,60 @@ +from zipfile import ZipFile + +from documents.models import Document + + +class BulkArchiveStrategy: + + def __init__(self, zipf: ZipFile): + self.zipf = zipf + + def make_unique_filename(self, + doc: Document, + archive: bool = False, + folder: str = ""): + counter = 0 + while True: + filename = folder + doc.get_public_filename(archive, counter) + if filename in self.zipf.namelist(): + counter += 1 + else: + return filename + + def add_document(self, doc: Document): + raise NotImplementedError() # pragma: no cover + + +class OriginalsOnlyStrategy(BulkArchiveStrategy): + + def add_document(self, doc: Document): + self.zipf.write(doc.source_path, self.make_unique_filename(doc)) + + +class ArchiveOnlyStrategy(BulkArchiveStrategy): + + def __init__(self, zipf): + super(ArchiveOnlyStrategy, self).__init__(zipf) + + def add_document(self, doc: Document): + if doc.has_archive_version: + self.zipf.write(doc.archive_path, + self.make_unique_filename(doc, archive=True)) + else: + self.zipf.write(doc.source_path, + self.make_unique_filename(doc)) + + +class OriginalAndArchiveStrategy(BulkArchiveStrategy): + + def add_document(self, doc: Document): + if doc.has_archive_version: + self.zipf.write( + doc.archive_path, self.make_unique_filename( + doc, archive=True, folder="archive/" + ) + ) + + self.zipf.write( + doc.source_path, + self.make_unique_filename(doc, folder="originals/") + ) diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index b01f82805..66736bdbf 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -192,14 +192,34 @@ class SavedViewSerializer(serializers.ModelSerializer): return saved_view -class BulkEditSerializer(serializers.Serializer): +class DocumentListSerializer(serializers.Serializer): documents = serializers.ListField( - child=serializers.IntegerField(), + required=True, label="Documents", - write_only=True + write_only=True, + child=serializers.IntegerField() ) + def _validate_document_id_list(self, documents, name="documents"): + if not type(documents) == list: + raise serializers.ValidationError(f"{name} must be a list") + if not all([type(i) == int for i in documents]): + raise serializers.ValidationError( + f"{name} must be a list of integers") + count = Document.objects.filter(id__in=documents).count() + if not count == len(documents): + raise serializers.ValidationError( + f"Some documents in {name} don't exist or were " + f"specified twice.") + + def validate_documents(self, documents): + self._validate_document_id_list(documents) + return documents + + +class BulkEditSerializer(DocumentListSerializer): + method = serializers.ChoiceField( choices=[ "set_correspondent", @@ -215,18 +235,6 @@ class BulkEditSerializer(serializers.Serializer): parameters = serializers.DictField(allow_empty=True) - def _validate_document_id_list(self, documents, name="documents"): - if not type(documents) == list: - raise serializers.ValidationError(f"{name} must be a list") - if not all([type(i) == int for i in documents]): - raise serializers.ValidationError( - f"{name} must be a list of integers") - count = Document.objects.filter(id__in=documents).count() - if not count == len(documents): - raise serializers.ValidationError( - f"Some documents in {name} don't exist or were " - f"specified twice.") - def _validate_tag_id_list(self, tags, name="tags"): if not type(tags) == list: raise serializers.ValidationError(f"{name} must be a list") @@ -238,10 +246,6 @@ class BulkEditSerializer(serializers.Serializer): raise serializers.ValidationError( f"Some tags in {name} don't exist or were specified twice.") - def validate_documents(self, documents): - self._validate_document_id_list(documents) - return documents - def validate_method(self, method): if method == "set_correspondent": return bulk_edit.set_correspondent @@ -392,9 +396,24 @@ class PostDocumentSerializer(serializers.Serializer): return None -class SelectionDataSerializer(serializers.Serializer): +class BulkDownloadSerializer(DocumentListSerializer): - documents = serializers.ListField( - required=True, - child=serializers.IntegerField() + content = serializers.ChoiceField( + choices=["archive", "originals", "both"], + default="archive" ) + + compression = serializers.ChoiceField( + choices=["none", "deflated", "bzip2", "lzma"], + default="none" + ) + + def validate_compression(self, compression): + import zipfile + + return { + "none": zipfile.ZIP_STORED, + "deflated": zipfile.ZIP_DEFLATED, + "bzip2": zipfile.ZIP_BZIP2, + "lzma": zipfile.ZIP_LZMA + }[compression] diff --git a/src/documents/tests/test_api.py b/src/documents/tests/test_api.py index 01e7210a5..7486154e1 100644 --- a/src/documents/tests/test_api.py +++ b/src/documents/tests/test_api.py @@ -1,7 +1,10 @@ +import datetime +import io import json import os import shutil import tempfile +import zipfile from unittest import mock from django.conf import settings @@ -1123,6 +1126,113 @@ class TestBulkEdit(DirectoriesMixin, APITestCase): self.assertCountEqual(response.data['selected_document_types'], [{"id": self.c1.id, "document_count": 1}, {"id": self.c2.id, "document_count": 0}]) +class TestBulkDownload(DirectoriesMixin, APITestCase): + + def setUp(self): + super(TestBulkDownload, self).setUp() + + user = User.objects.create_superuser(username="temp_admin") + self.client.force_login(user=user) + + self.doc1 = Document.objects.create(title="unrelated", checksum="A") + self.doc2 = Document.objects.create(title="document A", filename="docA.pdf", mime_type="application/pdf", checksum="B", created=datetime.datetime(2021, 1, 1)) + self.doc2b = Document.objects.create(title="document A", filename="docA2.pdf", mime_type="application/pdf", checksum="D", created=datetime.datetime(2021, 1, 1)) + self.doc3 = Document.objects.create(title="document B", filename="docB.jpg", mime_type="image/jpeg", checksum="C", created=datetime.datetime(2020, 3, 21), archive_filename="docB.pdf", archive_checksum="D") + + shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), self.doc2.source_path) + shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "simple.png"), self.doc2b.source_path) + shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "simple.jpg"), self.doc3.source_path) + shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "test_with_bom.pdf"), self.doc3.archive_path) + + def test_download_originals(self): + response = self.client.post("/api/documents/bulk_download/", json.dumps({ + "documents": [self.doc2.id, self.doc3.id], + "content": "originals" + }), content_type='application/json') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response['Content-Type'], 'application/zip') + + with zipfile.ZipFile(io.BytesIO(response.content)) as zipf: + self.assertEqual(len(zipf.filelist), 2) + self.assertIn("2021-01-01 document A.pdf", zipf.namelist()) + self.assertIn("2020-03-21 document B.jpg", zipf.namelist()) + + with self.doc2.source_file as f: + self.assertEqual(f.read(), zipf.read("2021-01-01 document A.pdf")) + + with self.doc3.source_file as f: + self.assertEqual(f.read(), zipf.read("2020-03-21 document B.jpg")) + + def test_download_default(self): + response = self.client.post("/api/documents/bulk_download/", json.dumps({ + "documents": [self.doc2.id, self.doc3.id] + }), content_type='application/json') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response['Content-Type'], 'application/zip') + + with zipfile.ZipFile(io.BytesIO(response.content)) as zipf: + self.assertEqual(len(zipf.filelist), 2) + self.assertIn("2021-01-01 document A.pdf", zipf.namelist()) + self.assertIn("2020-03-21 document B.pdf", zipf.namelist()) + + with self.doc2.source_file as f: + self.assertEqual(f.read(), zipf.read("2021-01-01 document A.pdf")) + + with self.doc3.archive_file as f: + self.assertEqual(f.read(), zipf.read("2020-03-21 document B.pdf")) + + def test_download_both(self): + response = self.client.post("/api/documents/bulk_download/", json.dumps({ + "documents": [self.doc2.id, self.doc3.id], + "content": "both" + }), content_type='application/json') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response['Content-Type'], 'application/zip') + + with zipfile.ZipFile(io.BytesIO(response.content)) as zipf: + self.assertEqual(len(zipf.filelist), 3) + self.assertIn("originals/2021-01-01 document A.pdf", zipf.namelist()) + self.assertIn("archive/2020-03-21 document B.pdf", zipf.namelist()) + self.assertIn("originals/2020-03-21 document B.jpg", zipf.namelist()) + + with self.doc2.source_file as f: + self.assertEqual(f.read(), zipf.read("originals/2021-01-01 document A.pdf")) + + with self.doc3.archive_file as f: + self.assertEqual(f.read(), zipf.read("archive/2020-03-21 document B.pdf")) + + with self.doc3.source_file as f: + self.assertEqual(f.read(), zipf.read("originals/2020-03-21 document B.jpg")) + + def test_filename_clashes(self): + response = self.client.post("/api/documents/bulk_download/", json.dumps({ + "documents": [self.doc2.id, self.doc2b.id] + }), content_type='application/json') + + self.assertEqual(response.status_code, 200) + self.assertEqual(response['Content-Type'], 'application/zip') + + with zipfile.ZipFile(io.BytesIO(response.content)) as zipf: + self.assertEqual(len(zipf.filelist), 2) + + self.assertIn("2021-01-01 document A.pdf", zipf.namelist()) + self.assertIn("2021-01-01 document A_01.pdf", zipf.namelist()) + + with self.doc2.source_file as f: + self.assertEqual(f.read(), zipf.read("2021-01-01 document A.pdf")) + + with self.doc2b.source_file as f: + self.assertEqual(f.read(), zipf.read("2021-01-01 document A_01.pdf")) + + def test_compression(self): + response = self.client.post("/api/documents/bulk_download/", json.dumps({ + "documents": [self.doc2.id, self.doc2b.id], + "compression": "lzma" + }), content_type='application/json') + class TestApiAuth(APITestCase): def test_auth_required(self): @@ -1146,4 +1256,5 @@ class TestApiAuth(APITestCase): self.assertEqual(self.client.get("/api/search/").status_code, 401) self.assertEqual(self.client.get("/api/search/auto_complete/").status_code, 401) self.assertEqual(self.client.get("/api/documents/bulk_edit/").status_code, 401) + self.assertEqual(self.client.get("/api/documents/bulk_download/").status_code, 401) self.assertEqual(self.client.get("/api/documents/selection_data/").status_code, 401) diff --git a/src/documents/views.py b/src/documents/views.py index 68d6e3c77..d886324ae 100755 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -2,6 +2,7 @@ import logging import os import tempfile import uuid +import zipfile from datetime import datetime from time import mktime @@ -34,6 +35,8 @@ from rest_framework.viewsets import ( from paperless.db import GnuPG from paperless.views import StandardPagination +from .bulk_download import OriginalAndArchiveStrategy, OriginalsOnlyStrategy, \ + ArchiveOnlyStrategy from .classifier import load_classifier from .filters import ( CorrespondentFilterSet, @@ -51,7 +54,9 @@ from .serialisers import ( DocumentTypeSerializer, PostDocumentSerializer, SavedViewSerializer, - BulkEditSerializer, SelectionDataSerializer + BulkEditSerializer, + DocumentListSerializer, + BulkDownloadSerializer ) @@ -444,7 +449,7 @@ class PostDocumentView(APIView): class SelectionDataView(APIView): permission_classes = (IsAuthenticated,) - serializer_class = SelectionDataSerializer + serializer_class = DocumentListSerializer parser_classes = (parsers.MultiPartParser, parsers.JSONParser) def get_serializer_context(self): @@ -606,3 +611,52 @@ class StatisticsView(APIView): 'documents_total': documents_total, 'documents_inbox': documents_inbox, }) + + +class BulkDownloadView(APIView): + + permission_classes = (IsAuthenticated,) + serializer_class = BulkDownloadSerializer + parser_classes = (parsers.JSONParser,) + + def get_serializer_context(self): + return { + 'request': self.request, + 'format': self.format_kwarg, + 'view': self + } + + def get_serializer(self, *args, **kwargs): + kwargs['context'] = self.get_serializer_context() + return self.serializer_class(*args, **kwargs) + + def post(self, request, format=None): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + + ids = serializer.validated_data.get('documents') + compression = serializer.validated_data.get('compression') + content = serializer.validated_data.get('content') + + os.makedirs(settings.SCRATCH_DIR, exist_ok=True) + temp = tempfile.NamedTemporaryFile(dir=settings.SCRATCH_DIR, suffix="-compressed-archive", delete=False) + + if content == 'both': + strategy_class = OriginalAndArchiveStrategy + elif content == 'originals': + strategy_class = OriginalsOnlyStrategy + else: + strategy_class = ArchiveOnlyStrategy + + with zipfile.ZipFile(temp.name, "w", compression) as zipf: + strategy = strategy_class(zipf) + for id in ids: + doc = Document.objects.get(id=id) + strategy.add_document(doc) + + with open(temp.name, "rb") as f: + response = HttpResponse(f, content_type="application/zip") + response["Content-Disposition"] = '{}; filename="{}"'.format( + "attachment", "documents.zip") + + return response diff --git a/src/paperless/urls.py b/src/paperless/urls.py index 40f4bd754..4e0b8f191 100755 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -23,7 +23,8 @@ from documents.views import ( PostDocumentView, SavedViewViewSet, BulkEditView, - SelectionDataView + SelectionDataView, + BulkDownloadView ) from paperless.views import FaviconView @@ -63,6 +64,9 @@ urlpatterns = [ re_path(r"^documents/selection_data/", SelectionDataView.as_view(), name="selection_data"), + re_path(r"^documents/bulk_download/", BulkDownloadView.as_view(), + name="bulk_download"), + path('token/', views.obtain_auth_token) ] + api_router.urls)), From 203bc162cd8807968f964bb35f75747397b8856c Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Sat, 20 Feb 2021 16:10:50 +0100 Subject: [PATCH 156/225] front end support for downloading multiple documents --- src-ui/messages.xlf | 49 +++++++++++-------- src-ui/package-lock.json | 5 ++ src-ui/package.json | 1 + .../bulk-editor/bulk-editor.component.html | 14 ++++++ .../bulk-editor/bulk-editor.component.ts | 7 +++ .../src/app/services/rest/document.service.ts | 4 ++ 6 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 731fdff7f..95e0fad15 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -1226,21 +1226,21 @@ Error executing bulk operation: src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 73 + 74 "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 112 + 113 "" and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 114 + 115 This is for messages like 'modify "tag1" and "tag2"' @@ -1248,7 +1248,7 @@ , src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 116 + 117 this is used to separate enumerations and should probably be a comma and a whitespace in most languages @@ -1256,7 +1256,7 @@ and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 117 + 118 this is for messages like 'modify "tag1", "tag2" and "tag3"' @@ -1264,112 +1264,112 @@ Confirm tags assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 126 + 127 This operation will add the tag "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 129 + 130 This operation will add the tags to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 131 + 132 This operation will remove the tag "" from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 134 + 135 This operation will remove the tags from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 136 + 137 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 - 138 + 139 Confirm correspondent assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 158 + 159 This operation will assign the correspondent "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 160 + 161 This operation will remove the correspondent from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 162 + 163 Confirm document type assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 181 + 182 This operation will assign the document type "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 183 + 184 This operation will remove the document type from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 185 + 186 Delete confirm src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 200 + 201 This operation will permanently delete selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 201 + 202 This operation cannot be undone. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 202 + 203 Delete document(s) src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 204 + 205 @@ -1393,6 +1393,13 @@ 27 + + Download originals + + src/app/components/document-list/bulk-editor/bulk-editor.component.html + 68 + + Suggestions: diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json index c3827f80c..148bc72eb 100644 --- a/src-ui/package-lock.json +++ b/src-ui/package-lock.json @@ -5545,6 +5545,11 @@ "schema-utils": "^2.6.5" } }, + "file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", diff --git a/src-ui/package.json b/src-ui/package.json index 14d828483..d6082c6b9 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -23,6 +23,7 @@ "@ng-bootstrap/ng-bootstrap": "^8.0.0", "@ng-select/ng-select": "^5.0.9", "bootstrap": "^4.5.0", + "file-saver": "^2.0.5", "ng-bootstrap": "^1.6.3", "ng2-pdf-viewer": "^6.3.2", "ngx-cookie-service": "^10.1.1", 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 053126445..98716e2f9 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 @@ -56,6 +56,20 @@
+
+ +
+ + +
+
+
- From 99a18516b283737cf81dbb28d69c07fca1694766 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Mon, 22 Feb 2021 00:17:16 +0100 Subject: [PATCH 183/225] tests --- src/paperless_tesseract/tests/test_parser.py | 41 ++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/paperless_tesseract/tests/test_parser.py b/src/paperless_tesseract/tests/test_parser.py index b64abb382..c389d446f 100644 --- a/src/paperless_tesseract/tests/test_parser.py +++ b/src/paperless_tesseract/tests/test_parser.py @@ -300,6 +300,47 @@ class TestParser(DirectoriesMixin, TestCase): "If you read this, it’s your own fault. Also check your screen orientation." ]) + def test_ocrmypdf_parameters(self): + parser = RasterisedDocumentParser(None) + params = parser.construct_ocrmypdf_parameters(input_file="input.pdf", output_file="output.pdf", + sidecar_file="sidecar.txt", mime_type="application/pdf", + safe_fallback=False) + + self.assertEqual(params['input_file'], "input.pdf") + self.assertEqual(params['output_file'], "output.pdf") + self.assertEqual(params['sidecar'], "sidecar.txt") + + with override_settings(OCR_CLEAN="none"): + params = parser.construct_ocrmypdf_parameters("", "", "", "") + self.assertNotIn("clean", params) + self.assertNotIn("clean_final", params) + + with override_settings(OCR_CLEAN="clean"): + params = parser.construct_ocrmypdf_parameters("", "", "", "") + self.assertTrue(params['clean']) + self.assertNotIn("clean_final", params) + + with override_settings(OCR_CLEAN="clean-final", OCR_MODE="skip"): + params = parser.construct_ocrmypdf_parameters("", "", "", "") + self.assertTrue(params['clean_final']) + self.assertNotIn("clean", params) + + with override_settings(OCR_CLEAN="clean-final", OCR_MODE="redo"): + params = parser.construct_ocrmypdf_parameters("", "", "", "") + self.assertTrue(params['clean']) + self.assertNotIn("clean_final", params) + + with override_settings(OCR_DESKEW=True, OCR_MODE="skip"): + params = parser.construct_ocrmypdf_parameters("", "", "", "") + self.assertTrue(params['deskew']) + + with override_settings(OCR_DESKEW=True, OCR_MODE="redo"): + params = parser.construct_ocrmypdf_parameters("", "", "", "") + self.assertNotIn('deskew', params) + + with override_settings(OCR_DESKEW=False, OCR_MODE="skip"): + params = parser.construct_ocrmypdf_parameters("", "", "", "") + self.assertNotIn('deskew', params) class TestParserFileTypes(DirectoriesMixin, TestCase): From d64818b46cc29e08da16a84fd944bb323e498c58 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Mon, 22 Feb 2021 11:11:04 +0100 Subject: [PATCH 184/225] fixes #591 --- .../management/commands/document_archiver.py | 12 +++++++++--- src/documents/tests/test_management.py | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/documents/management/commands/document_archiver.py b/src/documents/management/commands/document_archiver.py index 0d3a5e82d..6a6056ed7 100644 --- a/src/documents/management/commands/document_archiver.py +++ b/src/documents/management/commands/document_archiver.py @@ -31,6 +31,11 @@ def handle_document(document_id): parser_class = get_parser_class_for_mime_type(mime_type) + if not parser_class: + logger.error(f"No parser found for mime type {mime_type}, cannot " + f"archive document {document} (ID: {document_id})") + return + parser = parser_class(logging_group=uuid.uuid4()) try: @@ -66,11 +71,12 @@ def handle_document(document_id): document.archive_path) shutil.move(thumbnail, document.thumbnail_path) - with AsyncWriter(index.open_index()) as writer: - index.update_document(writer, document) + with index.open_index_writer() as writer: + index.update_document(writer, document) except Exception as e: - logger.exception(f"Error while parsing document {document}") + logger.exception(f"Error while parsing document {document} " + f"(ID: {document_id})") finally: parser.cleanup() diff --git a/src/documents/tests/test_management.py b/src/documents/tests/test_management.py index 40eaaf277..f7beb8907 100644 --- a/src/documents/tests/test_management.py +++ b/src/documents/tests/test_management.py @@ -49,6 +49,21 @@ class TestArchiver(DirectoriesMixin, TestCase): self.assertTrue(filecmp.cmp(sample_file, doc.source_path)) self.assertEqual(doc.archive_filename, "none/A.pdf") + def test_unknown_mime_type(self): + doc = self.make_models() + doc.mime_type = "sdgfh" + doc.save() + shutil.copy(sample_file, doc.source_path) + + handle_document(doc.pk) + + doc = Document.objects.get(id=doc.id) + + self.assertIsNotNone(doc.checksum) + self.assertIsNone(doc.archive_checksum) + self.assertIsNone(doc.archive_filename) + self.assertTrue(os.path.isfile(doc.source_path)) + @override_settings(PAPERLESS_FILENAME_FORMAT="{title}") def test_naming_priorities(self): doc1 = Document.objects.create(checksum="A", title="document", content="first document", mime_type="application/pdf", filename="document.pdf") From aa3d91a338a66b39774acdfdc092be5f1672ff61 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Mon, 22 Feb 2021 11:38:16 +0100 Subject: [PATCH 185/225] associate error messages with documents --- .../management/commands/document_archiver.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/documents/management/commands/document_archiver.py b/src/documents/management/commands/document_archiver.py index 6a6056ed7..497e8fe7e 100644 --- a/src/documents/management/commands/document_archiver.py +++ b/src/documents/management/commands/document_archiver.py @@ -4,7 +4,9 @@ import multiprocessing import logging import os import shutil +import sys import uuid +from io import TextIOBase import tqdm from django import db @@ -12,7 +14,6 @@ from django.conf import settings from django.core.management.base import BaseCommand from django.db import transaction from filelock import FileLock -from whoosh.writing import AsyncWriter from documents.models import Document from ... import index @@ -24,11 +25,28 @@ from ...parsers import get_parser_class_for_mime_type logger = logging.getLogger("paperless.management.archiver") +class LoggerWriter(TextIOBase): + + def __init__(self, doc: Document): + self.doc = doc + + def write(self, message): + # if statement reduces the amount of newlines that are + # printed to the logger + if message != '\n': + logger.error( + f"Document {self.doc} (ID: {self.doc.pk}): {message}" + ) + + def handle_document(document_id): document = Document.objects.get(id=document_id) mime_type = document.mime_type + # redirect errors to the log and associate them with the current document + sys.stderr = LoggerWriter(document) + parser_class = get_parser_class_for_mime_type(mime_type) if not parser_class: From 6d934da5dd3e14d6cd6120574790cfbf75bdeb5a Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Mon, 22 Feb 2021 11:52:54 +0100 Subject: [PATCH 186/225] Revert "associate error messages with documents" This reverts commit aa3d91a3 --- .../management/commands/document_archiver.py | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/documents/management/commands/document_archiver.py b/src/documents/management/commands/document_archiver.py index 497e8fe7e..6a6056ed7 100644 --- a/src/documents/management/commands/document_archiver.py +++ b/src/documents/management/commands/document_archiver.py @@ -4,9 +4,7 @@ import multiprocessing import logging import os import shutil -import sys import uuid -from io import TextIOBase import tqdm from django import db @@ -14,6 +12,7 @@ from django.conf import settings from django.core.management.base import BaseCommand from django.db import transaction from filelock import FileLock +from whoosh.writing import AsyncWriter from documents.models import Document from ... import index @@ -25,28 +24,11 @@ from ...parsers import get_parser_class_for_mime_type logger = logging.getLogger("paperless.management.archiver") -class LoggerWriter(TextIOBase): - - def __init__(self, doc: Document): - self.doc = doc - - def write(self, message): - # if statement reduces the amount of newlines that are - # printed to the logger - if message != '\n': - logger.error( - f"Document {self.doc} (ID: {self.doc.pk}): {message}" - ) - - def handle_document(document_id): document = Document.objects.get(id=document_id) mime_type = document.mime_type - # redirect errors to the log and associate them with the current document - sys.stderr = LoggerWriter(document) - parser_class = get_parser_class_for_mime_type(mime_type) if not parser_class: From 3b553f645523efdbfdf3ef879a7d3ad6bc64856e Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Mon, 22 Feb 2021 11:53:13 +0100 Subject: [PATCH 187/225] changelog --- docs/changelog.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index abe667400..b23ca5137 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -25,6 +25,8 @@ paperless-ng 1.2.0 with polling from 1 to 5 seconds. This will decrease the likelihood of paperless consuming partially written files. +* Fixed a crash of the document archiver management command when trying to process documents with unknown mime types. + * Paperless no longer depends on ``libpoppler-cpp-dev``. .. note:: From 127d30918d5dcb04f7ac9877f893a2a26df0775c Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Mon, 22 Feb 2021 12:03:07 +0100 Subject: [PATCH 188/225] lets hope this works! --- .github/workflows/ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87b527cad..d816687ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,6 +112,13 @@ jobs: uses: actions/setup-node@v2 with: node-version: '15' + - + name: Configure version on dev branches + if: startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' + run: | + git_hash=$(git rev-parse --short "$GITHUB_SHA") + git_branch=${GITHUB_REF#refs/heads/} + sed -i -E 's/version: "(.*)"/version: "$git_branch $git_hash"/g' src-ui/src/environments/environment.prod.ts - name: Build frontend run: ./compile-frontend.sh From 8da85d360955857d969df0afbc29b0a21d360a37 Mon Sep 17 00:00:00 2001 From: Jonas Winkler <17569239+jonaswinkler@users.noreply.github.com> Date: Mon, 22 Feb 2021 13:25:21 +0100 Subject: [PATCH 189/225] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d816687ca..dbb718cc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,7 +118,7 @@ jobs: run: | git_hash=$(git rev-parse --short "$GITHUB_SHA") git_branch=${GITHUB_REF#refs/heads/} - sed -i -E 's/version: "(.*)"/version: "$git_branch $git_hash"/g' src-ui/src/environments/environment.prod.ts + sed -i -E "s/version: \"(.*)\"/version: \"${git_branch} ${git_hash}\"/g" src-ui/src/environments/environment.prod.ts - name: Build frontend run: ./compile-frontend.sh From 8797a58efc76fa3da7dc708fdd4899034d9abbf5 Mon Sep 17 00:00:00 2001 From: Joel Nordell Date: Mon, 22 Feb 2021 22:20:41 -0600 Subject: [PATCH 190/225] Add apple-touch-icon for iOS devices "Add to Home Screen" --- src-ui/angular.json | 2 ++ src-ui/src/apple-touch-icon.png | Bin 0 -> 6272 bytes src-ui/src/index.html | 1 + 3 files changed, 3 insertions(+) create mode 100644 src-ui/src/apple-touch-icon.png diff --git a/src-ui/angular.json b/src-ui/angular.json index a26dd8713..edf428fc7 100644 --- a/src-ui/angular.json +++ b/src-ui/angular.json @@ -36,6 +36,7 @@ "aot": true, "assets": [ "src/favicon.ico", + "src/apple-touch-icon.png", "src/assets", "src/manifest.webmanifest", { "glob": "pdf.worker.min.js", @@ -112,6 +113,7 @@ "karmaConfig": "karma.conf.js", "assets": [ "src/favicon.ico", + "src/apple-touch-icon.png", "src/assets", "src/manifest.webmanifest" ], diff --git a/src-ui/src/apple-touch-icon.png b/src-ui/src/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..46388a873d15b42999b0a173c8d33a224b80ca51 GIT binary patch literal 6272 zcmb_h^;cBU7QQe#ghQh;lz@tWbW3+jch^uuNew-SfFPw35&{AO0y2OINT-y5!cY=J zNq494uJ4!k54^k9y?4!7_ndY1*?WKc`}V|WYbuiv(-H#!K%%OmpbNfR|J{U8@ToiP zN&voYc+08k6M|PTp?x&?o5)ke#2Wx^n*F=+p1*!64-Ue86pelKJRE#bHeLt-g+g&V zyCc2rY&;R%9$t=_+Y+<@aQCmOf~>y(`>iZT8R~J`sM#5WiFE-7i!gbRtoF!+5UWck zV^apreVQQsyouX!T+oWxhe~lm$DN-#Cv3^aob&}JDijp*Ke0t`e%bu{+||6c0^#78 zdX{aqnJ(ann+~#E>1UsPw7<4i1eGPE<{QT+3|H>~B0exf$it&xGK94J0JGEod7$Ez z0_VJGxK0mn19^IJn(L-Qhe6Op!elDCJ~7_c{!z|4&L1ZSWirlfL_Ill8X__HVK8s{ z>fiT|6^`S=Mr2>PER}k3eP^==!k>kg-pEC5WCLLwOZfhOnKhtHG>R;#)`N4uGc6U zYw=!RjKMB<_j4936NEgDxAa=@26SX7XrR^}PFKpuzz^66&19lvTWUgJmM2Fn5c2QI z(9w0fwhGaF-(dCgNtO7s*LZ}ndk4kQLW@T2y+gfDk~~IQ(TbOwE|>a>yNp^!TCVTE z+$oq=zhh#=lG-}@eH2>K{}LvX7T++LX$! zB!q$+Qi+t?oa|pn2{1Rwk6$Pgk-5g=tUc?oH16E)oNQkh10m$B^NS|5Q!O|rofQJ@ zKQH->e$j=oQKIijyS_4wRYP2IxyvuJ*=dr$0N2hRDW{D)qC(54itv<9K3gcpb0Sor z`RJXLaJshf)DRU4c)s1o0*6p0AMzLl8k{d1>j}Jj0zXydf$c|zLNHiNlRZ=Khj!xo zIE^GA*@d6OWSl-(=F%F*na)}%qhjmo5Ornh zJP){z0$l4}Cuw%%G0CJCmtrgdoUAU@nw(#TPv!w-?hZlD!bYf`cDsp)AoI9HkAG|R z4bT^x^wK%6(xe6N46F-+K`%SZKm6ZGVOcD9V)lL$K2wK3TP;_b2vVYyiZHo@gXU{rf?b=$j3sVtd>%k}_%a&Ffd z#-gqX-)&UC$(0%2KWcLWGcW?y>J9oq$?slNxfCN_ap%?5PT=V#g~tgVo;_fxSFT2$ z8fPBXhk$%bwdE7RxMifJw7jp(f{_W~#Suw_4rR$z&!zJL)dU%L3Mw0It{D0L=w9emcX^bG~R zDR=;RY-)AyIvaEO9Cqdd4mYiW59~M-%q61Y^~UlN&|In1rb?hUEq?;w{sxKMMi{-L zf{})&vWRTT$COkRb43r?)|MkJs%koByAu^tQlIUOA#mIHT%e^C3A)M}TjIT&o)3(6 z3YlzGAH3Z$SFGMQsC?+c;F0tz>-+*XPWcM_5-(Khb|t@8v5ARca)|`zG-Kj-@l?6u zK6#buzzNX+5|87{jYW+<6U;+uB6w>Tm;t1>THfD6)vdwx+zV&gD-?Kj7@|z_Gjt-6sm+>v%Qf(-|Qub3mMT&G`(x(R2FJp>A z<%>QgR*RvzT4*PDovUYjLIF{)J<)yr0P`QbM1hbBnH5%sN}oUYRitH|*I($XM7m_l zu6Pnc!Qhyn{hMzhpcHI9`x^po1I`b0__z;K{Ae+~K|qCiRx;|+KTZAu(O~G5auJx) zw!C`8wq*I1B>nT7(*EzyrO_*RB@6IG_A)7Viev%K;Aa8!V> z{g=MnTgrRY`x`FTkGRSqB%*g$*Ki%g%sj;SMrqz=QBtJv2RB$0#NUHK2D zp+djvRN4M$UdDv6CiPW>E^s6Z*=3`*Ya)%`d{ZlG^wqxAe{M_rV%=5C<=m}d;_8yy zZx0&1Dn)Pmh6Z%JV8HtUVQfeKz^kFEy?!8cokiPNlry9#;*Vh=X}IQzXqX z{tn3;SU(tMU0J-npDAJ|T*G_O#Uim?hToU~GR!Mbi^ znm6z}G?;+VQEJ{aN^y4*&?R7(x~@nxGyy?abcU7})lMDQ6QcZBHm>iB0Mc#4!(zZ+ei-ae6BSK?2;=>jP{QYHfr^1wb-! z>YplHygj3Q!u!Og1c&r~*_k#?I{U=;2$o=V^#TbuY%_xd8@G`B(TqB2CEGrY9B6-j z8kiz$Y=(6R0rQ5Y**_U~dq~PHaPdAP#=%T&dhEX3ij3}j$Q>w^sE zToP*j6@>_ECv;NMucsCQRCRskl5P?CI`L2q^~JcEN9gbA$=>EatZ!J-eo(X)NZLr< zJ8av;!U0qlxWRJ~a;u4ycmC7Rg`ohUq< zRa4{+mF(?rwUMNF-LlC~qf1cy`{$qgC-;hMrcaXUQrkcy!fJ!qyCzHM$fE<~2dusp zYuB$vyT%luu`xP-wk%&&RS|0>74>zzi*1o}IKG7`s36{fG5PXJe)!D(kGpq>k@0UZ z>d8@VOQW=lTvdwj8h>Ds><2Ym9Kur!ac%#+GN!JEKG)tr>CeVE8PrDho)~d1ki1UH zd?Ec-%O$0*$NbmNxEK4u4nNaFj86|JF^Gv9jI=VgzI5o>hNxR2JTBFBwmyx$benze z_$_z9Lggdep`%F ze5j|U2;2CYeVAk8CM{sa_S64-pS^t%db?rN(r$MUD8F4l6*HjtfO7r^MxOkR2qBMU z(@64oDP@H7-$Lqu76sSKM)&gmId_>@{(ZHX-&V;nh>)=@(Ton7tJ%cTd;PUbBJJl?3-kMD&L z=Nn9!nWGCqi_-!jfQ+>agOBqtg-&VFqT#vQ)4GOf5)hMVG>qLi-}pKh7LHw_wU_EY z8Q0_6Ww<@$0V^4zCnjQ&=W37X< ztfa8#Gr|!w0==$ZR#yrFfAZcU!oiuAayH^>?$T%z?2Nib$lpy^wdYB{bEg2sc6eGh zA8)l2`cb5GCIX%->@b*m^VAPZk3o2nUc)?hXcwP25HzVuP&5Tb8NN0ZxogyvYoIZ6 zwhBV6%4lGJ^jGG}%oQ8lv7-5pxtomwt;p}l=}`ZdL&D;ME-#63OV3t@EFN4IX1%F& z{IM;j%o`sPEv|NLru+|FFv87XIaVmkPPi%k-J2^?1oSPaYLcs=pr+svi@*a?FTdcI zds#VSViau^j0YPFz{oITM2x1`XO~1#2=a&^K!qJ`LVVLNdqmm{CD{OpLOT^s;ZILwMoYX~$-wwVVvUd2kE zA2%}rJ%dA1t;HhYeew zdOlY(wqv$dySH+5D^6luUl;3Pr*P@?m3D@-;VoqV-;}=j{H$&vMHgE7CQ0c_UP>9G z?FY(+nl*y~mLZQ@rxLCrUb}4G3s$Z7B7S^w85ZJ5rdw1?;dcN9i19@fIpGk4=DzhB zuM}d@AMdOCllK;iaUStjZz+9ip7KV5nrH03bnez^!m0n{qtmVa(Dh5i~6VTg;y%(KqiSu;`rI`YL-15j{mk zR+XZ{f7pCG-%=b5EtT2D7kaWT(bqrSDnOm;O|`HdcWp);W+hp9*KmGl)%|{>ArmST z0lKk5(JI}Sde>n4u>vPNzxF^}g}%abf<9|7CnJ<@PyC%gUiw_NtsU^e+Ft;~ASe%w z&G2jc^Lh`Mk_OFK069KneKlE2Ev;`dkH#kGRSF*Q?~r|?ubIDouh!w*xQDCGTjcbq=X_!?CKKi60fX5}nOstzS6uT%tsz;a{*aYau(Yz#0YMA$O`BHef-b4*H zr&gOBt(`<^4atVs6R#0Z7=I0Q*eX{F%P_ywOB%BA5^oB;$E2+@5!!^;Y z{h;eBP~A2=8r*Lq3Fig0D~zlEPLKjJVIB{Y_YQ~ZxhZ3KJ)B@*brCz!v^pVivtcVF z>;eGKB3FP0}nE!j9ERY=4ZMKYdOO5Hcle5MzdQ_x+u z`RBzsT4TjIMSb#z>u_PL@&uzS8~`*6s3S;RZ;!}PjoF*Fu!GW|Ko2$0d~|xjc%V~4 zbY*=K^t^uwCCJ6z3bU<(57G~Ei$4dea;`SV9GmY*T__0nK4~NIgUiG~TL=VbjTGr%3dynp zN%8um$;jEwa3&J%Q-%+TZp!Gw({p^e*HHg`IW+9=yC)`TM|N@XqvYl+DiGKvIi@!F zszy<4C@*Fqeh#)9>ZyLCr5FD|SY+5;m(m%JMHCXer<0~>(kyn*t|jwELH5>=!k&^g z6l`GrBbl0K=a5WKu_aD|SA-b$8UkC`Gw=Yx4AAGeZJ&_P6MTek)eny|7h7gixlu=c zEQ}d^xSISfjADdb<=)6yWc(b745d$}<1} literal 0 HcmV?d00001 diff --git a/src-ui/src/index.html b/src-ui/src/index.html index b05f9123b..39ecd824c 100644 --- a/src-ui/src/index.html +++ b/src-ui/src/index.html @@ -9,6 +9,7 @@ + From f397c5472c014dead0650aa82fd7df0fff18554b Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Tue, 23 Feb 2021 12:35:24 +0100 Subject: [PATCH 191/225] validation for regular expressions on matching models #605 --- src/documents/serialisers.py | 39 +++++++++++++------------- src/documents/tests/test_api.py | 37 +++++++++++++++++++++++- src/locale/en_US/LC_MESSAGES/django.po | 10 +++++-- 3 files changed, 63 insertions(+), 23 deletions(-) diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 66736bdbf..652e5fe30 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -1,11 +1,13 @@ +import re + import magic from django.utils.text import slugify from rest_framework import serializers from rest_framework.fields import SerializerMethodField from . import bulk_edit -from .models import Correspondent, Tag, Document, Log, DocumentType, \ - SavedView, SavedViewFilterRule +from .models import Correspondent, Tag, Document, DocumentType, \ + SavedView, SavedViewFilterRule, MatchingModel from .parsers import is_mime_type_supported from django.utils.translation import gettext as _ @@ -33,16 +35,27 @@ class DynamicFieldsModelSerializer(serializers.ModelSerializer): self.fields.pop(field_name) -class CorrespondentSerializer(serializers.ModelSerializer): +class MatchingModelSerializer(serializers.ModelSerializer): document_count = serializers.IntegerField(read_only=True) - last_correspondence = serializers.DateTimeField(read_only=True) - def get_slug(self, obj): return slugify(obj.name) slug = SerializerMethodField() + def validate_match(self, match): + if 'matching_algorithm' in self.initial_data and self.initial_data['matching_algorithm'] == MatchingModel.MATCH_REGEX: # NOQA: E501 + try: + re.compile(match) + except Exception as e: + raise serializers.ValidationError(_("Invalid regular expresssion: ") + str(e)) + return match + + +class CorrespondentSerializer(MatchingModelSerializer): + + last_correspondence = serializers.DateTimeField(read_only=True) + class Meta: model = Correspondent fields = ( @@ -57,13 +70,7 @@ class CorrespondentSerializer(serializers.ModelSerializer): ) -class DocumentTypeSerializer(serializers.ModelSerializer): - - document_count = serializers.IntegerField(read_only=True) - - def get_slug(self, obj): - return slugify(obj.name) - slug = SerializerMethodField() +class DocumentTypeSerializer(MatchingModelSerializer): class Meta: model = DocumentType @@ -78,13 +85,7 @@ class DocumentTypeSerializer(serializers.ModelSerializer): ) -class TagSerializer(serializers.ModelSerializer): - - document_count = serializers.IntegerField(read_only=True) - - def get_slug(self, obj): - return slugify(obj.name) - slug = SerializerMethodField() +class TagSerializer(MatchingModelSerializer): class Meta: model = Tag diff --git a/src/documents/tests/test_api.py b/src/documents/tests/test_api.py index 7486154e1..cbd10ac71 100644 --- a/src/documents/tests/test_api.py +++ b/src/documents/tests/test_api.py @@ -14,7 +14,7 @@ from rest_framework.test import APITestCase from whoosh.writing import AsyncWriter from documents import index, bulk_edit -from documents.models import Document, Correspondent, DocumentType, Tag, SavedView +from documents.models import Document, Correspondent, DocumentType, Tag, SavedView, MatchingModel from documents.tests.utils import DirectoriesMixin @@ -772,6 +772,41 @@ class TestDocumentApi(DirectoriesMixin, APITestCase): self.assertEqual(response.status_code, 200) self.assertListEqual(response.data, ["test", "test2"]) + def test_invalid_regex_other_algorithm(self): + for endpoint in ['correspondents', 'tags', 'document_types']: + response = self.client.post(f"/api/{endpoint}/", { + "name": "test", + "matching_algorithm": MatchingModel.MATCH_ANY, + "match": "[" + }, format='json') + self.assertEqual(response.status_code, 201, endpoint) + + def test_invalid_regex(self): + for endpoint in ['correspondents', 'tags', 'document_types']: + response = self.client.post(f"/api/{endpoint}/", { + "name": "test", + "matching_algorithm": MatchingModel.MATCH_REGEX, + "match": "[" + }, format='json') + self.assertEqual(response.status_code, 400, endpoint) + + def test_valid_regex(self): + for endpoint in ['correspondents', 'tags', 'document_types']: + response = self.client.post(f"/api/{endpoint}/", { + "name": "test", + "matching_algorithm": MatchingModel.MATCH_REGEX, + "match": "[0-9]" + }, format='json') + self.assertEqual(response.status_code, 201, endpoint) + + def test_regex_no_algorithm(self): + for endpoint in ['correspondents', 'tags', 'document_types']: + response = self.client.post(f"/api/{endpoint}/", { + "name": "test", + "match": "[0-9]" + }, format='json') + self.assertEqual(response.status_code, 201, endpoint) + class TestBulkEdit(DirectoriesMixin, APITestCase): diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po index cb31bbc8c..c234d5c2a 100644 --- a/src/locale/en_US/LC_MESSAGES/django.po +++ b/src/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-02-16 14:52+0100\n" +"POT-Creation-Date: 2021-02-23 12:30+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -346,7 +346,11 @@ msgstr "" msgid "filter rules" msgstr "" -#: documents/serialisers.py:370 +#: documents/serialisers.py:52 +msgid "Invalid regular expresssion: " +msgstr "" + +#: documents/serialisers.py:376 #, python-format msgid "File type %(type)s not supported" msgstr "" @@ -411,7 +415,7 @@ msgstr "" msgid "French" msgstr "" -#: paperless/urls.py:114 +#: paperless/urls.py:118 msgid "Paperless-ng administration" msgstr "" From 25366af4bb29892bcd8804658170e80b075de542 Mon Sep 17 00:00:00 2001 From: Jonas Winkler <17569239+jonaswinkler@users.noreply.github.com> Date: Tue, 23 Feb 2021 13:03:20 +0100 Subject: [PATCH 192/225] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8fb1f8e9..0bbd6c8ea 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,11 +9,11 @@ If you want to implement something big: Please start a discussion about that in ## Python -Use python 3.6 for development. Paperless supports python 3.6, 3.7 and 3.8. +Paperless supports python 3.6, 3.7, 3.8 and 3.9. ## Branches -master always reflects the latest release. +master always reflects the latest release. Apart from changes to the documentation or readme, absolutely no functional changes on this branch in between releases. dev contains all changes that will be part of the next release. Use this branch to start making your changes. From 5d94a983d2da4b1e180e91ae4c9290a7ebd91eb1 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Tue, 23 Feb 2021 13:09:41 +0100 Subject: [PATCH 193/225] move codestyle checks into separate job --- .github/workflows/ci.yml | 44 ++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dbb718cc8..cb61b753b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: name: Set up Python uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: 3.7 - name: Get pip cache dir id: pip-cache @@ -49,6 +49,39 @@ jobs: name: documentation path: docs/_build/html/ + codestyle: + runs-on: ubuntu-20.04 + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - + name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + - + name: Persistent Github pip cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip${{ matrix.python-version }} + - + name: Prepare tests + run: | + pip install --upgrade pipenv + pipenv install --system --dev --ignore-pipfile + - + name: Codestyle + run: | + cd src/ + pycodestyle + tests: runs-on: ubuntu-20.04 strategy: @@ -87,11 +120,6 @@ jobs: run: | cd src/ pytest - - - name: Codestyle - run: | - cd src/ - pycodestyle - name: Publish coverage results if: matrix.python-version == '3.8' @@ -130,7 +158,7 @@ jobs: path: src/documents/static/frontend/ build-release: - needs: [frontend, documentation, tests] + needs: [frontend, documentation, tests, codestyle] runs-on: ubuntu-20.04 steps: - @@ -240,7 +268,7 @@ jobs: build-docker-image: if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/ng-')) runs-on: ubuntu-latest - needs: [frontend, tests] + needs: [frontend, tests, codestyle] steps: - name: Prepare From 5ce73574c8ba1cadcac0d4450fd58479bacb17a1 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Tue, 23 Feb 2021 12:12:00 +0000 Subject: [PATCH 194/225] Apply translations in de translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po' on the 'de' language. --- src/locale/de/LC_MESSAGES/django.po | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/locale/de/LC_MESSAGES/django.po b/src/locale/de/LC_MESSAGES/django.po index 358c9b721..d1a07d18f 100644 --- a/src/locale/de/LC_MESSAGES/django.po +++ b/src/locale/de/LC_MESSAGES/django.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-02-16 14:52+0100\n" +"POT-Creation-Date: 2021-02-23 12:30+0100\n" "PO-Revision-Date: 2021-02-16 18:37+0000\n" "Last-Translator: Jonas Winkler, 2021\n" "Language-Team: German (https://www.transifex.com/paperless/teams/115905/de/)\n" @@ -354,7 +354,11 @@ msgstr "Filterregel" msgid "filter rules" msgstr "Filterregeln" -#: documents/serialisers.py:370 +#: documents/serialisers.py:52 +msgid "Invalid regular expresssion: " +msgstr "Ungültiger regulärer Ausdruck:" + +#: documents/serialisers.py:376 #, python-format msgid "File type %(type)s not supported" msgstr "Dateityp %(type)s nicht unterstützt" @@ -421,7 +425,7 @@ msgstr "Niederländisch" msgid "French" msgstr "Französisch" -#: paperless/urls.py:114 +#: paperless/urls.py:118 msgid "Paperless-ng administration" msgstr "Paperless-ng Administration" From 065ff6eaf5c212bc2a19adf1abb78e1c2aab8fc3 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Tue, 23 Feb 2021 13:13:12 +0100 Subject: [PATCH 195/225] rename some steps --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb61b753b..85db2d482 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,7 +72,7 @@ jobs: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ runner.os }}-pip${{ matrix.python-version }} - - name: Prepare tests + name: Install dependencies run: | pip install --upgrade pipenv pipenv install --system --dev --ignore-pipfile @@ -109,7 +109,7 @@ jobs: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ runner.os }}-pip${{ matrix.python-version }} - - name: Prepare tests + name: Install dependencies run: | sudo apt-get update -qq sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript optipng From f888647b12deec0a63bd3b35a7bd3c288a10290e Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Tue, 23 Feb 2021 13:13:38 +0100 Subject: [PATCH 196/225] front end support for displaying error messages about regular expressions. --- .../correspondent-edit-dialog.component.html | 2 +- .../document-type-edit-dialog.component.html | 2 +- .../tag-list/tag-edit-dialog/tag-edit-dialog.component.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 bbc2c8453..26a6dc219 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 @@ -8,7 +8,7 @@ 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 fd4c3b7f0..418a5c281 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 @@ -20,7 +20,7 @@ - +
- + +
+ +
+
- + +
+ +
+ diff --git a/src-ui/src/app/components/common/date-dropdown/date-dropdown.component.ts b/src-ui/src/app/components/common/date-dropdown/date-dropdown.component.ts index 27472bdc7..e21b28312 100644 --- a/src-ui/src/app/components/common/date-dropdown/date-dropdown.component.ts +++ b/src-ui/src/app/components/common/date-dropdown/date-dropdown.component.ts @@ -1,7 +1,10 @@ import { formatDate } from '@angular/common'; import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core'; +import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; import { Subject, Subscription } from 'rxjs'; -import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { debounceTime } from 'rxjs/operators'; +import { SettingsService } from 'src/app/services/settings.service'; +import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter'; export interface DateSelection { before?: string @@ -16,10 +19,17 @@ const LAST_YEAR = 3 @Component({ selector: 'app-date-dropdown', templateUrl: './date-dropdown.component.html', - styleUrls: ['./date-dropdown.component.scss'] + styleUrls: ['./date-dropdown.component.scss'], + providers: [ + {provide: NgbDateAdapter, useClass: ISODateAdapter}, + ] }) export class DateDropdownComponent implements OnInit, OnDestroy { + constructor(settings: SettingsService) { + this.datePlaceHolder = settings.getLocalizedDateInputFormat() + } + quickFilters = [ {id: LAST_7_DAYS, name: $localize`Last 7 days`}, {id: LAST_MONTH, name: $localize`Last month`}, @@ -27,6 +37,8 @@ export class DateDropdownComponent implements OnInit, OnDestroy { {id: LAST_YEAR, name: $localize`Last year`} ] + datePlaceHolder: string + @Input() dateBefore: string diff --git a/src-ui/src/app/utils/ngb-iso-date-adapter.ts b/src-ui/src/app/utils/ngb-iso-date-adapter.ts new file mode 100644 index 000000000..e43602a16 --- /dev/null +++ b/src-ui/src/app/utils/ngb-iso-date-adapter.ts @@ -0,0 +1,27 @@ +import { Injectable } from "@angular/core"; +import { NgbDateAdapter, NgbDateStruct } from "@ng-bootstrap/ng-bootstrap"; + +@Injectable() +export class ISODateAdapter extends NgbDateAdapter { + + fromModel(value: string | null): NgbDateStruct | null { + if (value) { + let date = new Date(value) + return { + day : date.getDate(), + month : date.getMonth() + 1, + year : date.getFullYear() + } + } else { + return null + } + } + + toModel(date: NgbDateStruct | null): string | null { + if (date) { + return date.year.toString().padStart(4, '0') + "-" + date.month.toString().padStart(2, '0') + "-" + date.day.toString().padStart(2, '0') + } else { + return null + } + } +} diff --git a/src-ui/src/app/utils/ngb-date-adapter.ts b/src-ui/src/app/utils/ngb-iso-date-time-adapter.ts similarity index 89% rename from src-ui/src/app/utils/ngb-date-adapter.ts rename to src-ui/src/app/utils/ngb-iso-date-time-adapter.ts index 19711319d..21a97a19a 100644 --- a/src-ui/src/app/utils/ngb-date-adapter.ts +++ b/src-ui/src/app/utils/ngb-iso-date-time-adapter.ts @@ -2,7 +2,7 @@ import { Injectable } from "@angular/core"; import { NgbDateAdapter, NgbDateStruct } from "@ng-bootstrap/ng-bootstrap"; @Injectable() -export class ISODateAdapter extends NgbDateAdapter { +export class ISODateTimeAdapter extends NgbDateAdapter { fromModel(value: string | null): NgbDateStruct | null { if (value) { From a5ba14e4464dbbc9e4167c98f644a7a5b34721c2 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Wed, 24 Feb 2021 19:03:03 +0100 Subject: [PATCH 216/225] version bump --- src-ui/src/environments/environment.prod.ts | 2 +- src/paperless/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src-ui/src/environments/environment.prod.ts b/src-ui/src/environments/environment.prod.ts index d170a0f02..6c97ac1cb 100644 --- a/src-ui/src/environments/environment.prod.ts +++ b/src-ui/src/environments/environment.prod.ts @@ -2,7 +2,7 @@ export const environment = { production: true, apiBaseUrl: "/api/", appTitle: "Paperless-ng", - version: "1.2.0", + version: "1.2.1", webSocketHost: window.location.host, webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:") }; diff --git a/src/paperless/version.py b/src/paperless/version.py index 09ac5f672..d93cd02b0 100644 --- a/src/paperless/version.py +++ b/src/paperless/version.py @@ -1 +1 @@ -__version__ = (1, 2, 0) +__version__ = (1, 2, 1) From f038bb90bb2c936064f5997bf627997056f7405d Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Wed, 24 Feb 2021 19:03:21 +0100 Subject: [PATCH 217/225] fixes for #600 --- src-ui/src/index.html | 2 +- src/documents/templates/index.html | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src-ui/src/index.html b/src-ui/src/index.html index 39ecd824c..4308a6b16 100644 --- a/src-ui/src/index.html +++ b/src-ui/src/index.html @@ -9,7 +9,7 @@ - + diff --git a/src/documents/templates/index.html b/src/documents/templates/index.html index 285e48d3f..b71aaaeb6 100644 --- a/src/documents/templates/index.html +++ b/src/documents/templates/index.html @@ -15,6 +15,7 @@ + {% translate "Paperless-ng is loading..." %} From 6a64bf6fc16e5b806faf1aa5a91866913ecc6051 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Wed, 24 Feb 2021 19:05:06 +0100 Subject: [PATCH 218/225] changelog --- docs/changelog.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index b23ca5137..d2ec7ec69 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,6 +5,17 @@ Changelog ********* +paperless-ng 1.2.1 +################## + +* Added translation into Portuguese (Brazil). + +* The date input fields now respect the currently selected date format. + +* Added a fancy icon when adding paperless to the home screen on iOS devices. + +* When using regular expression matching, the regular expression is now validated before saving the tag/correspondent/type. + paperless-ng 1.2.0 ################## From 6ae65e0ab43b90122b0d94871fc912f022693149 Mon Sep 17 00:00:00 2001 From: jonaswinkler <17569239+jonaswinkler@users.noreply.github.com> Date: Wed, 24 Feb 2021 19:21:15 +0100 Subject: [PATCH 219/225] changelog --- docs/changelog.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index d2ec7ec69..137fa2fd2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,11 +8,11 @@ Changelog paperless-ng 1.2.1 ################## -* Added translation into Portuguese (Brazil). +* `Rodrigo Avelino `_ translated Paperless into Portuguese (Brazil). * The date input fields now respect the currently selected date format. -* Added a fancy icon when adding paperless to the home screen on iOS devices. +* Added a fancy icon when adding paperless to the home screen on iOS devices. Thanks to `Joel Nordell `_. * When using regular expression matching, the regular expression is now validated before saving the tag/correspondent/type. From 8465b8cfca7ca8c36c02047166bd86e50e8feabc Mon Sep 17 00:00:00 2001 From: Jonas Winkler <17569239+jonaswinkler@users.noreply.github.com> Date: Wed, 24 Feb 2021 19:27:16 +0100 Subject: [PATCH 220/225] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 01ac30fa1..b21173ba5 100644 --- a/README.md +++ b/README.md @@ -77,9 +77,9 @@ The documentation for Paperless-ng is available on [ReadTheDocs](https://paperle # Translation -Paperless is currently available in English, German, Dutch, French, and Portuguese. Translation is coordinated at transifex: https://www.transifex.com/paperless/paperless-ng +Paperless is currently available in English, German, Dutch, French, and Portuguese. -If you want to see paperless in your own language, request that language at transifex and you can start translating after I approve the language. +There's an active translation project at transifex! If you want to help out by translating paperless into your language, please head over to https://github.com/jonaswinkler/paperless-ng/issues/212 for details. # Feature Requests From c22af0a7825fad1db9bc23075e06449a182e63b8 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Wed, 24 Feb 2021 18:43:39 +0000 Subject: [PATCH 221/225] Apply translations in en_GB translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po' on the 'en_GB' language. --- src/locale/en_GB/LC_MESSAGES/django.po | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/locale/en_GB/LC_MESSAGES/django.po b/src/locale/en_GB/LC_MESSAGES/django.po index f0c5b0381..dbdd4b2a5 100644 --- a/src/locale/en_GB/LC_MESSAGES/django.po +++ b/src/locale/en_GB/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-02-16 14:52+0100\n" +"POT-Creation-Date: 2021-02-24 16:49+0100\n" "PO-Revision-Date: 2021-02-16 18:37+0000\n" "Last-Translator: Jonas Winkler, 2021\n" "Language-Team: English (United Kingdom) (https://www.transifex.com/paperless/teams/115905/en_GB/)\n" @@ -355,7 +355,12 @@ msgstr "filter rule" msgid "filter rules" msgstr "filter rules" -#: documents/serialisers.py:370 +#: documents/serialisers.py:52 +#, python-format +msgid "Invalid regular expresssion: %(error)s" +msgstr "Invalid regular expresssion: %(error)s" + +#: documents/serialisers.py:378 #, python-format msgid "File type %(type)s not supported" msgstr "File type %(type)s not supported" @@ -420,7 +425,11 @@ msgstr "Dutch" msgid "French" msgstr "French" -#: paperless/urls.py:114 +#: paperless/settings.py:296 +msgid "Portuguese (Brazil)" +msgstr "Portuguese (Brazil)" + +#: paperless/urls.py:118 msgid "Paperless-ng administration" msgstr "Paperless-ng administration" From 800f4deb909ee2b661e234743d1fa2fbf1698c87 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Wed, 24 Feb 2021 18:43:40 +0000 Subject: [PATCH 222/225] Translate /src-ui/messages.xlf in en_GB translation completed for the source file '/src-ui/messages.xlf' on the 'en_GB' language. --- src-ui/src/locale/messages.en_GB.xlf | 48 ++++++++++++++++++---------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src-ui/src/locale/messages.en_GB.xlf b/src-ui/src/locale/messages.en_GB.xlf index f5e711c39..b413a50a1 100644 --- a/src-ui/src/locale/messages.en_GB.xlf +++ b/src-ui/src/locale/messages.en_GB.xlf @@ -606,7 +606,7 @@ Use system language src/app/components/manage/settings/settings.component.ts - 91 + 92
@@ -614,15 +614,7 @@ Use date format of display language src/app/components/manage/settings/settings.component.ts - 96 - - - - ISO 8601 - ISO 8601 - - src/app/components/manage/settings/settings.component.ts - 97 + 98 @@ -630,7 +622,7 @@ Error while storing settings on server: src/app/components/manage/settings/settings.component.ts - 114 + 115 @@ -1832,6 +1824,14 @@ 21 + + Invalid date. + Invalid date. + + src/app/components/common/input/date/date.component.html + 13 + + Yes Yes @@ -1861,7 +1861,7 @@ English (US) src/app/services/settings.service.ts - 82 + 88 @@ -1869,7 +1869,7 @@ English (GB) src/app/services/settings.service.ts - 83 + 89 @@ -1877,7 +1877,7 @@ German src/app/services/settings.service.ts - 84 + 90 @@ -1885,7 +1885,7 @@ Dutch src/app/services/settings.service.ts - 85 + 91 @@ -1893,7 +1893,23 @@ French src/app/services/settings.service.ts - 86 + 92 + + + + Portuguese (Brazil) + Portuguese (Brazil) + + src/app/services/settings.service.ts + 93 + + + + ISO 8601 + ISO 8601 + + src/app/services/settings.service.ts + 98 From 2f2b5b90ea0812e3d9212169f4847b53b93b5f2e Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+nikonratm@users.noreply.github.com> Date: Sun, 17 Jan 2021 19:46:44 +0100 Subject: [PATCH 223/225] Dark mode compatibility --- src-ui/src/theme_dark.scss | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src-ui/src/theme_dark.scss b/src-ui/src/theme_dark.scss index 4e850f017..751ff2b15 100644 --- a/src-ui/src/theme_dark.scss +++ b/src-ui/src/theme_dark.scss @@ -246,6 +246,16 @@ $border-color-dark-mode: #47494f; } } + .btn-light:not(:disabled):not(.disabled) { + background-color: $bg-dark-mode; + color: $text-color-dark-mode-accent; + + &:hover { + background-color: $text-color-dark-mode; + color: $bg-dark-mode; + } + } + .btn-link:not(:disabled):not(.disabled) { color: $primary-dark-mode; } @@ -366,6 +376,12 @@ $border-color-dark-mode: #47494f; .progress-bar.bg-primary { background-color: darken($primary-dark-mode, 5%) !important; } + + .ngb-dp-header, + .ngb-dp-weekdays, + .ngb-dp-month { + background-color: $bg-light-dark-mode; + } } body.color-scheme-dark { From e43ab23a45e7065f8cb29417445ba00c5e33cfbd Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Wed, 24 Feb 2021 19:29:33 +0000 Subject: [PATCH 224/225] Apply translations in fr translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po' on the 'fr' language. --- src/locale/fr/LC_MESSAGES/django.po | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/locale/fr/LC_MESSAGES/django.po b/src/locale/fr/LC_MESSAGES/django.po index 6acd81649..3b4fbce3f 100644 --- a/src/locale/fr/LC_MESSAGES/django.po +++ b/src/locale/fr/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-02-23 13:20+0100\n" +"POT-Creation-Date: 2021-02-24 16:49+0100\n" "PO-Revision-Date: 2021-02-16 18:37+0000\n" "Last-Translator: Philmo67, 2021\n" "Language-Team: French (https://www.transifex.com/paperless/teams/115905/fr/)\n" @@ -428,6 +428,10 @@ msgstr "Néerlandais" msgid "French" msgstr "Français" +#: paperless/settings.py:296 +msgid "Portuguese (Brazil)" +msgstr "Portugais (Brésil)" + #: paperless/urls.py:118 msgid "Paperless-ng administration" msgstr "Administration de Paperless-ng" From d6cbea97f9b5993f12b1d1ea02fc99ca3a811587 Mon Sep 17 00:00:00 2001 From: "transifex-integration[bot]" <43880903+transifex-integration[bot]@users.noreply.github.com> Date: Wed, 24 Feb 2021 19:30:01 +0000 Subject: [PATCH 225/225] Translate /src-ui/messages.xlf in fr translation completed for the source file '/src-ui/messages.xlf' on the 'fr' language. --- src-ui/src/locale/messages.fr.xlf | 50 ++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src-ui/src/locale/messages.fr.xlf b/src-ui/src/locale/messages.fr.xlf index 79045b3b4..05b9fdab7 100644 --- a/src-ui/src/locale/messages.fr.xlf +++ b/src-ui/src/locale/messages.fr.xlf @@ -606,7 +606,7 @@ Utiliser la langue du système src/app/components/manage/settings/settings.component.ts - 91 + 92 @@ -614,15 +614,7 @@ Utiliser le format de date de la langue d'affichage src/app/components/manage/settings/settings.component.ts - 96 - - - - ISO 8601 - ISO 8601 - - src/app/components/manage/settings/settings.component.ts - 97 + 98 @@ -630,7 +622,7 @@ Une erreur s'est produite lors de l'enregistrement des paramètres sur le serveur : src/app/components/manage/settings/settings.component.ts - 114 + 115 @@ -819,7 +811,7 @@ Document processing - Traitement de document + Traitement de documents src/app/components/manage/settings/settings.component.html 118 @@ -1832,6 +1824,14 @@ 21 + + Invalid date. + Date incorrecte. + + src/app/components/common/input/date/date.component.html + 13 + + Yes Oui @@ -1861,7 +1861,7 @@ Anglais (US) src/app/services/settings.service.ts - 82 + 88 @@ -1869,7 +1869,7 @@ Anglais (GB) src/app/services/settings.service.ts - 83 + 89 @@ -1877,7 +1877,7 @@ Allemand src/app/services/settings.service.ts - 84 + 90 @@ -1885,7 +1885,7 @@ Néerlandais src/app/services/settings.service.ts - 85 + 91 @@ -1893,7 +1893,23 @@ Français src/app/services/settings.service.ts - 86 + 92 + + + + Portuguese (Brazil) + Portugais (Brésil) + + src/app/services/settings.service.ts + 93 + + + + ISO 8601 + ISO 8601 + + src/app/services/settings.service.ts + 98