Merge remote-tracking branch 'upstream/dev' into feature/popover-previews

This commit is contained in:
Michael Shamoon 2021-02-24 12:41:44 -08:00
commit 6f22297158
160 changed files with 11865 additions and 2503 deletions

View File

@ -2,6 +2,7 @@ name: ci
on: on:
push: push:
tags: ng-*
branches-ignore: branches-ignore:
- 'translations**' - 'translations**'
pull_request: pull_request:
@ -19,7 +20,7 @@ jobs:
name: Set up Python name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.8 python-version: 3.7
- -
name: Get pip cache dir name: Get pip cache dir
id: pip-cache id: pip-cache
@ -34,8 +35,6 @@ jobs:
- -
name: Install dependencies name: Install dependencies
run: | run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev
pip install --upgrade pipenv pip install --upgrade pipenv
pipenv install --system --dev --ignore-pipfile pipenv install --system --dev --ignore-pipfile
- -
@ -50,6 +49,39 @@ jobs:
name: documentation name: documentation
path: docs/_build/html/ 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: Install dependencies
run: |
pip install --upgrade pipenv
pipenv install --system --dev --ignore-pipfile
-
name: Codestyle
run: |
cd src/
pycodestyle
tests: tests:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
strategy: strategy:
@ -77,10 +109,10 @@ jobs:
path: ${{ steps.pip-cache.outputs.dir }} path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip${{ matrix.python-version }} key: ${{ runner.os }}-pip${{ matrix.python-version }}
- -
name: Prepare tests name: Install dependencies
run: | run: |
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev unpaper tesseract-ocr imagemagick ghostscript optipng sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript optipng
pip install --upgrade pipenv pip install --upgrade pipenv
pipenv install --system --dev --ignore-pipfile pipenv install --system --dev --ignore-pipfile
- -
@ -88,11 +120,6 @@ jobs:
run: | run: |
cd src/ cd src/
pytest pytest
-
name: Codestyle
run: |
cd src/
pycodestyle
- -
name: Publish coverage results name: Publish coverage results
if: matrix.python-version == '3.8' if: matrix.python-version == '3.8'
@ -113,6 +140,13 @@ jobs:
uses: actions/setup-node@v2 uses: actions/setup-node@v2
with: with:
node-version: '15' 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 name: Build frontend
run: ./compile-frontend.sh run: ./compile-frontend.sh
@ -124,7 +158,7 @@ jobs:
path: src/documents/static/frontend/ path: src/documents/static/frontend/
build-release: build-release:
needs: [frontend, documentation, tests] needs: [frontend, documentation, tests, codestyle]
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- -
@ -139,7 +173,7 @@ jobs:
name: Install dependencies name: Install dependencies
run: | run: |
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev gettext liblept5 sudo apt-get install -qq --no-install-recommends gettext liblept5
pip3 install -r requirements.txt pip3 install -r requirements.txt
- -
name: Download frontend artifact name: Download frontend artifact
@ -234,7 +268,7 @@ jobs:
build-docker-image: 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-')) 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 runs-on: ubuntu-latest
needs: [frontend, tests] needs: [frontend, tests, codestyle]
steps: steps:
- -
name: Prepare name: Prepare

View File

@ -9,11 +9,11 @@ If you want to implement something big: Please start a discussion about that in
## Python ## 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 ## 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. dev contains all changes that will be part of the next release. Use this branch to start making your changes.

View File

@ -10,10 +10,6 @@ RUN ./configure && make
FROM python:3.7-slim FROM python:3.7-slim
WORKDIR /usr/src/paperless/
COPY requirements.txt ./
# Binary dependencies # Binary dependencies
RUN apt-get update \ RUN apt-get update \
&& apt-get -y --no-install-recommends install \ && apt-get -y --no-install-recommends install \
@ -49,59 +45,61 @@ RUN apt-get update \
tesseract-ocr-spa \ tesseract-ocr-spa \
unpaper \ unpaper \
zlib1g \ zlib1g \
&& rm -rf /var/lib/apt/lists/*
# This pulls in updated dependencies from bullseye to fix some issues with file type detection. # This pulls in updated dependencies from bullseye to fix some issues with file type detection.
# TODO: Remove this once bullseye releases. # TODO: Remove this once bullseye releases.
RUN echo "deb http://deb.debian.org/debian bullseye main" > /etc/apt/sources.list.d/bullseye.list \ && echo "deb http://deb.debian.org/debian bullseye main" > /etc/apt/sources.list.d/bullseye.list \
&& apt-get update \ && apt-get update \
&& apt-get install --no-install-recommends -y file libmagic-dev \ && apt-get install --no-install-recommends -y file libmagic-dev \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& rm /etc/apt/sources.list.d/bullseye.list && rm /etc/apt/sources.list.d/bullseye.list
# Python dependencies
RUN apt-get update \
&& apt-get -y --no-install-recommends install \
build-essential \
libpoppler-cpp-dev \
libpq-dev \
libqpdf-dev \
&& python3 -m pip install --upgrade --no-cache-dir supervisor \
&& python3 -m pip install --no-cache-dir -r requirements.txt \
&& apt-get -y purge build-essential libqpdf-dev \
&& apt-get -y autoremove --purge \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir /var/log/supervisord /var/run/supervisord
# copy scripts
# this fixes issues with imagemagick and PDF
COPY docker/imagemagick-policy.xml /etc/ImageMagick-6/policy.xml
COPY gunicorn.conf.py ./
COPY docker/supervisord.conf /etc/supervisord.conf
COPY docker/docker-entrypoint.sh /sbin/docker-entrypoint.sh
# copy jbig2enc # copy jbig2enc
COPY --from=jbig2enc /usr/src/jbig2enc/src/.libs/libjbig2enc* /usr/local/lib/ COPY --from=jbig2enc /usr/src/jbig2enc/src/.libs/libjbig2enc* /usr/local/lib/
COPY --from=jbig2enc /usr/src/jbig2enc/src/jbig2 /usr/local/bin/ COPY --from=jbig2enc /usr/src/jbig2enc/src/jbig2 /usr/local/bin/
COPY --from=jbig2enc /usr/src/jbig2enc/src/*.h /usr/local/include/ COPY --from=jbig2enc /usr/src/jbig2enc/src/*.h /usr/local/include/
WORKDIR /usr/src/paperless/src/
COPY requirements.txt ../
# Python dependencies
RUN apt-get update \
&& apt-get -y --no-install-recommends install \
build-essential \
libpq-dev \
libqpdf-dev \
&& python3 -m pip install --upgrade --no-cache-dir supervisor \
&& python3 -m pip install --no-cache-dir -r ../requirements.txt \
&& apt-get -y purge build-essential libqpdf-dev \
&& apt-get -y autoremove --purge \
&& rm -rf /var/lib/apt/lists/*
# setup docker-specific things
COPY docker/ ./docker/
RUN cd docker \
&& cp imagemagick-policy.xml /etc/ImageMagick-6/policy.xml \
&& mkdir /var/log/supervisord /var/run/supervisord \
&& cp supervisord.conf /etc/supervisord.conf \
&& cp docker-entrypoint.sh /sbin/docker-entrypoint.sh \
&& chmod 755 /sbin/docker-entrypoint.sh \
&& chmod +x install_management_commands.sh \
&& ./install_management_commands.sh \
&& cd .. \
&& rm docker -rf
COPY gunicorn.conf.py ../
# copy app # copy app
COPY src/ ./src/ COPY src/ ./
# add users, setup scripts # add users, setup scripts
RUN addgroup --gid 1000 paperless \ RUN addgroup --gid 1000 paperless \
&& useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \ && useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \
&& chown -R paperless:paperless . \ && chown -R paperless:paperless ../ \
&& chmod 755 /sbin/docker-entrypoint.sh && sudo -HEu paperless python3 manage.py collectstatic --clear --no-input \
&& sudo -HEu paperless python3 manage.py compilemessages
WORKDIR /usr/src/paperless/src/
RUN sudo -HEu paperless python3 manage.py collectstatic --clear --no-input
RUN sudo -HEu paperless python3 manage.py compilemessages
VOLUME ["/usr/src/paperless/data", "/usr/src/paperless/media", "/usr/src/paperless/consume", "/usr/src/paperless/export"] VOLUME ["/usr/src/paperless/data", "/usr/src/paperless/media", "/usr/src/paperless/consume", "/usr/src/paperless/export"]
ENTRYPOINT ["/sbin/docker-entrypoint.sh"] ENTRYPOINT ["/sbin/docker-entrypoint.sh"]

18
Pipfile
View File

@ -17,19 +17,19 @@ django-filter = "~=2.4.0"
django-q = "~=1.3.4" django-q = "~=1.3.4"
djangorestframework = "~=3.12.2" djangorestframework = "~=3.12.2"
filelock = "*" filelock = "*"
fuzzywuzzy = "*" fuzzywuzzy = {extras = ["speedup"], version = "*"}
gunicorn = "*" gunicorn = "*"
imap-tools = "*" imap-tools = "*"
langdetect = "*" langdetect = "*"
pdftotext = "*" # numpy 1.20.0 drops python 3.6 support
numpy = "~=1.19.5"
pathvalidate = "*" pathvalidate = "*"
# pinned to 8.1.0, since aarch64 wheels might not be available beyond that https://github.com/python-pillow/Pillow/issues/5202 # 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" pillow = "==8.1.0"
pikepdf = "~=2.2.5" pikepdf = "~=2.5.0"
python-gnupg = "*" python-gnupg = "*"
python-dotenv = "*" python-dotenv = "*"
python-dateutil = "*" python-dateutil = "*"
python-Levenshtein = "*"
python-magic = "*" python-magic = "*"
psycopg2-binary = "*" psycopg2-binary = "*"
redis = "*" redis = "*"
@ -38,10 +38,10 @@ scikit-learn="==0.24.0"
# Prevent scipy updates because 1.6 is incompatible with python 3.6 # Prevent scipy updates because 1.6 is incompatible with python 3.6
scipy="~=1.5.4" scipy="~=1.5.4"
whitenoise = "~=5.2.0" whitenoise = "~=5.2.0"
watchdog = "*" watchdog = "~=1.0.0"
whoosh="~=2.7.4" whoosh="~=2.7.4"
inotifyrecursive = "~=0.3.4" inotifyrecursive = "~=0.3.4"
ocrmypdf = "~=11.4.5" ocrmypdf = "~=11.6"
tqdm = "*" tqdm = "*"
tika = "*" tika = "*"
# TODO: This will sadly also install daphne+dependencies, # TODO: This will sadly also install daphne+dependencies,
@ -49,6 +49,12 @@ tika = "*"
channels = "~=3.0" channels = "~=3.0"
channels-redis = "*" channels-redis = "*"
uvicorn = {extras = ["standard"], version = "*"} uvicorn = {extras = ["standard"], version = "*"}
concurrent-log-handler = "*"
# uvloop 0.15+ incompatible with python 3.6
uvloop = "~=0.14.0"
# TODO: keep an eye on piwheel builds and update this once available (https://www.piwheels.org/project/cryptography/)
cryptography = "~=3.3.2"
"pdfminer.six" = "*"
[dev-packages] [dev-packages]
coveralls = "*" coveralls = "*"

396
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "d80d2539a4528a8fd9e848875c2e2d5bcdb3e98154f45b612706094b84ecaaea" "sha256": "71959eb287fc97969263be5e3a1b1f4f369b7a5ace85bd1947a25b9b92e17e8a"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": {}, "requires": {},
@ -60,11 +60,11 @@
}, },
"autobahn": { "autobahn": {
"hashes": [ "hashes": [
"sha256:410a93e0e29882c8b5d5ab05d220b07609b886ef5f23c0b8d39153254ffd6895", "sha256:41a3a3f89cde48643baf4e105d9491c566295f9abee951379e59121784044b8b",
"sha256:52ee4236ff9a1fcbbd9500439dcf3284284b37f8a6b31ecc8a36e00cf9f95049" "sha256:7e6b1bf95196b733978bab2d54a7ab8899c16ce11be369dc58422c07b7eea726"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==20.12.3" "version": "==21.2.1"
}, },
"automat": { "automat": {
"hashes": [ "hashes": [
@ -90,46 +90,47 @@
}, },
"cffi": { "cffi": {
"hashes": [ "hashes": [
"sha256:00a1ba5e2e95684448de9b89888ccd02c98d512064b4cb987d48f4b40aa0421e", "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813",
"sha256:00e28066507bfc3fe865a31f325c8391a1ac2916219340f87dfad602c3e48e5d", "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06",
"sha256:045d792900a75e8b1e1b0ab6787dd733a8190ffcf80e8c8ceb2fb10a29ff238a", "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea",
"sha256:0638c3ae1a0edfb77c6765d487fee624d2b1ee1bdfeffc1f0b58c64d149e7eec", "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee",
"sha256:105abaf8a6075dc96c1fe5ae7aae073f4696f2905fde6aeada4c9d2926752362", "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396",
"sha256:155136b51fd733fa94e1c2ea5211dcd4c8879869008fc811648f16541bf99668", "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73",
"sha256:1a465cbe98a7fd391d47dce4b8f7e5b921e6cd805ef421d04f5f66ba8f06086c", "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315",
"sha256:1d2c4994f515e5b485fd6d3a73d05526aa0fcf248eb135996b088d25dfa1865b", "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1",
"sha256:23f318bf74b170c6e9adb390e8bd282457f6de46c19d03b52f3fd042b5e19654", "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49",
"sha256:2c24d61263f511551f740d1a065eb0212db1dbbbbd241db758f5244281590c06", "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892",
"sha256:51a8b381b16ddd370178a65360ebe15fbc1c71cf6f584613a7ea08bfad946698", "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482",
"sha256:594234691ac0e9b770aee9fcdb8fa02c22e43e5c619456efd0d6c2bf276f3eb2", "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058",
"sha256:5cf4be6c304ad0b6602f5c4e90e2f59b47653ac1ed9c662ed379fe48a8f26b0c", "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5",
"sha256:64081b3f8f6f3c3de6191ec89d7dc6c86a8a43911f7ecb422c60e90c70be41c7", "sha256:5560dbf8deedbffb638d8a2da31da91094db361cc07f8a501a339b2daae2cbcc",
"sha256:6bc25fc545a6b3d57b5f8618e59fc13d3a3a68431e8ca5fd4c13241cd70d0009", "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53",
"sha256:798caa2a2384b1cbe8a2a139d80734c9db54f9cc155c99d7cc92441a23871c03", "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045",
"sha256:7c6b1dece89874d9541fc974917b631406233ea0440d0bdfbb8e03bf39a49b3b", "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3",
"sha256:840793c68105fe031f34d6a086eaea153a0cd5c491cde82a74b420edd0a2b909", "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5",
"sha256:8d6603078baf4e11edc4168a514c5ce5b3ba6e3e9c374298cb88437957960a53", "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e",
"sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35", "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c",
"sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26", "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369",
"sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b", "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827",
"sha256:a5ed8c05548b54b998b9498753fb9cadbfd92ee88e884641377d8a8b291bcc01", "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053",
"sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb", "sha256:9338beed13d880320450d95c9e07ccf839faa3ea7b75d788f4ed46d845044a71",
"sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293", "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa",
"sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd", "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4",
"sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d", "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322",
"sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3", "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132",
"sha256:be8661bcee1bc2fc4b033a6ab65bd1f87ce5008492601695d0b9a4e820c3bde5", "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62",
"sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d", "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa",
"sha256:d5ff0621c88ce83a28a10d2ce719b2ee85635e85c515f12bac99a95306da4b2e", "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0",
"sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca", "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396",
"sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d", "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e",
"sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775", "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991",
"sha256:ec80dc47f54e6e9a78181ce05feb71a0353854cc26999db963695f950b5fb375", "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6",
"sha256:f032b34669220030f905152045dfa27741ce1a6db3324a5bc0b96b6c7420c87b", "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1",
"sha256:f60567825f791c6f8a592f3c6e3bd93dd2934e3f9dac189308426bd76b00ef3b", "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406",
"sha256:f803eaa94c2fcda012c047e62bc7a51b0bdabda1cad7a92a522694ea2d76e49f" "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d",
"sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"
], ],
"version": "==1.14.4" "version": "==1.14.5"
}, },
"channels": { "channels": {
"hashes": [ "hashes": [
@ -172,6 +173,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==15.0" "version": "==15.0"
}, },
"concurrent-log-handler": {
"hashes": [
"sha256:00d5ca24d463a7013c3479b026f34b76da4b50df8d76194132b8d8403c014379",
"sha256:b12f79abed3f94121c25ce9c24cdb57d889282ec6ff61f5535ab2068dc37d409"
],
"index": "pypi",
"version": "==0.9.19"
},
"constantly": { "constantly": {
"hashes": [ "hashes": [
"sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35", "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35",
@ -181,24 +190,24 @@
}, },
"cryptography": { "cryptography": {
"hashes": [ "hashes": [
"sha256:0003a52a123602e1acee177dc90dd201f9bb1e73f24a070db7d36c588e8f5c7d", "sha256:0d7b69674b738068fa6ffade5c962ecd14969690585aaca0a1b1fc9058938a72",
"sha256:0e85aaae861d0485eb5a79d33226dd6248d2a9f133b81532c8f5aae37de10ff7", "sha256:1bd0ccb0a1ed775cd7e2144fe46df9dc03eefd722bbcf587b3e0616ea4a81eff",
"sha256:594a1db4511bc4d960571536abe21b4e5c3003e8750ab8365fafce71c5d86901", "sha256:3c284fc1e504e88e51c428db9c9274f2da9f73fdf5d7e13a36b8ecb039af6e6c",
"sha256:69e836c9e5ff4373ce6d3ab311c1a2eed274793083858d3cd4c7d12ce20d5f9c", "sha256:49570438e60f19243e7e0d504527dd5fe9b4b967b5a1ff21cc12b57602dd85d3",
"sha256:788a3c9942df5e4371c199d10383f44a105d67d401fb4304178020142f020244", "sha256:541dd758ad49b45920dda3b5b48c968f8b2533d8981bcdb43002798d8f7a89ed",
"sha256:7e177e4bea2de937a584b13645cab32f25e3d96fc0bc4a4cf99c27dc77682be6", "sha256:5a60d3780149e13b7a6ff7ad6526b38846354d11a15e21068e57073e29e19bed",
"sha256:83d9d2dfec70364a74f4e7c70ad04d3ca2e6a08b703606993407bf46b97868c5", "sha256:7951a966613c4211b6612b0352f5bf29989955ee592c4a885d8c7d0f830d0433",
"sha256:84ef7a0c10c24a7773163f917f1cb6b4444597efd505a8aed0a22e8c4780f27e", "sha256:922f9602d67c15ade470c11d616f2b2364950602e370c76f0c94c94ae672742e",
"sha256:982f661bffc7a24b6d4f8ebe3291f17cf3833a0941c6f4d9d55c790b9aa2cdb3", "sha256:a0f0b96c572fc9f25c3f4ddbf4688b9b38c69836713fb255f4a2715d93cbaf44",
"sha256:9e21301f7a1e7c03dbea73e8602905a4ebba641547a462b26dd03451e5769e7c", "sha256:a777c096a49d80f9d2979695b835b0f9c9edab73b59e4ceb51f19724dda887ed",
"sha256:9f6b0492d111b43de5f70052e24c1f0951cb9e6022188ebcb1cc3a3d301469b0", "sha256:a9a4ac9648d39ce71c2f63fe7dc6db144b9fa567ddfc48b9fde1b54483d26042",
"sha256:a69bd3c68b98298f490e84519b954335154917eaab52cf582fa2c5c7efc6e812", "sha256:aa4969f24d536ae2268c902b2c3d62ab464b5a66bcb247630d208a79a8098e9b",
"sha256:b4890d5fb9b7a23e3bf8abf5a8a7da8e228f1e97dc96b30b95685df840b6914a", "sha256:c7390f9b2119b2b43160abb34f63277a638504ef8df99f11cb52c1fda66a2e6f",
"sha256:c366df0401d1ec4e548bebe8f91d55ebcc0ec3137900d214dd7aac8427ef3030", "sha256:ddd06e71c449a4fe10d0c60846280ee35d69ce49e3e413ce46d5f129e1468083",
"sha256:dc42f645f8f3a489c3dd416730a514e7a91a59510ddaadc09d04224c098d3302" "sha256:e18e6ab84dfb0ab997faf8cca25a86ff15dfea4027b986322026cc99e0a892da"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "index": "pypi",
"version": "==3.3.1" "version": "==3.3.2"
}, },
"daphne": { "daphne": {
"hashes": [ "hashes": [
@ -218,11 +227,11 @@
}, },
"django": { "django": {
"hashes": [ "hashes": [
"sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7", "sha256:32ce792ee9b6a0cbbec340123e229ac9f765dff8c2a4ae9247a14b2ba3a365a7",
"sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9" "sha256:baf099db36ad31f970775d0be5587cc58a6256a6771a44eb795b554d45f211b8"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.1.5" "version": "==3.1.7"
}, },
"django-cors-headers": { "django-cors-headers": {
"hashes": [ "hashes": [
@ -234,11 +243,11 @@
}, },
"django-extensions": { "django-extensions": {
"hashes": [ "hashes": [
"sha256:7cd002495ff0a0e5eb6cdd6be759600905b4e4079232ea27618fc46bdd853651", "sha256:674ad4c3b1587a884881824f40212d51829e662e52f85b012cd83d83fe1271d9",
"sha256:c7f88625a53f631745d4f2bef9ec4dcb999ed59476393bdbbe99db8596778846" "sha256:9507f8761ee760748938fd8af766d0608fb2738cf368adfa1b2451f61c15ae35"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.1.0" "version": "==3.1.1"
}, },
"django-filter": { "django-filter": {
"hashes": [ "hashes": [
@ -281,6 +290,9 @@
"version": "==3.0.12" "version": "==3.0.12"
}, },
"fuzzywuzzy": { "fuzzywuzzy": {
"extras": [
"speedup"
],
"hashes": [ "hashes": [
"sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8", "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8",
"sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993" "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"
@ -416,14 +428,6 @@
], ],
"version": "==0.4.0" "version": "==0.4.0"
}, },
"importlib-metadata": {
"hashes": [
"sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771",
"sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"
],
"markers": "python_version < '3.8'",
"version": "==3.4.0"
},
"incremental": { "incremental": {
"hashes": [ "hashes": [
"sha256:717e12246dddf231a349175f48d74d93e2897244939173b01974ab6661406b9f", "sha256:717e12246dddf231a349175f48d74d93e2897244939173b01974ab6661406b9f",
@ -449,11 +453,11 @@
}, },
"joblib": { "joblib": {
"hashes": [ "hashes": [
"sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f", "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7",
"sha256:7ad866067ac1fdec27d51c8678ea760601b70e32ff1881d4dc8e1171f2b64b24" "sha256:feeb1ec69c4d45129954f1b7034954241eedfd6ba39b5e9e4b6883be3332d5e5"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==1.0.0" "version": "==1.0.1"
}, },
"langdetect": { "langdetect": {
"hashes": [ "hashes": [
@ -582,16 +586,16 @@
"sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827", "sha256:dbd18bcf4889b720ba13a27ec2f2aac1981bd41203b3a3b27ba7a33f88ae4827",
"sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60" "sha256:df609c82f18c5b9f6cb97271f03315ff0dbe481a2a02e56aeb1b1a985ce38e60"
], ],
"markers": "python_version >= '3.6'", "index": "pypi",
"version": "==1.19.5" "version": "==1.19.5"
}, },
"ocrmypdf": { "ocrmypdf": {
"hashes": [ "hashes": [
"sha256:416a9c4321bfc844f250694b8c68ebb538f60609bbc8686bd9f84a13c5127d68", "sha256:0f624456a50be0b0bc8c0b59704d159f637616c093a1cabe8bb383706561bcf7",
"sha256:f45fc7e844e6026d6080a623a2936be120fc077d99aaa599df022acf35fb31e6" "sha256:b829ad640a6160423162012e094ee2f7cd074ec99efadd7f7486954ec9182985"
], ],
"index": "pypi", "index": "pypi",
"version": "==11.4.5" "version": "==11.6.2"
}, },
"pathvalidate": { "pathvalidate": {
"hashes": [ "hashes": [
@ -606,42 +610,37 @@
"sha256:b9aac0ebeafb21c08bf65f2039f4b2c5f78a3449d0a41df711d72445649e952a", "sha256:b9aac0ebeafb21c08bf65f2039f4b2c5f78a3449d0a41df711d72445649e952a",
"sha256:d78877ba8d8bf957f3bb636c4f73f4f6f30f56c461993877ac22c39c20837509" "sha256:d78877ba8d8bf957f3bb636c4f73f4f6f30f56c461993877ac22c39c20837509"
], ],
"markers": "python_version >= '3.4'",
"version": "==20201018"
},
"pdftotext": {
"hashes": [
"sha256:98aeb8b07a4127e1a30223bd933ef080bbd29aa88f801717ca6c5618380b8aa6"
],
"index": "pypi", "index": "pypi",
"version": "==2.1.5" "version": "==20201018"
}, },
"pikepdf": { "pikepdf": {
"hashes": [ "hashes": [
"sha256:0e67e5beeeed5422b3b8e862e4777fed5a4cd3c72e711e2a449a65d9ee641448", "sha256:02815df9499d3a6dfac2e07e4d2fdbe25fcbefae208970e76bff90af4a402d49",
"sha256:138155ae1f71634cd6eca79f5517f77b2067ef0bd5b627ea9414e308fe868dc5", "sha256:06b0c3004cc9e9068ebc62bb59c3c3a54e7af13867f4a326690d79c69a1cf288",
"sha256:15cf648dd760a47c55a4106b601b92bb653ae98155b10f04310553629c6695dd", "sha256:1a471c6ca288fbcd0e1b0e128ef12bb14c5e7db745786308ba292fc7cff30bb5",
"sha256:1d6a011ae4c501c78509caf19cbe152c2e3cb5c267f7b47bc3db8cd3436585a7", "sha256:489ed0fd1281beb0343a34fe8b9d94407c440ed0419ab2e6f5ea297a41824a31",
"sha256:211f529313953e44ae42eb896c2b688668385e6e8f9d04d21484bddb3c42b34c", "sha256:5106b27f7085ed449e057b9988f07c80a87292d2bf46c585a8635ac7a3ccf0d5",
"sha256:22049ad288d603a7fc68e90a0722770d307886788373ddfe71fbf614ced0f5b2", "sha256:51acffba6f3d21674eea7a0432ce1adaf0743641d57844a5e3dc92b4a7e81c85",
"sha256:24f7c371f6ecbee8f0ae30030992fc75cd32cd575dcfca8d466a03a8290377ca", "sha256:53d694d70dd072a47bd2dd71329dcef0f809dcd8084d1d11c31baf3b64cd345c",
"sha256:26cdf561632866d584fedb6b1c1fce78cefa49b5cae54c65aa6a6ca5fe6de4ac", "sha256:6a640fef52dc785abd354d6800a52ecc02656c98dbfc2ecde559323b001bd43f",
"sha256:2c37afcd21a2eb1da1773687e853327fa8ec7d2c5cd90cdcd70180f55f0221e1", "sha256:7006ef95f847412605dea6e772019f637949eaeaf65363d5d6afd9aa96bf5623",
"sha256:65b8ec6403814f51e1b9c7e18a8ff26087fcc7a199b1405583e5ff9eb931db56", "sha256:81e13b62877dbc089095e7efa03c27834bdf6b49d404d064cb227b0e179ce049",
"sha256:66a03103aadb2e2738271cb18c89837ac3980fa0b4687195c4c150228b7e79de", "sha256:8fc3e97b24dafbd4b1ac057c8f144c18467ce55d342a1a8c42688890ead58ef3",
"sha256:6e8f0124354c53a66f83ec5a18111b760aeff1a64db3a86e7ee5fed8e8624707", "sha256:9158dc4d3ef4e2301fb1879d5825530fdb32143ced770d60fa8e5badeee70a35",
"sha256:70f2836cd468aa25bc8b09a2b9561364bd75d3e6ddb0e50a25d248d7da6cff25", "sha256:961337a10b42bd656b59116ce1c574eafd515b45a513221df6ae1f11734bfb6c",
"sha256:82cebf68952cfb65c86d880eb782a0c558b37531cdae59f2e11fcd0f2bb4669c", "sha256:994ccac972357a7b9b147217e1beea2f7688697944b862dbb2a3e64aa9a5ff14",
"sha256:84ad3e8fd5f3251fb5b534614da64b04a264ce9348f0fe35b781c0fb378b0f82", "sha256:a8e9abf7db0351357b55c3f935979e7dc14f3f259a25d15bcc86abce730955a7",
"sha256:af13fbc022efa85d1ae161129d4cde66493479db52b9adb74d525b890a078208", "sha256:b836eda7f70b9dd75ccdeaf4e78b38393118a66821a69a10054b1430f945d1fd",
"sha256:c1d40fb8f8192c75f54f0e74a569ccf45e4e13bed8da78a78a5b488be29979bf", "sha256:b859a225f6bd953472c50f4df612b4575df646e560189e3720310ad65b6805a0",
"sha256:d147ec1ab58512871fdf40a161809f698eaa75720b4a230198e7e028582b20a1", "sha256:cae106bd461cfad73c554c09f6db1d5f2c6a28f5b8cb0602b63046840f488226",
"sha256:dedad1f68d6b0b54000f7f99386351f1c6e19c8cf70a9700d8dd06b9809c54fb", "sha256:cdc75b8fa5a650f4fc91214a315358fde7470e09b95a00981b73a7c4ac5ddb97",
"sha256:e72c3f5b624b9c7341fd6a7e657926d4cf12a7ea453681ffd7332cabc3530c62", "sha256:e2c28da4f37ad9a3efbedbbfc6f1084941bde43903d30dfdbb338d5ba94c9f50",
"sha256:eb75f22e261b3bc69b6fc9a17b1d6966c95e79d3e792b7737a018a2bf6a2b07f" "sha256:e596bd8fdbf40bfb8dc8068cdbc7e5b72052188d1ad8ca84da9d6b77658a8b31",
"sha256:ee957b9c60b6def20cbdf656d35859ce211eec02dafa3abb9d5ca937d32a3c3b",
"sha256:f9428d4b1f70af4f4560be4dccbbc5ab5308c00c5b62ed2f1c44ce9e2591b3d2"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.2.5" "version": "==2.5.2"
}, },
"pillow": { "pillow": {
"hashes": [ "hashes": [
@ -690,6 +689,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1" "version": "==0.13.1"
}, },
"portalocker": {
"hashes": [
"sha256:afa66c85041d7bac1532acb74ff26cc2406472dcf7145c90c31ff8ca4f1cc146",
"sha256:e5f6ffb2f360e9aef615a7c284143d2a93bb640c62e8e45a703e6083fc5aa114"
],
"markers": "python_version >= '3'",
"version": "==2.2.1"
},
"psycopg2-binary": { "psycopg2-binary": {
"hashes": [ "hashes": [
"sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c", "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c",
@ -819,27 +826,26 @@
}, },
"python-levenshtein": { "python-levenshtein": {
"hashes": [ "hashes": [
"sha256:108edd3c271f1afda8b21a8d9da81886414dfb6940a085fa7903c592e0f8f54b", "sha256:212db61934fcb819f5cb2fcb5ad5c0e2e43f3161f524eef4a135f7285dfb308a",
"sha256:1ff19b712c5974080b003fd26ef365cd93dfc1a5e690be621f79f3e63e00a7cc", "sha256:d92fe5c3b10c8ad8f2d880499f5e96ed24dbc7cd0414a665c2e2505feaf4ec58",
"sha256:554e273a88060d177e7b3c1e6ea9158dde11563bfae8f7f661f73f47e5ff0911" "sha256:dc2395fbd148a1ab31090dd113c366695934b9e85fe5a4b2a032745efd0346f6"
], ],
"index": "pypi", "version": "==0.12.2"
"version": "==0.12.1"
}, },
"python-magic": { "python-magic": {
"hashes": [ "hashes": [
"sha256:356efa93c8899047d1eb7d3eb91e871ba2f5b1376edbaf4cc305e3c872207355", "sha256:8551e804c09a3398790bd9e392acb26554ae2609f29c72abb0b9dee9a5571eae",
"sha256:b757db2a5289ea3f1ced9e60f072965243ea43a2221430048fd8cacab17be0ce" "sha256:ca884349f2c92ce830e3f498c5b7c7051fe2942c3ee4332f65213b8ebff15a62"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.4.18" "version": "==0.4.22"
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
"sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4", "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
"sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5" "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
], ],
"version": "==2020.5" "version": "==2021.1"
}, },
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
@ -1091,11 +1097,11 @@
}, },
"tqdm": { "tqdm": {
"hashes": [ "hashes": [
"sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a", "sha256:65185676e9fdf20d154cffd1c5de8e39ef9696ff7e59fe0156b1b08e468736af",
"sha256:fe3d08dd00a526850568d542ff9de9bbc2a09a791da3c334f3213d8d0bbbca65" "sha256:70657337ec104eb4f3fb229285358f23f045433f6aea26846cdd55f0fd68945c"
], ],
"index": "pypi", "index": "pypi",
"version": "==4.56.0" "version": "==4.57.0"
}, },
"twisted": { "twisted": {
"extras": [ "extras": [
@ -1133,20 +1139,11 @@
}, },
"txaio": { "txaio": {
"hashes": [ "hashes": [
"sha256:1488d31d564a116538cc1265ac3f7979fb6223bb5a9e9f1479436ee2c17d8549", "sha256:7d6f89745680233f1c4db9ddb748df5e88d2a7a37962be174c0fd04c8dba1dc8",
"sha256:a8676d6c68aea1f0e2548c4afdb8e6253873af3bc2659bb5bcd9f39dff7ff90f" "sha256:c16b55f9a67b2419cfdf8846576e2ec9ba94fe6978a83080c352a80db31c93fb"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==20.12.1" "version": "==21.2.1"
},
"typing-extensions": {
"hashes": [
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
],
"markers": "python_version < '3.8'",
"version": "==3.7.4.3"
}, },
"tzlocal": { "tzlocal": {
"hashes": [ "hashes": [
@ -1168,11 +1165,11 @@
"standard" "standard"
], ],
"hashes": [ "hashes": [
"sha256:1079c50a06f6338095b4f203e7861dbff318dde5f22f3a324fc6e94c7654164c", "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202",
"sha256:ef1e0bb5f7941c6fe324e06443ddac0331e1632a776175f87891c7bd02694355" "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.13.3" "version": "==0.13.4"
}, },
"uvloop": { "uvloop": {
"hashes": [ "hashes": [
@ -1188,6 +1185,7 @@
"sha256:e7514d7a48c063226b7d06617cbb12a14278d4323a065a8d46a7962686ce2e95", "sha256:e7514d7a48c063226b7d06617cbb12a14278d4323a065a8d46a7962686ce2e95",
"sha256:f07909cd9fc08c52d294b1570bba92186181ca01fe3dc9ffba68955273dd7362" "sha256:f07909cd9fc08c52d294b1570bba92186181ca01fe3dc9ffba68955273dd7362"
], ],
"index": "pypi",
"version": "==0.14.0" "version": "==0.14.0"
}, },
"watchdog": { "watchdog": {
@ -1215,11 +1213,10 @@
}, },
"watchgod": { "watchgod": {
"hashes": [ "hashes": [
"sha256:59700dab7445aa8e6067a5b94f37bae90fc367554549b1ed2e9d0f4f38a90d2a", "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29",
"sha256:5fb60afa9558b79736395db1cb60ad3ed59df5c2f507a3ff729220cf1251ffdc", "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"
"sha256:e9cca0ab9c63f17fc85df9fd8bd18156ff00aff04ebe5976cee473f4968c6858"
], ],
"version": "==0.6" "version": "==0.7"
}, },
"wcwidth": { "wcwidth": {
"hashes": [ "hashes": [
@ -1273,14 +1270,6 @@
"index": "pypi", "index": "pypi",
"version": "==2.7.4" "version": "==2.7.4"
}, },
"zipp": {
"hashes": [
"sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108",
"sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"
],
"markers": "python_version >= '3.6'",
"version": "==3.4.0"
},
"zope.interface": { "zope.interface": {
"hashes": [ "hashes": [
"sha256:05a97ba92c1c7c26f25c9f671aa1ef85ffead6cdad13770e5b689cf983adc7e1", "sha256:05a97ba92c1c7c26f25c9f671aa1ef85ffead6cdad13770e5b689cf983adc7e1",
@ -1501,11 +1490,11 @@
}, },
"faker": { "faker": {
"hashes": [ "hashes": [
"sha256:0783729c61501d52efea2967aff6e6fcb8370f0f6b5a558f2a81233642ae529a", "sha256:31a58ec5a8f4672f24da3b5ddea02c82a712de1de3179b432948e5c34d787aca",
"sha256:6b2995ffff6c2b02bc5daad96f8c24c021e5bd491d9d53d31bcbd66f348181d4" "sha256:aadfe0efe11ecbbbc5b3b0b0fab050c2acbd2d8e5201769546d43d236bfff663"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==5.8.0" "version": "==6.4.1"
}, },
"filelock": { "filelock": {
"hashes": [ "hashes": [
@ -1532,22 +1521,6 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.2.0" "version": "==1.2.0"
}, },
"importlib-metadata": {
"hashes": [
"sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771",
"sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"
],
"markers": "python_version < '3.8'",
"version": "==3.4.0"
},
"importlib-resources": {
"hashes": [
"sha256:885b8eae589179f661c909d699a546cf10d83692553e34dca1bf5eb06f7f6217",
"sha256:bfdad047bce441405a49cf8eb48ddce5e56c696e185f59147a8b79e75e9e6380"
],
"markers": "python_version < '3.7'",
"version": "==5.1.0"
},
"iniconfig": { "iniconfig": {
"hashes": [ "hashes": [
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
@ -1558,12 +1531,11 @@
}, },
"jinja2": { "jinja2": {
"hashes": [ "hashes": [
"sha256:3f172970d5670703bd3812e8ca6459a9a7e069fa8e51b40195f83c81db191ec4", "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "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": { "markupsafe": {
"hashes": [ "hashes": [
@ -1573,8 +1545,12 @@
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
"sha256:19536834abffb3fa155017053c607cb835b2ecc6a3a2554a88043d991dffb736", "sha256:19536834abffb3fa155017053c607cb835b2ecc6a3a2554a88043d991dffb736",
"sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f",
"sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014",
"sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f",
"sha256:3d61f15e39611aacd91b7e71d903787da86d9e80896e683c0103fced9add7834", "sha256:3d61f15e39611aacd91b7e71d903787da86d9e80896e683c0103fced9add7834",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
@ -1584,36 +1560,51 @@
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85",
"sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:7952deddf24b85c88dab48f6ec366ac6e39d2761b5280f2f9594911e03fcd064", "sha256:7952deddf24b85c88dab48f6ec366ac6e39d2761b5280f2f9594911e03fcd064",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850",
"sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1",
"sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
"sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5",
"sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c",
"sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", "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'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.1" "version": "==1.1.1"
}, },
"packaging": { "packaging": {
"hashes": [ "hashes": [
"sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858", "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
"sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093" "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "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": { "pluggy": {
"hashes": [ "hashes": [
@ -1641,11 +1632,11 @@
}, },
"pygments": { "pygments": {
"hashes": [ "hashes": [
"sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435", "sha256:37a13ba168a02ac54cc5891a42b1caec333e59b66addb7fa633ea8a6d73445c0",
"sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337" "sha256:b21b072d0ccdf29297a82a2363359d99623597b8a265b8081760e4d0f7153c88"
], ],
"markers": "python_version >= '3.5'", "markers": "python_version >= '3.5'",
"version": "==2.7.4" "version": "==2.8.0"
}, },
"pyparsing": { "pyparsing": {
"hashes": [ "hashes": [
@ -1706,11 +1697,11 @@
}, },
"pytest-xdist": { "pytest-xdist": {
"hashes": [ "hashes": [
"sha256:1d8edbb1a45e8e1f8e44b1260583107fc23f8bc8da6d18cb331ff61d41258ecf", "sha256:2447a1592ab41745955fb870ac7023026f20a5f0bfccf1b52a879bd193d46450",
"sha256:f127e11e84ad37cc1de1088cb2990f3c354630d428af3f71282de589c5bb779b" "sha256:718887296892f92683f6a51f25a3ae584993b06f7076ce1e1fd482e59a8220a2"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.2.0" "version": "==2.2.1"
}, },
"python-dateutil": { "python-dateutil": {
"hashes": [ "hashes": [
@ -1722,10 +1713,10 @@
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
"sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4", "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
"sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5" "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
], ],
"version": "==2020.5" "version": "==2021.1"
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
@ -1838,20 +1829,11 @@
}, },
"tox": { "tox": {
"hashes": [ "hashes": [
"sha256:76df3db6eee929bb62bdbacca5bb6bc840669d98e86a015b7a57b7df0a6eaf8b", "sha256:89afa9c59c04beb55eda789c7a65feb1a70fde117f85f1bd1c27c66758456e60",
"sha256:854e6e4a71c614b488f81cb88df3b92edcb1a9ec43d4102e6289e9669bbf7f18" "sha256:ed1e650cf6368bcbc4a071eeeba363c480920e0ed8a9ad1793c7caaa5ad33d49"
], ],
"index": "pypi", "index": "pypi",
"version": "==3.21.3" "version": "==3.22.0"
},
"typing-extensions": {
"hashes": [
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
],
"markers": "python_version < '3.8'",
"version": "==3.7.4.3"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
@ -1863,19 +1845,11 @@
}, },
"virtualenv": { "virtualenv": {
"hashes": [ "hashes": [
"sha256:219ee956e38b08e32d5639289aaa5bd190cfbe7dafcb8fa65407fca08e808f9c", "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d",
"sha256:227a8fed626f2f20a6cdb0870054989f82dd27b2560a911935ba905a2a5e0034" "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "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": [
"sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108",
"sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"
],
"markers": "python_version >= '3.6'",
"version": "==3.4.0"
} }
} }
} }

View File

@ -1,4 +1,5 @@
[![ci](https://github.com/jonaswinkler/paperless-ng/workflows/ci/badge.svg)](https://github.com/jonaswinkler/paperless-ng/actions) [![ci](https://github.com/jonaswinkler/paperless-ng/workflows/ci/badge.svg)](https://github.com/jonaswinkler/paperless-ng/actions)
![Ansible Role](https://github.com/jonaswinkler/paperless-ng/workflows/Ansible%20Role/badge.svg)
[![Documentation Status](https://readthedocs.org/projects/paperless-ng/badge/?version=latest)](https://paperless-ng.readthedocs.io/en/latest/?badge=latest) [![Documentation Status](https://readthedocs.org/projects/paperless-ng/badge/?version=latest)](https://paperless-ng.readthedocs.io/en/latest/?badge=latest)
[![Gitter](https://badges.gitter.im/paperless-ng/community.svg)](https://gitter.im/paperless-ng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Gitter](https://badges.gitter.im/paperless-ng/community.svg)](https://gitter.im/paperless-ng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Docker Hub Pulls](https://img.shields.io/docker/pulls/jonaswinkler/paperless-ng.svg)](https://hub.docker.com/r/jonaswinkler/paperless-ng) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/jonaswinkler/paperless-ng.svg)](https://hub.docker.com/r/jonaswinkler/paperless-ng)
@ -6,15 +7,16 @@
# Paperless-ng # 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:
# Survey * 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. 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.
If you already used Paperless-ng for a bit, would like to give some anonymous feedback, and help me decide on what to focus on next: I've created a survey, [see here](https://github.com/jonaswinkler/paperless-ng/issues/402). Thank you! 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.
*See also [Feature Requests](#feature-requests)*
# How it Works # How it Works
@ -37,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. * 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). * 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)) * 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. * 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. Should be pretty snappy. Will be mobile friendly in the future. * Single page application front end.
* Includes a dashboard that shows basic statistics and has document upload. * Includes a dashboard that shows basic statistics and has document upload.
* Filtering by tags, correspondents, types, and more. * Filtering by tags, correspondents, types, and more.
* Customizable views can be saved and displayed on the dashboard. * Customizable views can be saved and displayed on the dashboard.
@ -49,14 +51,13 @@ Here's what you get:
* Searching for similar documents ("More like this") * Searching for similar documents ("More like this")
* Email processing: Paperless adds documents from your email accounts. * Email processing: Paperless adds documents from your email accounts.
* Configure multiple accounts and filters for each account. * 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 mail, paperless can move these mail to a new folder, mark them as read, flag them as important or delete them.
* Machine learning powered document matching. * 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. * 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. 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).
For a complete list of changes from paperless, check out the [changelog](https://paperless-ng.readthedocs.io/en/latest/changelog.html)
# Getting started # Getting started
@ -76,9 +77,9 @@ The documentation for Paperless-ng is available on [ReadTheDocs](https://paperle
# Translation # Translation
Paperless is currently available in English, German, Dutch and French. 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 # Feature Requests
@ -108,4 +109,4 @@ These projects also exist, but their status and compatibility with paperless-ng
# Important Note # 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.

View File

@ -107,7 +107,7 @@ Example Playbook
paperlessng_db_type: postgresql paperlessng_db_type: postgresql
paperlessng_db_pass: PLEASEPROVIDEASTRONGPASSWORDHERE paperlessng_db_pass: PLEASEPROVIDEASTRONGPASSWORDHERE
paperless_secret_key: AGAINPLEASECHANGETHISNOW paperlessng_secret_key: AGAINPLEASECHANGETHISNOW
paperlessng_ocr_languages: paperlessng_ocr_languages:
- eng - eng

View File

@ -23,14 +23,14 @@ paperlessng_filename_format:
paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv" paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv"
# Hosting & Security # Hosting & Security
paperless_secret_key: PLEASECHANGETHISFORTHELOVEOFGOD paperlessng_secret_key: PLEASECHANGETHISFORTHELOVEOFGOD
paperless_allowed_hosts: "*" paperlessng_allowed_hosts: "*"
paperless_cors_allowed_hosts: http://localhost:8000 paperlessng_cors_allowed_hosts: http://localhost:8000
paperless_force_script_name: paperlessng_force_script_name:
paperless_static_url: /static/ paperlessng_static_url: /static/
paperless_auto_login_username: paperlessng_auto_login_username:
paperless_cookie_prefix: "" paperlessng_cookie_prefix: ""
paperless_enable_http_remote_user: False paperlessng_enable_http_remote_user: False
# OCR settings # OCR settings
paperlessng_ocr_languages: paperlessng_ocr_languages:

View File

@ -38,7 +38,7 @@
- name: verify uploaded document has been accepted - name: verify uploaded document has been accepted
uri: uri:
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/" url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/paperless/"
headers: headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}' Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes return_content: yes
@ -51,7 +51,7 @@
- name: verify uploaded document has been consumed - name: verify uploaded document has been consumed
uri: uri:
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/" url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/paperless/"
headers: headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}' Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes return_content: yes

View File

@ -15,7 +15,6 @@
- imagemagick - imagemagick
- optipng - optipng
- gnupg - gnupg
- libpoppler-cpp-dev
- libpq-dev - libpq-dev
- libmagic-dev - libmagic-dev
- mime-support - mime-support
@ -286,21 +285,21 @@
line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}" line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}"
# Hosting & Security # Hosting & Security
- regexp: PAPERLESS_SECRET_KEY - regexp: PAPERLESS_SECRET_KEY
line: "PAPERLESS_SECRET_KEY={{ paperless_secret_key }}" line: "PAPERLESS_SECRET_KEY={{ paperlessng_secret_key }}"
- regexp: PAPERLESS_ALLOWED_HOSTS - regexp: PAPERLESS_ALLOWED_HOSTS
line: "PAPERLESS_ALLOWED_HOSTS={{ paperless_allowed_hosts }}" line: "PAPERLESS_ALLOWED_HOSTS={{ paperlessng_allowed_hosts }}"
- regexp: PAPERLESS_CORS_ALLOWED_HOSTS - regexp: PAPERLESS_CORS_ALLOWED_HOSTS
line: "PAPERLESS_CORS_ALLOWED_HOSTS={{ paperless_cors_allowed_hosts }}" line: "PAPERLESS_CORS_ALLOWED_HOSTS={{ paperlessng_cors_allowed_hosts }}"
- regexp: PAPERLESS_FORCE_SCRIPT_NAME - regexp: PAPERLESS_FORCE_SCRIPT_NAME
line: "PAPERLESS_FORCE_SCRIPT_NAME={{ paperless_force_script_name }}" line: "PAPERLESS_FORCE_SCRIPT_NAME={{ paperlessng_force_script_name }}"
- regexp: PAPERLESS_STATIC_URL - regexp: PAPERLESS_STATIC_URL
line: "PAPERLESS_STATIC_URL={{ paperless_static_url }}" line: "PAPERLESS_STATIC_URL={{ paperlessng_static_url }}"
- regexp: PAPERLESS_AUTO_LOGIN_USERNAME - regexp: PAPERLESS_AUTO_LOGIN_USERNAME
line: "PAPERLESS_AUTO_LOGIN_USERNAME={{ paperless_auto_login_username }}" line: "PAPERLESS_AUTO_LOGIN_USERNAME={{ paperlessng_auto_login_username }}"
- regexp: PAPERLESS_COOKIE_PREFIX - regexp: PAPERLESS_COOKIE_PREFIX
line: "PAPERLESS_COOKIE_PREFIX={{ paperless_cookie_prefix }}" line: "PAPERLESS_COOKIE_PREFIX={{ paperlessng_cookie_prefix }}"
- regexp: PAPERLESS_ENABLE_HTTP_REMOTE_USER - regexp: PAPERLESS_ENABLE_HTTP_REMOTE_USER
line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperless_enable_http_remote_user }}" line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperlessng_enable_http_remote_user }}"
# OCR settings # OCR settings
- regexp: PAPERLESS_OCR_LANGUAGE - regexp: PAPERLESS_OCR_LANGUAGE
line: "PAPERLESS_OCR_LANGUAGE={{ paperlessng_ocr_languages | join('+') }}" line: "PAPERLESS_OCR_LANGUAGE={{ paperlessng_ocr_languages | join('+') }}"

View File

@ -1,5 +1,5 @@
files: 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 translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po
- source: /src-ui/messages.xlf - 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

View File

@ -0,0 +1,6 @@
for command in document_archiver document_exporter document_importer mail_fetcher document_create_classifier document_index document_renamer document_retagger document_thumbnails document_sanity_checker;
do
echo "installing $command..."
sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command
chmod +x /usr/local/bin/$command
done

View File

@ -0,0 +1,15 @@
#!/bin/bash
set -e
cd /usr/src/paperless/src/
if [[ $(id -u) == 0 ]] ;
then
sudo -HEu paperless python3 manage.py management_command "$@"
elif [[ $(id -un) == "paperless" ]] ;
then
python3 manage.py management_command "$@"
else
echo "Unknown user."
fi

View File

@ -23,6 +23,12 @@ Options available to any installation of paperless:
* The document exporter is also able to update an already existing export. * The document exporter is also able to update an already existing export.
Therefore, incremental backups with ``rsync`` are entirely possible. Therefore, incremental backups with ``rsync`` are entirely possible.
.. caution::
You cannot import the export generated with one version of paperless in a
different version of paperless. The export contains an exact image of the
database, and migrations may change the database layout.
Options available to docker installations: Options available to docker installations:
* Backup the docker volumes. These usually reside within * Backup the docker volumes. These usually reside within
@ -101,17 +107,17 @@ Then you can start paperless-ng with ``-d`` to have it run in the background.
update to newer versions. In order to enable updates as described above, either update to newer versions. In order to enable updates as described above, either
get the new ``docker-compose.yml`` file from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_ get the new ``docker-compose.yml`` file from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
or edit the ``docker-compose.yml`` file, find the line that says or edit the ``docker-compose.yml`` file, find the line that says
.. code:: .. code::
image: jonaswinkler/paperless-ng:0.9.x image: jonaswinkler/paperless-ng:0.9.x
and replace the version with ``latest``: and replace the version with ``latest``:
.. code:: .. code::
image: jonaswinkler/paperless-ng:latest image: jonaswinkler/paperless-ng:latest
Bare Metal Route Bare Metal Route
================ ================
@ -171,26 +177,63 @@ Most of the update process is automated when using the ansible role.
$ ansible-playbook playbook.yml $ ansible-playbook playbook.yml
Downgrading Paperless
#####################
Downgrades are possible. However, some updates also contain database migrations (these change the layout of the database and may move data).
In order to move back from a version that applied database migrations, you'll have to revert the database migration *before* downgrading,
and then downgrade paperless.
This table lists the most recent database migrations for each versions:
+---------+-------------------------+
| Version | Latest migration number |
+---------+-------------------------+
| 1.0.0 | 1011 |
+---------+-------------------------+
| 1.1.0 | 1011 |
+---------+-------------------------+
| 1.1.1 | 1012 |
+---------+-------------------------+
Execute the following management command to migrate your database:
.. code:: shell-session
$ python3 manage.py migrate documents <migration number>
.. note::
Some migrations cannot be undone. The command will issue errors if that happens.
.. _utilities-management-commands:
Management utilities Management utilities
#################### ####################
Paperless comes with some management commands that perform various maintenance Paperless comes with some management commands that perform various maintenance
tasks on your paperless instance. You can invoke these commands either by tasks on your paperless instance. You can invoke these commands in the following way:
With docker-compose, while paperless is running:
.. code:: shell-session .. code:: shell-session
$ cd /path/to/paperless $ cd /path/to/paperless
$ docker-compose run --rm webserver <command> <arguments> $ docker-compose exec webserver <command> <arguments>
or With docker, while paperless is running:
.. code:: shell-session
$ docker exec -it <container-name> <command> <arguments>
Bare metal:
.. code:: shell-session .. code:: shell-session
$ cd /path/to/paperless/src $ cd /path/to/paperless/src
$ python3 manage.py <command> <arguments> $ python3 manage.py <command> <arguments>
depending on whether you use docker or not.
All commands have built-in help, which can be accessed by executing them with All commands have built-in help, which can be accessed by executing them with
the argument ``--help``. the argument ``--help``.
@ -210,7 +253,7 @@ backup or migration to another DMS.
-c, --compare-checksums -c, --compare-checksums
-f, --use-filename-format -f, --use-filename-format
-d, --delete -d, --delete
``target`` is a folder to which the data gets written. This includes documents, ``target`` is a folder to which the data gets written. This includes documents,
thumbnails and a ``manifest.json`` file. The manifest contains all metadata from thumbnails and a ``manifest.json`` file. The manifest contains all metadata from
the database (correspondents, tags, etc). the database (correspondents, tags, etc).
@ -367,6 +410,34 @@ the naming scheme.
The command takes no arguments and processes all your documents at once. The command takes no arguments and processes all your documents at once.
.. _utilities-sanity-checker:
Sanity checker
==============
Paperless has a built-in sanity checker that inspects your document collection for issues.
The issues detected by the sanity checker are as follows:
* Missing original files.
* Missing archive files.
* Inaccessible original files due to improper permissions.
* Inaccessible archive files due to improper permissions.
* Corrupted original documents by comparing their checksum against what is stored in the database.
* Corrupted archive documents by comparing their checksum against what is stored in the database.
* Missing thumbnails.
* Inaccessible thumbnails due to improper permissions.
* Documents without any content (warning).
* Orphaned files in the media directory (warning). These are files that are not referenced by any document im paperless.
.. code::
document_sanity_checker
The command takes no arguments. Depending on the size of your document archive, this may take some time.
Fetching e-mail Fetching e-mail
=============== ===============

View File

@ -217,6 +217,7 @@ will create a directory structure as follows:
Paperless provides the following placeholders withing filenames: Paperless provides the following placeholders withing filenames:
* ``{asn}``: The archive serial number of the document, or "none".
* ``{correspondent}``: The name of the correspondent, or "none". * ``{correspondent}``: The name of the correspondent, or "none".
* ``{document_type}``: The name of the document type, or "none". * ``{document_type}``: The name of the document type, or "none".
* ``{tag_list}``: A comma separated list of all tags assigned to the document. * ``{tag_list}``: A comma separated list of all tags assigned to the document.

View File

@ -5,6 +5,106 @@
Changelog Changelog
********* *********
paperless-ng 1.2.1
##################
* `Rodrigo Avelino <https://github.com/rodavelino>`_ 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. Thanks to `Joel Nordell <https://github.com/joelnordell>`_.
* When using regular expression matching, the regular expression is now validated before saving the tag/correspondent/type.
paperless-ng 1.2.0
##################
* Changes to the OCRmyPDF integration
* Added support for deskewing and automatic rotation of incorrectly rotated pages. This is enabled by default, see :ref:`configuration-ocr`.
* Better support for encrypted files.
* Better support for various other PDF files: Paperless will now attempt to force OCR with safe options when OCR fails with the configured options.
* Added an explicit option to skip cleaning with ``unpaper``.
* Download multiple selected documents as a zip archive.
* The document list now remembers the current page.
* Improved responsiveness when switching between saved views and the document list.
* Increased the default wait time when observing files in the consumption folder
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::
Some packages that paperless depends on are slowly dropping Python 3.6
support one after another, including the web server. Supporting Python
3.6 means that I cannot update these packages anymore.
At some point, paperless will drop Python 3.6 support. If using a bare
metal installation and you're still on Python 3.6, upgrade to 3.7 or newer.
If using docker, this does not affect you.
paperless-ng 1.1.4
##################
* Added English (GB) locale.
* Added ISO-8601 date display option.
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
##################
* Always show top left corner of thumbnails, even for extra wide documents.
* Added a management command for executing the sanity checker directly.
See :ref:`utilities-sanity-checker`.
* The weekly sanity check now reports messages in the log files.
* Fixed an issue with the metadata tab not reporting anything in case of missing files.
* Reverted a change from 1.1.0 that caused huge memory usage due to redis caching.
* Some memory usage optimizations.
paperless-ng 1.1.1
##################
This release contains new database migrations.
* Fixed a bug in the sanity checker that would cause it to display "x not in list" errors instead of actual issues.
* Fixed a bug with filename generation for archive filenames that would cause the archive files of two documents to overlap.
* This happened when ``PAPERLESS_FILENAME_FORMAT`` is used and the filenames of two or more documents are the same, except for the file extension.
* Paperless will now store the archive filename in the database as well instead of deriving it from the original filename, and use the
same logic for detecting and avoiding filename clashes that's also used for original filenames.
* The migrations will repair any missing archive files. If you're using tika, ensure that tika is running while performing the migration. Docker-compose will take care of that.
* Fixed a bug with thumbnail regeneration when TIKA integration was used.
* Added ASN as a placeholder field to the filename format.
* The docker image now comes with built-in shortcuts for most management commands. These are now the recommended way to execute management commands, since these
also ensure that they're always executed as the paperless user and you're less likely to run into permission issues. See :ref:`utilities-management-commands`.
paperless-ng 1.1.0 paperless-ng 1.1.0
################## ##################
@ -17,7 +117,7 @@ paperless-ng 1.1.0
or added with one of the mobile apps. or added with one of the mobile apps.
* Documents are successfully added to paperless. * Documents are successfully added to paperless.
* Document consumption failed (with error messages) * Document consumption failed (with error messages)
* Configuration options to enable/disable individual notifications. * Configuration options to enable/disable individual notifications.
* Live updates to document lists and saved views when new documents are added. * Live updates to document lists and saved views when new documents are added.
@ -37,6 +137,8 @@ paperless-ng 1.1.0
Paperless will continue to work with WSGI, but you will not get any status notifications. Paperless will continue to work with WSGI, but you will not get any status notifications.
Apache ``mod_wsgi`` users, see :ref:`this note <faq-mod_wsgi>`.
* Paperless now offers suggestions for tags, correspondents and types on the document detail page. * Paperless now offers suggestions for tags, correspondents and types on the document detail page.
* Added an interactive easy install script that automatically downloads, configures and starts paperless with docker. * Added an interactive easy install script that automatically downloads, configures and starts paperless with docker.
@ -56,6 +158,13 @@ paperless-ng 1.1.0
* Better info section in the side bar. * Better info section in the side bar.
* Paperless no longer logs to the database. Instead, logs are written to rotating log files. This solves many "database is locked"
issues on Raspberry Pi, especially when SQLite is used.
* By default, log files are written to ``PAPERLESS_DATA_DIR/log/``. Logging settings can be adjusted with
``PAPERLESS_LOGGING_DIR``, ``PAPERLESS_LOGROTATE_MAX_SIZE`` and
``PAPERLESS_LOGROTATE_MAX_BACKUPS``.
paperless-ng 1.0.0 paperless-ng 1.0.0
################## ##################

View File

@ -28,7 +28,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Paperless-ng' project = u'Paperless-ng'
copyright = u'2015, Daniel Quinn' copyright = u'2021, Daniel Quinn, Jonas Winkler'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the

View File

@ -72,13 +72,13 @@ PAPERLESS_CONSUMPTION_DIR=<path>
container. Change the local consumption directory in the docker-compose.yml container. Change the local consumption directory in the docker-compose.yml
file instead. file instead.
Defaults to "../consume", relative to the "src" directory. Defaults to "../consume/", relative to the "src" directory.
PAPERLESS_DATA_DIR=<path> PAPERLESS_DATA_DIR=<path>
This is where paperless stores all its data (search index, SQLite database, This is where paperless stores all its data (search index, SQLite database,
classification model, etc). classification model, etc).
Defaults to "../data", relative to the "src" directory. Defaults to "../data/", relative to the "src" directory.
PAPERLESS_MEDIA_ROOT=<path> PAPERLESS_MEDIA_ROOT=<path>
This is where your documents and thumbnails are stored. This is where your documents and thumbnails are stored.
@ -86,7 +86,7 @@ PAPERLESS_MEDIA_ROOT=<path>
You can set this and PAPERLESS_DATA_DIR to the same folder to have paperless You can set this and PAPERLESS_DATA_DIR to the same folder to have paperless
store all its data within the same volume. store all its data within the same volume.
Defaults to "../media", relative to the "src" directory. Defaults to "../media/", relative to the "src" directory.
PAPERLESS_STATICDIR=<path> PAPERLESS_STATICDIR=<path>
Override the default STATIC_ROOT here. This is where all static files Override the default STATIC_ROOT here. This is where all static files
@ -94,7 +94,7 @@ PAPERLESS_STATICDIR=<path>
Unless you're doing something fancy, there is no need to override this. Unless you're doing something fancy, there is no need to override this.
Defaults to "../static", relative to the "src" directory. Defaults to "../static/", relative to the "src" directory.
PAPERLESS_FILENAME_FORMAT=<format> PAPERLESS_FILENAME_FORMAT=<format>
Changes the filenames paperless uses to store documents in the media directory. Changes the filenames paperless uses to store documents in the media directory.
@ -102,6 +102,25 @@ PAPERLESS_FILENAME_FORMAT=<format>
Default is none, which disables this feature. Default is none, which disables this feature.
PAPERLESS_LOGGING_DIR=<path>
This is where paperless will store log files.
Defaults to "``PAPERLESS_DATA_DIR``/log/".
Logging
#######
PAPERLESS_LOGROTATE_MAX_SIZE=<num>
Maximum file size for log files before they are rotated, in bytes.
Defaults to 1 MiB.
PAPERLESS_LOGROTATE_MAX_BACKUPS=<num>
Number of rotated log files to keep.
Defaults to 20.
Hosting & Security Hosting & Security
################## ##################
@ -183,7 +202,6 @@ Paperless uses `OCRmyPDF <https://ocrmypdf.readthedocs.io/en/latest/>`_ for
performing OCR on documents and images. Paperless uses sensible defaults for performing OCR on documents and images. Paperless uses sensible defaults for
most settings, but all of them can be configured to your needs. most settings, but all of them can be configured to your needs.
PAPERLESS_OCR_LANGUAGE=<lang> PAPERLESS_OCR_LANGUAGE=<lang>
Customize the language that paperless will attempt to use when Customize the language that paperless will attempt to use when
parsing documents. parsing documents.
@ -226,6 +244,54 @@ PAPERLESS_OCR_MODE=<mode>
The default is ``skip``, which only performs OCR when necessary and always The default is ``skip``, which only performs OCR when necessary and always
creates archived documents. creates archived documents.
Read more about this in the `OCRmyPDF documentation <https://ocrmypdf.readthedocs.io/en/latest/advanced.html#when-ocr-is-skipped>`_.
PAPERLESS_OCR_CLEAN=<mode>
Tells paperless to use ``unpaper`` to clean any input document before
sending it to tesseract. This uses more resources, but generally results
in better OCR results. The following modes are available:
* ``clean``: Apply unpaper.
* ``clean-final``: Apply unpaper, and use the cleaned images to build the
output file instead of the original images.
* ``none``: Do not apply unpaper.
Defaults to ``clean``.
.. note::
``clean-final`` is incompatible with ocr mode ``redo``. When both
``clean-final`` and the ocr mode ``redo`` is configured, ``clean``
is used instead.
PAPERLESS_OCR_DESKEW=<bool>
Tells paperless to correct skewing (slight rotation of input images mainly
due to improper scanning)
Defaults to ``true``, which enables this feature.
.. note::
Deskewing is incompatible with ocr mode ``redo``. Deskewing will get
disabled automatically if ``redo`` is used as the ocr mode.
PAPERLESS_OCR_ROTATE_PAGES=<bool>
Tells paperless to correct page rotation (90°, 180° and 270° rotation).
If you notice that paperless is not rotating incorrectly rotated
pages (or vice versa), try adjusting the threshold up or down (see below).
Defaults to ``true``, which enables this feature.
PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD=<num>
Adjust the threshold for automatic page rotation by ``PAPERLESS_OCR_ROTATE_PAGES``.
This is an arbitrary value reported by tesseract. "15" is a very conservative value,
whereas "2" is a very aggressive option and will often result in correctly rotated pages
being rotated as well.
Defaults to "12".
PAPERLESS_OCR_OUTPUT_TYPE=<type> PAPERLESS_OCR_OUTPUT_TYPE=<type>
Specify the the type of PDF documents that paperless should produce. Specify the the type of PDF documents that paperless should produce.
@ -252,7 +318,6 @@ PAPERLESS_OCR_PAGES=<num>
Defaults to 0, which disables this feature and always uses all pages. Defaults to 0, which disables this feature and always uses all pages.
PAPERLESS_OCR_IMAGE_DPI=<num> PAPERLESS_OCR_IMAGE_DPI=<num>
Paperless will OCR any images you put into the system and convert them Paperless will OCR any images you put into the system and convert them
into PDF documents. This is useful if your scanner produces images. into PDF documents. This is useful if your scanner produces images.
@ -263,8 +328,8 @@ PAPERLESS_OCR_IMAGE_DPI=<num>
Set this to the DPI your scanner produces images at. Set this to the DPI your scanner produces images at.
Default is none, which causes paperless to fail if no DPI information is Default is none, which will automatically calculate image DPI so that
present in an image. the produced PDF documents are A4 sized.
PAPERLESS_OCR_USER_ARGS=<json> PAPERLESS_OCR_USER_ARGS=<json>
@ -273,7 +338,7 @@ PAPERLESS_OCR_USER_ARGS=<json>
the API of OCRmyPDF, you have to specify these in a format that can be the API of OCRmyPDF, you have to specify these in a format that can be
passed to the API. See `the API reference of OCRmyPDF <https://ocrmypdf.readthedocs.io/en/latest/api.html#reference>`_ passed to the API. See `the API reference of OCRmyPDF <https://ocrmypdf.readthedocs.io/en/latest/api.html#reference>`_
for valid parameters. All command line options are supported, but they for valid parameters. All command line options are supported, but they
use underscores instead of dashed. use underscores instead of dashes.
.. caution:: .. caution::
@ -333,7 +398,7 @@ requires are as follows:
PAPERLESS_TIKA_ENABLED: 1 PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000 PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998 PAPERLESS_TIKA_ENDPOINT: http://tika:9998
# ... # ...
gotenberg: gotenberg:
@ -536,3 +601,65 @@ PAPERLESS_GS_BINARY=<path>
PAPERLESS_OPTIPNG_BINARY=<path> PAPERLESS_OPTIPNG_BINARY=<path>
Defaults to "/usr/bin/optipng". Defaults to "/usr/bin/optipng".
.. _configuration-docker:
Docker-specific options
#######################
These options don't have any effect in ``paperless.conf``. These options adjust
the behavior of the docker container. Configure these in `docker-compose.env`.
PAPERLESS_WEBSERVER_WORKERS=<num>
The number of worker processes the webserver should spawn. More worker processes
usually result in the front end to load data much quicker. However, each worker process
also loads the entire application into memory separately, so increasing this value
will increase RAM usage.
Consider configuring this to 1 on low power devices with limited amount of RAM.
Defaults to 2.
USERMAP_UID=<uid>
The ID of the paperless user in the container. Set this to your actual user ID on the
host system, which you can get by executing
.. code:: shell-session
$ id -u
Paperless will change ownership on its folders to this user, so you need to get this right
in order to be able to write to the consumption directory.
Defaults to 1000.
USERMAP_GID=<gid>
The ID of the paperless Group in the container. Set this to your actual group ID on the
host system, which you can get by executing
.. code:: shell-session
$ id -g
Paperless will change ownership on its folders to this group, so you need to get this right
in order to be able to write to the consumption directory.
Defaults to 1000.
PAPERLESS_OCR_LANGUAGES=<list>
Additional OCR languages to install. By default, paperless comes with
English, German, Italian, Spanish and French. If your language is not in this list, install
additional languages with this configuration option:
.. code:: bash
PAPERLESS_OCR_LANGUAGES=tur ces
To actually use these languages, also set the default OCR language of paperless:
.. code:: bash
PAPERLESS_OCR_LANGUAGE=tur
Defaults to none, which does not install any additional languages.

View File

@ -109,6 +109,30 @@ This will build the front end and put it in a location from which the Django ser
it as static content. This way, you can verify that authentication is working. it as static content. This way, you can verify that authentication is working.
Building the documentation
==========================
The documentation is built using sphinx. I've configured ReadTheDocs to automatically build
the documentation when changes are pushed. If you want to build the documentation locally,
this is how you do it:
1. Install python dependencies.
.. code:: shell-session
$ cd /path/to/paperless
$ pipenv install --dev
2. Build the documentation
.. code:: shell-session
$ cd /path/to/paperless/docs
$ pipenv run make clean html
This will build the HTML documentation, and put the resulting files in the ``_build/html``
directory.
Extending Paperless Extending Paperless
=================== ===================

View File

@ -62,9 +62,9 @@ file extensions do not matter.
**A:** The short answer is yes. I've tested it on a Raspberry Pi 3 B. **A:** The short answer is yes. I've tested it on a Raspberry Pi 3 B.
The long answer is that certain parts of The long answer is that certain parts of
Paperless will run very slow, such as the tesseract OCR. On Raspberry Pi, Paperless will run very slow, such as the OCR. On Raspberry Pi,
try to OCR documents before feeding them into paperless so that paperless can try to OCR documents before feeding them into paperless so that paperless can
reuse the text. The web interface should be a lot snappier, since it runs reuse the text. The web interface is a lot snappier, since it runs
in your browser and paperless has to do much less work to serve the data. in your browser and paperless has to do much less work to serve the data.
.. note:: .. note::
@ -76,7 +76,14 @@ in your browser and paperless has to do much less work to serve the data.
**Q:** *How do I install paperless-ng on Raspberry Pi?* **Q:** *How do I install paperless-ng on Raspberry Pi?*
**A:** Docker images are available for arm and arm64 hardware, so just follow **A:** Docker images are available for arm and arm64 hardware, so just follow
the docker-compose instructions, or go the bare metal route. the docker-compose instructions. Apart from more required disk space compared to
a bare metal installation, docker comes with close to zero overhead, even on
Raspberry Pi.
If you decide to got with the bare metal route, be aware that some of the
python requirements do not have precompiled packages for ARM / ARM64. Installation
of these will require additional development libraries and compilation will take
a long time.
**Q:** *How do I run this on unRaid?* **Q:** *How do I run this on unRaid?*
@ -95,12 +102,21 @@ occasionally build the image on and see if it works.
**Q:** *How do I proxy this with NGINX?* **Q:** *How do I proxy this with NGINX?*
.. code:: **A:** See :ref:`here <setup-nginx>`.
location / { .. _faq-mod_wsgi:
proxy_pass http://localhost:8000/
}
And that's about it. Paperless serves everything, including static files by itself **Q:** *How do I get WebSocket support with Apache mod_wsgi*?
when running the docker image. If you want to do anything fancy, you have to
install paperless bare metal. **A:** ``mod_wsgi`` by itself does not support ASGI. Paperless will continue
to work with WSGI, but certain features such as status notifications about
document consumption won't be available.
If you want to continue using ``mod_wsgi``, you will have to run an ASGI-enabled
web server as well that processes WebSocket connections, and configure Apache to
redirect WebSocket connections to this server. Multiple options for ASGI servers
exist:
* ``gunicorn`` with ``uvicorn`` as the worker implementation (the default of paperless)
* ``daphne`` as a standalone server, which is the reference implementation for ASGI.
* ``uvicorn`` as a standalone server

View File

@ -70,8 +70,8 @@ Contents
configuration configuration
api api
faq faq
extending
troubleshooting troubleshooting
extending
contributing contributing
scanners scanners
screenshots screenshots

View File

@ -81,6 +81,7 @@ Installation
You can go multiple routes to setup and run Paperless: You can go multiple routes to setup and run Paperless:
* :ref:`Use the easy install docker script <setup-docker_script>`
* :ref:`Pull the image from Docker Hub <setup-docker_hub>` * :ref:`Pull the image from Docker Hub <setup-docker_hub>`
* :ref:`Build the Docker image yourself <setup-docker_build>` * :ref:`Build the Docker image yourself <setup-docker_build>`
* :ref:`Install Paperless directly on your system manually (bare metal) <setup-bare_metal>` * :ref:`Install Paperless directly on your system manually (bare metal) <setup-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/ .. _CLI Basics: https://sehn.tech/post/devops-with-docker/
.. _idempotent: https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#Idempotency .. _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: .. _setup-docker_hub:
Install Paperless from Docker Hub Install Paperless from Docker Hub
@ -157,7 +176,10 @@ Install Paperless from Docker Hub
5. Modify ``docker-compose.env``, following the comments in the file. The 5. Modify ``docker-compose.env``, following the comments in the file. The
most important change is to set ``USERMAP_UID`` and ``USERMAP_GID`` 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 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 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 1000 (the default for the first normal user on most systems), it will
@ -176,14 +198,10 @@ Install Paperless from Docker Hub
with the default configuration. You will need to use ``PAPERLESS_CONSUMER_POLLING``, with the default configuration. You will need to use ``PAPERLESS_CONSUMER_POLLING``,
which will disable inotify. See :ref:`here <configuration-polling>`. which will disable inotify. See :ref:`here <configuration-polling>`.
6. Now head over to: https://hub.docker.com/r/jonaswinkler/paperless-ng and choose your preferred 6. Run ``docker-compose pull``, followed by ``docker-compose up -d``.
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. This will pull the image, create and start the necessary containers.
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 7. To be able to login, you will need a super user. To create it, execute the
containers, but your are not done yet!
8. To be able to login, you will need a super user. To create it, execute the
following command: following command:
.. code-block:: shell-session .. code-block:: shell-session
@ -193,8 +211,8 @@ Install Paperless from Docker Hub
This will prompt you to set a username, an optional e-mail address and This will prompt you to set a username, an optional e-mail address and
finally a password (at least 8 characters). finally a password (at least 8 characters).
9. The default ``docker-compose.yml`` exports the webserver on your local port 8. 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 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. 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. Use the login credentials you have created with the previous step.
@ -203,7 +221,7 @@ Install Paperless from Docker Hub
.. _setup-docker_build: .. _setup-docker_build:
Build the docker image yourself Build the Docker image yourself
=============================== ===============================
1. Clone the entire repository of paperless: 1. Clone the entire repository of paperless:
@ -234,14 +252,14 @@ Build the docker image yourself
4. Run the ``compile-frontend.sh`` script. This requires ``node`` and ``npm >= v15``. 4. Run the ``compile-frontend.sh`` script. This requires ``node`` and ``npm >= v15``.
5. Follow steps 2 to 7 of :ref:`setup-docker_hub`. When asked to run 5. Follow steps 3 to 8 of :ref:`setup-docker_hub`. When asked to run
``docker-compose up -d`` to start the containers, do ``docker-compose pull`` to pull the image, do
.. code:: shell-session .. code:: shell-session
$ docker-compose build $ docker-compose build
before that to build the image. instead to build the image.
.. _setup-bare_metal: .. _setup-bare_metal:
@ -262,7 +280,6 @@ writing. Windows is not and will never be supported.
* ``imagemagick`` >= 6 for PDF conversion * ``imagemagick`` >= 6 for PDF conversion
* ``optipng`` for optimizing thumbnails * ``optipng`` for optimizing thumbnails
* ``gnupg`` for handling encrypted documents * ``gnupg`` for handling encrypted documents
* ``libpoppler-cpp-dev`` for PDF to text conversion
* ``libpq-dev`` for PostgreSQL * ``libpq-dev`` for PostgreSQL
* ``libmagic-dev`` for mime type detection * ``libmagic-dev`` for mime type detection
* ``mime-support`` for mime type detection * ``mime-support`` for mime type detection
@ -291,7 +308,7 @@ writing. Windows is not and will never be supported.
2. Install ``redis`` >= 5.0 and configure it to start automatically. 2. Install ``redis`` >= 5.0 and configure it to start automatically.
3. Optional. Install ``postgresql`` and configure a database, user and password for paperless. If you do not wish 3. Optional. Install ``postgresql`` and configure a database, user and password for paperless. If you do not wish
to use PostgreSQL, SQLite is avialable as well. to use PostgreSQL, SQLite is available as well.
4. Get the release archive from `<https://github.com/jonaswinkler/paperless-ng/releases>`_. 4. Get the release archive from `<https://github.com/jonaswinkler/paperless-ng/releases>`_.
If you clone the git repo as it is, you also have to compile the front end by yourself. If you clone the git repo as it is, you also have to compile the front end by yourself.
@ -315,8 +332,14 @@ writing. Windows is not and will never be supported.
* Set ``PAPERLESS_OCR_LANGUAGE`` to the language most of your documents are written in. * Set ``PAPERLESS_OCR_LANGUAGE`` to the language most of your documents are written in.
* Set ``PAPERLESS_TIME_ZONE`` to your local time zone. * Set ``PAPERLESS_TIME_ZONE`` to your local time zone.
6. Setup permissions. Create a system users under which you wish to run paperless. Ensure that these directories exist 6. Create a system user under which you wish to run paperless.
and that the user has write permissions to the following directories
.. code:: shell-session
adduser paperless --system --home /opt/paperless --group
7. Ensure that these directories exist
and that the paperless user has write permissions to the following directories:
* ``/opt/paperless/media`` * ``/opt/paperless/media``
* ``/opt/paperless/data`` * ``/opt/paperless/data``
@ -324,30 +347,32 @@ writing. Windows is not and will never be supported.
Adjust as necessary if you configured different folders. Adjust as necessary if you configured different folders.
7. Install python requirements from the ``requirements.txt`` file. 8. Install python requirements from the ``requirements.txt`` file.
It is up to you if you wish to use a virtual environment or not. It is up to you if you wish to use a virtual environment or not.
.. code:: shell-session .. code:: shell-session
pip3 install -r requirements.txt sudo -Hu paperless pip3 install -r requirements.txt
This will install all python dependencies in the home directory of
the new paperless user.
8. Go to ``/opt/paperless/src``, and execute the following commands: 9. Go to ``/opt/paperless/src``, and execute the following commands:
.. code:: bash .. code:: bash
# This creates the database schema. # This creates the database schema.
python3 manage.py migrate sudo -Hu paperless python3 manage.py migrate
# This creates your first paperless user # This creates your first paperless user
python3 manage.py createsuperuser sudo -Hu paperless python3 manage.py createsuperuser
9. Optional: Test that paperless is working by executing 10. Optional: Test that paperless is working by executing
.. code:: bash .. code:: bash
# This collects static files from paperless and django. # This collects static files from paperless and django.
python3 manage.py runserver sudo -Hu paperless python3 manage.py runserver
and pointing your browser to http://localhost:8000/. and pointing your browser to http://localhost:8000/.
@ -362,7 +387,7 @@ writing. Windows is not and will never be supported.
This will not start the consumer. Paperless does this in a This will not start the consumer. Paperless does this in a
separate process. separate process.
10. Setup systemd services to run paperless automatically. You may 11. Setup systemd services to run paperless automatically. You may
use the service definition files included in the ``scripts`` folder use the service definition files included in the ``scripts`` folder
as a starting point. as a starting point.
@ -378,14 +403,7 @@ writing. Windows is not and will never be supported.
These services rely on redis and optionally the database server, but These services rely on redis and optionally the database server, but
don't need to be started in any particular order. The example files don't need to be started in any particular order. The example files
depend on redis being started. If you use a database server, you should depend on redis being started. If you use a database server, you should
add additinal dependencies. add additional dependencies.
.. hint::
You may optionally set up your preferred web server to serve
paperless as a wsgi application directly instead of running the
``webserver`` service. The module containing the wsgi application
is named ``paperless.wsgi``.
.. caution:: .. caution::
@ -394,10 +412,13 @@ writing. Windows is not and will never be supported.
however, the documentation of GUnicorn states that you should however, the documentation of GUnicorn states that you should
use a proxy server in front of gunicorn instead. use a proxy server in front of gunicorn instead.
11. Optional: Install a samba server and make the consumption folder For instructions on how to use nginx for that,
:ref:`see the instructions below <setup-nginx>`.
12. Optional: Install a samba server and make the consumption folder
available as a network share. available as a network share.
12. Configure ImageMagick to allow processing of PDF documents. Most distributions have 13. Configure ImageMagick to allow processing of PDF documents. Most distributions have
this disabled by default, since PDF documents can contain malware. If this disabled by default, since PDF documents can contain malware. If
you don't do this, paperless will fall back to ghostscript for certain steps you don't do this, paperless will fall back to ghostscript for certain steps
such as thumbnail generation. such as thumbnail generation.
@ -414,7 +435,7 @@ writing. Windows is not and will never be supported.
<policy domain="coder" rights="read|write" pattern="PDF" /> <policy domain="coder" rights="read|write" pattern="PDF" />
13. Optional: Install the `jbig2enc <https://ocrmypdf.readthedocs.io/en/latest/jbig2.html>`_ 14. Optional: Install the `jbig2enc <https://ocrmypdf.readthedocs.io/en/latest/jbig2.html>`_
encoder. This will reduce the size of generated PDF documents. You'll most likely need encoder. This will reduce the size of generated PDF documents. You'll most likely need
to compile this by yourself, because this software has been patented until around 2017 and to compile this by yourself, because this software has been patented until around 2017 and
binary packages are not available for most distributions. binary packages are not available for most distributions.
@ -423,9 +444,10 @@ writing. Windows is not and will never be supported.
Install Paperless using ansible Install Paperless using ansible
=============================== ===============================
.. note::
This role currently only supports Debian 10 Buster and Ubuntu 20.04 Focal or later as target hosts. .. note::
This role currently only supports Debian 10 Buster and Ubuntu 20.04 Focal or later as target hosts.
1. Install ansible 2.7+ on the management node. 1. Install ansible 2.7+ on the management node.
This may be the target host paperless-ng is being installed on or any remote host which can access the target host. This may be the target host paperless-ng is being installed on or any remote host which can access the target host.
@ -462,7 +484,7 @@ Install Paperless using ansible
.. code:: sh .. code:: sh
cd paperless-ng cd paperless-ng
git checkout ng-0.9.14 git checkout ng-1.0.0
3. Create an ansible ``playbook.yml`` in the paperless-ng root directory: 3. Create an ansible ``playbook.yml`` in the paperless-ng root directory:
@ -512,7 +534,7 @@ Install Paperless using ansible
.. code:: yaml .. code:: yaml
paperless_secret_key: PleaseGenerateAStrongKeyForThis paperlessng_secret_key: PleaseGenerateAStrongKeyForThis
paperlessng_superuser_name: YourUserName paperlessng_superuser_name: YourUserName
paperlessng_superuser_email: name@domain.tld paperlessng_superuser_email: name@domain.tld
@ -570,7 +592,7 @@ Migration to paperless-ng is then performed in a few simple steps:
paperless. paperless.
3. Download the latest release of paperless-ng. You can either go with the 3. Download the latest release of paperless-ng. You can either go with the
docker-compose files from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_ docker-compose files from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`__
or clone the repository to build the image yourself (see :ref:`above <setup-docker_build>`). or clone the repository to build the image yourself (see :ref:`above <setup-docker_build>`).
You can either replace your current paperless folder or put paperless-ng You can either replace your current paperless folder or put paperless-ng
in a different location. in a different location.
@ -740,7 +762,8 @@ configuring some options in paperless can help improve performance immensely:
* Stick with SQLite to save some resources. * Stick with SQLite to save some resources.
* Consider setting ``PAPERLESS_OCR_PAGES`` to 1, so that paperless will only OCR * Consider setting ``PAPERLESS_OCR_PAGES`` to 1, so that paperless will only OCR
the first page of your documents. the first page of your documents. In most cases, this page contains enough
information to be able to find it.
* ``PAPERLESS_TASK_WORKERS`` and ``PAPERLESS_THREADS_PER_WORKER`` are configured * ``PAPERLESS_TASK_WORKERS`` and ``PAPERLESS_THREADS_PER_WORKER`` are configured
to use all cores. The Raspberry Pi models 3 and up have 4 cores, meaning that to use all cores. The Raspberry Pi models 3 and up have 4 cores, meaning that
paperless will use 2 workers and 2 threads per worker. This may result in paperless will use 2 workers and 2 threads per worker. This may result in
@ -751,8 +774,13 @@ configuring some options in paperless can help improve performance immensely:
your documents before feeding them into paperless. Some scanners are able to your documents before feeding them into paperless. Some scanners are able to
do this! You might want to even specify ``skip_noarchive`` to skip archive do this! You might want to even specify ``skip_noarchive`` to skip archive
file generation for already ocr'ed documents entirely. file generation for already ocr'ed documents entirely.
* If you want to perform OCR on the the device, consider using ``PAPERLESS_OCR_CLEAN=none``.
This will speed up OCR times and use less memory at the expense of slightly worse
OCR results.
* Set ``PAPERLESS_OPTIMIZE_THUMBNAILS`` to 'false' if you want faster consumption * Set ``PAPERLESS_OPTIMIZE_THUMBNAILS`` to 'false' if you want faster consumption
times. Thumbnails will be about 20% larger. times. Thumbnails will be about 20% larger.
* If using docker, consider setting ``PAPERLESS_WEBSERVER_WORKERS`` to
1. This will save some memory.
For details, refer to :ref:`configuration`. For details, refer to :ref:`configuration`.
@ -769,3 +797,46 @@ For details, refer to :ref:`configuration`.
well as on any other device. well as on any other device.
.. _redis: https://redis.io/ .. _redis: https://redis.io/
.. _setup-nginx:
Using nginx as a reverse proxy
##############################
If you want to expose paperless to the internet, you should hide it behind a
reverse proxy with SSL enabled.
In addition to the usual configuration for SSL,
the following configuration is required for paperless to operate:
.. code:: nginx
http {
# Adjust as required. This is the maximum size for file uploads.
# The default value 1M might be a little too small.
client_max_body_size 10M;
server {
location / {
# Adjust host and port as required.
proxy_pass http://localhost:8000/;
# These configuration options are required for WebSockets to work.
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
}
}
Also read `this <https://channels.readthedocs.io/en/stable/deploying.html#nginx-supervisor-ubuntu>`__, towards the end of the section.

View File

@ -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, to these folders. This happens when pointing these directories to NFS shares,
for example. for example.
Ensure that `chown` is possible on these directories. Ensure that ``chown`` is possible on these directories.
Classifier error: No training data available 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. 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. The algorithm will only learn from documents not in your inbox.
UserWarning in sklearn on every single document UserWarning in sklearn on every single document
############################################### ###############################################
@ -91,6 +93,31 @@ 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 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. the file ``classification_model.pickle`` in the data directory and let paperless recreate it.
504 Server Error: Gateway Timeout when adding Office documents
##############################################################
You may experience these errors when using the optional TIKA integration:
.. code::
requests.exceptions.HTTPError: 504 Server Error: Gateway Timeout for url: http://gotenberg:3000/convert/office
Gotenberg is a server that converts Office documents into PDF documents and has a default timeout of 10 seconds.
When conversion takes longer, Gotenberg raises this error.
You can increase the timeout by configuring an environment variable for gotenberg (see also `here <https://thecodingmachine.github.io/gotenberg/#environment_variables.default_wait_timeout>`__).
If using docker-compose, this is achieved by the following configuration change in the ``docker-compose.yml`` file:
.. code:: yaml
gotenberg:
image: thecodingmachine/gotenberg
restart: unless-stopped
environment:
DISABLE_GOOGLE_CHROME: 1
DEFAULT_WAIT_TIMEOUT: 30
Permission denied errors in the consumption directory Permission denied errors in the consumption directory
##################################################### #####################################################
@ -106,6 +133,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. 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..." Web-UI stuck at "Loading..."
############################ ############################
@ -148,4 +207,4 @@ This might have multiple reasons.
SENDFILE=0 SENDFILE=0
to your `docker-compose.env` file. to your `docker-compose.env` file.

View File

@ -1,21 +1,9 @@
import os
bind = '0.0.0.0:8000' bind = '0.0.0.0:8000'
backlog = 2048 workers = int(os.getenv("PAPERLESS_WEBSERVER_WORKERS", 2))
workers = 3
worker_class = 'uvicorn.workers.UvicornWorker' worker_class = 'uvicorn.workers.UvicornWorker'
worker_connections = 1000 timeout = 120
timeout = 20
keepalive = 2
spew = False
daemon = False
pidfile = None
umask = 0
user = None
group = None
tmp_upload_dir = None
loglevel = 'info'
errorlog = '-'
accesslog = '-'
proc_name = None
def pre_fork(server, worker): def pre_fork(server, worker):
pass pass

View File

@ -15,7 +15,7 @@ ask() {
fi fi
array=$3 array=$3
if [[ -z $3 || " ${array[@]} " =~ " ${result} " ]]; then if [[ -z $3 || " ${array[@]} " =~ " ${result} " ]]; then
ask_result=$result ask_result=$result
return return
else else
echo "Invalid option: $result" echo "Invalid option: $result"
@ -23,6 +23,47 @@ ask() {
done 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 "############################################" echo "############################################"
echo "### Paperless-ng docker installation ###" 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 "Point this to a folder where your scanner is able to put your scanned"
echo "documents." echo "documents."
echo "" echo ""
echo "HINT: If paperless is unable to pick up any files from this directory after" echo "CAUTION: You must specify an absolute path starting with / or a relative "
echo "installation, you might need to configure PAPERLESS_CONSUMER_POLLING." echo "path starting with ./ here. Examples:"
echo "See the documentation for details." echo " /mnt/consume"
echo "" echo " ./consume"
echo "CAUTION: You must specify an absolute path starting with /"
echo "" echo ""
ask "Consume folder" "$TARGET_FOLDER/consume" ask_docker_folder "Consume folder" "$TARGET_FOLDER/consume"
CONSUME_FOLDER=$ask_result CONSUME_FOLDER=$ask_result
echo "" 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 "Docker usually stores managed folders in /var/lib/docker/volumes."
echo "" echo ""
echo "CAUTION: If specified, you must specify an absolute path starting with /" echo "CAUTION: If specified, you must specify an absolute path starting with /"
echo "or a relative path starting with ./ here."
echo "" echo ""
ask "Media folder" "" ask_docker_folder "Media folder" ""
MEDIA_FOLDER=$ask_result MEDIA_FOLDER=$ask_result
echo "" 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 "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 "As with the media folder, leave empty to have this managed by docker."
echo "" 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 DATA_FOLDER=$ask_result
echo "" echo ""
@ -166,8 +210,36 @@ done
ask "Email" "$USERNAME@localhost" ask "Email" "$USERNAME@localhost"
EMAIL=$ask_result EMAIL=$ask_result
echo "Done collecting data. Press any key to install." echo ""
read 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 ""
echo "Installing paperless..." 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 sed -i "s#- \./consume:/usr/src/paperless/consume#- $CONSUME_FOLDER:/usr/src/paperless/consume#g" docker-compose.yml
if [[ -n $MEDIA_FOLDER ]] ; then 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 fi
if [[ -n $DATA_FOLDER ]] ; then if [[ -n $DATA_FOLDER ]] ; then

View File

@ -41,6 +41,10 @@
#PAPERLESS_OCR_OUTPUT_TYPE=pdfa #PAPERLESS_OCR_OUTPUT_TYPE=pdfa
#PAPERLESS_OCR_PAGES=1 #PAPERLESS_OCR_PAGES=1
#PAPERLESS_OCR_IMAGE_DPI=300 #PAPERLESS_OCR_IMAGE_DPI=300
#PAPERLESS_OCR_CLEAN=clean
#PAPERLESS_OCR_DESKEW=true
#PAPERLESS_OCR_ROTATE_PAGES=true
#PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD=12.0
#PAPERLESS_OCR_USER_ARGS={} #PAPERLESS_OCR_USER_ARGS={}
#PAPERLESS_CONVERT_MEMORY_LIMIT=0 #PAPERLESS_CONVERT_MEMORY_LIMIT=0
#PAPERLESS_CONVERT_TMPDIR=/var/tmp/paperless #PAPERLESS_CONVERT_TMPDIR=/var/tmp/paperless

View File

@ -12,29 +12,30 @@ arrow==0.17.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2,
asgiref==3.3.1; python_version >= '3.5' asgiref==3.3.1; python_version >= '3.5'
async-timeout==3.0.1; python_full_version >= '3.5.3' async-timeout==3.0.1; python_full_version >= '3.5.3'
attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
autobahn==20.12.3; python_version >= '3.6' autobahn==21.2.1; python_version >= '3.6'
automat==20.2.0 automat==20.2.0
blessed==1.17.12 blessed==1.17.12
certifi==2020.12.5 certifi==2020.12.5
cffi==1.14.4 cffi==1.14.5
channels-redis==3.2.0 channels-redis==3.2.0
channels==3.0.3 channels==3.0.3
chardet==4.0.0; python_version >= '3.1' chardet==4.0.0; python_version >= '3.1'
click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
coloredlogs==15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' coloredlogs==15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
concurrent-log-handler==0.9.19
constantly==15.1.0 constantly==15.1.0
cryptography==3.3.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' cryptography==3.3.2
daphne==3.0.1; python_version >= '3.6' daphne==3.0.1; python_version >= '3.6'
dateparser==0.7.6 dateparser==0.7.6
django-cors-headers==3.7.0 django-cors-headers==3.7.0
django-extensions==3.1.0 django-extensions==3.1.1
django-filter==2.4.0 django-filter==2.4.0
django-picklefield==3.0.1; python_version >= '3' django-picklefield==3.0.1; python_version >= '3'
django-q==1.3.4 django-q==1.3.4
django==3.1.5 django==3.1.7
djangorestframework==3.12.2 djangorestframework==3.12.2
filelock==3.0.12 filelock==3.0.12
fuzzywuzzy==0.18.0 fuzzywuzzy[speedup]==0.18.0
gunicorn==20.0.4 gunicorn==20.0.4
h11==0.12.0; python_version >= '3.6' 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' hiredis==1.1.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
@ -44,22 +45,21 @@ hyperlink==21.0.0
idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
imap-tools==0.37.0 imap-tools==0.37.0
img2pdf==0.4.0 img2pdf==0.4.0
importlib-metadata==3.4.0; python_version < '3.8'
incremental==17.5.0 incremental==17.5.0
inotify-simple==1.3.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' inotify-simple==1.3.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
inotifyrecursive==0.3.5 inotifyrecursive==0.3.5
joblib==1.0.0; python_version >= '3.6' joblib==1.0.1; python_version >= '3.6'
langdetect==1.0.8 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' 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 msgpack==1.0.2
numpy==1.19.5; python_version >= '3.6' numpy==1.19.5
ocrmypdf==11.4.5 ocrmypdf==11.6.2
pathvalidate==2.3.2 pathvalidate==2.3.2
pdfminer.six==20201018; python_version >= '3.4' pdfminer.six==20201018
pdftotext==2.1.5 pikepdf==2.5.2
pikepdf==2.2.5
pillow==8.1.0 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' pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
portalocker==2.2.1; python_version >= '3'
psycopg2-binary==2.8.6 psycopg2-binary==2.8.6
pyasn1-modules==0.2.8 pyasn1-modules==0.2.8
pyasn1==0.4.8 pyasn1==0.4.8
@ -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-dateutil==2.8.1
python-dotenv==0.15.0 python-dotenv==0.15.0
python-gnupg==0.4.6 python-gnupg==0.4.6
python-levenshtein==0.12.1 python-levenshtein==0.12.2
python-magic==0.4.18 python-magic==0.4.22
pytz==2020.5 pytz==2021.1
pyyaml==5.4.1 pyyaml==5.4.1
redis==3.5.3 redis==3.5.3
regex==2020.11.13 regex==2020.11.13
@ -85,19 +85,17 @@ sortedcontainers==2.3.0
sqlparse==0.4.1; python_version >= '3.5' sqlparse==0.4.1; python_version >= '3.5'
threadpoolctl==2.1.0; python_version >= '3.5' threadpoolctl==2.1.0; python_version >= '3.5'
tika==1.24 tika==1.24
tqdm==4.56.0 tqdm==4.57.0
twisted[tls]==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' twisted[tls]==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
txaio==20.12.1; python_version >= '3.6' txaio==21.2.1; python_version >= '3.6'
typing-extensions==3.7.4.3; python_version < '3.8'
tzlocal==2.1 tzlocal==2.1
urllib3==1.26.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4' urllib3==1.26.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
uvicorn[standard]==0.13.3 uvicorn[standard]==0.13.4
uvloop==0.14.0 uvloop==0.14.0
watchdog==1.0.2 watchdog==1.0.2
watchgod==0.6 watchgod==0.7
wcwidth==0.2.5 wcwidth==0.2.5
websockets==8.1 websockets==8.1
whitenoise==5.2.0 whitenoise==5.2.0
whoosh==2.7.4 whoosh==2.7.4
zipp==3.4.0; python_version >= '3.6'
zope.interface==5.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' zope.interface==5.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'

16
resources/logo.txt Normal file
View File

@ -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
/

View File

@ -18,7 +18,9 @@
"locales": { "locales": {
"de": "src/locale/messages.de.xlf", "de": "src/locale/messages.de.xlf",
"nl-NL": "src/locale/messages.nl_NL.xlf", "nl-NL": "src/locale/messages.nl_NL.xlf",
"fr": "src/locale/messages.fr.xlf" "fr": "src/locale/messages.fr.xlf",
"en-GB": "src/locale/messages.en_GB.xlf",
"pt-BR": "src/locale/messages.pt_BR.xlf"
} }
}, },
"architect": { "architect": {
@ -35,6 +37,7 @@
"aot": true, "aot": true,
"assets": [ "assets": [
"src/favicon.ico", "src/favicon.ico",
"src/apple-touch-icon.png",
"src/assets", "src/assets",
"src/manifest.webmanifest", { "src/manifest.webmanifest", {
"glob": "pdf.worker.min.js", "glob": "pdf.worker.min.js",
@ -111,6 +114,7 @@
"karmaConfig": "karma.conf.js", "karmaConfig": "karma.conf.js",
"assets": [ "assets": [
"src/favicon.ico", "src/favicon.ico",
"src/apple-touch-icon.png",
"src/assets", "src/assets",
"src/manifest.webmanifest" "src/manifest.webmanifest"
], ],

View File

@ -52,17 +52,17 @@
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2155249406916744630" datatype="html"> <trans-unit id="2155249406916744630" datatype="html">
<source>View &quot;<x id="PH" equiv-text="this.list.savedView.name"/>&quot; saved successfully.</source> <source>View &quot;<x id="PH" equiv-text="this.list.activeSavedViewTitle"/>&quot; saved successfully.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">109</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6837554170707123455" datatype="html"> <trans-unit id="6837554170707123455" datatype="html">
<source>View &quot;<x id="PH" equiv-text="savedView.name"/>&quot; created successfully.</source> <source>View &quot;<x id="PH" equiv-text="savedView.name"/>&quot; created successfully.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">130</context> <context context-type="linenumber">136</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="9ca82952a6bc860b5391d5975322d8af8ceddfa4" datatype="html"> <trans-unit id="9ca82952a6bc860b5391d5975322d8af8ceddfa4" datatype="html">
@ -114,8 +114,8 @@
<context context-type="linenumber">72</context> <context context-type="linenumber">72</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="72e7d343f9165602cce1ca7faffbc565fd31ef92" datatype="html"> <trans-unit id="5f5ce787c428d917c30c9bd70789a618e09743a7" datatype="html">
<source>Save &quot;<x id="INTERPOLATION" equiv-text="{{list.savedViewTitle}}"/>&quot;</source> <source>Save &quot;<x id="INTERPOLATION" equiv-text="{{list.activeSavedViewTitle}}"/>&quot;</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">71</context> <context context-type="linenumber">71</context>
@ -188,35 +188,35 @@
<source>Confirm delete</source> <source>Confirm delete</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">203</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5382975254277698192" datatype="html"> <trans-unit id="5382975254277698192" datatype="html">
<source>Do you really want to delete document &quot;<x id="PH" equiv-text="this.document.title"/>&quot;?</source> <source>Do you really want to delete document &quot;<x id="PH" equiv-text="this.document.title"/>&quot;?</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">200</context> <context context-type="linenumber">204</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6691075929777935948" datatype="html"> <trans-unit id="6691075929777935948" datatype="html">
<source>The files for this document will be deleted permanently. This operation cannot be undone.</source> <source>The files for this document will be deleted permanently. This operation cannot be undone.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">205</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="719892092227206532" datatype="html"> <trans-unit id="719892092227206532" datatype="html">
<source>Delete document</source> <source>Delete document</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">203</context> <context context-type="linenumber">207</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1844801255494293730" datatype="html"> <trans-unit id="1844801255494293730" datatype="html">
<source>Error deleting document: <x id="PH" equiv-text="JSON.stringify(error)"/></source> <source>Error deleting document: <x id="PH" equiv-text="JSON.stringify(error)"/></source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">210</context> <context context-type="linenumber">214</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="826b25211922a1b46436589233cb6f1a163d89b7" datatype="html"> <trans-unit id="826b25211922a1b46436589233cb6f1a163d89b7" datatype="html">
@ -513,13 +513,6 @@
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5ca707824ab93066c7d9b44e1b8bf216725c2c22" datatype="html">
<source>Filter</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/logs/logs.component.html</context>
<context context-type="linenumber">7</context>
</context-group>
</trans-unit>
<trans-unit id="5610279464668232148" datatype="html"> <trans-unit id="5610279464668232148" datatype="html">
<source>Saved view &quot;<x id="PH" equiv-text="savedView.name"/>&quot; deleted.</source> <source>Saved view &quot;<x id="PH" equiv-text="savedView.name"/>&quot; deleted.</source>
<context-group purpose="location"> <context-group purpose="location">
@ -538,21 +531,21 @@
<source>Use system language</source> <source>Use system language</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">91</context> <context context-type="linenumber">92</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7729897675462249787" datatype="html"> <trans-unit id="7729897675462249787" datatype="html">
<source>Use date format of display language</source> <source>Use date format of display language</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">95</context> <context context-type="linenumber">98</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8488620293789898901" datatype="html"> <trans-unit id="8488620293789898901" datatype="html">
<source>Error while storing settings on server: <x id="PH" equiv-text="JSON.stringify(error.error)"/></source> <source>Error while storing settings on server: <x id="PH" equiv-text="JSON.stringify(error.error)"/></source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">111</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="121cc5391cd2a5115bc2b3160379ee5b36cd7716" datatype="html"> <trans-unit id="121cc5391cd2a5115bc2b3160379ee5b36cd7716" datatype="html">
@ -1081,6 +1074,13 @@
<context context-type="linenumber">46</context> <context context-type="linenumber">46</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6523384805359286307" datatype="html">
<source>Title: <x id="PH" equiv-text="rule.value"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit id="02d184c288f567825a1fcbf83bcd3099a10853d5" datatype="html"> <trans-unit id="02d184c288f567825a1fcbf83bcd3099a10853d5" datatype="html">
<source>Filter tags</source> <source>Filter tags</source>
<context-group purpose="location"> <context-group purpose="location">
@ -1219,21 +1219,21 @@
<source>Error executing bulk operation: <x id="PH" equiv-text="JSON.stringify(error.error)"/></source> <source>Error executing bulk operation: <x id="PH" equiv-text="JSON.stringify(error.error)"/></source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">74</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7894972847287473517" datatype="html"> <trans-unit id="7894972847287473517" datatype="html">
<source>&quot;<x id="PH" equiv-text="items[0].name"/>&quot;</source> <source>&quot;<x id="PH" equiv-text="items[0].name"/>&quot;</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">112</context> <context context-type="linenumber">113</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8639884465898458690" datatype="html"> <trans-unit id="8639884465898458690" datatype="html">
<source>&quot;<x id="PH" equiv-text="items[0].name"/>&quot; and &quot;<x id="PH_1" equiv-text="items[1].name"/>&quot;</source> <source>&quot;<x id="PH" equiv-text="items[0].name"/>&quot; and &quot;<x id="PH_1" equiv-text="items[1].name"/>&quot;</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">114</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
<note priority="1" from="description">This is for messages like &apos;modify &quot;tag1&quot; and &quot;tag2&quot;&apos;</note> <note priority="1" from="description">This is for messages like &apos;modify &quot;tag1&quot; and &quot;tag2&quot;&apos;</note>
</trans-unit> </trans-unit>
@ -1241,7 +1241,7 @@
<source>, </source> <source>, </source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">116</context> <context context-type="linenumber">117</context>
</context-group> </context-group>
<note priority="1" from="description">this is used to separate enumerations and should probably be a comma and a whitespace in most languages</note> <note priority="1" from="description">this is used to separate enumerations and should probably be a comma and a whitespace in most languages</note>
</trans-unit> </trans-unit>
@ -1249,7 +1249,7 @@
<source><x id="PH" equiv-text="list"/> and &quot;<x id="PH_1" equiv-text="items[items.length - 1].name"/>&quot;</source> <source><x id="PH" equiv-text="list"/> and &quot;<x id="PH_1" equiv-text="items[items.length - 1].name"/>&quot;</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">117</context> <context context-type="linenumber">118</context>
</context-group> </context-group>
<note priority="1" from="description">this is for messages like &apos;modify &quot;tag1&quot;, &quot;tag2&quot; and &quot;tag3&quot;&apos;</note> <note priority="1" from="description">this is for messages like &apos;modify &quot;tag1&quot;, &quot;tag2&quot; and &quot;tag3&quot;&apos;</note>
</trans-unit> </trans-unit>
@ -1257,112 +1257,112 @@
<source>Confirm tags assignment</source> <source>Confirm tags assignment</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">126</context> <context context-type="linenumber">127</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6619516195038467207" datatype="html"> <trans-unit id="6619516195038467207" datatype="html">
<source>This operation will add the tag &quot;<x id="PH" equiv-text="tag.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will add the tag &quot;<x id="PH" equiv-text="tag.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">129</context> <context context-type="linenumber">130</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1894412783609570695" datatype="html"> <trans-unit id="1894412783609570695" datatype="html">
<source>This operation will add the tags <x id="PH" equiv-text="this._localizeList(changedTags.itemsToAdd)"/> to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will add the tags <x id="PH" equiv-text="this._localizeList(changedTags.itemsToAdd)"/> to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">131</context> <context context-type="linenumber">132</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7181166515756808573" datatype="html"> <trans-unit id="7181166515756808573" datatype="html">
<source>This operation will remove the tag &quot;<x id="PH" equiv-text="tag.name"/>&quot; from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will remove the tag &quot;<x id="PH" equiv-text="tag.name"/>&quot; from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">134</context> <context context-type="linenumber">135</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3819792277998068944" datatype="html"> <trans-unit id="3819792277998068944" datatype="html">
<source>This operation will remove the tags <x id="PH" equiv-text="this._localizeList(changedTags.itemsToRemove)"/> from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will remove the tags <x id="PH" equiv-text="this._localizeList(changedTags.itemsToRemove)"/> from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">136</context> <context context-type="linenumber">137</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2739066218579571288" datatype="html"> <trans-unit id="2739066218579571288" datatype="html">
<source>This operation will add the tags <x id="PH" equiv-text="this._localizeList(changedTags.itemsToAdd)"/> and remove the tags <x id="PH_1" equiv-text="this._localizeList(changedTags.itemsToRemove)"/> on <x id="PH_2" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will add the tags <x id="PH" equiv-text="this._localizeList(changedTags.itemsToAdd)"/> and remove the tags <x id="PH_1" equiv-text="this._localizeList(changedTags.itemsToRemove)"/> on <x id="PH_2" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">138</context> <context context-type="linenumber">139</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2996713129519325161" datatype="html"> <trans-unit id="2996713129519325161" datatype="html">
<source>Confirm correspondent assignment</source> <source>Confirm correspondent assignment</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">158</context> <context context-type="linenumber">159</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6900893559485781849" datatype="html"> <trans-unit id="6900893559485781849" datatype="html">
<source>This operation will assign the correspondent &quot;<x id="PH" equiv-text="correspondent.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will assign the correspondent &quot;<x id="PH" equiv-text="correspondent.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">160</context> <context context-type="linenumber">161</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1257522660364398440" datatype="html"> <trans-unit id="1257522660364398440" datatype="html">
<source>This operation will remove the correspondent from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will remove the correspondent from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">162</context> <context context-type="linenumber">163</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5393409374423140648" datatype="html"> <trans-unit id="5393409374423140648" datatype="html">
<source>Confirm document type assignment</source> <source>Confirm document type assignment</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">181</context> <context context-type="linenumber">182</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="332180123895325027" datatype="html"> <trans-unit id="332180123895325027" datatype="html">
<source>This operation will assign the document type &quot;<x id="PH" equiv-text="documentType.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will assign the document type &quot;<x id="PH" equiv-text="documentType.name"/>&quot; to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">183</context> <context context-type="linenumber">184</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2236642492594872779" datatype="html"> <trans-unit id="2236642492594872779" datatype="html">
<source>This operation will remove the document type from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will remove the document type from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">185</context> <context context-type="linenumber">186</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="749430623564850405" datatype="html"> <trans-unit id="749430623564850405" datatype="html">
<source>Delete confirm</source> <source>Delete confirm</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">200</context> <context context-type="linenumber">201</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4303174930844518780" datatype="html"> <trans-unit id="4303174930844518780" datatype="html">
<source>This operation will permanently delete <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source> <source>This operation will permanently delete <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">202</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5641451190833696892" datatype="html"> <trans-unit id="5641451190833696892" datatype="html">
<source>This operation cannot be undone.</source> <source>This operation cannot be undone.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">202</context> <context context-type="linenumber">203</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6734339521247847366" datatype="html"> <trans-unit id="6734339521247847366" datatype="html">
<source>Delete document(s)</source> <source>Delete document(s)</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">204</context> <context context-type="linenumber">205</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8b0609df23817024b3bed12beb9b64fc1009f588" datatype="html"> <trans-unit id="8b0609df23817024b3bed12beb9b64fc1009f588" datatype="html">
@ -1386,6 +1386,13 @@
<context context-type="linenumber">27</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="fc2de37422d7c4af6686842283cc2afd781b6848" datatype="html">
<source>Download originals</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit id="a1e6c11f20d4bf6e8e6b43e3c6d2561b2080645e" datatype="html"> <trans-unit id="a1e6c11f20d4bf6e8e6b43e3c6d2561b2080645e" datatype="html">
<source>Suggestions:</source> <source>Suggestions:</source>
<context-group purpose="location"> <context-group purpose="location">
@ -1414,20 +1421,20 @@
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="46c8fe557cf52c9389783627d4f85453f4ddb459" datatype="html"> <trans-unit id="c0d907c2687c09612395aee6ef7c04ca8e5e5e0a" datatype="html">
<source>Documents in inbox: <x id="INTERPOLATION" equiv-text="{{statistics.documents_inbox}}"/></source> <source>Total documents: <x id="INTERPOLATION" equiv-text="{{statistics?.documents_total}}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit id="c327c0e67bcac7494dcbaa9afb3b42d5008c6438" datatype="html">
<source>Total documents: <x id="INTERPOLATION" equiv-text="{{statistics.documents_total}}"/></source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">4</context> <context context-type="linenumber">4</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="13e8d49dbcad9f9d71e66a9a56d6f328cff430c9" datatype="html">
<source>Documents in inbox: <x id="INTERPOLATION" equiv-text="{{statistics?.documents_inbox}}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit id="6443586946875325554" datatype="html"> <trans-unit id="6443586946875325554" datatype="html">
<source>Processing: <x id="PH" equiv-text="countUploadingAndProcessing"/></source> <source>Processing: <x id="PH" equiv-text="countUploadingAndProcessing"/></source>
<context-group purpose="location"> <context-group purpose="location">
@ -1470,6 +1477,13 @@
<context context-type="linenumber">126</context> <context context-type="linenumber">126</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1405142710727603568" datatype="html">
<source>HTTP error: <x id="PH" equiv-text="error.status"/> <x id="PH_1" equiv-text="error.statusText"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context>
<context context-type="linenumber">136</context>
</context-group>
</trans-unit>
<trans-unit id="e022072b3e4dd77e3f09960817ef3359a49963b3" datatype="html"> <trans-unit id="e022072b3e4dd77e3f09960817ef3359a49963b3" datatype="html">
<source>Upload new documents</source> <source>Upload new documents</source>
<context-group purpose="location"> <context-group purpose="location">
@ -1584,6 +1598,13 @@
<context context-type="linenumber">21</context> <context context-type="linenumber">21</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="d6529debfc1613db22d6fa096ebfeb8a85fa739d" datatype="html">
<source>Invalid date.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit id="2807800733729323332" datatype="html"> <trans-unit id="2807800733729323332" datatype="html">
<source>Yes</source> <source>Yes</source>
<context-group purpose="location"> <context-group purpose="location">
@ -1609,49 +1630,70 @@
<source>English (US)</source> <source>English (US)</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">82</context> <context context-type="linenumber">88</context>
</context-group>
</trans-unit>
<trans-unit id="6987083569809053351" datatype="html">
<source>English (GB)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">89</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1858110241312746425" datatype="html"> <trans-unit id="1858110241312746425" datatype="html">
<source>German</source> <source>German</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">83</context> <context context-type="linenumber">90</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3071065188816255493" datatype="html"> <trans-unit id="3071065188816255493" datatype="html">
<source>Dutch</source> <source>Dutch</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">84</context> <context context-type="linenumber">91</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7633754075223722162" datatype="html"> <trans-unit id="7633754075223722162" datatype="html">
<source>French</source> <source>French</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">85</context> <context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit id="9184513005098760425" datatype="html">
<source>Portuguese (Brazil)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit id="4912706592792948707" datatype="html">
<source>ISO 8601</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">98</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2119857572761283468" datatype="html"> <trans-unit id="2119857572761283468" datatype="html">
<source>Document already exists.</source> <source>Document already exists.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">14</context> <context context-type="linenumber">15</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="148389968432135849" datatype="html"> <trans-unit id="148389968432135849" datatype="html">
<source>File not found.</source> <source>File not found.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">15</context> <context context-type="linenumber">16</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1520671543092565667" datatype="html"> <trans-unit id="1520671543092565667" datatype="html">
<source>Pre-consume script does not exist.</source> <source>Pre-consume script does not exist.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">17</context>
</context-group> </context-group>
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1659,7 +1701,7 @@
<source>Error while executing pre-consume script.</source> <source>Error while executing pre-consume script.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">17</context> <context context-type="linenumber">18</context>
</context-group> </context-group>
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1667,7 +1709,7 @@
<source>Post-consume script does not exist.</source> <source>Post-consume script does not exist.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">18</context> <context context-type="linenumber">19</context>
</context-group> </context-group>
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1675,7 +1717,7 @@
<source>Error while executing post-consume script.</source> <source>Error while executing post-consume script.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">19</context> <context context-type="linenumber">20</context>
</context-group> </context-group>
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1683,49 +1725,49 @@
<source>Received new file.</source> <source>Received new file.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">20</context> <context context-type="linenumber">21</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7337565919209746135" datatype="html"> <trans-unit id="7337565919209746135" datatype="html">
<source>File type not supported.</source> <source>File type not supported.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">21</context> <context context-type="linenumber">22</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5002399167376099234" datatype="html"> <trans-unit id="5002399167376099234" datatype="html">
<source>Processing document...</source> <source>Processing document...</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">22</context> <context context-type="linenumber">23</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1085975194762600381" datatype="html"> <trans-unit id="1085975194762600381" datatype="html">
<source>Generating thumbnail...</source> <source>Generating thumbnail...</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">23</context> <context context-type="linenumber">24</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3280851677698431426" datatype="html"> <trans-unit id="3280851677698431426" datatype="html">
<source>Retrieving date from document...</source> <source>Retrieving date from document...</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">24</context> <context context-type="linenumber">25</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7162102384876037296" datatype="html"> <trans-unit id="7162102384876037296" datatype="html">
<source>Saving document...</source> <source>Saving document...</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">25</context> <context context-type="linenumber">26</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4550450765009165976" datatype="html"> <trans-unit id="4550450765009165976" datatype="html">
<source>Finished.</source> <source>Finished.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">26</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1519954996184640001" datatype="html"> <trans-unit id="1519954996184640001" datatype="html">

View File

@ -2055,9 +2055,9 @@
} }
}, },
"@ng-bootstrap/ng-bootstrap": { "@ng-bootstrap/ng-bootstrap": {
"version": "8.0.0", "version": "8.0.4",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.0.tgz", "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.4.tgz",
"integrity": "sha512-v77Gfd8xHH+exq0WqIqVRlxbUEHdA/2+RUJenUP2IDTQN9E1rWl7O461/kosr+0XPuxPArHQJxhh/WsCYckcNg==", "integrity": "sha512-EdxTwOPOtlvfnwrglPniulmzdnXdXH3lTGaGAY1HrYRvdtGg6wicRvl+BvwVE/3Qik5NPkOWMVghUHpv3evIYg==",
"requires": { "requires": {
"tslib": "^2.0.0" "tslib": "^2.0.0"
} }
@ -5545,6 +5545,11 @@
"schema-utils": "^2.6.5" "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": { "file-uri-to-path": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",

View File

@ -20,9 +20,10 @@
"@angular/platform-browser": "~10.1.5", "@angular/platform-browser": "~10.1.5",
"@angular/platform-browser-dynamic": "~10.1.5", "@angular/platform-browser-dynamic": "~10.1.5",
"@angular/router": "~10.1.5", "@angular/router": "~10.1.5",
"@ng-bootstrap/ng-bootstrap": "^8.0.0", "@ng-bootstrap/ng-bootstrap": "^8.0.4",
"@ng-select/ng-select": "^5.0.9", "@ng-select/ng-select": "^5.0.9",
"bootstrap": "^4.5.0", "bootstrap": "^4.5.0",
"file-saver": "^2.0.5",
"ng-bootstrap": "^1.6.3", "ng-bootstrap": "^1.6.3",
"ngx-cookie-service": "^10.1.1", "ngx-cookie-service": "^10.1.1",
"ngx-file-drop": "^10.0.0", "ngx-file-drop": "^10.0.0",

View File

@ -3,7 +3,7 @@ import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbDateAdapter, NgbDateParserFormatter, NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { DocumentListComponent } from './components/document-list/document-list.component'; import { DocumentListComponent } from './components/document-list/document-list.component';
import { DocumentDetailComponent } from './components/document-detail/document-detail.component'; import { DocumentDetailComponent } from './components/document-detail/document-detail.component';
@ -39,7 +39,6 @@ import { SelectComponent } from './components/common/input/select/select.compone
import { CheckComponent } from './components/common/input/check/check.component'; import { CheckComponent } from './components/common/input/check/check.component';
import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component'; import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component';
import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { DateTimeComponent } from './components/common/input/date-time/date-time.component';
import { TagsComponent } from './components/common/input/tags/tags.component'; import { TagsComponent } from './components/common/input/tags/tags.component';
import { SortableDirective } from './directives/sortable.directive'; import { SortableDirective } from './directives/sortable.directive';
import { CookieService } from 'ngx-cookie-service'; import { CookieService } from 'ngx-cookie-service';
@ -59,14 +58,21 @@ import { NgSelectModule } from '@ng-select/ng-select';
import { NumberComponent } from './components/common/input/number/number.component'; import { NumberComponent } from './components/common/input/number/number.component';
import { SafePipe } from './pipes/safe.pipe'; import { SafePipe } from './pipes/safe.pipe';
import { CustomDatePipe } from './pipes/custom-date.pipe'; import { CustomDatePipe } from './pipes/custom-date.pipe';
import { DateComponent } from './components/common/input/date/date.component';
import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter';
import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter';
import localeFr from '@angular/common/locales/fr'; import localeFr from '@angular/common/locales/fr';
import localeNl from '@angular/common/locales/nl'; import localeNl from '@angular/common/locales/nl';
import localeDe from '@angular/common/locales/de'; import localeDe from '@angular/common/locales/de';
import localePt from '@angular/common/locales/pt-PT';
import localeEnGb from '@angular/common/locales/en-GB';
registerLocaleData(localeFr) registerLocaleData(localeFr)
registerLocaleData(localeNl) registerLocaleData(localeNl)
registerLocaleData(localeDe) registerLocaleData(localeDe)
registerLocaleData(localePt, "pt-BR")
registerLocaleData(localeEnGb)
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -101,7 +107,6 @@ registerLocaleData(localeDe)
SelectComponent, SelectComponent,
CheckComponent, CheckComponent,
SaveViewConfigDialogComponent, SaveViewConfigDialogComponent,
DateTimeComponent,
TagsComponent, TagsComponent,
SortableDirective, SortableDirective,
SavedViewWidgetComponent, SavedViewWidgetComponent,
@ -117,7 +122,8 @@ registerLocaleData(localeDe)
SelectDialogComponent, SelectDialogComponent,
NumberComponent, NumberComponent,
SafePipe, SafePipe,
CustomDatePipe CustomDatePipe,
DateComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -138,7 +144,9 @@ registerLocaleData(localeDe)
multi: true multi: true
}, },
FilterPipe, FilterPipe,
DocumentTitlePipe DocumentTitlePipe,
{provide: NgbDateAdapter, useClass: ISODateTimeAdapter},
{provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter}
], ],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })

View File

@ -163,13 +163,13 @@
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<div class="d-flex w-100"> <div class="d-flex w-100 flex-wrap">
<a class="nav-link" target="_blank" rel="noopener noreferrer" href="https://github.com/jonaswinkler/paperless-ng"> <a class="nav-link pr-0 pb-0" target="_blank" rel="noopener noreferrer" href="https://github.com/jonaswinkler/paperless-ng">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon bi bi-github" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/> <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
</svg>&nbsp;<ng-container i18n>GitHub</ng-container> </svg>&nbsp;<ng-container i18n>GitHub</ng-container>
</a> </a>
<a class="nav-link-additional small text-muted" target="_blank" rel="noopener noreferrer" href="https://github.com/jonaswinkler/paperless-ng/discussions/categories/feature-requests" title="Suggest an idea"> <a class="nav-link-additional small text-muted ml-3" target="_blank" rel="noopener noreferrer" href="https://github.com/jonaswinkler/paperless-ng/discussions/categories/feature-requests" title="Suggest an idea">
<svg xmlns="http://www.w3.org/2000/svg" width=".9rem" height=".9rem" fill="currentColor" class="bi bi-lightbulb pr-1" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width=".9rem" height=".9rem" fill="currentColor" class="bi bi-lightbulb pr-1" viewBox="0 0 16 16">
<path d="M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13a.5.5 0 0 1 0 1 .5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1 0-1 .5.5 0 0 1 0-1 .5.5 0 0 1-.46-.302l-.761-1.77a1.964 1.964 0 0 0-.453-.618A5.984 5.984 0 0 1 2 6zm6-5a5 5 0 0 0-3.479 8.592c.263.254.514.564.676.941L5.83 12h4.342l.632-1.467c.162-.377.413-.687.676-.941A5 5 0 0 0 8 1z"/> <path d="M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13a.5.5 0 0 1 0 1 .5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1 0-1 .5.5 0 0 1 0-1 .5.5 0 0 1-.46-.302l-.761-1.77a1.964 1.964 0 0 0-.453-.618A5.984 5.984 0 0 1 2 6zm6-5a5 5 0 0 0-3.479 8.592c.263.254.514.564.676.941L5.83 12h4.342l.632-1.467c.162-.377.413-.687.676-.941A5 5 0 0 0 8 1z"/>
</svg> </svg>
@ -177,7 +177,7 @@
</a> </a>
</div> </div>
</li> </li>
<li class="nav-item"> <li class="nav-item mt-2">
<div class="px-3 py-2 text-muted small"> <div class="px-3 py-2 text-muted small">
{{versionString}} {{versionString}}
</div> </div>

View File

@ -20,8 +20,17 @@
</div> </div>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input type="date" class="form-control" id="date_after" [(ngModel)]="dateAfter" (change)="onChangeDebounce()"> <input class="form-control" [placeholder]="datePlaceHolder" id="dateAfter" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()"
[(ngModel)]="dateAfter" ngbDatepicker #dateAfterPicker="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="dateAfterPicker.toggle()" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16">
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/>
</svg>
</button>
</div>
</div> </div>
</div> </div>
<div class="list-group-item d-flex flex-column align-items-start" role="menuitem"> <div class="list-group-item d-flex flex-column align-items-start" role="menuitem">
@ -36,8 +45,17 @@
</div> </div>
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input type="date" class="form-control" id="date_before" [(ngModel)]="dateBefore" (change)="onChangeDebounce()"> <input class="form-control" [placeholder]="datePlaceHolder" id="dateBefore" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()"
[(ngModel)]="dateBefore" ngbDatepicker #dateBeforePicker="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="dateBeforePicker.toggle()" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16">
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/>
</svg>
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,7 +1,10 @@
import { formatDate } from '@angular/common'; import { formatDate } from '@angular/common';
import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core'; import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core';
import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';
import { Subject, Subscription } from 'rxjs'; 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 { export interface DateSelection {
before?: string before?: string
@ -16,10 +19,17 @@ const LAST_YEAR = 3
@Component({ @Component({
selector: 'app-date-dropdown', selector: 'app-date-dropdown',
templateUrl: './date-dropdown.component.html', 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 { export class DateDropdownComponent implements OnInit, OnDestroy {
constructor(settings: SettingsService) {
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
}
quickFilters = [ quickFilters = [
{id: LAST_7_DAYS, name: $localize`Last 7 days`}, {id: LAST_7_DAYS, name: $localize`Last 7 days`},
{id: LAST_MONTH, name: $localize`Last month`}, {id: LAST_MONTH, name: $localize`Last month`},
@ -27,6 +37,8 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
{id: LAST_YEAR, name: $localize`Last year`} {id: LAST_YEAR, name: $localize`Last year`}
] ]
datePlaceHolder: string
@Input() @Input()
dateBefore: string dateBefore: string

View File

@ -1,13 +0,0 @@
<div class="form-row">
<div class="form-group col">
<label for="created_date">{{titleDate}}</label>
<input type="date" class="form-control" id="created_date" [(ngModel)]="dateValue" (change)="dateOrTimeChanged()">
</div>
<div class="form-group col" *ngIf="titleTime">
<label for="created_time">{{titleTime}}</label>
<input type="time" class="form-control" id="created_time" [(ngModel)]="timeValue" (change)="dateOrTimeChanged()">
</div>
</div>
<!-- <small *ngIf="hint" class="form-text text-muted">{{hint}}</small> -->

View File

@ -1,61 +0,0 @@
import { formatDate } from '@angular/common';
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateTimeComponent),
multi: true
}],
selector: 'app-input-date-time',
templateUrl: './date-time.component.html',
styleUrls: ['./date-time.component.scss']
})
export class DateTimeComponent implements OnInit,ControlValueAccessor {
constructor() {
}
onChange = (newValue: any) => {};
onTouched = () => {};
writeValue(newValue: any): void {
this.dateValue = formatDate(newValue, 'yyyy-MM-dd', "en-US")
this.timeValue = formatDate(newValue, 'HH:mm:ss', 'en-US')
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}
@Input()
titleDate: string = "Date"
@Input()
titleTime: string
@Input()
disabled: boolean = false
@Input()
hint: string
timeValue
dateValue
ngOnInit(): void {
}
dateOrTimeChanged() {
this.onChange(formatDate(this.dateValue + "T" + this.timeValue,"yyyy-MM-ddTHH:mm:ssZZZZZ", "en-us", "UTC"))
}
}

View File

@ -0,0 +1,15 @@
<div class="form-group">
<label [for]="inputId">{{title}}</label>
<div class="input-group">
<input [class.is-invalid]="error" class="form-control" [placeholder]="placeholder" [id]="inputId" (dateSelect)="onChange(value)" (change)="onChange(value)"
name="dp" [(ngModel)]="value" ngbDatepicker #datePicker="ngbDatepicker" #datePickerContent="ngModel">
<div class="input-group-append">
<button class="btn btn-outline-secondary calendar" (click)="datePicker.toggle()" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-calendar" viewBox="0 0 16 16">
<path d="M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5zM1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4H1z"/>
</svg>
</button>
</div>
<div class="invalid-feedback" *ngIf="error" i18n>Invalid date.</div>
</div>
</div>

View File

@ -1,20 +1,20 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DateTimeComponent } from './date-time.component'; import { DateComponent } from './date.component';
describe('DateTimeComponent', () => { describe('DateComponent', () => {
let component: DateTimeComponent; let component: DateComponent;
let fixture: ComponentFixture<DateTimeComponent>; let fixture: ComponentFixture<DateComponent>;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ DateTimeComponent ] declarations: [ DateComponent ]
}) })
.compileComponents(); .compileComponents();
}); });
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(DateTimeComponent); fixture = TestBed.createComponent(DateComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -0,0 +1,32 @@
import { Component, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { NgbDateAdapter, NgbDateParserFormatter, NgbDatepickerContent } from '@ng-bootstrap/ng-bootstrap';
import { SettingsService } from 'src/app/services/settings.service';
import { v4 as uuidv4 } from 'uuid';
import { AbstractInputComponent } from '../abstract-input';
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DateComponent),
multi: true
}],
selector: 'app-input-date',
templateUrl: './date.component.html',
styleUrls: ['./date.component.scss']
})
export class DateComponent extends AbstractInputComponent<string> implements OnInit {
constructor(private settings: SettingsService) {
super()
}
ngOnInit(): void {
super.ngOnInit()
this.placeholder = this.settings.getLocalizedDateInputFormat()
}
placeholder: string
}

View File

@ -12,7 +12,7 @@ import { DocumentService } from 'src/app/services/rest/document.service';
templateUrl: './saved-view-widget.component.html', templateUrl: './saved-view-widget.component.html',
styleUrls: ['./saved-view-widget.component.scss'] styleUrls: ['./saved-view-widget.component.scss']
}) })
export class SavedViewWidgetComponent implements OnInit { export class SavedViewWidgetComponent implements OnInit, OnDestroy {
constructor( constructor(
private documentService: DocumentService, private documentService: DocumentService,
@ -48,7 +48,7 @@ export class SavedViewWidgetComponent implements OnInit {
if (this.savedView.show_in_sidebar) { if (this.savedView.show_in_sidebar) {
this.router.navigate(['view', this.savedView.id]) this.router.navigate(['view', this.savedView.id])
} else { } else {
this.list.load(this.savedView) this.list.loadSavedView(this.savedView, true)
this.router.navigate(["documents"]) this.router.navigate(["documents"])
} }
} }

View File

@ -1,6 +1,6 @@
<app-widget-frame title="Statistics" i18n-title> <app-widget-frame title="Statistics" i18n-title>
<ng-container content> <ng-container content>
<p class="card-text" i18n>Documents in inbox: {{statistics.documents_inbox}}</p> <p class="card-text" i18n *ngIf="statistics?.documents_inbox != null">Documents in inbox: {{statistics?.documents_inbox}}</p>
<p class="card-text" i18n>Total documents: {{statistics.documents_total}}</p> <p class="card-text" i18n>Total documents: {{statistics?.documents_total}}</p>
</ng-container> </ng-container>
</app-widget-frame> </app-widget-frame>

View File

@ -1,6 +1,7 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { ConsumerStatusService } from 'src/app/services/consumer-status.service';
import { environment } from 'src/environments/environment'; import { environment } from 'src/environments/environment';
export interface Statistics { export interface Statistics {
@ -14,20 +15,34 @@ export interface Statistics {
templateUrl: './statistics-widget.component.html', templateUrl: './statistics-widget.component.html',
styleUrls: ['./statistics-widget.component.scss'] styleUrls: ['./statistics-widget.component.scss']
}) })
export class StatisticsWidgetComponent implements OnInit { export class StatisticsWidgetComponent implements OnInit, OnDestroy {
constructor(private http: HttpClient) { } constructor(private http: HttpClient,
private consumerStatusService: ConsumerStatusService) { }
statistics: Statistics = {} statistics: Statistics = {}
getStatistics(): Observable<Statistics> { subscription: Subscription
private getStatistics(): Observable<Statistics> {
return this.http.get(`${environment.apiBaseUrl}statistics/`) return this.http.get(`${environment.apiBaseUrl}statistics/`)
} }
ngOnInit(): void { reload() {
this.getStatistics().subscribe(statistics => { this.getStatistics().subscribe(statistics => {
this.statistics = statistics this.statistics = statistics
}) })
} }
ngOnInit(): void {
this.reload()
this.subscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
this.reload()
})
}
ngOnDestroy(): void {
this.subscription.unsubscribe()
}
} }

View File

@ -1,6 +1,6 @@
<app-widget-frame title="Upload new documents" i18n-title> <app-widget-frame title="Upload new documents" i18n-title>
<div header-buttons> <div header-buttons>
<a *ngIf="getStatusCompleted().length > 0" (click)="dismissAll()" [routerLink]="" > <a *ngIf="getStatusSuccess().length > 0" (click)="dismissCompleted()" [routerLink]="" >
<span i18n="This button dismisses all status messages about processed documents on the dashboard (failed and successful)">Dismiss completed</span>&nbsp; <span i18n="This button dismisses all status messages about processed documents on the dashboard (failed and successful)">Dismiss completed</span>&nbsp;
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" fill="currentColor" class="bi bi-check2-all" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" fill="currentColor" class="bi bi-check2-all" viewBox="0 0 16 16">
<path d="M12.354 4.354a.5.5 0 0 0-.708-.708L5 10.293 1.854 7.146a.5.5 0 1 0-.708.708l3.5 3.5a.5.5 0 0 0 .708 0l7-7zm-4.208 7l-.896-.897.707-.707.543.543 6.646-6.647a.5.5 0 0 1 .708.708l-7 7a.5.5 0 0 1-.708 0z"/> <path d="M12.354 4.354a.5.5 0 0 0-.708-.708L5 10.293 1.854 7.146a.5.5 0 1 0-.708.708l3.5 3.5a.5.5 0 0 0 .708 0l7-7zm-4.208 7l-.896-.897.707-.707.543.543 6.646-6.647a.5.5 0 0 1 .708.708l-7 7a.5.5 0 0 1-.708 0z"/>

View File

@ -92,8 +92,8 @@ export class UploadFileWidgetComponent implements OnInit {
this.consumerStatusService.dismiss(status) this.consumerStatusService.dismiss(status)
} }
dismissAll() { dismissCompleted() {
this.consumerStatusService.dismissAll() this.consumerStatusService.dismissCompleted()
} }
ngOnInit(): void { ngOnInit(): void {
@ -133,7 +133,7 @@ export class UploadFileWidgetComponent implements OnInit {
break; break;
} }
default: { default: {
this.consumerStatusService.fail(status, `${error.status} ${error.statusText}`) this.consumerStatusService.fail(status, $localize`HTTP error: ${error.status} ${error.statusText}`)
break; break;
} }
} }

View File

@ -48,7 +48,7 @@
<app-input-text #inputTitle i18n-title title="Title" formControlName="title" [error]="error?.title"></app-input-text> <app-input-text #inputTitle i18n-title title="Title" formControlName="title" [error]="error?.title"></app-input-text>
<app-input-number i18n-title title="Archive serial number" [error]="error?.archive_serial_number" formControlName='archive_serial_number'></app-input-number> <app-input-number i18n-title title="Archive serial number" [error]="error?.archive_serial_number" formControlName='archive_serial_number'></app-input-number>
<app-input-date-time i18n-titleDate titleDate="Date created" formControlName="created"></app-input-date-time> <app-input-date i18n-title title="Date created" formControlName="created" [error]="error?.created"></app-input-date>
<app-input-select [items]="correspondents" i18n-title title="Correspondent" formControlName="correspondent" [allowNull]="true" <app-input-select [items]="correspondents" i18n-title title="Correspondent" formControlName="correspondent" [allowNull]="true"
(createNew)="createCorrespondent()" [suggestions]="suggestions?.correspondents"></app-input-select> (createNew)="createCorrespondent()" [suggestions]="suggestions?.correspondents"></app-input-select>
<app-input-select [items]="documentTypes" i18n-title title="Document type" formControlName="document_type" [allowNull]="true" <app-input-select [items]="documentTypes" i18n-title title="Document type" formControlName="document_type" [allowNull]="true"

View File

@ -107,9 +107,13 @@ export class DocumentDetailComponent implements OnInit {
this.document = doc this.document = doc
this.documentsService.getMetadata(doc.id).subscribe(result => { this.documentsService.getMetadata(doc.id).subscribe(result => {
this.metadata = result this.metadata = result
}, error => {
this.metadata = null
}) })
this.documentsService.getSuggestions(doc.id).subscribe(result => { this.documentsService.getSuggestions(doc.id).subscribe(result => {
this.suggestions = result this.suggestions = result
}, error => {
this.suggestions = null
}) })
this.title = this.documentTitlePipe.transform(doc.title) this.title = this.documentTitlePipe.transform(doc.title)
this.documentForm.patchValue(doc) this.documentForm.patchValue(doc)
@ -179,8 +183,8 @@ export class DocumentDetailComponent implements OnInit {
close() { close() {
this.openDocumentService.closeDocument(this.document) this.openDocumentService.closeDocument(this.document)
if (this.documentListViewService.savedViewId) { if (this.documentListViewService.activeSavedViewId) {
this.router.navigate(['view', this.documentListViewService.savedViewId]) this.router.navigate(['view', this.documentListViewService.activeSavedViewId])
} else { } else {
this.router.navigate(['documents']) this.router.navigate(['documents'])
} }

View File

@ -56,6 +56,20 @@
</div> </div>
</div> </div>
<div class="col-auto ml-auto mb-2 mb-xl-0 d-flex"> <div class="col-auto ml-auto mb-2 mb-xl-0 d-flex">
<div class="btn-group btn-group-sm mr-2">
<button type="button" class="btn btn-outline-primary btn-sm" (click)="downloadSelected()">
<svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#download" />
</svg>&nbsp;<ng-container i18n>Download</ng-container>
</button>
<div class="btn-group" ngbDropdown role="group" aria-label="Button group with nested dropdown">
<button class="btn btn-outline-primary btn-sm dropdown-toggle-split" ngbDropdownToggle></button>
<div class="dropdown-menu shadow" ngbDropdownMenu>
<button ngbDropdownItem i18n (click)="downloadSelected('originals')">Download originals</button>
</div>
</div>
</div>
<button type="button" class="btn btn-sm btn-outline-danger" (click)="applyDelete()"> <button type="button" class="btn btn-sm btn-outline-danger" (click)="applyDelete()">
<svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor"> <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#trash" /> <use xlink:href="assets/bootstrap-icons.svg#trash" />

View File

@ -15,6 +15,7 @@ import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable
import { MatchingModel } from 'src/app/data/matching-model'; import { MatchingModel } from 'src/app/data/matching-model';
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service'; import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
import { ToastService } from 'src/app/services/toast.service'; import { ToastService } from 'src/app/services/toast.service';
import { saveAs } from 'file-saver';
@Component({ @Component({
selector: 'app-bulk-editor', selector: 'app-bulk-editor',
@ -137,7 +138,7 @@ export class BulkEditorComponent {
} else { } 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.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.btnClass = "btn-warning"
modal.componentInstance.btnCaption = $localize`Confirm` modal.componentInstance.btnCaption = $localize`Confirm`
modal.componentInstance.confirmClicked.subscribe(() => { modal.componentInstance.confirmClicked.subscribe(() => {
@ -207,4 +208,10 @@ export class BulkEditorComponent {
this.executeBulkOperation(modal, "delete", {}) this.executeBulkOperation(modal, "delete", {})
}) })
} }
downloadSelected(content = "archive") {
this.documentService.bulkDownload(Array.from(this.list.selected), content).subscribe((result: any) => {
saveAs(result, 'documents.zip');
})
}
} }

View File

@ -6,7 +6,7 @@
.doc-img { .doc-img {
object-fit: cover; object-fit: cover;
object-position: top; object-position: top left;
height: 100%; height: 100%;
position: absolute; position: absolute;
mix-blend-mode: multiply; mix-blend-mode: multiply;

View File

@ -2,7 +2,7 @@
.doc-img { .doc-img {
object-fit: cover; object-fit: cover;
object-position: top; object-position: top left;
height: 200px; height: 200px;
mix-blend-mode: multiply; mix-blend-mode: multiply;
} }

View File

@ -63,12 +63,12 @@
<div class="btn-group ml-2 flex-fill" ngbDropdown role="group"> <div class="btn-group ml-2 flex-fill" ngbDropdown role="group">
<button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" ngbDropdownToggle i18n>Views</button> <button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" ngbDropdownToggle i18n>Views</button>
<div class="dropdown-menu shadow dropdown-menu-right" ngbDropdownMenu> <div class="dropdown-menu shadow dropdown-menu-right" ngbDropdownMenu>
<ng-container *ngIf="!list.savedViewId"> <ng-container *ngIf="!list.activeSavedViewId">
<button ngbDropdownItem *ngFor="let view of savedViewService.allViews" (click)="loadViewConfig(view)">{{view.name}}</button> <button ngbDropdownItem *ngFor="let view of savedViewService.allViews" (click)="loadViewConfig(view)">{{view.name}}</button>
<div class="dropdown-divider" *ngIf="savedViewService.allViews.length > 0"></div> <div class="dropdown-divider" *ngIf="savedViewService.allViews.length > 0"></div>
</ng-container> </ng-container>
<button ngbDropdownItem (click)="saveViewConfig()" *ngIf="list.savedViewId" i18n>Save "{{list.savedViewTitle}}"</button> <button ngbDropdownItem (click)="saveViewConfig()" *ngIf="list.activeSavedViewId" i18n>Save "{{list.activeSavedViewTitle}}"</button>
<button ngbDropdownItem (click)="saveViewConfigAs()" i18n>Save as...</button> <button ngbDropdownItem (click)="saveViewConfigAs()" i18n>Save as...</button>
</div> </div>
</div> </div>
@ -86,7 +86,7 @@
<span i18n *ngIf="list.selected.size == 0">{list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}}</span>&nbsp;<span i18n *ngIf="isFiltered">(filtered)</span> <span i18n *ngIf="list.selected.size == 0">{list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}}</span>&nbsp;<span i18n *ngIf="isFiltered">(filtered)</span>
</p> </p>
<ngb-pagination [pageSize]="list.currentPageSize" [collectionSize]="list.collectionSize" [(page)]="list.currentPage" [maxSize]="5" <ngb-pagination [pageSize]="list.currentPageSize" [collectionSize]="list.collectionSize" [(page)]="list.currentPage" [maxSize]="5"
[rotate]="true" (pageChange)="list.reload()" aria-label="Default pagination"></ngb-pagination> [rotate]="true" aria-label="Default pagination"></ngb-pagination>
</div> </div>
<div *ngIf="displayMode == 'largeCards'"> <div *ngIf="displayMode == 'largeCards'">

View File

@ -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 { ActivatedRoute, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subscription } from 'rxjs'; 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 { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { DOCUMENT_SORT_FIELDS } from 'src/app/services/rest/document.service'; import { DOCUMENT_SORT_FIELDS } from 'src/app/services/rest/document.service';
import { SavedViewService } from 'src/app/services/rest/saved-view.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 { FilterEditorComponent } from './filter-editor/filter-editor.component';
import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component'; import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component';
@ -46,7 +46,7 @@ export class DocumentListComponent implements OnInit, OnDestroy {
} }
getTitle() { getTitle() {
return this.list.savedViewTitle || $localize`Documents` return this.list.activeSavedViewTitle || $localize`Documents`
} }
getSortFields() { getSortFields() {
@ -73,19 +73,18 @@ export class DocumentListComponent implements OnInit, OnDestroy {
this.list.reload() this.list.reload()
}) })
this.route.paramMap.subscribe(params => { this.route.paramMap.subscribe(params => {
this.list.clear()
if (params.has('id')) { if (params.has('id')) {
this.savedViewService.getCached(+params.get('id')).subscribe(view => { this.savedViewService.getCached(+params.get('id')).subscribe(view => {
if (!view) { if (!view) {
this.router.navigate(["404"]) this.router.navigate(["404"])
return return
} }
this.list.savedView = view this.list.activateSavedView(view)
this.list.reload() this.list.reload()
this.rulesChanged() this.rulesChanged()
}) })
} else { } else {
this.list.savedView = null this.list.activateSavedView(null)
this.list.reload() this.list.reload()
this.rulesChanged() this.rulesChanged()
} }
@ -99,16 +98,23 @@ export class DocumentListComponent implements OnInit, OnDestroy {
} }
loadViewConfig(view: PaperlessSavedView) { loadViewConfig(view: PaperlessSavedView) {
this.list.load(view) this.list.loadSavedView(view)
this.list.reload() this.list.reload()
this.rulesChanged() this.rulesChanged()
} }
saveViewConfig() { saveViewConfig() {
this.savedViewService.update(this.list.savedView).subscribe(result => { if (this.list.activeSavedViewId != null) {
this.toastService.showInfo($localize`View "${this.list.savedView.name}" saved successfully.`) 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() { saveViewConfigAs() {
@ -116,7 +122,7 @@ export class DocumentListComponent implements OnInit, OnDestroy {
modal.componentInstance.defaultName = this.filterEditor.generateFilterName() modal.componentInstance.defaultName = this.filterEditor.generateFilterName()
modal.componentInstance.saveClicked.subscribe(formValue => { modal.componentInstance.saveClicked.subscribe(formValue => {
modal.componentInstance.buttonsEnabled = false modal.componentInstance.buttonsEnabled = false
let savedView = { let savedView: PaperlessSavedView = {
name: formValue.name, name: formValue.name,
show_on_dashboard: formValue.showOnDashboard, show_on_dashboard: formValue.showOnDashboard,
show_in_sidebar: formValue.showInSideBar, show_in_sidebar: formValue.showInSideBar,
@ -137,8 +143,8 @@ export class DocumentListComponent implements OnInit, OnDestroy {
resetFilters(): void { resetFilters(): void {
this.filterRulesModified = false this.filterRulesModified = false
if (this.list.savedViewId) { if (this.list.activeSavedViewId) {
this.savedViewService.getCached(this.list.savedViewId).subscribe(viewUntouched => { this.savedViewService.getCached(this.list.activeSavedViewId).subscribe(viewUntouched => {
this.list.filterRules = viewUntouched.filter_rules this.list.filterRules = viewUntouched.filter_rules
this.list.reload() this.list.reload()
}) })
@ -150,11 +156,11 @@ export class DocumentListComponent implements OnInit, OnDestroy {
rulesChanged() { rulesChanged() {
let modified = false 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 modified = this.list.filterRules.length > 0 // documents list is modified if it has any filters
} else { } else {
// compare savedView current filters vs original // 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 let filterRulesInitial = view.filter_rules
if (this.list.filterRules.length !== filterRulesInitial.length) modified = true if (this.list.filterRules.length !== filterRulesInitial.length) modified = true

View File

@ -46,6 +46,8 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
return $localize`Without any tag` 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[] = [] let filterRules: FilterRule[] = []
if (this._titleFilter) { if (this._titleFilter) {
filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter}) filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter})

View File

@ -8,7 +8,7 @@
<div class="modal-body"> <div class="modal-body">
<app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text> <app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text>
<app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> <app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match"></app-input-text> <app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></app-input-check> <app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></app-input-check>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@ -9,7 +9,7 @@
<app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text> <app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text>
<app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> <app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match"></app-input-text> <app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check> <app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
</div> </div>

View File

@ -1,27 +1,18 @@
<app-page-header title="Logs" i18n-title> <app-page-header title="Logs" i18n-title>
<div ngbDropdown class="btn-group">
<button class="btn btn-outline-primary btn-sm" id="dropdownBasic1" ngbDropdownToggle>
<svg class="toolbaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#funnel" />
</svg>&nbsp;<ng-container i18n>Filter</ng-container>
</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1">
<button *ngFor="let f of getLevels()" ngbDropdownItem (click)="setLevel(f.id)"
[class.active]="level == f.id">{{f.name}}</button>
</div>
</div>
</app-page-header> </app-page-header>
<div class="bg-dark p-3 mb-3 text-light text-monospace" infiniteScroll (scrolled)="onScroll()">
<ul ngbNav #nav="ngbNav" [(activeId)]="activeLog" (activeIdChange)="reloadLogs()" class="nav-tabs">
<li *ngFor="let logFile of logFiles" [ngbNavItem]="logFile">
<a ngbNavLink>{{logFile}}.log</a>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
<div class="bg-dark p-3 mb-3 text-light text-monospace log-container">
<p <p
class="m-0 p-0 log-entry-{{log.level}}" class="m-0 p-0 log-entry-{{getLogLevel(log)}}"
*ngFor="let log of logs"> *ngFor="let log of logs" style="white-space: pre;">{{log}}</p>
{{log.created | customDate:'short'}}
{{getLevelText(log.level)}}
{{log.message}}
</p>
</div> </div>

View File

@ -13,4 +13,12 @@
.log-entry-50 { .log-entry-50 {
color: lightcoral !important; color: lightcoral !important;
font-weight: bold; font-weight: bold;
}
.log-container {
overflow: scroll;
height: calc(100vh - 190px);
top: 70px;
} }

View File

@ -1,5 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { LOG_LEVELS, LOG_LEVEL_INFO, PaperlessLog } from 'src/app/data/paperless-log';
import { LogService } from 'src/app/services/rest/log.service'; import { LogService } from 'src/app/services/rest/log.service';
@Component({ @Component({
@ -11,38 +10,42 @@ export class LogsComponent implements OnInit {
constructor(private logService: LogService) { } constructor(private logService: LogService) { }
logs: PaperlessLog[] = [] logs: string[] = []
level: number = LOG_LEVEL_INFO
logFiles: string[] = []
activeLog: string
ngOnInit(): void { ngOnInit(): void {
this.reload() this.logService.list().subscribe(result => {
} this.logFiles = result
if (this.logFiles.length > 0) {
reload() { this.activeLog = this.logFiles[0]
this.logService.list(1, 50, 'created', true, {'level__gte': this.level}).subscribe(result => this.logs = result.results) this.reloadLogs()
} }
getLevelText(level: number) {
return LOG_LEVELS.find(l => l.id == level)?.name
}
onScroll() {
let lastCreated = null
if (this.logs.length > 0) {
lastCreated = new Date(this.logs[this.logs.length-1].created).toISOString()
}
this.logService.list(1, 25, 'created', true, {'created__lt': lastCreated, 'level__gte': this.level}).subscribe(result => {
this.logs.push(...result.results)
}) })
} }
getLevels() { reloadLogs() {
return LOG_LEVELS this.logService.get(this.activeLog).subscribe(result => {
this.logs = result
}, error => {
this.logs = []
})
} }
setLevel(id) { getLogLevel(log: string) {
this.level = id if (log.indexOf("[DEBUG]") != -1) {
this.reload() return 10
} else if (log.indexOf("[WARNING]") != -1) {
return 30
} else if (log.indexOf("[ERROR]") != -1) {
return 40
} else if (log.indexOf("[CRITICAL]") != -1) {
return 50
} else {
return 20
}
} }
} }

View File

@ -34,7 +34,7 @@
<div class="col"> <div class="col">
<select class="form-control" formControlName="dateLocale"> <select class="form-control" formControlName="dateLocale">
<option *ngFor="let lang of dateLocaleOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> - {{today | date:'shortDate':null:lang.code}}</span></option> <option *ngFor="let lang of dateLocaleOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> - {{today | customDate:'shortDate':null:lang.code}}</span></option>
</select> </select>
</div> </div>
@ -167,7 +167,7 @@
</li> </li>
</ul> </ul>
<div [ngbNavOutlet]="nav" class="border-left border-right border-bottom p-3 mb-3 shadow"></div> <div [ngbNavOutlet]="nav" class="border-left border-right border-bottom p-3 mb-3 shadow-sm"></div>
<button type="submit" class="btn btn-primary" i18n>Save</button> <button type="submit" class="btn btn-primary" i18n>Save</button>
</form> </form>

View File

@ -34,7 +34,7 @@ export class SettingsComponent implements OnInit {
savedViews: PaperlessSavedView[] savedViews: PaperlessSavedView[]
get computedDateLocale(): string { get computedDateLocale(): string {
return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage || this.currentLocale
} }
constructor( constructor(
@ -86,11 +86,15 @@ export class SettingsComponent implements OnInit {
} }
get displayLanguageOptions(): LanguageOption[] { get displayLanguageOptions(): LanguageOption[] {
return [{code: "", name: $localize`Use system language`}].concat(this.settings.getLanguageOptions()) return [
{code: "", name: $localize`Use system language`}
].concat(this.settings.getLanguageOptions())
} }
get dateLocaleOptions(): LanguageOption[] { 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`}
].concat(this.settings.getDateLocaleOptions())
} }
get today() { get today() {

View File

@ -20,7 +20,7 @@
<app-input-check i18n-title title="Inbox tag" formControlName="is_inbox_tag" i18n-hint hint="Inbox tags are automatically assigned to all consumed documents."></app-input-check> <app-input-check i18n-title title="Inbox tag" formControlName="is_inbox_tag" i18n-hint hint="Inbox tags are automatically assigned to all consumed documents."></app-input-check>
<app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> <app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match"></app-input-text> <app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></app-input-text>
<app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check> <app-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></app-input-check>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">

View File

@ -1,27 +0,0 @@
export const LOG_LEVEL_DEBUG = 10
export const LOG_LEVEL_INFO = 20
export const LOG_LEVEL_WARNING = 30
export const LOG_LEVEL_ERROR = 40
export const LOG_LEVEL_CRITICAL = 50
export const LOG_LEVELS = [
{id: LOG_LEVEL_DEBUG, name: "DEBUG"},
{id: LOG_LEVEL_INFO, name: "INFO"},
{id: LOG_LEVEL_WARNING, name: "WARNING"},
{id: LOG_LEVEL_ERROR, name: "ERROR"},
{id: LOG_LEVEL_CRITICAL, name: "CRITICAL"}
]
export interface PaperlessLog {
id?: number
group?: string
message?: string
created?: Date
level?: number
}

View File

@ -2,18 +2,29 @@ import { DatePipe } from '@angular/common';
import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core'; import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core';
import { SettingsService, SETTINGS_KEYS } from '../services/settings.service'; 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({ @Pipe({
name: 'customDate' name: 'customDate'
}) })
export class CustomDatePipe extends DatePipe implements PipeTransform { export class CustomDatePipe extends DatePipe implements PipeTransform {
constructor(@Inject(LOCALE_ID) locale: string, private settings: SettingsService) { 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 { 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)
}
} }
} }

View File

@ -169,15 +169,20 @@ export class ConsumerStatusService {
} }
dismiss(status: FileStatus) { dismiss(status: FileStatus) {
let index = this.consumerStatus.findIndex(s => s.filename == status.filename) let index
if (status.taskId != null) {
index = this.consumerStatus.findIndex(s => s.taskId == status.taskId)
} else {
index = this.consumerStatus.findIndex(s => s.filename == status.filename)
}
if (index > -1) { if (index > -1) {
this.consumerStatus.splice(index, 1) this.consumerStatus.splice(index, 1)
} }
} }
dismissAll() { dismissCompleted() {
this.consumerStatus = this.consumerStatus.filter(status => status.phase < FileStatusPhase.SUCCESS) this.consumerStatus = this.consumerStatus.filter(status => status.phase != FileStatusPhase.SUCCESS)
} }
onDocumentConsumptionFinished() { onDocumentConsumptionFinished() {

View File

@ -8,6 +8,23 @@ import { DOCUMENT_LIST_SERVICE } from '../data/storage-keys';
import { DocumentService } from './rest/document.service'; import { DocumentService } from './rest/document.service';
import { SettingsService, SETTINGS_KEYS } from './settings.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<number>
}
/** /**
* This service manages the document list which is displayed using the document list view. * 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 { export class DocumentListViewService {
static DEFAULT_SORT_FIELD = 'created'
isReloading: boolean = false isReloading: boolean = false
documents: PaperlessDocument[] = []
currentPage = 1
currentPageSize: number = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)
collectionSize: number
rangeSelectionAnchorIndex: number rangeSelectionAnchorIndex: number
lastRangeSelectionToIndex: number lastRangeSelectionToIndex: number
/** currentPageSize: number = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)
* 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
get savedView(): PaperlessSavedView { private listViewStates: Map<number, ListViewState> = new Map()
return this._savedViewConfig
private _activeSavedViewId: number = null
get activeSavedViewId() {
return this._activeSavedViewId
} }
set savedView(value: PaperlessSavedView) { get activeSavedViewTitle() {
if (value && !this._savedViewConfig || value && value.id != this._savedViewConfig.id) { return this.activeListViewState.title
//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() private defaultListViewState(): ListViewState {
this._savedViewConfig = Object.assign({}, value) return {
} else if (this._savedViewConfig && !value) { title: null,
//saved view active, but document list requested documents: [],
this.selectNone() currentPage: 1,
this._savedViewConfig = null collectionSize: null,
sortField: "created",
sortReverse: true,
filterRules: [],
selected: new Set<number>()
} }
} }
get savedViewId() { private get activeListViewState() {
return this.savedView?.id if (!this.listViewStates.has(this._activeSavedViewId)) {
this.listViewStates.set(this._activeSavedViewId, this.defaultListViewState())
}
return this.listViewStates.get(this._activeSavedViewId)
} }
get savedViewTitle() { activateSavedView(view: PaperlessSavedView) {
return this.savedView?.name this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null
} if (view) {
this._activeSavedViewId = view.id
get documentListView() { this.loadSavedView(view)
return this._documentListViewConfig } else {
} this._activeSavedViewId = null
set documentListView(value) {
if (value) {
this._documentListViewConfig = Object.assign({}, value)
this.saveDocumentListView()
} }
} }
/** loadSavedView(view: PaperlessSavedView, closeCurrentView: boolean = false) {
* This is what switches between the saved views and the document list view. Everything on the document list uses if (closeCurrentView) {
* this property to determine the settings for the currently displayed document list. this._activeSavedViewId = null
*/ }
get view() { this.activeListViewState.filterRules = cloneFilterRules(view.filter_rules)
return this.savedView || this.documentListView this.activeListViewState.sortField = view.sort_field
} this.activeListViewState.sortReverse = view.sort_reverse
if (this._activeSavedViewId) {
load(view: PaperlessSavedView) { this.activeListViewState.title = view.name
this.documentListView.filter_rules = cloneFilterRules(view.filter_rules) }
this.documentListView.sort_reverse = view.sort_reverse this.reduceSelectionToFilter()
this.documentListView.sort_field = view.sort_field
this.saveDocumentListView()
}
clear() {
this.collectionSize = null
this.documents = []
this.currentPage = 1
} }
reload(onFinish?) { reload(onFinish?) {
this.isReloading = true this.isReloading = true
let activeListViewState = this.activeListViewState
this.documentService.listFiltered( this.documentService.listFiltered(
this.currentPage, activeListViewState.currentPage,
this.currentPageSize, this.currentPageSize,
this.view.sort_field, activeListViewState.sortField,
this.view.sort_reverse, activeListViewState.sortReverse,
this.view.filter_rules).subscribe( activeListViewState.filterRules).subscribe(
result => { result => {
this.collectionSize = result.count this.isReloading = false
this.documents = result.results activeListViewState.collectionSize = result.count
activeListViewState.documents = result.results
if (onFinish) { if (onFinish) {
onFinish() onFinish()
} }
this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null
this.isReloading = false
}, },
error => { 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 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.reload()
} }
this.isReloading = false
}) })
} }
set filterRules(filterRules: FilterRule[]) { set filterRules(filterRules: FilterRule[]) {
//we're going to clone the filterRules object, since we don't this.activeListViewState.filterRules = filterRules
//want changes in the filter editor to propagate into here right away.
this.view.filter_rules = filterRules
this.reload() this.reload()
this.reduceSelectionToFilter() this.reduceSelectionToFilter()
this.saveDocumentListView() this.saveDocumentListView()
} }
get filterRules(): FilterRule[] { get filterRules(): FilterRule[] {
return this.view.filter_rules return this.activeListViewState.filterRules
} }
set sortField(field: string) { set sortField(field: string) {
this.view.sort_field = field this.activeListViewState.sortField = field
this.saveDocumentListView()
this.reload() this.reload()
this.saveDocumentListView()
} }
get sortField(): string { get sortField(): string {
return this.view.sort_field return this.activeListViewState.sortField
} }
set sortReverse(reverse: boolean) { set sortReverse(reverse: boolean) {
this.view.sort_reverse = reverse this.activeListViewState.sortReverse = reverse
this.saveDocumentListView()
this.reload() this.reload()
this.saveDocumentListView()
} }
get sortReverse(): boolean { 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<number> {
return this.activeListViewState.selected
} }
setSort(field: string, reverse: boolean) { setSort(field: string, reverse: boolean) {
this.view.sort_field = field this.activeListViewState.sortField = field
this.view.sort_reverse = reverse this.activeListViewState.sortReverse = reverse
this.saveDocumentListView()
this.reload() this.reload()
this.saveDocumentListView()
} }
private 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[]) { quickFilter(filterRules: FilterRule[]) {
this.savedView = null this._activeSavedViewId = null
this.view.filter_rules = filterRules this.activeListViewState.filterRules = filterRules
this.activeListViewState.currentPage = 1
this.reduceSelectionToFilter() this.reduceSelectionToFilter()
this.saveDocumentListView() this.saveDocumentListView()
this.router.navigate(["documents"]) this.router.navigate(["documents"])
@ -217,8 +252,6 @@ export class DocumentListViewService {
} }
} }
selected = new Set<number>()
selectNone() { selectNone() {
this.selected.clear() this.selected.clear()
this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null
@ -227,13 +260,11 @@ export class DocumentListViewService {
reduceSelectionToFilter() { reduceSelectionToFilter() {
if (this.selected.size > 0) { if (this.selected.size > 0) {
this.documentService.listAllFilteredIds(this.filterRules).subscribe(ids => { this.documentService.listAllFilteredIds(this.filterRules).subscribe(ids => {
let subset = new Set<number>() for (let id of this.selected) {
for (let id of ids) { if (!ids.includes(id)) {
if (this.selected.has(id)) { this.selected.delete(id)
subset.add(id)
} }
} }
this.selected = subset
}) })
} }
} }
@ -287,20 +318,21 @@ export class DocumentListViewService {
} }
constructor(private documentService: DocumentService, private settings: SettingsService, private router: Router) { 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) { if (documentListViewConfigJson) {
try { 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) { } catch (e) {
sessionStorage.removeItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG) 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'
} }
} }
} }

View File

@ -134,4 +134,8 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument>
return this.http.get<PaperlessDocumentSuggestions>(this.getResourceUrl(id, 'suggestions')) return this.http.get<PaperlessDocumentSuggestions>(this.getResourceUrl(id, 'suggestions'))
} }
bulkDownload(ids: number[], content="both") {
return this.http.post(this.getResourceUrl(null, 'bulk_download'), {"documents": ids, "content": content}, { responseType: 'blob' })
}
} }

View File

@ -1,14 +1,21 @@
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { PaperlessLog } from 'src/app/data/paperless-log'; import { Observable } from 'rxjs';
import { AbstractPaperlessService } from './abstract-paperless-service'; import { environment } from 'src/environments/environment';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class LogService extends AbstractPaperlessService<PaperlessLog> { export class LogService {
constructor(http: HttpClient) { constructor(private http: HttpClient) {
super(http, 'logs') }
list(): Observable<string[]> {
return this.http.get<string[]>(`${environment.apiBaseUrl}logs/`)
}
get(id: string): Observable<string[]> {
return this.http.get<string[]>(`${environment.apiBaseUrl}logs/${id}/`)
} }
} }

View File

@ -1,5 +1,5 @@
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core'; import { Inject, Injectable, LOCALE_ID, Renderer2, RendererFactory2 } from '@angular/core';
import { Meta } from '@angular/platform-browser'; import { Meta } from '@angular/platform-browser';
import { CookieService } from 'ngx-cookie-service'; import { CookieService } from 'ngx-cookie-service';
@ -10,9 +10,14 @@ export interface PaperlessSettings {
} }
export interface LanguageOption { export interface LanguageOption {
code: string, code: string
name: string, name: string
englishName?: string englishName?: string
/**
* A date format string for use by the date selectors. MUST contain 'yyyy', 'mm' and 'dd'.
*/
dateInputFormat?: string
} }
export const SETTINGS_KEYS = { export const SETTINGS_KEYS = {
@ -54,7 +59,8 @@ export class SettingsService {
private rendererFactory: RendererFactory2, private rendererFactory: RendererFactory2,
@Inject(DOCUMENT) private document, @Inject(DOCUMENT) private document,
private cookieService: CookieService, private cookieService: CookieService,
private meta: Meta private meta: Meta,
@Inject(LOCALE_ID) private localeId: string
) { ) {
this.renderer = rendererFactory.createRenderer(null, null); this.renderer = rendererFactory.createRenderer(null, null);
@ -77,13 +83,20 @@ export class SettingsService {
getLanguageOptions(): LanguageOption[] { getLanguageOptions(): LanguageOption[] {
return [ return [
{code: "en-US", name: $localize`English (US)`, englishName: "English (US)"}, {code: "en-us", name: $localize`English (US)`, englishName: "English (US)", dateInputFormat: "mm/dd/yyyy"},
{code: "de", name: $localize`German`, englishName: "German"}, {code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)", dateInputFormat: "dd/mm/yyyy"},
{code: "nl", name: $localize`Dutch`, englishName: "Dutch"}, {code: "de", name: $localize`German`, englishName: "German", dateInputFormat: "dd.mm.yyyy"},
{code: "fr", name: $localize`French`, englishName: "French"} {code: "nl", name: $localize`Dutch`, englishName: "Dutch", dateInputFormat: "dd-mm-yyyy"},
{code: "fr", name: $localize`French`, englishName: "French", dateInputFormat: "dd/mm/yyyy"},
{code: "pt-br", name: $localize`Portuguese (Brazil)`, englishName: "Portuguese (Brazil)", dateInputFormat: "dd/mm/yyyy"}
] ]
} }
getDateLocaleOptions(): LanguageOption[] {
let isoOption: LanguageOption = {code: "iso-8601", name: $localize`ISO 8601`, dateInputFormat: "yyyy-mm-dd"}
return [isoOption].concat(this.getLanguageOptions())
}
private getLanguageCookieName() { private getLanguageCookieName() {
let prefix = "" let prefix = ""
if (this.meta.getTag('name=cookie_prefix')) { if (this.meta.getTag('name=cookie_prefix')) {
@ -104,6 +117,11 @@ export class SettingsService {
} }
} }
getLocalizedDateInputFormat(): string {
let dateLocale = this.get(SETTINGS_KEYS.DATE_LOCALE) || this.getLanguage() || this.localeId.toLowerCase()
return this.getDateLocaleOptions().find(o => o.code == dateLocale)?.dateInputFormat || "yyyy-mm-dd"
}
get(key: string): any { get(key: string): any {
let setting = SETTINGS.find(s => s.key == key) let setting = SETTINGS.find(s => s.key == key)

View File

@ -0,0 +1,59 @@
import { Injectable } from "@angular/core"
import { NgbDateParserFormatter, NgbDateStruct } from "@ng-bootstrap/ng-bootstrap"
import { SettingsService } from "../services/settings.service"
@Injectable()
export class LocalizedDateParserFormatter extends NgbDateParserFormatter {
constructor(private settings: SettingsService) {
super()
}
private getDateInputFormat() {
return this.settings.getLocalizedDateInputFormat()
}
/**
* This constructs a regular expression from a date input format which is then
* used to parse dates.
*/
private getDateParseRegex() {
return new RegExp(
"^" + this.getDateInputFormat()
.replace('dd', '(?<day>[0-9]+)')
.replace('mm', '(?<month>[0-9]+)')
.replace('yyyy', '(?<year>[0-9]+)')
.split('.').join('\\.\\s*') + "$" // allow whitespace(s) after dot (specific for German)
)
}
parse(value: string): NgbDateStruct | null {
let match = this.getDateParseRegex().exec(value)
if (match) {
let dateStruct = {
day: +match.groups.day,
month: +match.groups.month,
year: +match.groups.year
}
if (dateStruct.year <= (new Date().getFullYear() - 2000)) {
dateStruct.year += 2000
} else if (dateStruct.year < 100) {
dateStruct.year += 1900
}
return dateStruct
} else {
return null
}
}
format(date: NgbDateStruct | null): string {
if (date) {
return this.getDateInputFormat()
.replace('dd', date.day.toString().padStart(2, '0'))
.replace('mm', date.month.toString().padStart(2, '0'))
.replace('yyyy', date.year.toString().padStart(4, '0'))
} else {
return null
}
}
}

View File

@ -0,0 +1,27 @@
import { Injectable } from "@angular/core";
import { NgbDateAdapter, NgbDateStruct } from "@ng-bootstrap/ng-bootstrap";
@Injectable()
export class ISODateAdapter extends NgbDateAdapter<string> {
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
}
}
}

View File

@ -0,0 +1,23 @@
import { Injectable } from "@angular/core";
import { NgbDateAdapter, NgbDateStruct } from "@ng-bootstrap/ng-bootstrap";
@Injectable()
export class ISODateTimeAdapter extends NgbDateAdapter<string> {
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 {
return date ? new Date(date.year, date.month - 1, date.day).toISOString() : null
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -2,7 +2,7 @@ export const environment = {
production: true, production: true,
apiBaseUrl: "/api/", apiBaseUrl: "/api/",
appTitle: "Paperless-ng", appTitle: "Paperless-ng",
version: "1.1.0", version: "1.2.1",
webSocketHost: window.location.host, webSocketHost: window.location.host,
webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:") webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:")
}; };

View File

@ -9,6 +9,7 @@
<meta name="theme-color" content="#17541f" /> <meta name="theme-color" content="#17541f" />
<link rel="manifest" href="manifest.webmanifest"> <link rel="manifest" href="manifest.webmanifest">
<link rel="icon" type="image/x-icon" href="favicon.ico"> <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="apple-touch-icon" href="apple-touch-icon.png">
</head> </head>
<body class="color-scheme-system"> <body class="color-scheme-system">
<app-root></app-root> <app-root></app-root>

View File

@ -58,11 +58,11 @@
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2155249406916744630"> <trans-unit datatype="html" id="2155249406916744630">
<source>View &quot;<x equiv-text="this.list.savedView.name" id="PH"/>&quot; saved successfully.</source> <source>View &quot;<x equiv-text="this.list.activeSavedViewTitle" id="PH"/>&quot; saved successfully.</source>
<target>Ansicht &quot;<x equiv-text="this.list.savedView.name" id="PH"/>&quot; erfolgreich gespeichert.</target> <target>Ansicht &quot;<x equiv-text="this.list.activeSavedViewTitle" id="PH"/>&quot; erfolgreich gespeichert.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">109</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6837554170707123455"> <trans-unit datatype="html" id="6837554170707123455">
@ -70,7 +70,7 @@
<target>Ansicht &quot;<x equiv-text="savedView.name" id="PH"/>&quot; erfolgreich erstellt.</target> <target>Ansicht &quot;<x equiv-text="savedView.name" id="PH"/>&quot; erfolgreich erstellt.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">130</context> <context context-type="linenumber">136</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="9ca82952a6bc860b5391d5975322d8af8ceddfa4"> <trans-unit datatype="html" id="9ca82952a6bc860b5391d5975322d8af8ceddfa4">
@ -129,9 +129,9 @@
<context context-type="linenumber">72</context> <context context-type="linenumber">72</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="72e7d343f9165602cce1ca7faffbc565fd31ef92"> <trans-unit datatype="html" id="5f5ce787c428d917c30c9bd70789a618e09743a7">
<source>Save &quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&quot;</source> <source>Save &quot;<x equiv-text="{{list.activeSavedViewTitle}}" id="INTERPOLATION"/>&quot;</source>
<target>&quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&quot; speichern</target> <target>&quot;<x equiv-text="{{list.activeSavedViewTitle}}" id="INTERPOLATION"/>&quot; speichern</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">71</context> <context context-type="linenumber">71</context>
@ -214,7 +214,7 @@
<target>Löschen bestätigen</target> <target>Löschen bestätigen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">203</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5382975254277698192"> <trans-unit datatype="html" id="5382975254277698192">
@ -222,7 +222,7 @@
<target>Möchten Sie das Dokument &quot;<x equiv-text="this.document.title" id="PH"/>&quot; wirklich löschen?</target> <target>Möchten Sie das Dokument &quot;<x equiv-text="this.document.title" id="PH"/>&quot; wirklich löschen?</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">200</context> <context context-type="linenumber">204</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6691075929777935948"> <trans-unit datatype="html" id="6691075929777935948">
@ -230,7 +230,7 @@
<target>Die Dateien dieses Dokuments werden permanent gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.</target> <target>Die Dateien dieses Dokuments werden permanent gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">205</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="719892092227206532"> <trans-unit datatype="html" id="719892092227206532">
@ -238,7 +238,7 @@
<target>Dokument löschen</target> <target>Dokument löschen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">203</context> <context context-type="linenumber">207</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1844801255494293730"> <trans-unit datatype="html" id="1844801255494293730">
@ -246,7 +246,7 @@
<target>Fehler beim Löschen des Dokuments: <x equiv-text="JSON.stringify(error)" id="PH"/></target> <target>Fehler beim Löschen des Dokuments: <x equiv-text="JSON.stringify(error)" id="PH"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">210</context> <context context-type="linenumber">214</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="826b25211922a1b46436589233cb6f1a163d89b7"> <trans-unit datatype="html" id="826b25211922a1b46436589233cb6f1a163d89b7">
@ -427,7 +427,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="203c7adb5bd74ae18e84e1e3b66ddfe76bc0023f"> <trans-unit datatype="html" id="203c7adb5bd74ae18e84e1e3b66ddfe76bc0023f">
<source>Original document metadata</source> <source>Original document metadata</source>
<target>Original-Metadaten</target> <target>Metadaten Original</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">121</context> <context context-type="linenumber">121</context>
@ -435,7 +435,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7174dcea4a3d0b743e7e4a78747e902230727f67"> <trans-unit datatype="html" id="7174dcea4a3d0b743e7e4a78747e902230727f67">
<source>Archived document metadata</source> <source>Archived document metadata</source>
<target>Metadaten des archivierten Dokuments</target> <target>Metadaten Archiv</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">122</context> <context context-type="linenumber">122</context>
@ -451,7 +451,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8765497970646365998"> <trans-unit datatype="html" id="8765497970646365998">
<source>Hello <x equiv-text="this.displayName" id="PH"/>, welcome to Paperless-ng!</source> <source>Hello <x equiv-text="this.displayName" id="PH"/>, welcome to Paperless-ng!</source>
<target>Hallo <x equiv-text="this.displayName" id="PH"/>, Willkommen zu Paperless-ng</target> <target>Hallo <x equiv-text="this.displayName" id="PH"/>, willkommen zu Paperless-ng!</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context> <context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
<context context-type="linenumber">33</context> <context context-type="linenumber">33</context>
@ -585,14 +585,6 @@
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5ca707824ab93066c7d9b44e1b8bf216725c2c22">
<source>Filter</source>
<target>Filtern</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/logs/logs.component.html</context>
<context context-type="linenumber">7</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="5610279464668232148"> <trans-unit datatype="html" id="5610279464668232148">
<source>Saved view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; deleted.</source> <source>Saved view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; deleted.</source>
<target>Gespeicherte Ansicht &quot;<x equiv-text="savedView.name" id="PH"/>&quot; gelöscht.</target> <target>Gespeicherte Ansicht &quot;<x equiv-text="savedView.name" id="PH"/>&quot; gelöscht.</target>
@ -614,7 +606,7 @@
<target>Benutze Systemsprache</target> <target>Benutze Systemsprache</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">91</context> <context context-type="linenumber">92</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7729897675462249787"> <trans-unit datatype="html" id="7729897675462249787">
@ -622,7 +614,7 @@
<target>Benutze Datumsformat der Anzeigesprache</target> <target>Benutze Datumsformat der Anzeigesprache</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">95</context> <context context-type="linenumber">98</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8488620293789898901"> <trans-unit datatype="html" id="8488620293789898901">
@ -630,7 +622,7 @@
<target>Fehler beim Speichern der Einstellungen auf dem Server: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target> <target>Fehler beim Speichern der Einstellungen auf dem Server: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">111</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716"> <trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
@ -1234,6 +1226,14 @@
<context context-type="linenumber">46</context> <context context-type="linenumber">46</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6523384805359286307">
<source>Title: <x equiv-text="rule.value" id="PH"/></source>
<target>Titel: <x equiv-text="rule.value" id="PH"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="02d184c288f567825a1fcbf83bcd3099a10853d5"> <trans-unit datatype="html" id="02d184c288f567825a1fcbf83bcd3099a10853d5">
<source>Filter tags</source> <source>Filter tags</source>
<target>Tags filtern</target> <target>Tags filtern</target>
@ -1392,7 +1392,7 @@
<target>Fehler beim Ausführung der Massenverarbeitung: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target> <target>Fehler beim Ausführung der Massenverarbeitung: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">74</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7894972847287473517"> <trans-unit datatype="html" id="7894972847287473517">
@ -1400,7 +1400,7 @@
<target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot;</target> <target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">112</context> <context context-type="linenumber">113</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8639884465898458690"> <trans-unit datatype="html" id="8639884465898458690">
@ -1408,7 +1408,7 @@
<target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot; und &quot;<x equiv-text="items[1].name" id="PH_1"/>&quot;</target> <target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot; und &quot;<x equiv-text="items[1].name" id="PH_1"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">114</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
<note from="description" priority="1">This is for messages like 'modify &quot;tag1&quot; and &quot;tag2&quot;'</note> <note from="description" priority="1">This is for messages like 'modify &quot;tag1&quot; and &quot;tag2&quot;'</note>
</trans-unit> </trans-unit>
@ -1417,7 +1417,7 @@
<target>, </target> <target>, </target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">116</context> <context context-type="linenumber">117</context>
</context-group> </context-group>
<note from="description" priority="1">this is used to separate enumerations and should probably be a comma and a whitespace in most languages</note> <note from="description" priority="1">this is used to separate enumerations and should probably be a comma and a whitespace in most languages</note>
</trans-unit> </trans-unit>
@ -1426,7 +1426,7 @@
<target><x equiv-text="list" id="PH"/> und &quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&quot;</target> <target><x equiv-text="list" id="PH"/> und &quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">117</context> <context context-type="linenumber">118</context>
</context-group> </context-group>
<note from="description" priority="1">this is for messages like 'modify &quot;tag1&quot;, &quot;tag2&quot; and &quot;tag3&quot;'</note> <note from="description" priority="1">this is for messages like 'modify &quot;tag1&quot;, &quot;tag2&quot; and &quot;tag3&quot;'</note>
</trans-unit> </trans-unit>
@ -1435,7 +1435,7 @@
<target>Tag-Zuweisung bestätigen</target> <target>Tag-Zuweisung bestätigen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">126</context> <context context-type="linenumber">127</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6619516195038467207"> <trans-unit datatype="html" id="6619516195038467207">
@ -1443,7 +1443,7 @@
<target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten das Tag &quot;<x equiv-text="tag.name" id="PH"/>&quot; hinzufügen.</target> <target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten das Tag &quot;<x equiv-text="tag.name" id="PH"/>&quot; hinzufügen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">129</context> <context context-type="linenumber">130</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1894412783609570695"> <trans-unit datatype="html" id="1894412783609570695">
@ -1451,7 +1451,7 @@
<target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten die Tags <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> hinzufügen.</target> <target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten die Tags <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> hinzufügen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">131</context> <context context-type="linenumber">132</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7181166515756808573"> <trans-unit datatype="html" id="7181166515756808573">
@ -1459,7 +1459,7 @@
<target>Diese Aktion wird das Tag &quot;<x equiv-text="tag.name" id="PH"/>&quot; von <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten entfernen.</target> <target>Diese Aktion wird das Tag &quot;<x equiv-text="tag.name" id="PH"/>&quot; von <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten entfernen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">134</context> <context context-type="linenumber">135</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3819792277998068944"> <trans-unit datatype="html" id="3819792277998068944">
@ -1467,7 +1467,7 @@
<target>Diese Aktion wird die Tags <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH"/> von <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten entfernen.</target> <target>Diese Aktion wird die Tags <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH"/> von <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten entfernen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">136</context> <context context-type="linenumber">137</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2739066218579571288"> <trans-unit datatype="html" id="2739066218579571288">
@ -1475,7 +1475,7 @@
<target>Diese Aktion wird die Tags <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> den <x equiv-text="this.list.selected.size" id="PH_2"/> ausgewählten Dokumenten hinzufügen und die Tags <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH_1"/> entfernen.</target> <target>Diese Aktion wird die Tags <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> den <x equiv-text="this.list.selected.size" id="PH_2"/> ausgewählten Dokumenten hinzufügen und die Tags <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH_1"/> entfernen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">138</context> <context context-type="linenumber">139</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2996713129519325161"> <trans-unit datatype="html" id="2996713129519325161">
@ -1483,7 +1483,7 @@
<target>Korrespondent-Zuweisung bestätigen</target> <target>Korrespondent-Zuweisung bestätigen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">158</context> <context context-type="linenumber">159</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6900893559485781849"> <trans-unit datatype="html" id="6900893559485781849">
@ -1491,7 +1491,7 @@
<target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten den Korrespondent &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; zuweisen.</target> <target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten den Korrespondent &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; zuweisen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">160</context> <context context-type="linenumber">161</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1257522660364398440"> <trans-unit datatype="html" id="1257522660364398440">
@ -1499,7 +1499,7 @@
<target>Diese Aktion wird bei <x equiv-text="this.list.selected.size" id="PH"/> ausgewählten Dokumenten den Korrespondent entfernen.</target> <target>Diese Aktion wird bei <x equiv-text="this.list.selected.size" id="PH"/> ausgewählten Dokumenten den Korrespondent entfernen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">162</context> <context context-type="linenumber">163</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5393409374423140648"> <trans-unit datatype="html" id="5393409374423140648">
@ -1507,7 +1507,7 @@
<target>Dokumenttyp-Zuweisung bestätigen</target> <target>Dokumenttyp-Zuweisung bestätigen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">181</context> <context context-type="linenumber">182</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="332180123895325027"> <trans-unit datatype="html" id="332180123895325027">
@ -1515,7 +1515,7 @@
<target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten den Dokumenttyp &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; zuweisen.</target> <target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH_1"/> ausgewählten Dokumenten den Dokumenttyp &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; zuweisen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">183</context> <context context-type="linenumber">184</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2236642492594872779"> <trans-unit datatype="html" id="2236642492594872779">
@ -1523,7 +1523,7 @@
<target>Diese Aktion wird bei <x equiv-text="this.list.selected.size" id="PH"/> ausgewählten Dokumenten den Dokumenttyp entfernen.</target> <target>Diese Aktion wird bei <x equiv-text="this.list.selected.size" id="PH"/> ausgewählten Dokumenten den Dokumenttyp entfernen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">185</context> <context context-type="linenumber">186</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="749430623564850405"> <trans-unit datatype="html" id="749430623564850405">
@ -1531,7 +1531,7 @@
<target>Löschen bestätigen</target> <target>Löschen bestätigen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">200</context> <context context-type="linenumber">201</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="4303174930844518780"> <trans-unit datatype="html" id="4303174930844518780">
@ -1539,7 +1539,7 @@
<target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH"/> ausgewählte Dokumente unwiderruflich löschen.</target> <target>Diese Aktion wird <x equiv-text="this.list.selected.size" id="PH"/> ausgewählte Dokumente unwiderruflich löschen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">202</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5641451190833696892"> <trans-unit datatype="html" id="5641451190833696892">
@ -1547,7 +1547,7 @@
<target>Diese Aktion kann nicht rückgängig gemacht werden.</target> <target>Diese Aktion kann nicht rückgängig gemacht werden.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">202</context> <context context-type="linenumber">203</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6734339521247847366"> <trans-unit datatype="html" id="6734339521247847366">
@ -1555,7 +1555,7 @@
<target>Dokument(e) löschen</target> <target>Dokument(e) löschen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">204</context> <context context-type="linenumber">205</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8b0609df23817024b3bed12beb9b64fc1009f588"> <trans-unit datatype="html" id="8b0609df23817024b3bed12beb9b64fc1009f588">
@ -1582,6 +1582,14 @@
<context context-type="linenumber">27</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="fc2de37422d7c4af6686842283cc2afd781b6848">
<source>Download originals</source>
<target>Originale herunterladen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="a1e6c11f20d4bf6e8e6b43e3c6d2561b2080645e"> <trans-unit datatype="html" id="a1e6c11f20d4bf6e8e6b43e3c6d2561b2080645e">
<source>Suggestions:</source> <source>Suggestions:</source>
<target>Vorschläge:</target> <target>Vorschläge:</target>
@ -1614,22 +1622,22 @@
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="46c8fe557cf52c9389783627d4f85453f4ddb459"> <trans-unit datatype="html" id="c0d907c2687c09612395aee6ef7c04ca8e5e5e0a">
<source>Documents in inbox: <x equiv-text="{{statistics.documents_inbox}}" id="INTERPOLATION"/></source> <source>Total documents: <x equiv-text="{{statistics?.documents_total}}" id="INTERPOLATION"/></source>
<target>Dokumente im Posteingang: <x equiv-text="{{statistics.documents_inbox}}" id="INTERPOLATION"/></target> <target>Anzahl Dokumente gesamt: <x equiv-text="{{statistics?.documents_total}}" id="INTERPOLATION"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="c327c0e67bcac7494dcbaa9afb3b42d5008c6438">
<source>Total documents: <x equiv-text="{{statistics.documents_total}}" id="INTERPOLATION"/></source>
<target>Anzahl Dokumente gesamt: <x equiv-text="{{statistics.documents_total}}" id="INTERPOLATION"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">4</context> <context context-type="linenumber">4</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="13e8d49dbcad9f9d71e66a9a56d6f328cff430c9">
<source>Documents in inbox: <x equiv-text="{{statistics?.documents_inbox}}" id="INTERPOLATION"/></source>
<target>Dokumente im Posteingang: <x equiv-text="{{statistics?.documents_inbox}}" id="INTERPOLATION"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6443586946875325554"> <trans-unit datatype="html" id="6443586946875325554">
<source>Processing: <x equiv-text="countUploadingAndProcessing" id="PH"/></source> <source>Processing: <x equiv-text="countUploadingAndProcessing" id="PH"/></source>
<target>Verarbeite: <x equiv-text="countUploadingAndProcessing" id="PH"/></target> <target>Verarbeite: <x equiv-text="countUploadingAndProcessing" id="PH"/></target>
@ -1678,6 +1686,14 @@
<context context-type="linenumber">126</context> <context context-type="linenumber">126</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1405142710727603568">
<source>HTTP error: <x equiv-text="error.status" id="PH"/> <x equiv-text="error.statusText" id="PH_1"/></source>
<target>HTTP-Fehler: <x equiv-text="error.status" id="PH"/> <x equiv-text="error.statusText" id="PH_1"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context>
<context context-type="linenumber">136</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e022072b3e4dd77e3f09960817ef3359a49963b3"> <trans-unit datatype="html" id="e022072b3e4dd77e3f09960817ef3359a49963b3">
<source>Upload new documents</source> <source>Upload new documents</source>
<target>Neue Dokumente hochladen</target> <target>Neue Dokumente hochladen</target>
@ -1746,7 +1762,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="ea8d9a9486d5639d1c38c012900b8d34d5e4135d"> <trans-unit datatype="html" id="ea8d9a9486d5639d1c38c012900b8d34d5e4135d">
<source>You can start uploading documents by dropping them in the file upload box to the right or by dropping them in the configured consumption folder and they'll start showing up in the documents list. After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and they will appear on the dashboard instead of this message.</source> <source>You can start uploading documents by dropping them in the file upload box to the right or by dropping them in the configured consumption folder and they'll start showing up in the documents list. After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and they will appear on the dashboard instead of this message.</source>
<target>Sie können Ihre Dokumente hochladen, indem Sie sie in die Hochladebox auf der rechten Seite ziehen oder in den konfigurierten Ablageordner kopieren/verschieben. Danach werden sie inder Dokumentliste erscheinen. Nachdem Sie den Dokumenten einige Metadaten zugewiesen haben, können Sie die Filtermechanismen zur Erstellung individueller Ansichten benutzen (zum Beispiel &quot;Kürzlich hinzugefügt&quot; oder &quot;Mit TODO markiert&quot;) und die Ansichten werden auf der Startseite erscheinen und diese Nachricht ersetzen.</target> <target>Sie können Ihre Dokumente hochladen, indem Sie sie in die Hochladebox auf der rechten Seite ziehen oder in den konfigurierten Ablageordner kopieren/verschieben. Danach werden sie in der Dokumentenliste erscheinen. Nachdem Sie den Dokumenten einige Metadaten zugewiesen haben, können Sie die Filtermechanismen zur Erstellung individueller Ansichten benutzen (zum Beispiel &quot;Kürzlich hinzugefügt&quot; oder &quot;Mit TODO markiert&quot;) und die Ansichten werden auf der Startseite anstelle dieser Nachricht erscheinen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html</context>
<context context-type="linenumber">6,7</context> <context context-type="linenumber">6,7</context>
@ -1762,7 +1778,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="edb41dc36af2e70789d8e3422325d930e783253e"> <trans-unit datatype="html" id="edb41dc36af2e70789d8e3422325d930e783253e">
<source>Once you've got a couple documents in paperless and added metadata to them, paperless can assign that metadata to new documents automatically.</source> <source>Once you've got a couple documents in paperless and added metadata to them, paperless can assign that metadata to new documents automatically.</source>
<target>Nachdem Sie einige Dokumente hochgeladen und mit Metadatebn haben, kann Paperless diese Metadaten automatisch passenden neuen Dokumenten zuweisen.</target> <target>Nachdem Sie einige Dokumente hochgeladen und diesen Korrespondenten, Tags und Dokumenttypen zugewiesen haben, kann Paperless-ng diese Metadaten automatisch passenden neuen Dokumenten zuweisen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html</context>
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
@ -1770,7 +1786,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="9dca488928c0b7af46c3866f86a6c2c8548399aa"> <trans-unit datatype="html" id="9dca488928c0b7af46c3866f86a6c2c8548399aa">
<source>You can configure paperless to read your mails and add documents from attached files.</source> <source>You can configure paperless to read your mails and add documents from attached files.</source>
<target>Sie können in Paperless Ihre E-Mail-Konten einrichten und Paperless wird automatisch Dokumente aus Dateianhängen erzeugen.</target> <target>Sie können in Paperless Ihre E-Mail-Konten einrichten und Paperless wird automatisch Dokumente aus Dateianhängen hinzufügen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html</context>
<context context-type="linenumber">11</context> <context context-type="linenumber">11</context>
@ -1808,6 +1824,14 @@
<context context-type="linenumber">21</context> <context context-type="linenumber">21</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="d6529debfc1613db22d6fa096ebfeb8a85fa739d">
<source>Invalid date.</source>
<target>Ungültiges Datum.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2807800733729323332"> <trans-unit datatype="html" id="2807800733729323332">
<source>Yes</source> <source>Yes</source>
<target>Ja</target> <target>Ja</target>
@ -1837,7 +1861,15 @@
<target>Englisch (US)</target> <target>Englisch (US)</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">82</context> <context context-type="linenumber">88</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6987083569809053351">
<source>English (GB)</source>
<target>Englisch (UK)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">89</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1858110241312746425"> <trans-unit datatype="html" id="1858110241312746425">
@ -1845,7 +1877,7 @@
<target>Deutsch</target> <target>Deutsch</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">83</context> <context context-type="linenumber">90</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3071065188816255493"> <trans-unit datatype="html" id="3071065188816255493">
@ -1853,7 +1885,7 @@
<target>Niederländisch</target> <target>Niederländisch</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">84</context> <context context-type="linenumber">91</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7633754075223722162"> <trans-unit datatype="html" id="7633754075223722162">
@ -1861,7 +1893,23 @@
<target>Französisch</target> <target>Französisch</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">85</context> <context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="9184513005098760425">
<source>Portuguese (Brazil)</source>
<target>Portugiesisch (Brasilien)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4912706592792948707">
<source>ISO 8601</source>
<target>ISO 8601</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">98</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2119857572761283468"> <trans-unit datatype="html" id="2119857572761283468">
@ -1869,7 +1917,7 @@
<target>Dokument existiert bereits.</target> <target>Dokument existiert bereits.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">14</context> <context context-type="linenumber">15</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="148389968432135849"> <trans-unit datatype="html" id="148389968432135849">
@ -1877,7 +1925,7 @@
<target>Datei nicht gefunden.</target> <target>Datei nicht gefunden.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">15</context> <context context-type="linenumber">16</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1520671543092565667"> <trans-unit datatype="html" id="1520671543092565667">
@ -1885,7 +1933,7 @@
<target>Pre-Consume-Skript existiert nicht.</target> <target>Pre-Consume-Skript existiert nicht.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">17</context>
</context-group> </context-group>
<note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1894,7 +1942,7 @@
<target>Fehler beim Ausführen des Pre-Consume-Skripts.</target> <target>Fehler beim Ausführen des Pre-Consume-Skripts.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">17</context> <context context-type="linenumber">18</context>
</context-group> </context-group>
<note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1903,7 +1951,7 @@
<target>Post-Consume-Skript existiert nicht.</target> <target>Post-Consume-Skript existiert nicht.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">18</context> <context context-type="linenumber">19</context>
</context-group> </context-group>
<note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1912,7 +1960,7 @@
<target>Fehler beim Ausführen des Post-Consume-Skripts.</target> <target>Fehler beim Ausführen des Post-Consume-Skripts.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">19</context> <context context-type="linenumber">20</context>
</context-group> </context-group>
<note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1921,7 +1969,7 @@
<target>Neue Datei erhalten.</target> <target>Neue Datei erhalten.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">20</context> <context context-type="linenumber">21</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7337565919209746135"> <trans-unit datatype="html" id="7337565919209746135">
@ -1929,7 +1977,7 @@
<target>Dateityp wird nicht unterstützt.</target> <target>Dateityp wird nicht unterstützt.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">21</context> <context context-type="linenumber">22</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5002399167376099234"> <trans-unit datatype="html" id="5002399167376099234">
@ -1937,7 +1985,7 @@
<target>Verarbeite Dokument...</target> <target>Verarbeite Dokument...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">22</context> <context context-type="linenumber">23</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1085975194762600381"> <trans-unit datatype="html" id="1085975194762600381">
@ -1945,7 +1993,7 @@
<target>Erzeuge Miniaturbild...</target> <target>Erzeuge Miniaturbild...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">23</context> <context context-type="linenumber">24</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3280851677698431426"> <trans-unit datatype="html" id="3280851677698431426">
@ -1953,7 +2001,7 @@
<target>Ermittle Datum des Dokuments...</target> <target>Ermittle Datum des Dokuments...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">24</context> <context context-type="linenumber">25</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7162102384876037296"> <trans-unit datatype="html" id="7162102384876037296">
@ -1961,7 +2009,7 @@
<target>Speichere Dokument...</target> <target>Speichere Dokument...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">25</context> <context context-type="linenumber">26</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="4550450765009165976"> <trans-unit datatype="html" id="4550450765009165976">
@ -1969,7 +2017,7 @@
<target>Abgeschlossen.</target> <target>Abgeschlossen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">26</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1519954996184640001"> <trans-unit datatype="html" id="1519954996184640001">
@ -2222,7 +2270,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7517655726614958140"> <trans-unit datatype="html" id="7517655726614958140">
<source>Any: Document contains any of these words (space separated)</source> <source>Any: Document contains any of these words (space separated)</source>
<target>Irgendein Wort: Dokument enthält eins der folgender Wörter</target> <target>Irgendein Wort: Dokument enthält eins der folgenden Wörter</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/data/matching-model.ts</context> <context context-type="sourcefile">src/app/data/matching-model.ts</context>
<context context-type="linenumber">12</context> <context context-type="linenumber">12</context>
@ -2270,7 +2318,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7548151332424148033"> <trans-unit datatype="html" id="7548151332424148033">
<source>Regular expression: Document matches this regular expression</source> <source>Regular expression: Document matches this regular expression</source>
<target>Regulärer Ausdruck: Dokument passt zum folgenden Audruck</target> <target>Regulärer Ausdruck: Dokument passt zum folgenden Ausdruck</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/data/matching-model.ts</context> <context context-type="sourcefile">src/app/data/matching-model.ts</context>
<context context-type="linenumber">15</context> <context context-type="linenumber">15</context>
@ -2286,7 +2334,7 @@
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8419167206585286450"> <trans-unit datatype="html" id="8419167206585286450">
<source>Fuzzy: Document contains a word similar to this word</source> <source>Fuzzy: Document contains a word similar to this word</source>
<target>Ungenau: Dokument enthält ein zum folgendem Wort ähnliches Wort</target> <target>Ungenau: Dokument enthält ein zum folgenden Wort ähnliches Wort</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/data/matching-model.ts</context> <context context-type="sourcefile">src/app/data/matching-model.ts</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">16</context>

File diff suppressed because it is too large Load Diff

View File

@ -58,11 +58,11 @@
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2155249406916744630"> <trans-unit datatype="html" id="2155249406916744630">
<source>View &quot;<x equiv-text="this.list.savedView.name" id="PH"/>&quot; saved successfully.</source> <source>View &quot;<x equiv-text="this.list.activeSavedViewTitle" id="PH"/>&quot; saved successfully.</source>
<target>Vue &quot;<x equiv-text="this.list.savedView.name" id="PH"/>&quot; enregistrée avec succès.</target> <target>Vue &quot;<x equiv-text="this.list.activeSavedViewTitle" id="PH"/>&quot; enregistrée avec succès.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">109</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6837554170707123455"> <trans-unit datatype="html" id="6837554170707123455">
@ -70,7 +70,7 @@
<target>Vue &quot;<x equiv-text="savedView.name" id="PH"/>&quot; créée avec succès.</target> <target>Vue &quot;<x equiv-text="savedView.name" id="PH"/>&quot; créée avec succès.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">130</context> <context context-type="linenumber">136</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="9ca82952a6bc860b5391d5975322d8af8ceddfa4"> <trans-unit datatype="html" id="9ca82952a6bc860b5391d5975322d8af8ceddfa4">
@ -129,9 +129,9 @@
<context context-type="linenumber">72</context> <context context-type="linenumber">72</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="72e7d343f9165602cce1ca7faffbc565fd31ef92"> <trans-unit datatype="html" id="5f5ce787c428d917c30c9bd70789a618e09743a7">
<source>Save &quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&quot;</source> <source>Save &quot;<x equiv-text="{{list.activeSavedViewTitle}}" id="INTERPOLATION"/>&quot;</source>
<target>Enregistrer &quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&quot;</target> <target>Enregistrer &quot;<x equiv-text="{{list.activeSavedViewTitle}}" id="INTERPOLATION"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">71</context> <context context-type="linenumber">71</context>
@ -214,7 +214,7 @@
<target>Confirmer la suppression</target> <target>Confirmer la suppression</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">203</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5382975254277698192"> <trans-unit datatype="html" id="5382975254277698192">
@ -222,7 +222,7 @@
<target>Voulez-vous vraiment supprimer le document &quot;<x equiv-text="this.document.title" id="PH"/>&quot; ?</target> <target>Voulez-vous vraiment supprimer le document &quot;<x equiv-text="this.document.title" id="PH"/>&quot; ?</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">200</context> <context context-type="linenumber">204</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6691075929777935948"> <trans-unit datatype="html" id="6691075929777935948">
@ -230,7 +230,7 @@
<target>Les fichiers liés à ce document seront supprimés définitivement. Cette action est irréversible.</target> <target>Les fichiers liés à ce document seront supprimés définitivement. Cette action est irréversible.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">205</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="719892092227206532"> <trans-unit datatype="html" id="719892092227206532">
@ -238,7 +238,7 @@
<target>Supprimer le document</target> <target>Supprimer le document</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">203</context> <context context-type="linenumber">207</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1844801255494293730"> <trans-unit datatype="html" id="1844801255494293730">
@ -246,7 +246,7 @@
<target>Une erreur s'est produite lors de la suppression du document : <x equiv-text="JSON.stringify(error)" id="PH"/></target> <target>Une erreur s'est produite lors de la suppression du document : <x equiv-text="JSON.stringify(error)" id="PH"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">210</context> <context context-type="linenumber">214</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="826b25211922a1b46436589233cb6f1a163d89b7"> <trans-unit datatype="html" id="826b25211922a1b46436589233cb6f1a163d89b7">
@ -585,14 +585,6 @@
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5ca707824ab93066c7d9b44e1b8bf216725c2c22">
<source>Filter</source>
<target>Filtrer</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/logs/logs.component.html</context>
<context context-type="linenumber">7</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="5610279464668232148"> <trans-unit datatype="html" id="5610279464668232148">
<source>Saved view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; deleted.</source> <source>Saved view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; deleted.</source>
<target>Vue &quot;<x equiv-text="savedView.name" id="PH"/>&quot; supprimée.</target> <target>Vue &quot;<x equiv-text="savedView.name" id="PH"/>&quot; supprimée.</target>
@ -614,7 +606,7 @@
<target>Utiliser la langue du système</target> <target>Utiliser la langue du système</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">91</context> <context context-type="linenumber">92</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7729897675462249787"> <trans-unit datatype="html" id="7729897675462249787">
@ -622,7 +614,7 @@
<target>Utiliser le format de date de la langue d'affichage</target> <target>Utiliser le format de date de la langue d'affichage</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">95</context> <context context-type="linenumber">98</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8488620293789898901"> <trans-unit datatype="html" id="8488620293789898901">
@ -630,7 +622,7 @@
<target>Une erreur s'est produite lors de l'enregistrement des paramètres sur le serveur : <x equiv-text="JSON.stringify(error.error)" id="PH"/></target> <target>Une erreur s'est produite lors de l'enregistrement des paramètres sur le serveur : <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">111</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716"> <trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
@ -649,12 +641,20 @@
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8bcabdf6b16cad0313a86c7e940c5e3ad7f9f8ab">
<source>Notifications</source>
<target>Notifications</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">115</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="99dee94e92dbd9e21a008d4569f9719ed206ae37"> <trans-unit datatype="html" id="99dee94e92dbd9e21a008d4569f9719ed206ae37">
<source>Saved views</source> <source>Saved views</source>
<target>Vues enregistrées</target> <target>Vues enregistrées</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">128</context> <context context-type="linenumber">133</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13"> <trans-unit datatype="html" id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13">
@ -777,68 +777,12 @@
<context context-type="linenumber">98</context> <context context-type="linenumber">98</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8bcabdf6b16cad0313a86c7e940c5e3ad7f9f8ab">
<source>Notifications</source>
<target>Notifications</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">102</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8680abbea249ebe9c2fe35556559c8e1a9eb5841">
<source>Document processing</source>
<target>Traitement de document</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">106</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2ad4d76b36341c589d94004ad2a213fd4d6f5ca0">
<source>Show notifications when new documents are detected</source>
<target>Afficher des notifications lorsque de nouveaux documents sont détectés</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">109</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e775f4f7c40249d31426ae61a21616a0c9d8e84f">
<source>Show notifications when document processing completes successfully</source>
<target>Afficher des notifications lorsque le traitement des documents se termine avec succès</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">110</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e3844dd174d8e817ddb551fae28f14ae80ca36b6">
<source>Show notifications when document processing fails</source>
<target>Afficher des notifications en cas d'échec du traitement des documents</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">111</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="af113f7c9f7e13145c3461f61a1aedf12d57bd71">
<source>Suppress notifications on dashboard</source>
<target>Supprimer les notifications du tableau de bord</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">112</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e27bd3804d2936a6897e81c2e52e294490e5e5a8">
<source>This will suppress all messages about document processing status on the dashboard.</source>
<target>Cela supprimera tous les messages liés au traitement de documents sur le tableau de bord.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">112</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82"> <trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82">
<source>Bulk editing</source> <source>Bulk editing</source>
<target>Edition en masse</target> <target>Edition en masse</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">116</context> <context context-type="linenumber">102</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586"> <trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586">
@ -846,7 +790,7 @@
<target>Afficher les messages de confirmation</target> <target>Afficher les messages de confirmation</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">120</context> <context context-type="linenumber">106</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a"> <trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a">
@ -854,7 +798,7 @@
<target>La suppression de documents requiert toujours une confirmation.</target> <target>La suppression de documents requiert toujours une confirmation.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">120</context> <context context-type="linenumber">106</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e"> <trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e">
@ -862,7 +806,55 @@
<target>Appliquer lors de la fermeture</target> <target>Appliquer lors de la fermeture</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">121</context> <context context-type="linenumber">107</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8680abbea249ebe9c2fe35556559c8e1a9eb5841">
<source>Document processing</source>
<target>Traitement de documents</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">118</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2ad4d76b36341c589d94004ad2a213fd4d6f5ca0">
<source>Show notifications when new documents are detected</source>
<target>Afficher des notifications lorsque de nouveaux documents sont détectés</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">122</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e775f4f7c40249d31426ae61a21616a0c9d8e84f">
<source>Show notifications when document processing completes successfully</source>
<target>Afficher des notifications lorsque le traitement des documents se termine avec succès</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">123</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e3844dd174d8e817ddb551fae28f14ae80ca36b6">
<source>Show notifications when document processing fails</source>
<target>Afficher des notifications en cas d'échec du traitement des documents</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">124</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="af113f7c9f7e13145c3461f61a1aedf12d57bd71">
<source>Suppress notifications on dashboard</source>
<target>Supprimer les notifications du tableau de bord</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">125</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e27bd3804d2936a6897e81c2e52e294490e5e5a8">
<source>This will suppress all messages about document processing status on the dashboard.</source>
<target>Cela supprimera tous les messages liés au traitement de documents sur le tableau de bord.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">125</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8cb90334f5dfd7fc67205085f59381e2a334ccfc"> <trans-unit datatype="html" id="8cb90334f5dfd7fc67205085f59381e2a334ccfc">
@ -870,7 +862,7 @@
<target>Apparaît sur</target> <target>Apparaît sur</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">140</context> <context context-type="linenumber">145</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5"> <trans-unit datatype="html" id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5">
@ -878,7 +870,7 @@
<target>Montrer sur le tableau de bord</target> <target>Montrer sur le tableau de bord</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">143</context> <context context-type="linenumber">148</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="541bfc5b123b3f8867fd681eaceefb663a811973"> <trans-unit datatype="html" id="541bfc5b123b3f8867fd681eaceefb663a811973">
@ -886,7 +878,7 @@
<target>Montrer dans la barre latérale</target> <target>Montrer dans la barre latérale</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">147</context> <context context-type="linenumber">152</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="abba764a7a595d04dc8c3b26e04b3780d4fdb540"> <trans-unit datatype="html" id="abba764a7a595d04dc8c3b26e04b3780d4fdb540">
@ -894,7 +886,7 @@
<target>Aucune vue sauvegardée n'est définie.</target> <target>Aucune vue sauvegardée n'est définie.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">157</context> <context context-type="linenumber">162</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="ef60a738a565f498b858e903e42bc5ffc3cc1299"> <trans-unit datatype="html" id="ef60a738a565f498b858e903e42bc5ffc3cc1299">
@ -1119,7 +1111,7 @@
<target>Gestion</target> <target>Gestion</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">112</context> <context context-type="linenumber">107</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="408cb6073e60c5d966296a3207fc596adca75e01"> <trans-unit datatype="html" id="408cb6073e60c5d966296a3207fc596adca75e01">
@ -1127,15 +1119,15 @@
<target>Administration</target> <target>Administration</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">154</context> <context context-type="linenumber">149</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="46aa32e581922d6d2c3d7bc4c87209ad5808b029"> <trans-unit datatype="html" id="321e4419a943044e674beb55b8039f42a9761ca5">
<source>Misc</source> <source>Info</source>
<target>Divers</target> <target>Info</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">160</context> <context context-type="linenumber">155</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="fcfd4675b4c90f08d18d3abede9a9a4dff4cfdc7"> <trans-unit datatype="html" id="fcfd4675b4c90f08d18d3abede9a9a4dff4cfdc7">
@ -1143,7 +1135,7 @@
<target>Documentation</target> <target>Documentation</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">167</context> <context context-type="linenumber">162</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="355a222236bc01b9a8cd3cb9ecf76891125aed69"> <trans-unit datatype="html" id="355a222236bc01b9a8cd3cb9ecf76891125aed69">
@ -1151,7 +1143,15 @@
<target>GitHub</target> <target>GitHub</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">174</context> <context context-type="linenumber">170</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="ea3a452c5238897cabc5781308cceb2d37dcf258">
<source>Suggest an idea</source>
<target>Suggérer une idée</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">176</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="af665f8de8fabe306aaf27443957e69bcbbce63c"> <trans-unit datatype="html" id="af665f8de8fabe306aaf27443957e69bcbbce63c">
@ -1167,7 +1167,7 @@
<target>Documents ouverts</target> <target>Documents ouverts</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">92</context> <context context-type="linenumber">87</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="dca5bf9344a759fa5a07f1b21f50286ec242ba44"> <trans-unit datatype="html" id="dca5bf9344a759fa5a07f1b21f50286ec242ba44">
@ -1175,7 +1175,7 @@
<target>Fermer tout</target> <target>Fermer tout</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">106</context> <context context-type="linenumber">101</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5195932016807797291"> <trans-unit datatype="html" id="5195932016807797291">
@ -1226,6 +1226,14 @@
<context context-type="linenumber">46</context> <context context-type="linenumber">46</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6523384805359286307">
<source>Title: <x equiv-text="rule.value" id="PH"/></source>
<target>Titre : <x equiv-text="rule.value" id="PH"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="02d184c288f567825a1fcbf83bcd3099a10853d5"> <trans-unit datatype="html" id="02d184c288f567825a1fcbf83bcd3099a10853d5">
<source>Filter tags</source> <source>Filter tags</source>
<target>Filtrer les étiquettes</target> <target>Filtrer les étiquettes</target>
@ -1384,7 +1392,7 @@
<target>Une erreur s'est produite lors de l'exécution de l'opération de masse : <x equiv-text="JSON.stringify(error.error)" id="PH"/></target> <target>Une erreur s'est produite lors de l'exécution de l'opération de masse : <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">74</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7894972847287473517"> <trans-unit datatype="html" id="7894972847287473517">
@ -1392,7 +1400,7 @@
<target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot;</target> <target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">112</context> <context context-type="linenumber">113</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8639884465898458690"> <trans-unit datatype="html" id="8639884465898458690">
@ -1400,7 +1408,7 @@
<target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot; et &quot;<x equiv-text="items[1].name" id="PH_1"/>&quot;</target> <target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot; et &quot;<x equiv-text="items[1].name" id="PH_1"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">114</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
<note from="description" priority="1">This is for messages like 'modify &quot;tag1&quot; and &quot;tag2&quot;'</note> <note from="description" priority="1">This is for messages like 'modify &quot;tag1&quot; and &quot;tag2&quot;'</note>
</trans-unit> </trans-unit>
@ -1409,7 +1417,7 @@
<target>, </target> <target>, </target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">116</context> <context context-type="linenumber">117</context>
</context-group> </context-group>
<note from="description" priority="1">this is used to separate enumerations and should probably be a comma and a whitespace in most languages</note> <note from="description" priority="1">this is used to separate enumerations and should probably be a comma and a whitespace in most languages</note>
</trans-unit> </trans-unit>
@ -1418,7 +1426,7 @@
<target><x equiv-text="list" id="PH"/> et &quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&quot;</target> <target><x equiv-text="list" id="PH"/> et &quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">117</context> <context context-type="linenumber">118</context>
</context-group> </context-group>
<note from="description" priority="1">this is for messages like 'modify &quot;tag1&quot;, &quot;tag2&quot; and &quot;tag3&quot;'</note> <note from="description" priority="1">this is for messages like 'modify &quot;tag1&quot;, &quot;tag2&quot; and &quot;tag3&quot;'</note>
</trans-unit> </trans-unit>
@ -1427,7 +1435,7 @@
<target>Confirmer l'affectation des étiquettes</target> <target>Confirmer l'affectation des étiquettes</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">126</context> <context context-type="linenumber">127</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6619516195038467207"> <trans-unit datatype="html" id="6619516195038467207">
@ -1435,7 +1443,7 @@
<target>Cette action affectera l'étiquette &quot;<x equiv-text="tag.name" id="PH"/>&quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target> <target>Cette action affectera l'étiquette &quot;<x equiv-text="tag.name" id="PH"/>&quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">129</context> <context context-type="linenumber">130</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1894412783609570695"> <trans-unit datatype="html" id="1894412783609570695">
@ -1443,7 +1451,7 @@
<target>Cette action affectera les étiquettes <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target> <target>Cette action affectera les étiquettes <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">131</context> <context context-type="linenumber">132</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7181166515756808573"> <trans-unit datatype="html" id="7181166515756808573">
@ -1451,7 +1459,7 @@
<target>Cette action supprimera l'étiquette &quot;<x equiv-text="tag.name" id="PH"/>&quot; de(s) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target> <target>Cette action supprimera l'étiquette &quot;<x equiv-text="tag.name" id="PH"/>&quot; de(s) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">134</context> <context context-type="linenumber">135</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3819792277998068944"> <trans-unit datatype="html" id="3819792277998068944">
@ -1459,7 +1467,7 @@
<target>Cette action supprimera les étiquettes <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH"/> de(s) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target> <target>Cette action supprimera les étiquettes <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH"/> de(s) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">136</context> <context context-type="linenumber">137</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2739066218579571288"> <trans-unit datatype="html" id="2739066218579571288">
@ -1467,7 +1475,7 @@
<target>Cette action affectera les étiquettes <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> et supprimera les étiquettes <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH_1"/> de(s) <x equiv-text="this.list.selected.size" id="PH_2"/> document(s) sélectionné(s).</target> <target>Cette action affectera les étiquettes <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> et supprimera les étiquettes <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH_1"/> de(s) <x equiv-text="this.list.selected.size" id="PH_2"/> document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">138</context> <context context-type="linenumber">139</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2996713129519325161"> <trans-unit datatype="html" id="2996713129519325161">
@ -1475,7 +1483,7 @@
<target>Confirmer l'affectation du correspondant</target> <target>Confirmer l'affectation du correspondant</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">158</context> <context context-type="linenumber">159</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6900893559485781849"> <trans-unit datatype="html" id="6900893559485781849">
@ -1483,7 +1491,7 @@
<target>Cette action affectera le correspondant &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/>document(s) sélectionné(s).</target> <target>Cette action affectera le correspondant &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/>document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">160</context> <context context-type="linenumber">161</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1257522660364398440"> <trans-unit datatype="html" id="1257522660364398440">
@ -1491,7 +1499,7 @@
<target>Cette action supprimera le correspondant de(s) <x equiv-text="this.list.selected.size" id="PH"/> document(s) sélectionné(s).</target> <target>Cette action supprimera le correspondant de(s) <x equiv-text="this.list.selected.size" id="PH"/> document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">162</context> <context context-type="linenumber">163</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5393409374423140648"> <trans-unit datatype="html" id="5393409374423140648">
@ -1499,7 +1507,7 @@
<target>Confirmer l'affectation du type de document</target> <target>Confirmer l'affectation du type de document</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">181</context> <context context-type="linenumber">182</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="332180123895325027"> <trans-unit datatype="html" id="332180123895325027">
@ -1507,7 +1515,7 @@
<target>Cette action affectera le type de document &quot;<x equiv-text="documentType.name" id="PH"/>&quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target> <target>Cette action affectera le type de document &quot;<x equiv-text="documentType.name" id="PH"/>&quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">183</context> <context context-type="linenumber">184</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2236642492594872779"> <trans-unit datatype="html" id="2236642492594872779">
@ -1515,7 +1523,7 @@
<target>Cette action supprimera le type de document de(s) <x equiv-text="this.list.selected.size" id="PH"/> document(s) sélectionné(s).</target> <target>Cette action supprimera le type de document de(s) <x equiv-text="this.list.selected.size" id="PH"/> document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">185</context> <context context-type="linenumber">186</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="749430623564850405"> <trans-unit datatype="html" id="749430623564850405">
@ -1523,7 +1531,7 @@
<target>Confirmer la suppression</target> <target>Confirmer la suppression</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">200</context> <context context-type="linenumber">201</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="4303174930844518780"> <trans-unit datatype="html" id="4303174930844518780">
@ -1531,7 +1539,7 @@
<target>Cette action supprimera définitivement <x equiv-text="this.list.selected.size" id="PH"/> document(s) sélectionné(s).</target> <target>Cette action supprimera définitivement <x equiv-text="this.list.selected.size" id="PH"/> document(s) sélectionné(s).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">202</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5641451190833696892"> <trans-unit datatype="html" id="5641451190833696892">
@ -1539,7 +1547,7 @@
<target>Cette action est irréversible.</target> <target>Cette action est irréversible.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">202</context> <context context-type="linenumber">203</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6734339521247847366"> <trans-unit datatype="html" id="6734339521247847366">
@ -1547,7 +1555,7 @@
<target>Supprimer le(s) document(s)</target> <target>Supprimer le(s) document(s)</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">204</context> <context context-type="linenumber">205</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8b0609df23817024b3bed12beb9b64fc1009f588"> <trans-unit datatype="html" id="8b0609df23817024b3bed12beb9b64fc1009f588">
@ -1574,6 +1582,14 @@
<context context-type="linenumber">27</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="fc2de37422d7c4af6686842283cc2afd781b6848">
<source>Download originals</source>
<target>Télécharger les originaux</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="a1e6c11f20d4bf6e8e6b43e3c6d2561b2080645e"> <trans-unit datatype="html" id="a1e6c11f20d4bf6e8e6b43e3c6d2561b2080645e">
<source>Suggestions:</source> <source>Suggestions:</source>
<target>Suggestions : </target> <target>Suggestions : </target>
@ -1606,22 +1622,22 @@
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="46c8fe557cf52c9389783627d4f85453f4ddb459"> <trans-unit datatype="html" id="c0d907c2687c09612395aee6ef7c04ca8e5e5e0a">
<source>Documents in inbox: <x equiv-text="{{statistics.documents_inbox}}" id="INTERPOLATION"/></source> <source>Total documents: <x equiv-text="{{statistics?.documents_total}}" id="INTERPOLATION"/></source>
<target>Documents dans la boîte de réception : <x equiv-text="{{statistics.documents_inbox}}" id="INTERPOLATION"/></target> <target>Nombre total de documents : <x equiv-text="{{statistics?.documents_total}}" id="INTERPOLATION"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="c327c0e67bcac7494dcbaa9afb3b42d5008c6438">
<source>Total documents: <x equiv-text="{{statistics.documents_total}}" id="INTERPOLATION"/></source>
<target>Nombre total de documents : <x equiv-text="{{statistics.documents_total}}" id="INTERPOLATION"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">4</context> <context context-type="linenumber">4</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="13e8d49dbcad9f9d71e66a9a56d6f328cff430c9">
<source>Documents in inbox: <x equiv-text="{{statistics?.documents_inbox}}" id="INTERPOLATION"/></source>
<target>Documents dans la boîte de réception : <x equiv-text="{{statistics?.documents_inbox}}" id="INTERPOLATION"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6443586946875325554"> <trans-unit datatype="html" id="6443586946875325554">
<source>Processing: <x equiv-text="countUploadingAndProcessing" id="PH"/></source> <source>Processing: <x equiv-text="countUploadingAndProcessing" id="PH"/></source>
<target>Traitement : <x equiv-text="countUploadingAndProcessing" id="PH"/></target> <target>Traitement : <x equiv-text="countUploadingAndProcessing" id="PH"/></target>
@ -1670,6 +1686,14 @@
<context context-type="linenumber">126</context> <context context-type="linenumber">126</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1405142710727603568">
<source>HTTP error: <x equiv-text="error.status" id="PH"/> <x equiv-text="error.statusText" id="PH_1"/></source>
<target>Erreur HTTP : <x equiv-text="error.status" id="PH"/> <x equiv-text="error.statusText" id="PH_1"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context>
<context context-type="linenumber">136</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e022072b3e4dd77e3f09960817ef3359a49963b3"> <trans-unit datatype="html" id="e022072b3e4dd77e3f09960817ef3359a49963b3">
<source>Upload new documents</source> <source>Upload new documents</source>
<target>Charger de nouveaux documents</target> <target>Charger de nouveaux documents</target>
@ -1800,6 +1824,14 @@
<context context-type="linenumber">21</context> <context context-type="linenumber">21</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="d6529debfc1613db22d6fa096ebfeb8a85fa739d">
<source>Invalid date.</source>
<target>Date incorrecte.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2807800733729323332"> <trans-unit datatype="html" id="2807800733729323332">
<source>Yes</source> <source>Yes</source>
<target>Oui</target> <target>Oui</target>
@ -1829,7 +1861,15 @@
<target>Anglais (US)</target> <target>Anglais (US)</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">82</context> <context context-type="linenumber">88</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6987083569809053351">
<source>English (GB)</source>
<target>Anglais (GB)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">89</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1858110241312746425"> <trans-unit datatype="html" id="1858110241312746425">
@ -1837,7 +1877,7 @@
<target>Allemand</target> <target>Allemand</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">83</context> <context context-type="linenumber">90</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3071065188816255493"> <trans-unit datatype="html" id="3071065188816255493">
@ -1845,7 +1885,7 @@
<target>Néerlandais</target> <target>Néerlandais</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">84</context> <context context-type="linenumber">91</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7633754075223722162"> <trans-unit datatype="html" id="7633754075223722162">
@ -1853,7 +1893,23 @@
<target>Français</target> <target>Français</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">85</context> <context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="9184513005098760425">
<source>Portuguese (Brazil)</source>
<target>Portugais (Brésil)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4912706592792948707">
<source>ISO 8601</source>
<target>ISO 8601</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">98</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2119857572761283468"> <trans-unit datatype="html" id="2119857572761283468">
@ -1861,7 +1917,7 @@
<target>Le document existe déjà.</target> <target>Le document existe déjà.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">14</context> <context context-type="linenumber">15</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="148389968432135849"> <trans-unit datatype="html" id="148389968432135849">
@ -1869,7 +1925,7 @@
<target>Fichier non trouvé.</target> <target>Fichier non trouvé.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">15</context> <context context-type="linenumber">16</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1520671543092565667"> <trans-unit datatype="html" id="1520671543092565667">
@ -1877,7 +1933,7 @@
<target>Le script de pré-traitement n'existe pas.</target> <target>Le script de pré-traitement n'existe pas.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">17</context>
</context-group> </context-group>
<note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1886,7 +1942,7 @@
<target>Erreur lors de l'exécution du script de pré-traitement.</target> <target>Erreur lors de l'exécution du script de pré-traitement.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">17</context> <context context-type="linenumber">18</context>
</context-group> </context-group>
<note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1895,7 +1951,7 @@
<target>Le script de post-traitement n'existe pas.</target> <target>Le script de post-traitement n'existe pas.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">18</context> <context context-type="linenumber">19</context>
</context-group> </context-group>
<note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1904,7 +1960,7 @@
<target>Erreur lors de l'exécution du script de post-traitement.</target> <target>Erreur lors de l'exécution du script de post-traitement.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">19</context> <context context-type="linenumber">20</context>
</context-group> </context-group>
<note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note> <note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
@ -1913,7 +1969,7 @@
<target>Réception d'un nouveau fichier.</target> <target>Réception d'un nouveau fichier.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">20</context> <context context-type="linenumber">21</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7337565919209746135"> <trans-unit datatype="html" id="7337565919209746135">
@ -1921,7 +1977,7 @@
<target>Type de fichier non pris en charge.</target> <target>Type de fichier non pris en charge.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">21</context> <context context-type="linenumber">22</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5002399167376099234"> <trans-unit datatype="html" id="5002399167376099234">
@ -1929,7 +1985,7 @@
<target>Traitement du document...</target> <target>Traitement du document...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">22</context> <context context-type="linenumber">23</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1085975194762600381"> <trans-unit datatype="html" id="1085975194762600381">
@ -1937,7 +1993,7 @@
<target>Génération de vignette...</target> <target>Génération de vignette...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">23</context> <context context-type="linenumber">24</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3280851677698431426"> <trans-unit datatype="html" id="3280851677698431426">
@ -1945,7 +2001,7 @@
<target>Extraction de la date du document...</target> <target>Extraction de la date du document...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">24</context> <context context-type="linenumber">25</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7162102384876037296"> <trans-unit datatype="html" id="7162102384876037296">
@ -1953,7 +2009,7 @@
<target>Enregistrement du document...</target> <target>Enregistrement du document...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">25</context> <context context-type="linenumber">26</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="4550450765009165976"> <trans-unit datatype="html" id="4550450765009165976">
@ -1961,7 +2017,7 @@
<target>Terminé.</target> <target>Terminé.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">26</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1519954996184640001"> <trans-unit datatype="html" id="1519954996184640001">

View File

@ -58,11 +58,11 @@
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2155249406916744630"> <trans-unit datatype="html" id="2155249406916744630">
<source>View &quot;<x equiv-text="this.list.savedView.name" id="PH"/>&quot; saved successfully.</source> <source>View &quot;<x equiv-text="this.list.activeSavedViewTitle" id="PH"/>&quot; saved successfully.</source>
<target>View &quot;<x equiv-text="this.list.savedView.name" id="PH"/>&quot; met succes opgeslagen.</target> <target>View &quot;<x equiv-text="this.list.activeSavedViewTitle" id="PH"/>&quot; met succes opgeslagen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">109</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6837554170707123455"> <trans-unit datatype="html" id="6837554170707123455">
@ -70,7 +70,7 @@
<target>View &quot;<x equiv-text="savedView.name" id="PH"/>&quot; met succes gemaakt.</target> <target>View &quot;<x equiv-text="savedView.name" id="PH"/>&quot; met succes gemaakt.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">130</context> <context context-type="linenumber">136</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="9ca82952a6bc860b5391d5975322d8af8ceddfa4"> <trans-unit datatype="html" id="9ca82952a6bc860b5391d5975322d8af8ceddfa4">
@ -129,9 +129,9 @@
<context context-type="linenumber">72</context> <context context-type="linenumber">72</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="72e7d343f9165602cce1ca7faffbc565fd31ef92"> <trans-unit datatype="html" id="5f5ce787c428d917c30c9bd70789a618e09743a7">
<source>Save &quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&quot;</source> <source>Save &quot;<x equiv-text="{{list.activeSavedViewTitle}}" id="INTERPOLATION"/>&quot;</source>
<target>Opslaan &quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&quot;</target> <target>Opslaan &quot;<x equiv-text="{{list.activeSavedViewTitle}}" id="INTERPOLATION"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">71</context> <context context-type="linenumber">71</context>
@ -214,7 +214,7 @@
<target>Bevestig het verwijderen</target> <target>Bevestig het verwijderen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">199</context> <context context-type="linenumber">203</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5382975254277698192"> <trans-unit datatype="html" id="5382975254277698192">
@ -222,7 +222,7 @@
<target>Wilt u het document echt verwijderen &quot;<x equiv-text="this.document.title" id="PH"/>&quot;?</target> <target>Wilt u het document echt verwijderen &quot;<x equiv-text="this.document.title" id="PH"/>&quot;?</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">200</context> <context context-type="linenumber">204</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6691075929777935948"> <trans-unit datatype="html" id="6691075929777935948">
@ -230,7 +230,7 @@
<target>De bestanden voor dit document worden definitief verwijderd. Deze bewerking kan niet ongedaan worden gemaakt.</target> <target>De bestanden voor dit document worden definitief verwijderd. Deze bewerking kan niet ongedaan worden gemaakt.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">205</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="719892092227206532"> <trans-unit datatype="html" id="719892092227206532">
@ -238,7 +238,7 @@
<target>Verwijder document</target> <target>Verwijder document</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">203</context> <context context-type="linenumber">207</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1844801255494293730"> <trans-unit datatype="html" id="1844801255494293730">
@ -246,7 +246,7 @@
<target>Fout bij het verwijderen van het document: <x equiv-text="JSON.stringify(error)" id="PH"/></target> <target>Fout bij het verwijderen van het document: <x equiv-text="JSON.stringify(error)" id="PH"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">210</context> <context context-type="linenumber">214</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="826b25211922a1b46436589233cb6f1a163d89b7"> <trans-unit datatype="html" id="826b25211922a1b46436589233cb6f1a163d89b7">
@ -585,14 +585,6 @@
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5ca707824ab93066c7d9b44e1b8bf216725c2c22">
<source>Filter</source>
<target>Filter</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/logs/logs.component.html</context>
<context context-type="linenumber">7</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="5610279464668232148"> <trans-unit datatype="html" id="5610279464668232148">
<source>Saved view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; deleted.</source> <source>Saved view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; deleted.</source>
<target>Opgeslagen view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; verwijderd.</target> <target>Opgeslagen view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; verwijderd.</target>
@ -622,7 +614,15 @@
<target>Datumopmaak van weergavetaal gebruiken</target> <target>Datumopmaak van weergavetaal gebruiken</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">95</context> <context context-type="linenumber">96</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4912706592792948707">
<source>ISO 8601</source>
<target>ISO 8601</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">97</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8488620293789898901"> <trans-unit datatype="html" id="8488620293789898901">
@ -630,7 +630,7 @@
<target>Fout bij het opslaan van de instellingen: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target> <target>Fout bij het opslaan van de instellingen: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">111</context> <context context-type="linenumber">114</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716"> <trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
@ -649,12 +649,20 @@
<context context-type="linenumber">10</context> <context context-type="linenumber">10</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8bcabdf6b16cad0313a86c7e940c5e3ad7f9f8ab">
<source>Notifications</source>
<target>Meldingen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">115</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="99dee94e92dbd9e21a008d4569f9719ed206ae37"> <trans-unit datatype="html" id="99dee94e92dbd9e21a008d4569f9719ed206ae37">
<source>Saved views</source> <source>Saved views</source>
<target>Opgeslagen views</target> <target>Opgeslagen views</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">128</context> <context context-type="linenumber">133</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13"> <trans-unit datatype="html" id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13">
@ -777,68 +785,12 @@
<context context-type="linenumber">98</context> <context context-type="linenumber">98</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8bcabdf6b16cad0313a86c7e940c5e3ad7f9f8ab">
<source>Notifications</source>
<target>Meldingen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">102</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="69c5a98f8aa92e4db060f10dcd37781c8f40a48f">
<source>Consumer status</source>
<target>Status van de verwerker</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">106</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2ad4d76b36341c589d94004ad2a213fd4d6f5ca0">
<source>Show notifications when new documents are detected</source>
<target>Toon meldingen wanneer nieuwe documenten worden gedetecteerd</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">109</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="f2361d3f65b6c77ef0a15fad8af8e858b043ace3">
<source>Show notifications when document consumption completes successfully</source>
<target>Toon meldingen wanneer documenten met succes zijn verwerkt.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">110</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2bcbcbe99e207803e21183580b98d90410dd8718">
<source>Show notifications when document consumption fails</source>
<target>Toon meldingen wanneer het verwerken van documenten faalt.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">111</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="af113f7c9f7e13145c3461f61a1aedf12d57bd71">
<source>Suppress notifications on dashboard</source>
<target>Onderdruk meldingen op het dashboard</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">112</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="59fe5c9233a2aaab3079a2300e4dfa439ddb1890">
<source>This will suppress all consumer related status messages on the dashboard.</source>
<target>Dit verbergt alle statusberichten op het dashboard die met de verwerker te maken hebben.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">112</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82"> <trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82">
<source>Bulk editing</source> <source>Bulk editing</source>
<target>Massabewerking</target> <target>Massabewerking</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">116</context> <context context-type="linenumber">102</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586"> <trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586">
@ -846,7 +798,7 @@
<target>Toon dialoogvenster ter bevestiging</target> <target>Toon dialoogvenster ter bevestiging</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">120</context> <context context-type="linenumber">106</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a"> <trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a">
@ -854,7 +806,7 @@
<target>Er zal altijd om bevestiging gevraagd worden voor het verwijderen van documenten.</target> <target>Er zal altijd om bevestiging gevraagd worden voor het verwijderen van documenten.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">120</context> <context context-type="linenumber">106</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e"> <trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e">
@ -862,7 +814,55 @@
<target>Toepassen bij het sluiten</target> <target>Toepassen bij het sluiten</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">121</context> <context context-type="linenumber">107</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8680abbea249ebe9c2fe35556559c8e1a9eb5841">
<source>Document processing</source>
<target>Verwerking van documenten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">118</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2ad4d76b36341c589d94004ad2a213fd4d6f5ca0">
<source>Show notifications when new documents are detected</source>
<target>Toon meldingen wanneer nieuwe documenten worden gedetecteerd</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">122</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e775f4f7c40249d31426ae61a21616a0c9d8e84f">
<source>Show notifications when document processing completes successfully</source>
<target>Toon een melding wanneer documenten met succes zijn verwerkt</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">123</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e3844dd174d8e817ddb551fae28f14ae80ca36b6">
<source>Show notifications when document processing fails</source>
<target>Toon meldingen wanneer het verwerken van documenten faalt</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">124</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="af113f7c9f7e13145c3461f61a1aedf12d57bd71">
<source>Suppress notifications on dashboard</source>
<target>Onderdruk meldingen op het dashboard</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">125</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e27bd3804d2936a6897e81c2e52e294490e5e5a8">
<source>This will suppress all messages about document processing status on the dashboard.</source>
<target>Dit verbergt alle statusberichten op het dashboard die met het verwerken van documenten te maken hebben.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">125</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8cb90334f5dfd7fc67205085f59381e2a334ccfc"> <trans-unit datatype="html" id="8cb90334f5dfd7fc67205085f59381e2a334ccfc">
@ -870,7 +870,7 @@
<target>Komt voor bij</target> <target>Komt voor bij</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">140</context> <context context-type="linenumber">145</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5"> <trans-unit datatype="html" id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5">
@ -878,7 +878,7 @@
<target>Toon op het dashboard</target> <target>Toon op het dashboard</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">143</context> <context context-type="linenumber">148</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="541bfc5b123b3f8867fd681eaceefb663a811973"> <trans-unit datatype="html" id="541bfc5b123b3f8867fd681eaceefb663a811973">
@ -886,7 +886,7 @@
<target>Toon in de zijbalk</target> <target>Toon in de zijbalk</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">147</context> <context context-type="linenumber">152</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="abba764a7a595d04dc8c3b26e04b3780d4fdb540"> <trans-unit datatype="html" id="abba764a7a595d04dc8c3b26e04b3780d4fdb540">
@ -894,7 +894,7 @@
<target>Geen opgeslagen views gedefinieerd.</target> <target>Geen opgeslagen views gedefinieerd.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">157</context> <context context-type="linenumber">162</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="ef60a738a565f498b858e903e42bc5ffc3cc1299"> <trans-unit datatype="html" id="ef60a738a565f498b858e903e42bc5ffc3cc1299">
@ -1119,7 +1119,7 @@
<target>Beheren</target> <target>Beheren</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">112</context> <context context-type="linenumber">107</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="408cb6073e60c5d966296a3207fc596adca75e01"> <trans-unit datatype="html" id="408cb6073e60c5d966296a3207fc596adca75e01">
@ -1127,15 +1127,15 @@
<target>Beheer</target> <target>Beheer</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">154</context> <context context-type="linenumber">149</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="46aa32e581922d6d2c3d7bc4c87209ad5808b029"> <trans-unit datatype="html" id="321e4419a943044e674beb55b8039f42a9761ca5">
<source>Misc</source> <source>Info</source>
<target>Andere</target> <target>Informatie</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">160</context> <context context-type="linenumber">155</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="fcfd4675b4c90f08d18d3abede9a9a4dff4cfdc7"> <trans-unit datatype="html" id="fcfd4675b4c90f08d18d3abede9a9a4dff4cfdc7">
@ -1143,7 +1143,7 @@
<target>Handleiding</target> <target>Handleiding</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">167</context> <context context-type="linenumber">162</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="355a222236bc01b9a8cd3cb9ecf76891125aed69"> <trans-unit datatype="html" id="355a222236bc01b9a8cd3cb9ecf76891125aed69">
@ -1151,7 +1151,15 @@
<target>GitHub</target> <target>GitHub</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">174</context> <context context-type="linenumber">170</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="ea3a452c5238897cabc5781308cceb2d37dcf258">
<source>Suggest an idea</source>
<target>Ideeënbus</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">176</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="af665f8de8fabe306aaf27443957e69bcbbce63c"> <trans-unit datatype="html" id="af665f8de8fabe306aaf27443957e69bcbbce63c">
@ -1167,7 +1175,7 @@
<target>Open documenten</target> <target>Open documenten</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">92</context> <context context-type="linenumber">87</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="dca5bf9344a759fa5a07f1b21f50286ec242ba44"> <trans-unit datatype="html" id="dca5bf9344a759fa5a07f1b21f50286ec242ba44">
@ -1175,7 +1183,7 @@
<target>Alles sluiten</target> <target>Alles sluiten</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">106</context> <context context-type="linenumber">101</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5195932016807797291"> <trans-unit datatype="html" id="5195932016807797291">
@ -1226,6 +1234,14 @@
<context context-type="linenumber">46</context> <context context-type="linenumber">46</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6523384805359286307">
<source>Title: <x equiv-text="rule.value" id="PH"/></source>
<target>Titel: <x equiv-text="rule.value" id="PH"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="02d184c288f567825a1fcbf83bcd3099a10853d5"> <trans-unit datatype="html" id="02d184c288f567825a1fcbf83bcd3099a10853d5">
<source>Filter tags</source> <source>Filter tags</source>
<target>Etiketten filteren</target> <target>Etiketten filteren</target>
@ -1384,7 +1400,7 @@
<target>Fout bij het uitvoeren van een massabewerking: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target> <target>Fout bij het uitvoeren van een massabewerking: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">73</context> <context context-type="linenumber">74</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7894972847287473517"> <trans-unit datatype="html" id="7894972847287473517">
@ -1392,7 +1408,7 @@
<target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot;</target> <target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">112</context> <context context-type="linenumber">113</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8639884465898458690"> <trans-unit datatype="html" id="8639884465898458690">
@ -1400,7 +1416,7 @@
<target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot; en &quot;<x equiv-text="items[1].name" id="PH_1"/>&quot;</target> <target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot; en &quot;<x equiv-text="items[1].name" id="PH_1"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">114</context> <context context-type="linenumber">115</context>
</context-group> </context-group>
<note from="description" priority="1">This is for messages like 'modify &quot;tag1&quot; and &quot;tag2&quot;'</note> <note from="description" priority="1">This is for messages like 'modify &quot;tag1&quot; and &quot;tag2&quot;'</note>
</trans-unit> </trans-unit>
@ -1409,7 +1425,7 @@
<target>, </target> <target>, </target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">116</context> <context context-type="linenumber">117</context>
</context-group> </context-group>
<note from="description" priority="1">this is used to separate enumerations and should probably be a comma and a whitespace in most languages</note> <note from="description" priority="1">this is used to separate enumerations and should probably be a comma and a whitespace in most languages</note>
</trans-unit> </trans-unit>
@ -1418,7 +1434,7 @@
<target><x equiv-text="list" id="PH"/> en &quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&quot;</target> <target><x equiv-text="list" id="PH"/> en &quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&quot;</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">117</context> <context context-type="linenumber">118</context>
</context-group> </context-group>
<note from="description" priority="1">this is for messages like 'modify &quot;tag1&quot;, &quot;tag2&quot; and &quot;tag3&quot;'</note> <note from="description" priority="1">this is for messages like 'modify &quot;tag1&quot;, &quot;tag2&quot; and &quot;tag3&quot;'</note>
</trans-unit> </trans-unit>
@ -1427,7 +1443,7 @@
<target>Bevestig toewijzen van etiketten</target> <target>Bevestig toewijzen van etiketten</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">126</context> <context context-type="linenumber">127</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6619516195038467207"> <trans-unit datatype="html" id="6619516195038467207">
@ -1435,7 +1451,7 @@
<target>Het etiket &quot;<x equiv-text="tag.name" id="PH"/>&quot; zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target> <target>Het etiket &quot;<x equiv-text="tag.name" id="PH"/>&quot; zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">129</context> <context context-type="linenumber">130</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1894412783609570695"> <trans-unit datatype="html" id="1894412783609570695">
@ -1443,7 +1459,7 @@
<target>De etiketten <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> zullen aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target> <target>De etiketten <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> zullen aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">131</context> <context context-type="linenumber">132</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7181166515756808573"> <trans-unit datatype="html" id="7181166515756808573">
@ -1451,7 +1467,7 @@
<target>Het etiket &quot;<x equiv-text="tag.name" id="PH"/>&quot; zal verwijderd worden van <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en).</target> <target>Het etiket &quot;<x equiv-text="tag.name" id="PH"/>&quot; zal verwijderd worden van <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">134</context> <context context-type="linenumber">135</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3819792277998068944"> <trans-unit datatype="html" id="3819792277998068944">
@ -1459,7 +1475,7 @@
<target>De etiketten <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH"/> zullen verwijderd worden van <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en).</target> <target>De etiketten <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH"/> zullen verwijderd worden van <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">136</context> <context context-type="linenumber">137</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2739066218579571288"> <trans-unit datatype="html" id="2739066218579571288">
@ -1467,7 +1483,7 @@
<target>De etiketten <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> zullen toegevoegd worden aan, en de etiketten <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH_1"/> zullen verwijderd worden van <x equiv-text="this.list.selected.size" id="PH_2"/> geselecteerd(e) document(en).</target> <target>De etiketten <x equiv-text="this._localizeList(changedTags.itemsToAdd)" id="PH"/> zullen toegevoegd worden aan, en de etiketten <x equiv-text="this._localizeList(changedTags.itemsToRemove)" id="PH_1"/> zullen verwijderd worden van <x equiv-text="this.list.selected.size" id="PH_2"/> geselecteerd(e) document(en).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">138</context> <context context-type="linenumber">139</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2996713129519325161"> <trans-unit datatype="html" id="2996713129519325161">
@ -1475,7 +1491,7 @@
<target>Bevestig toewijzen van correspondent</target> <target>Bevestig toewijzen van correspondent</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">158</context> <context context-type="linenumber">159</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6900893559485781849"> <trans-unit datatype="html" id="6900893559485781849">
@ -1483,7 +1499,7 @@
<target>De correspondent &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target> <target>De correspondent &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">160</context> <context context-type="linenumber">161</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1257522660364398440"> <trans-unit datatype="html" id="1257522660364398440">
@ -1491,7 +1507,7 @@
<target>De correspondent zal verwijderd worden van <x equiv-text="this.list.selected.size" id="PH"/> geselecteerd(e) document(en).</target> <target>De correspondent zal verwijderd worden van <x equiv-text="this.list.selected.size" id="PH"/> geselecteerd(e) document(en).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">162</context> <context context-type="linenumber">163</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5393409374423140648"> <trans-unit datatype="html" id="5393409374423140648">
@ -1499,7 +1515,7 @@
<target>Bevestig toewijzen van documenttype</target> <target>Bevestig toewijzen van documenttype</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">181</context> <context context-type="linenumber">182</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="332180123895325027"> <trans-unit datatype="html" id="332180123895325027">
@ -1507,7 +1523,7 @@
<target>Het documenttype &quot;<x equiv-text="documentType.name" id="PH"/>&quot; zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target> <target>Het documenttype &quot;<x equiv-text="documentType.name" id="PH"/>&quot; zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">183</context> <context context-type="linenumber">184</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2236642492594872779"> <trans-unit datatype="html" id="2236642492594872779">
@ -1515,7 +1531,7 @@
<target>Het documenttype zal verwijderd worden van <x equiv-text="this.list.selected.size" id="PH"/> geselecteerd(e) document(en).</target> <target>Het documenttype zal verwijderd worden van <x equiv-text="this.list.selected.size" id="PH"/> geselecteerd(e) document(en).</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">185</context> <context context-type="linenumber">186</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="749430623564850405"> <trans-unit datatype="html" id="749430623564850405">
@ -1523,7 +1539,7 @@
<target>Bevestig verwijderen</target> <target>Bevestig verwijderen</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">200</context> <context context-type="linenumber">201</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="4303174930844518780"> <trans-unit datatype="html" id="4303174930844518780">
@ -1531,7 +1547,7 @@
<target><x equiv-text="this.list.selected.size" id="PH"/> geselecteerd(e) document(en) zullen permanent worden verwijderd.</target> <target><x equiv-text="this.list.selected.size" id="PH"/> geselecteerd(e) document(en) zullen permanent worden verwijderd.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">202</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5641451190833696892"> <trans-unit datatype="html" id="5641451190833696892">
@ -1539,7 +1555,7 @@
<target>Deze actie kan niet ongedaan worden gemaakt.</target> <target>Deze actie kan niet ongedaan worden gemaakt.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">202</context> <context context-type="linenumber">203</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6734339521247847366"> <trans-unit datatype="html" id="6734339521247847366">
@ -1547,7 +1563,7 @@
<target>Verwijder document(en)</target> <target>Verwijder document(en)</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">204</context> <context context-type="linenumber">205</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8b0609df23817024b3bed12beb9b64fc1009f588"> <trans-unit datatype="html" id="8b0609df23817024b3bed12beb9b64fc1009f588">
@ -1574,6 +1590,14 @@
<context context-type="linenumber">27</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="fc2de37422d7c4af6686842283cc2afd781b6848">
<source>Download originals</source>
<target>Originelen downloaden</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="a1e6c11f20d4bf6e8e6b43e3c6d2561b2080645e"> <trans-unit datatype="html" id="a1e6c11f20d4bf6e8e6b43e3c6d2561b2080645e">
<source>Suggestions:</source> <source>Suggestions:</source>
<target>Suggesties:</target> <target>Suggesties:</target>
@ -1606,22 +1630,22 @@
<context context-type="linenumber">1</context> <context context-type="linenumber">1</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="46c8fe557cf52c9389783627d4f85453f4ddb459"> <trans-unit datatype="html" id="c0d907c2687c09612395aee6ef7c04ca8e5e5e0a">
<source>Documents in inbox: <x equiv-text="{{statistics.documents_inbox}}" id="INTERPOLATION"/></source> <source>Total documents: <x equiv-text="{{statistics?.documents_total}}" id="INTERPOLATION"/></source>
<target>Documenten in &quot;Postvak in&quot;: <x equiv-text="{{statistics.documents_inbox}}" id="INTERPOLATION"/></target> <target>Totaal aantal documenten: <x equiv-text="{{statistics?.documents_total}}" id="INTERPOLATION"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="c327c0e67bcac7494dcbaa9afb3b42d5008c6438">
<source>Total documents: <x equiv-text="{{statistics.documents_total}}" id="INTERPOLATION"/></source>
<target>Totaal aantal documenten: <x equiv-text="{{statistics.documents_total}}" id="INTERPOLATION"/></target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">4</context> <context context-type="linenumber">4</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="13e8d49dbcad9f9d71e66a9a56d6f328cff430c9">
<source>Documents in inbox: <x equiv-text="{{statistics?.documents_inbox}}" id="INTERPOLATION"/></source>
<target>Documenten in &quot;Postvak in&quot;: <x equiv-text="{{statistics?.documents_inbox}}" id="INTERPOLATION"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6443586946875325554"> <trans-unit datatype="html" id="6443586946875325554">
<source>Processing: <x equiv-text="countUploadingAndProcessing" id="PH"/></source> <source>Processing: <x equiv-text="countUploadingAndProcessing" id="PH"/></source>
<target>Bezig met verwerken: <x equiv-text="countUploadingAndProcessing" id="PH"/></target> <target>Bezig met verwerken: <x equiv-text="countUploadingAndProcessing" id="PH"/></target>
@ -1662,14 +1686,22 @@
<context context-type="linenumber">123</context> <context context-type="linenumber">123</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3994065460580948013"> <trans-unit datatype="html" id="7446520539098045935">
<source>Waiting for consumer...</source> <source>Upload complete, waiting...</source>
<target>Wachten op het verwerken...</target> <target>Upload voltooid, klaar voor meer...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context>
<context context-type="linenumber">126</context> <context context-type="linenumber">126</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1405142710727603568">
<source>HTTP error: <x equiv-text="error.status" id="PH"/> <x equiv-text="error.statusText" id="PH_1"/></source>
<target>HTTP fout: <x equiv-text="error.status" id="PH"/> <x equiv-text="error.statusText" id="PH_1"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context>
<context context-type="linenumber">136</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="e022072b3e4dd77e3f09960817ef3359a49963b3"> <trans-unit datatype="html" id="e022072b3e4dd77e3f09960817ef3359a49963b3">
<source>Upload new documents</source> <source>Upload new documents</source>
<target>Nieuwe documenten opladen</target> <target>Nieuwe documenten opladen</target>
@ -1701,21 +1733,23 @@
<context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html</context>
<context context-type="linenumber">4</context> <context context-type="linenumber">4</context>
</context-group> </context-group>
<note from="description" priority="1">This button dismisses all status messages about processed documents on the dashboard (failed and successful)</note>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1fc4e0a1e93fdda0ed3c9e590971d283afb68265"> <trans-unit datatype="html" id="90917e1a0a7bb59e9d11bdde9183e9391963e17b">
<source><x equiv-text="{{getStatusHidden().length}}" id="INTERPOLATION"/> more hidden</source> <source>{VAR_PLURAL, plural, =1 {One more document} other {<x id="INTERPOLATION"/> more documents}}</source>
<target><x equiv-text="{{getStatusHidden().length}}" id="INTERPOLATION"/> niet getoond</target> <target>{VAR_PLURAL, plural, =1 {Nog één document} other {Nog <x id="INTERPOLATION"/> documenten}}</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html</context>
<context context-type="linenumber">24</context> <context context-type="linenumber">25</context>
</context-group> </context-group>
<note from="description" priority="1">This is shown as a summary line when there are more than 5 document in the processing pipeline.</note>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="710254a196a2649674438edf8a15b7ab1f48271b"> <trans-unit datatype="html" id="710254a196a2649674438edf8a15b7ab1f48271b">
<source>Open document</source> <source>Open document</source>
<target>Open document</target> <target>Open document</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html</context> <context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html</context>
<context context-type="linenumber">41</context> <context context-type="linenumber">45</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="45854ddec74086b271e62be6a363f4fa5036f7fc"> <trans-unit datatype="html" id="45854ddec74086b271e62be6a363f4fa5036f7fc">
@ -1830,12 +1864,20 @@
<context context-type="linenumber">82</context> <context context-type="linenumber">82</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6987083569809053351">
<source>English (GB)</source>
<target>Engels (Brits)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">83</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="1858110241312746425"> <trans-unit datatype="html" id="1858110241312746425">
<source>German</source> <source>German</source>
<target>Duits</target> <target>Duits</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">83</context> <context context-type="linenumber">84</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3071065188816255493"> <trans-unit datatype="html" id="3071065188816255493">
@ -1843,7 +1885,7 @@
<target>Nederlands</target> <target>Nederlands</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">84</context> <context context-type="linenumber">85</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7633754075223722162"> <trans-unit datatype="html" id="7633754075223722162">
@ -1851,7 +1893,7 @@
<target>Frans</target> <target>Frans</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">85</context> <context context-type="linenumber">86</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="2119857572761283468"> <trans-unit datatype="html" id="2119857572761283468">
@ -1859,7 +1901,7 @@
<target>Document bestaat al.</target> <target>Document bestaat al.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">14</context> <context context-type="linenumber">15</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="148389968432135849"> <trans-unit datatype="html" id="148389968432135849">
@ -1867,7 +1909,7 @@
<target>Bestand niet gevonden.</target> <target>Bestand niet gevonden.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">15</context> <context context-type="linenumber">16</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1520671543092565667"> <trans-unit datatype="html" id="1520671543092565667">
@ -1875,39 +1917,43 @@
<target>Pre-verwerkingsscript bestaat niet.</target> <target>Pre-verwerkingsscript bestaat niet.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">17</context>
</context-group> </context-group>
<note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7742915911032564889"> <trans-unit datatype="html" id="7742915911032564889">
<source>Error while executing pre-consume script.</source> <source>Error while executing pre-consume script.</source>
<target>Fout tijdens het uitvoeren van het pre-verwerkingsscript</target> <target>Fout tijdens het uitvoeren van het pre-verwerkingsscript</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">17</context> <context context-type="linenumber">18</context>
</context-group> </context-group>
<note from="description" priority="1">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="8995193730018060346"> <trans-unit datatype="html" id="8995193730018060346">
<source>Post-consume script does not exist.</source> <source>Post-consume script does not exist.</source>
<target>Post-verwerkingsscript bestaat niet.</target> <target>Post-verwerkingsscript bestaat niet.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">18</context> <context context-type="linenumber">19</context>
</context-group> </context-group>
<note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="256773668518189604"> <trans-unit datatype="html" id="256773668518189604">
<source>Error while executing post-consume script.</source> <source>Error while executing post-consume script.</source>
<target>Fout tijdens het uitvoeren van het post-verwerkingsscript</target> <target>Fout tijdens het uitvoeren van het post-verwerkingsscript</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">19</context> <context context-type="linenumber">20</context>
</context-group> </context-group>
<note from="description" priority="1">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="6252258095055634191"> <trans-unit datatype="html" id="6252258095055634191">
<source>Received new file.</source> <source>Received new file.</source>
<target>Nieuw bestand ontvangen.</target> <target>Nieuw bestand ontvangen.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">20</context> <context context-type="linenumber">21</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7337565919209746135"> <trans-unit datatype="html" id="7337565919209746135">
@ -1915,7 +1961,7 @@
<target>Bestandstype niet ondersteund.</target> <target>Bestandstype niet ondersteund.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">21</context> <context context-type="linenumber">22</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="5002399167376099234"> <trans-unit datatype="html" id="5002399167376099234">
@ -1923,7 +1969,7 @@
<target>Document wordt verwerkt...</target> <target>Document wordt verwerkt...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">22</context> <context context-type="linenumber">23</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1085975194762600381"> <trans-unit datatype="html" id="1085975194762600381">
@ -1931,7 +1977,7 @@
<target>Voorbeeldweergave wordt gemaakt...</target> <target>Voorbeeldweergave wordt gemaakt...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">23</context> <context context-type="linenumber">24</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="3280851677698431426"> <trans-unit datatype="html" id="3280851677698431426">
@ -1939,7 +1985,7 @@
<target>Datum wordt gezocht in document...</target> <target>Datum wordt gezocht in document...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">24</context> <context context-type="linenumber">25</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="7162102384876037296"> <trans-unit datatype="html" id="7162102384876037296">
@ -1947,7 +1993,7 @@
<target>Document wordt opgeslagen...</target> <target>Document wordt opgeslagen...</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">25</context> <context context-type="linenumber">26</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="4550450765009165976"> <trans-unit datatype="html" id="4550450765009165976">
@ -1955,7 +2001,7 @@
<target>Klaar.</target> <target>Klaar.</target>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/consumer-status.service.ts</context> <context context-type="sourcefile">src/app/services/consumer-status.service.ts</context>
<context context-type="linenumber">26</context> <context context-type="linenumber">27</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit datatype="html" id="1519954996184640001"> <trans-unit datatype="html" id="1519954996184640001">

File diff suppressed because it is too large Load Diff

View File

@ -247,6 +247,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) { .btn-link:not(:disabled):not(.disabled) {
color: $primary-dark-mode; color: $primary-dark-mode;
} }
@ -390,6 +400,11 @@ $border-color-dark-mode: #47494f;
border-bottom-color: $bg-dark-mode-accent; border-bottom-color: $bg-dark-mode-accent;
} }
.ngb-dp-header,
.ngb-dp-weekdays,
.ngb-dp-month {
background-color: $bg-light-dark-mode;
}
} }
body.color-scheme-dark { body.color-scheme-dark {

BIN
src/clash.pdf Normal file

Binary file not shown.

View File

@ -1,10 +1,6 @@
from django.contrib import admin from django.contrib import admin
from django.utils.html import format_html, format_html_join
from django.utils.safestring import mark_safe
from whoosh.writing import AsyncWriter
from . import index from .models import Correspondent, Document, DocumentType, Tag, \
from .models import Correspondent, Document, DocumentType, Log, Tag, \
SavedView, SavedViewFilterRule SavedView, SavedViewFilterRule
@ -50,26 +46,31 @@ class DocumentAdmin(admin.ModelAdmin):
"modified", "modified",
"mime_type", "mime_type",
"storage_type", "storage_type",
"filename") "filename",
"checksum",
"archive_filename",
"archive_checksum"
)
list_display_links = ("title",) list_display_links = ("title",)
list_display = ( list_display = (
"correspondent", "id",
"title", "title",
"tags_", "mime_type",
"created", "filename",
"archive_filename"
) )
list_filter = ( list_filter = (
"document_type", ("mime_type"),
"tags", ("archive_serial_number", admin.EmptyFieldListFilter),
"correspondent" ("archive_filename", admin.EmptyFieldListFilter),
) )
filter_horizontal = ("tags",) filter_horizontal = ("tags",)
ordering = ["-created"] ordering = ["-id"]
date_hierarchy = "created" date_hierarchy = "created"
@ -81,56 +82,24 @@ class DocumentAdmin(admin.ModelAdmin):
created_.short_description = "Created" created_.short_description = "Created"
def delete_queryset(self, request, queryset): def delete_queryset(self, request, queryset):
ix = index.open_index() from documents import index
with AsyncWriter(ix) as writer:
with index.open_index_writer() as writer:
for o in queryset: for o in queryset:
index.remove_document(writer, o) index.remove_document(writer, o)
super(DocumentAdmin, self).delete_queryset(request, queryset) super(DocumentAdmin, self).delete_queryset(request, queryset)
def delete_model(self, request, obj): def delete_model(self, request, obj):
from documents import index
index.remove_document_from_index(obj) index.remove_document_from_index(obj)
super(DocumentAdmin, self).delete_model(request, obj) super(DocumentAdmin, self).delete_model(request, obj)
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
from documents import index
index.add_or_update_document(obj) index.add_or_update_document(obj)
super(DocumentAdmin, self).save_model(request, obj, form, change) super(DocumentAdmin, self).save_model(request, obj, form, change)
@mark_safe
def tags_(self, obj):
r = ""
for tag in obj.tags.all():
r += self._html_tag(
"span",
tag.name + ", "
)
return r
@staticmethod
def _html_tag(kind, inside=None, **kwargs):
attributes = format_html_join(' ', '{}="{}"', kwargs.items())
if inside is not None:
return format_html("<{kind} {attributes}>{inside}</{kind}>",
kind=kind, attributes=attributes, inside=inside)
return format_html("<{} {}/>", kind, attributes)
class LogAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
list_display = ("created", "message", "level",)
list_filter = ("level", "created",)
ordering = ('-created',)
list_display_links = ("created", "message")
class RuleInline(admin.TabularInline): class RuleInline(admin.TabularInline):
model = SavedViewFilterRule model = SavedViewFilterRule
@ -149,5 +118,4 @@ admin.site.register(Correspondent, CorrespondentAdmin)
admin.site.register(Tag, TagAdmin) admin.site.register(Tag, TagAdmin)
admin.site.register(DocumentType, DocumentTypeAdmin) admin.site.register(DocumentType, DocumentTypeAdmin)
admin.site.register(Document, DocumentAdmin) admin.site.register(Document, DocumentAdmin)
admin.site.register(Log, LogAdmin)
admin.site.register(SavedView, SavedViewAdmin) admin.site.register(SavedView, SavedViewAdmin)

View File

@ -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/")
)

View File

@ -2,9 +2,7 @@ import itertools
from django.db.models import Q from django.db.models import Q
from django_q.tasks import async_task from django_q.tasks import async_task
from whoosh.writing import AsyncWriter
from documents import index
from documents.models import Document, Correspondent, DocumentType from documents.models import Document, Correspondent, DocumentType
@ -99,8 +97,9 @@ def modify_tags(doc_ids, add_tags, remove_tags):
def delete(doc_ids): def delete(doc_ids):
Document.objects.filter(id__in=doc_ids).delete() Document.objects.filter(id__in=doc_ids).delete()
ix = index.open_index() from documents import index
with AsyncWriter(ix) as writer:
with index.open_index_writer() as writer:
for id in doc_ids: for id in doc_ids:
index.remove_document_by_id(writer, id) index.remove_document_by_id(writer, id)

View File

@ -5,10 +5,6 @@ import pickle
import re import re
from django.conf import settings from django.conf import settings
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import MultiLabelBinarizer, LabelBinarizer
from sklearn.utils.multiclass import type_of_target
from documents.models import Document, MatchingModel from documents.models import Document, MatchingModel
@ -17,7 +13,7 @@ class IncompatibleClassifierVersionError(Exception):
pass pass
logger = logging.getLogger(__name__) logger = logging.getLogger("paperless.classifier")
def preprocess_content(content): def preprocess_content(content):
@ -34,14 +30,15 @@ def load_classifier():
) )
return None return None
classifier = DocumentClassifier()
try: try:
classifier = DocumentClassifier() classifier.load()
classifier.reload()
except (EOFError, IncompatibleClassifierVersionError) as e: except (EOFError, IncompatibleClassifierVersionError) as e:
# there's something wrong with the model file. # there's something wrong with the model file.
logger.error( logger.exception(
f"Unrecoverable error while loading document " f"Unrecoverable error while loading document "
f"classification model: {str(e)}, deleting model file." f"classification model, deleting model file."
) )
os.unlink(settings.MODEL_FILE) os.unlink(settings.MODEL_FILE)
classifier = None classifier = None
@ -59,10 +56,6 @@ class DocumentClassifier(object):
FORMAT_VERSION = 6 FORMAT_VERSION = 6
def __init__(self): def __init__(self):
# mtime of the model file on disk. used to prevent reloading when
# nothing has changed.
self.classifier_version = 0
# hash of the training data. used to prevent re-training when the # hash of the training data. used to prevent re-training when the
# training data has not changed. # training data has not changed.
self.data_hash = None self.data_hash = None
@ -73,30 +66,23 @@ class DocumentClassifier(object):
self.correspondent_classifier = None self.correspondent_classifier = None
self.document_type_classifier = None self.document_type_classifier = None
def reload(self): def load(self):
if os.path.getmtime(settings.MODEL_FILE) > self.classifier_version: with open(settings.MODEL_FILE, "rb") as f:
with open(settings.MODEL_FILE, "rb") as f: schema_version = pickle.load(f)
schema_version = pickle.load(f)
if schema_version != self.FORMAT_VERSION: if schema_version != self.FORMAT_VERSION:
raise IncompatibleClassifierVersionError( raise IncompatibleClassifierVersionError(
"Cannor load classifier, incompatible versions.") "Cannor load classifier, incompatible versions.")
else: else:
if self.classifier_version > 0: self.data_hash = pickle.load(f)
# Don't be confused by this check. It's simply here self.data_vectorizer = pickle.load(f)
# so that we wont log anything on initial reload. self.tags_binarizer = pickle.load(f)
logger.info("Classifier updated on disk, "
"reloading classifier models")
self.data_hash = pickle.load(f)
self.data_vectorizer = pickle.load(f)
self.tags_binarizer = pickle.load(f)
self.tags_classifier = pickle.load(f) self.tags_classifier = pickle.load(f)
self.correspondent_classifier = pickle.load(f) self.correspondent_classifier = pickle.load(f)
self.document_type_classifier = pickle.load(f) self.document_type_classifier = pickle.load(f)
self.classifier_version = os.path.getmtime(settings.MODEL_FILE)
def save_classifier(self): def save(self):
with open(settings.MODEL_FILE, "wb") as f: with open(settings.MODEL_FILE, "wb") as f:
pickle.dump(self.FORMAT_VERSION, f) pickle.dump(self.FORMAT_VERSION, f)
pickle.dump(self.data_hash, f) pickle.dump(self.data_hash, f)
@ -109,13 +95,14 @@ class DocumentClassifier(object):
pickle.dump(self.document_type_classifier, f) pickle.dump(self.document_type_classifier, f)
def train(self): def train(self):
data = list() data = list()
labels_tags = list() labels_tags = list()
labels_correspondent = list() labels_correspondent = list()
labels_document_type = list() labels_document_type = list()
# Step 1: Extract and preprocess training data from the database. # Step 1: Extract and preprocess training data from the database.
logging.getLogger(__name__).debug("Gathering data from database...") logger.debug("Gathering data from database...")
m = hashlib.sha1() m = hashlib.sha1()
for doc in Document.objects.order_by('pk').exclude(tags__is_inbox_tag=True): # NOQA: E501 for doc in Document.objects.order_by('pk').exclude(tags__is_inbox_tag=True): # NOQA: E501
preprocessed_content = preprocess_content(doc.content) preprocessed_content = preprocess_content(doc.content)
@ -162,7 +149,7 @@ class DocumentClassifier(object):
num_correspondents = len(set(labels_correspondent) | {-1}) - 1 num_correspondents = len(set(labels_correspondent) | {-1}) - 1
num_document_types = len(set(labels_document_type) | {-1}) - 1 num_document_types = len(set(labels_document_type) | {-1}) - 1
logging.getLogger(__name__).debug( logger.debug(
"{} documents, {} tag(s), {} correspondent(s), " "{} documents, {} tag(s), {} correspondent(s), "
"{} document type(s).".format( "{} document type(s).".format(
len(data), len(data),
@ -172,8 +159,12 @@ class DocumentClassifier(object):
) )
) )
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import MultiLabelBinarizer, LabelBinarizer
# Step 2: vectorize data # Step 2: vectorize data
logging.getLogger(__name__).debug("Vectorizing data...") logger.debug("Vectorizing data...")
self.data_vectorizer = CountVectorizer( self.data_vectorizer = CountVectorizer(
analyzer="word", analyzer="word",
ngram_range=(1, 2), ngram_range=(1, 2),
@ -183,7 +174,7 @@ class DocumentClassifier(object):
# Step 3: train the classifiers # Step 3: train the classifiers
if num_tags > 0: if num_tags > 0:
logging.getLogger(__name__).debug("Training tags classifier...") logger.debug("Training tags classifier...")
if num_tags == 1: if num_tags == 1:
# Special case where only one tag has auto: # Special case where only one tag has auto:
@ -202,12 +193,12 @@ class DocumentClassifier(object):
self.tags_classifier.fit(data_vectorized, labels_tags_vectorized) self.tags_classifier.fit(data_vectorized, labels_tags_vectorized)
else: else:
self.tags_classifier = None self.tags_classifier = None
logging.getLogger(__name__).debug( logger.debug(
"There are no tags. Not training tags classifier." "There are no tags. Not training tags classifier."
) )
if num_correspondents > 0: if num_correspondents > 0:
logging.getLogger(__name__).debug( logger.debug(
"Training correspondent classifier..." "Training correspondent classifier..."
) )
self.correspondent_classifier = MLPClassifier(tol=0.01) self.correspondent_classifier = MLPClassifier(tol=0.01)
@ -217,13 +208,13 @@ class DocumentClassifier(object):
) )
else: else:
self.correspondent_classifier = None self.correspondent_classifier = None
logging.getLogger(__name__).debug( logger.debug(
"There are no correspondents. Not training correspondent " "There are no correspondents. Not training correspondent "
"classifier." "classifier."
) )
if num_document_types > 0: if num_document_types > 0:
logging.getLogger(__name__).debug( logger.debug(
"Training document type classifier..." "Training document type classifier..."
) )
self.document_type_classifier = MLPClassifier(tol=0.01) self.document_type_classifier = MLPClassifier(tol=0.01)
@ -233,7 +224,7 @@ class DocumentClassifier(object):
) )
else: else:
self.document_type_classifier = None self.document_type_classifier = None
logging.getLogger(__name__).debug( logger.debug(
"There are no document types. Not training document type " "There are no document types. Not training document type "
"classifier." "classifier."
) )
@ -265,6 +256,8 @@ class DocumentClassifier(object):
return None return None
def predict_tags(self, content): def predict_tags(self, content):
from sklearn.utils.multiclass import type_of_target
if self.tags_classifier: if self.tags_classifier:
X = self.data_vectorizer.transform([preprocess_content(content)]) X = self.data_vectorizer.transform([preprocess_content(content)])
y = self.tags_classifier.predict(X) y = self.tags_classifier.predict(X)

View File

@ -47,6 +47,8 @@ MESSAGE_FINISHED = "finished"
class Consumer(LoggingMixin): class Consumer(LoggingMixin):
logging_name = "paperless.consumer"
def _send_progress(self, current_progress, max_progress, status, def _send_progress(self, current_progress, max_progress, status,
message=None, document_id=None): message=None, document_id=None):
payload = { payload = {
@ -198,8 +200,6 @@ class Consumer(LoggingMixin):
MESSAGE_UNSUPPORTED_TYPE, MESSAGE_UNSUPPORTED_TYPE,
f"Unsupported mime type {mime_type}" f"Unsupported mime type {mime_type}"
) )
else:
self.log("debug", f"Parser: {parser_class.__name__}")
# Notify all listeners that we're going to do some work. # Notify all listeners that we're going to do some work.
@ -220,6 +220,8 @@ class Consumer(LoggingMixin):
document_parser = parser_class(self.logging_group, progress_callback) document_parser = parser_class(self.logging_group, progress_callback)
self.log("debug", f"Parser: {type(document_parser).__name__}")
# However, this already created working directories which we have to # However, this already created working directories which we have to
# clean up. # clean up.
@ -239,7 +241,7 @@ class Consumer(LoggingMixin):
self._send_progress(70, 100, 'WORKING', self._send_progress(70, 100, 'WORKING',
MESSAGE_GENERATING_THUMBNAIL) MESSAGE_GENERATING_THUMBNAIL)
thumbnail = document_parser.get_optimised_thumbnail( thumbnail = document_parser.get_optimised_thumbnail(
self.path, mime_type) self.path, mime_type, self.filename)
text = document_parser.get_text() text = document_parser.get_text()
date = document_parser.get_date() date = document_parser.get_date()
@ -290,8 +292,7 @@ class Consumer(LoggingMixin):
# After everything is in the database, copy the files into # After everything is in the database, copy the files into
# place. If this fails, we'll also rollback the transaction. # place. If this fails, we'll also rollback the transaction.
with FileLock(settings.MEDIA_LOCK): with FileLock(settings.MEDIA_LOCK):
document.filename = generate_unique_filename( document.filename = generate_unique_filename(document)
document, settings.ORIGINALS_DIR)
create_source_path_directory(document.source_path) create_source_path_directory(document.source_path)
self._write(document.storage_type, self._write(document.storage_type,
@ -301,6 +302,10 @@ class Consumer(LoggingMixin):
thumbnail, document.thumbnail_path) thumbnail, document.thumbnail_path)
if archive_path and os.path.isfile(archive_path): if archive_path and os.path.isfile(archive_path):
document.archive_filename = generate_unique_filename(
document,
archive_filename=True
)
create_source_path_directory(document.archive_path) create_source_path_directory(document.archive_path)
self._write(document.storage_type, self._write(document.storage_type,
archive_path, document.archive_path) archive_path, document.archive_path)

View File

@ -8,6 +8,9 @@ from django.conf import settings
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
logger = logging.getLogger("paperless.filehandling")
class defaultdictNoStr(defaultdict): class defaultdictNoStr(defaultdict):
def __str__(self): def __str__(self):
@ -76,12 +79,40 @@ def many_to_dictionary(field):
return mydictionary return mydictionary
def generate_unique_filename(doc, root): def generate_unique_filename(doc,
archive_filename=False):
"""
Generates a unique filename for doc in settings.ORIGINALS_DIR.
The returned filename is guaranteed to be either the current filename
of the document if unchanged, or a new filename that does not correspondent
to any existing files. The function will append _01, _02, etc to the
filename before the extension to avoid conflicts.
If archive_filename is True, return a unique archive filename instead.
"""
if archive_filename:
old_filename = doc.archive_filename
root = settings.ARCHIVE_DIR
else:
old_filename = doc.filename
root = settings.ORIGINALS_DIR
# If generating archive filenames, try to make a name that is similar to
# the original filename first.
if archive_filename and doc.filename:
new_filename = os.path.splitext(doc.filename)[0] + ".pdf"
if new_filename == old_filename or not os.path.exists(os.path.join(root, new_filename)): # NOQA: E501
return new_filename
counter = 0 counter = 0
while True: while True:
new_filename = generate_filename(doc, counter) new_filename = generate_filename(
if new_filename == doc.filename: doc, counter, archive_filename=archive_filename)
if new_filename == old_filename:
# still the same as before. # still the same as before.
return new_filename return new_filename
@ -91,7 +122,7 @@ def generate_unique_filename(doc, root):
return new_filename return new_filename
def generate_filename(doc, counter=0, append_gpg=True): def generate_filename(doc, counter=0, append_gpg=True, archive_filename=False):
path = "" path = ""
try: try:
@ -120,6 +151,11 @@ def generate_filename(doc, counter=0, append_gpg=True):
else: else:
document_type = "none" document_type = "none"
if doc.archive_serial_number:
asn = str(doc.archive_serial_number)
else:
asn = "none"
path = settings.PAPERLESS_FILENAME_FORMAT.format( path = settings.PAPERLESS_FILENAME_FORMAT.format(
title=pathvalidate.sanitize_filename( title=pathvalidate.sanitize_filename(
doc.title, replacement_text="-"), doc.title, replacement_text="-"),
@ -133,6 +169,7 @@ def generate_filename(doc, counter=0, append_gpg=True):
added_year=doc.added.year if doc.added else "none", added_year=doc.added.year if doc.added else "none",
added_month=f"{doc.added.month:02}" if doc.added else "none", added_month=f"{doc.added.month:02}" if doc.added else "none",
added_day=f"{doc.added.day:02}" if doc.added else "none", added_day=f"{doc.added.day:02}" if doc.added else "none",
asn=asn,
tags=tags, tags=tags,
tag_list=tag_list tag_list=tag_list
).strip() ).strip()
@ -140,23 +177,21 @@ def generate_filename(doc, counter=0, append_gpg=True):
path = path.strip(os.sep) path = path.strip(os.sep)
except (ValueError, KeyError, IndexError): except (ValueError, KeyError, IndexError):
logging.getLogger(__name__).warning( logger.warning(
f"Invalid PAPERLESS_FILENAME_FORMAT: " f"Invalid PAPERLESS_FILENAME_FORMAT: "
f"{settings.PAPERLESS_FILENAME_FORMAT}, falling back to default") f"{settings.PAPERLESS_FILENAME_FORMAT}, falling back to default")
counter_str = f"_{counter:02}" if counter else "" counter_str = f"_{counter:02}" if counter else ""
filetype_str = ".pdf" if archive_filename else doc.file_type
if len(path) > 0: if len(path) > 0:
filename = f"{path}{counter_str}{doc.file_type}" filename = f"{path}{counter_str}{filetype_str}"
else: else:
filename = f"{doc.pk:07}{counter_str}{doc.file_type}" filename = f"{doc.pk:07}{counter_str}{filetype_str}"
# Append .gpg for encrypted files # Append .gpg for encrypted files
if append_gpg and doc.storage_type == doc.STORAGE_TYPE_GPG: if append_gpg and doc.storage_type == doc.STORAGE_TYPE_GPG:
filename += ".gpg" filename += ".gpg"
return filename return filename
def archive_name_from_filename(filename):
return os.path.splitext(filename)[0] + ".pdf"

View File

@ -12,7 +12,7 @@ from whoosh.qparser.dateparse import DateParserPlugin
from whoosh.writing import AsyncWriter from whoosh.writing import AsyncWriter
logger = logging.getLogger(__name__) logger = logging.getLogger("paperless.index")
class JsonFormatter(Formatter): class JsonFormatter(Formatter):
@ -78,14 +78,30 @@ def open_index(recreate=False):
try: try:
if exists_in(settings.INDEX_DIR) and not recreate: if exists_in(settings.INDEX_DIR) and not recreate:
return open_dir(settings.INDEX_DIR, schema=get_schema()) return open_dir(settings.INDEX_DIR, schema=get_schema())
except Exception as e: except Exception:
logger.error(f"Error while opening the index: {e}, recreating.") logger.exception(f"Error while opening the index, recreating.")
if not os.path.isdir(settings.INDEX_DIR): if not os.path.isdir(settings.INDEX_DIR):
os.makedirs(settings.INDEX_DIR, exist_ok=True) os.makedirs(settings.INDEX_DIR, exist_ok=True)
return create_in(settings.INDEX_DIR, get_schema()) return create_in(settings.INDEX_DIR, get_schema())
@contextmanager
def open_index_writer(ix=None, optimize=False):
if ix:
writer = AsyncWriter(ix)
else:
writer = AsyncWriter(open_index())
try:
yield writer
except Exception as e:
logger.exception(str(e))
writer.cancel()
finally:
writer.commit(optimize=optimize)
def update_document(writer, doc): def update_document(writer, doc):
tags = ",".join([t.name for t in doc.tags.all()]) tags = ",".join([t.name for t in doc.tags.all()])
writer.update_document( writer.update_document(
@ -110,14 +126,12 @@ def remove_document_by_id(writer, doc_id):
def add_or_update_document(document): def add_or_update_document(document):
ix = open_index() with open_index_writer() as writer:
with AsyncWriter(ix) as writer:
update_document(writer, document) update_document(writer, document)
def remove_document_from_index(document): def remove_document_from_index(document):
ix = open_index() with open_index_writer() as writer:
with AsyncWriter(ix) as writer:
remove_document(writer, document) remove_document(writer, document)

View File

@ -4,33 +4,24 @@ import uuid
from django.conf import settings from django.conf import settings
class PaperlessHandler(logging.Handler):
def emit(self, record):
if settings.DISABLE_DBHANDLER:
return
# We have to do the import here or Django will barf when it tries to
# load this because the apps aren't loaded at that point
from .models import Log
kwargs = {"message": record.msg, "level": record.levelno}
if hasattr(record, "group"):
kwargs["group"] = record.group
Log.objects.create(**kwargs)
class LoggingMixin: class LoggingMixin:
logging_group = None logging_group = None
logging_name = None
def renew_logging_group(self): def renew_logging_group(self):
self.logging_group = uuid.uuid4() self.logging_group = uuid.uuid4()
def log(self, level, message, **kwargs): def log(self, level, message, **kwargs):
target = ".".join([self.__class__.__module__, self.__class__.__name__]) if self.logging_name:
logger = logging.getLogger(target) logger = logging.getLogger(self.logging_name)
else:
name = ".".join([
self.__class__.__module__,
self.__class__.__name__
])
logger = logging.getLogger(name)
getattr(logger, level)(message, extra={ getattr(logger, level)(message, extra={
"group": self.logging_group "group": self.logging_group

View File

@ -16,12 +16,12 @@ from whoosh.writing import AsyncWriter
from documents.models import Document from documents.models import Document
from ... import index from ... import index
from ...file_handling import create_source_path_directory from ...file_handling import create_source_path_directory, \
from ...mixins import Renderable generate_unique_filename
from ...parsers import get_parser_class_for_mime_type from ...parsers import get_parser_class_for_mime_type
logger = logging.getLogger(__name__) logger = logging.getLogger("paperless.management.archiver")
def handle_document(document_id): def handle_document(document_id):
@ -31,38 +31,57 @@ def handle_document(document_id):
parser_class = get_parser_class_for_mime_type(mime_type) 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()) parser = parser_class(logging_group=uuid.uuid4())
try: try:
parser.parse(document.source_path, mime_type) parser.parse(
document.source_path,
mime_type,
document.get_public_filename())
thumbnail = parser.get_optimised_thumbnail(
document.source_path,
mime_type,
document.get_public_filename()
)
if parser.get_archive_path(): if parser.get_archive_path():
with transaction.atomic(): with transaction.atomic():
with open(parser.get_archive_path(), 'rb') as f: with open(parser.get_archive_path(), 'rb') as f:
checksum = hashlib.md5(f.read()).hexdigest() checksum = hashlib.md5(f.read()).hexdigest()
# i'm going to save first so that in case the file move # I'm going to save first so that in case the file move
# fails, the database is rolled back. # fails, the database is rolled back.
# we also don't use save() since that triggers the filehandling # We also don't use save() since that triggers the filehandling
# logic, and we don't want that yet (file not yet in place) # logic, and we don't want that yet (file not yet in place)
document.archive_filename = generate_unique_filename(
document, archive_filename=True)
Document.objects.filter(pk=document.pk).update( Document.objects.filter(pk=document.pk).update(
archive_checksum=checksum, archive_checksum=checksum,
content=parser.get_text() content=parser.get_text(),
archive_filename=document.archive_filename
) )
with FileLock(settings.MEDIA_LOCK): with FileLock(settings.MEDIA_LOCK):
create_source_path_directory(document.archive_path) create_source_path_directory(document.archive_path)
shutil.move(parser.get_archive_path(), shutil.move(parser.get_archive_path(),
document.archive_path) document.archive_path)
shutil.move(thumbnail, document.thumbnail_path)
with AsyncWriter(index.open_index()) as writer: with index.open_index_writer() as writer:
index.update_document(writer, document) index.update_document(writer, document)
except Exception as e: except Exception as e:
logger.error(f"Error while parsing document {document}: {str(e)}") logger.exception(f"Error while parsing document {document} "
f"(ID: {document_id})")
finally: finally:
parser.cleanup() parser.cleanup()
class Command(Renderable, BaseCommand): class Command(BaseCommand):
help = """ help = """
Using the current classification model, assigns correspondents, tags Using the current classification model, assigns correspondents, tags
@ -71,10 +90,6 @@ class Command(Renderable, BaseCommand):
modified) after their initial import. modified) after their initial import.
""".replace(" ", "") """.replace(" ", "")
def __init__(self, *args, **kwargs):
self.verbosity = 0
BaseCommand.__init__(self, *args, **kwargs)
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument( parser.add_argument(
"-f", "--overwrite", "-f", "--overwrite",
@ -106,7 +121,7 @@ class Command(Renderable, BaseCommand):
document_ids = list(map( document_ids = list(map(
lambda doc: doc.id, lambda doc: doc.id,
filter( filter(
lambda d: overwrite or not d.archive_checksum, lambda d: overwrite or not d.has_archive_version,
documents documents
) )
)) ))

View File

@ -1,6 +1,7 @@
import logging import logging
import os import os
from pathlib import Path from pathlib import Path
from threading import Thread
from time import sleep from time import sleep
from django.conf import settings from django.conf import settings
@ -17,11 +18,11 @@ try:
except ImportError: except ImportError:
INotify = flags = None INotify = flags = None
logger = logging.getLogger(__name__) logger = logging.getLogger("paperless.management.consumer")
def _tags_from_path(filepath): def _tags_from_path(filepath):
"""Walk up the directory tree from filepath to CONSUMPTION_DIr """Walk up the directory tree from filepath to CONSUMPTION_DIR
and get or create Tag IDs for every directory. and get or create Tag IDs for every directory.
""" """
tag_ids = set() tag_ids = set()
@ -54,10 +55,10 @@ def _consume(filepath):
if settings.CONSUMER_SUBDIRS_AS_TAGS: if settings.CONSUMER_SUBDIRS_AS_TAGS:
tag_ids = _tags_from_path(filepath) tag_ids = _tags_from_path(filepath)
except Exception as e: except Exception as e:
logger.error( logger.exception("Error creating tags from path")
"Error creating tags from path: {}".format(e))
try: try:
logger.info(f"Adding {filepath} to the task queue.")
async_task("documents.tasks.consume_file", async_task("documents.tasks.consume_file",
filepath, filepath,
override_tag_ids=tag_ids if tag_ids else None, override_tag_ids=tag_ids if tag_ids else None,
@ -66,14 +67,14 @@ def _consume(filepath):
# Catch all so that the consumer won't crash. # Catch all so that the consumer won't crash.
# This is also what the test case is listening for to check for # This is also what the test case is listening for to check for
# errors. # errors.
logger.error( logger.exception("Error while consuming document")
"Error while consuming document: {}".format(e))
def _consume_wait_unmodified(file, num_tries=20, wait_time=1): def _consume_wait_unmodified(file):
logger.debug(f"Waiting for file {file} to remain unmodified")
mtime = -1 mtime = -1
current_try = 0 current_try = 0
while current_try < num_tries: while current_try < settings.CONSUMER_POLLING_RETRY_COUNT:
try: try:
new_mtime = os.stat(file).st_mtime new_mtime = os.stat(file).st_mtime
except FileNotFoundError: except FileNotFoundError:
@ -84,7 +85,7 @@ def _consume_wait_unmodified(file, num_tries=20, wait_time=1):
_consume(file) _consume(file)
return return
mtime = new_mtime mtime = new_mtime
sleep(wait_time) sleep(settings.CONSUMER_POLLING_DELAY)
current_try += 1 current_try += 1
logger.error(f"Timeout while waiting on file {file} to remain unmodified.") logger.error(f"Timeout while waiting on file {file} to remain unmodified.")
@ -93,10 +94,14 @@ def _consume_wait_unmodified(file, num_tries=20, wait_time=1):
class Handler(FileSystemEventHandler): class Handler(FileSystemEventHandler):
def on_created(self, event): def on_created(self, event):
_consume_wait_unmodified(event.src_path) Thread(
target=_consume_wait_unmodified, args=(event.src_path,)
).start()
def on_moved(self, event): def on_moved(self, event):
_consume_wait_unmodified(event.dest_path) Thread(
target=_consume_wait_unmodified, args=(event.dest_path,)
).start()
class Command(BaseCommand): class Command(BaseCommand):
@ -108,12 +113,7 @@ class Command(BaseCommand):
# This is here primarily for the tests and is irrelevant in production. # This is here primarily for the tests and is irrelevant in production.
stop_flag = False stop_flag = False
def __init__(self, *args, **kwargs): observer = None
self.logger = logging.getLogger(__name__)
BaseCommand.__init__(self, *args, **kwargs)
self.observer = None
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument( parser.add_argument(
@ -161,7 +161,7 @@ class Command(BaseCommand):
logger.debug("Consumer exiting.") logger.debug("Consumer exiting.")
def handle_polling(self, directory, recursive): def handle_polling(self, directory, recursive):
logging.getLogger(__name__).info( logger.info(
f"Polling directory for changes: {directory}") f"Polling directory for changes: {directory}")
self.observer = PollingObserver(timeout=settings.CONSUMER_POLLING) self.observer = PollingObserver(timeout=settings.CONSUMER_POLLING)
self.observer.schedule(Handler(), directory, recursive=recursive) self.observer.schedule(Handler(), directory, recursive=recursive)
@ -176,7 +176,7 @@ class Command(BaseCommand):
self.observer.join() self.observer.join()
def handle_inotify(self, directory, recursive): def handle_inotify(self, directory, recursive):
logging.getLogger(__name__).info( logger.info(
f"Using inotify to watch directory for changes: {directory}") f"Using inotify to watch directory for changes: {directory}")
inotify = INotify() inotify = INotify()

View File

@ -1,10 +1,9 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from ...mixins import Renderable
from ...tasks import train_classifier from ...tasks import train_classifier
class Command(Renderable, BaseCommand): class Command(BaseCommand):
help = """ help = """
Trains the classifier on your data and saves the resulting models to a Trains the classifier on your data and saves the resulting models to a

View File

@ -16,10 +16,9 @@ from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \
EXPORTER_ARCHIVE_NAME EXPORTER_ARCHIVE_NAME
from paperless.db import GnuPG from paperless.db import GnuPG
from ...file_handling import generate_filename, delete_empty_directories from ...file_handling import generate_filename, delete_empty_directories
from ...mixins import Renderable
class Command(Renderable, BaseCommand): class Command(BaseCommand):
help = """ help = """
Decrypt and rename all files in our collection into a given target Decrypt and rename all files in our collection into a given target
@ -140,7 +139,7 @@ class Command(Renderable, BaseCommand):
thumbnail_target = os.path.join(self.target, thumbnail_name) thumbnail_target = os.path.join(self.target, thumbnail_name)
document_dict[EXPORTER_THUMBNAIL_NAME] = thumbnail_name document_dict[EXPORTER_THUMBNAIL_NAME] = thumbnail_name
if os.path.exists(document.archive_path): if document.has_archive_version:
archive_name = base_name + "-archive.pdf" archive_name = base_name + "-archive.pdf"
archive_target = os.path.join(self.target, archive_name) archive_target = os.path.join(self.target, archive_name)
document_dict[EXPORTER_ARCHIVE_NAME] = archive_name document_dict[EXPORTER_ARCHIVE_NAME] = archive_name

View File

@ -15,7 +15,6 @@ from documents.models import Document
from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \ from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \
EXPORTER_ARCHIVE_NAME EXPORTER_ARCHIVE_NAME
from ...file_handling import create_source_path_directory from ...file_handling import create_source_path_directory
from ...mixins import Renderable
from ...signals.handlers import update_filename_and_move_files from ...signals.handlers import update_filename_and_move_files
@ -28,7 +27,7 @@ def disable_signal(sig, receiver, sender):
sig.connect(receiver=receiver, sender=sender) sig.connect(receiver=receiver, sender=sender)
class Command(Renderable, BaseCommand): class Command(BaseCommand):
help = """ help = """
Using a manifest.json file, load the data from there, and import the Using a manifest.json file, load the data from there, and import the
@ -152,6 +151,9 @@ class Command(Renderable, BaseCommand):
shutil.copy2(thumbnail_path, document.thumbnail_path) shutil.copy2(thumbnail_path, document.thumbnail_path)
if archive_path: if archive_path:
create_source_path_directory(document.archive_path) create_source_path_directory(document.archive_path)
# TODO: this assumes that the export is valid and
# archive_filename is present on all documents with
# archived files
shutil.copy2(archive_path, document.archive_path) shutil.copy2(archive_path, document.archive_path)
document.save() document.save()

View File

@ -1,24 +1,17 @@
from django.core.management import BaseCommand from django.core.management import BaseCommand
from django.db import transaction from django.db import transaction
from documents.mixins import Renderable
from documents.tasks import index_reindex, index_optimize from documents.tasks import index_reindex, index_optimize
class Command(Renderable, BaseCommand): class Command(BaseCommand):
help = "Manages the document index." help = "Manages the document index."
def __init__(self, *args, **kwargs):
self.verbosity = 0
BaseCommand.__init__(self, *args, **kwargs)
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument("command", choices=['reindex', 'optimize']) parser.add_argument("command", choices=['reindex', 'optimize'])
def handle(self, *args, **options): def handle(self, *args, **options):
self.verbosity = options["verbosity"]
with transaction.atomic(): with transaction.atomic():
if options['command'] == 'reindex': if options['command'] == 'reindex':
index_reindex() index_reindex()

View File

@ -1,12 +0,0 @@
from django.core.management.base import BaseCommand
from documents.models import Log
class Command(BaseCommand):
help = "A quick & dirty way to see what's in the logs"
def handle(self, *args, **options):
for log in Log.objects.order_by("pk"):
print(log)

View File

@ -5,23 +5,16 @@ from django.core.management.base import BaseCommand
from django.db.models.signals import post_save from django.db.models.signals import post_save
from documents.models import Document from documents.models import Document
from ...mixins import Renderable
class Command(Renderable, BaseCommand): class Command(BaseCommand):
help = """ help = """
This will rename all documents to match the latest filename format. This will rename all documents to match the latest filename format.
""".replace(" ", "") """.replace(" ", "")
def __init__(self, *args, **kwargs):
self.verbosity = 0
BaseCommand.__init__(self, *args, **kwargs)
def handle(self, *args, **options): def handle(self, *args, **options):
self.verbosity = options["verbosity"]
logging.getLogger().handlers[0].level = logging.ERROR logging.getLogger().handlers[0].level = logging.ERROR
for document in tqdm.tqdm(Document.objects.all()): for document in tqdm.tqdm(Document.objects.all()):

Some files were not shown because too many files have changed in this diff Show More