mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-12-19 01:21:19 +00:00
Compare commits
388 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4909c3133 | ||
|
|
2216330118 | ||
|
|
ca090aa2fa | ||
|
|
ec7ca2c352 | ||
|
|
2eb5e289ce | ||
|
|
40ce38254b | ||
|
|
0ad2b05455 | ||
|
|
c74d261f6a | ||
|
|
68fe18fe36 | ||
|
|
47313db2c7 | ||
|
|
127129fbeb | ||
|
|
f43a8d6f2e | ||
|
|
7cde850d98 | ||
|
|
1bcfc5b54d | ||
|
|
7a8494da4d | ||
|
|
65733863b1 | ||
|
|
e62ecefcd7 | ||
|
|
0aacebf783 | ||
|
|
43b1700e91 | ||
|
|
9b4625b4a5 | ||
|
|
27998df6e4 | ||
|
|
eb5eabf3c6 | ||
|
|
76cee78408 | ||
|
|
0ba35a6eb5 | ||
|
|
6a58bd30ad | ||
|
|
e71d055ef2 | ||
|
|
cb2eff47cc | ||
|
|
4696f9896b | ||
|
|
424835880d | ||
|
|
e0e0ee4a77 | ||
|
|
5eca0ce554 | ||
|
|
f5a06ac0dd | ||
|
|
81ea8873e0 | ||
|
|
63b4ccacde | ||
|
|
ccc39e0e55 | ||
|
|
d264df1504 | ||
|
|
9b7bc16b3e | ||
|
|
f7f51f4f73 | ||
|
|
143ae1092c | ||
|
|
6470a443ac | ||
|
|
7795baf989 | ||
|
|
5ab717c6be | ||
|
|
fac265486a | ||
|
|
707efff644 | ||
|
|
15291fd659 | ||
|
|
6535a20b21 | ||
|
|
8c922dbffc | ||
|
|
874f5fd2ca | ||
|
|
50864b10dd | ||
|
|
57a5df1fce | ||
|
|
14828b15d8 | ||
|
|
5f03e9e4cc | ||
|
|
c2b82fa063 | ||
|
|
40c4cfda75 | ||
|
|
5c5ce6eedb | ||
|
|
2fd1074729 | ||
|
|
2e379ad422 | ||
|
|
c1cecb3e0e | ||
|
|
b0d13bed72 | ||
|
|
d23a0d230c | ||
|
|
91b87f4e2c | ||
|
|
a77c1f3ad1 | ||
|
|
4c3cdaab0e | ||
|
|
3e86c3d0d0 | ||
|
|
5a40d3c6fe | ||
|
|
f79c1e0bcc | ||
|
|
8f14ff7210 | ||
|
|
90732d6a2f | ||
|
|
f179ff9da4 | ||
|
|
90fd999220 | ||
|
|
04afbdd938 | ||
|
|
5d3ef3d338 | ||
|
|
4477d083e8 | ||
|
|
b7b3f54617 | ||
|
|
5fc9f3f4da | ||
|
|
c4b31f2f72 | ||
|
|
6c6b0511a6 | ||
|
|
6d20fc14ab | ||
|
|
895c10600d | ||
|
|
c024efa578 | ||
|
|
0022588b02 | ||
|
|
47ec3d9149 | ||
|
|
811b82181d | ||
|
|
32318ddbf6 | ||
|
|
65a416fdae | ||
|
|
58d1f98368 | ||
|
|
4cd7bf7123 | ||
|
|
d4dea13eee | ||
|
|
507cdca757 | ||
|
|
27bfb2d3b0 | ||
|
|
43118a1ffd | ||
|
|
729b305860 | ||
|
|
72950d7872 | ||
|
|
364ffe8f38 | ||
|
|
84d822b497 | ||
|
|
df99035c93 | ||
|
|
63fc68cf83 | ||
|
|
9c742fbf08 | ||
|
|
9965c35d2f | ||
|
|
0af567bc72 | ||
|
|
0959a4e915 | ||
|
|
83be71f622 | ||
|
|
9c985b36b3 | ||
|
|
4ef0e20b5c | ||
|
|
d1cef044e5 | ||
|
|
f1d4860c79 | ||
|
|
f1d6dd7d36 | ||
|
|
46ae7f16dd | ||
|
|
5da441a001 | ||
|
|
d495bfab51 | ||
|
|
4b4f3e76cc | ||
|
|
ab0e97c1a0 | ||
|
|
381af329bd | ||
|
|
4a0d17fc57 | ||
|
|
e61f042547 | ||
|
|
77815af5fb | ||
|
|
e9cdb21164 | ||
|
|
59bc467c50 | ||
|
|
254dc62ca6 | ||
|
|
253bde4e25 | ||
|
|
ea9e852216 | ||
|
|
89150e99bf | ||
|
|
5fd05f3d20 | ||
|
|
6c1a5f1a98 | ||
|
|
7815a011af | ||
|
|
efca0083bf | ||
|
|
4c983b0e25 | ||
|
|
d81f64ec06 | ||
|
|
5735313392 | ||
|
|
259b2fe429 | ||
|
|
bb739a55a5 | ||
|
|
7436b75566 | ||
|
|
573ac882f5 | ||
|
|
7f70a886d5 | ||
|
|
4a1c0c2971 | ||
|
|
ad37ccd76d | ||
|
|
d3a4b17d59 | ||
|
|
4233d73569 | ||
|
|
6d9a0162b2 | ||
|
|
72f89de994 | ||
|
|
bf01799d57 | ||
|
|
6b63d3855b | ||
|
|
f0e0e780c5 | ||
|
|
646803b93e | ||
|
|
3026b59af4 | ||
|
|
5adee00ca1 | ||
|
|
47e887dac5 | ||
|
|
70395a035c | ||
|
|
2f63dcb3b5 | ||
|
|
468ea09965 | ||
|
|
b6a94d33b8 | ||
|
|
40c89a8a38 | ||
|
|
92a61902b5 | ||
|
|
23ee01b1ce | ||
|
|
fc727e0472 | ||
|
|
9427c70b10 | ||
|
|
cba71ed8e0 | ||
|
|
728778bdf4 | ||
|
|
30a92e25c1 | ||
|
|
4862eb5e84 | ||
|
|
42fd62ecfd | ||
|
|
67437f7b9c | ||
|
|
6c961dfad9 | ||
|
|
b0b5f9e0a1 | ||
|
|
69df7f8ef6 | ||
|
|
0bb0fc5a19 | ||
|
|
be15e86458 | ||
|
|
24eff6b02b | ||
|
|
ddb96829ca | ||
|
|
4290f38e81 | ||
|
|
ee4a5d53ad | ||
|
|
1f4d2e76d7 | ||
|
|
9816efb816 | ||
|
|
8da1521508 | ||
|
|
a7846925b1 | ||
|
|
d678bd71e8 | ||
|
|
67754efd36 | ||
|
|
80e0fc21cc | ||
|
|
1025f6ccb5 | ||
|
|
ede06f4fd4 | ||
|
|
6dd0de0201 | ||
|
|
7f01ead374 | ||
|
|
cba507258d | ||
|
|
ae93999f6a | ||
|
|
1ff7a89a35 | ||
|
|
31cd32406c | ||
|
|
e5f750dcbe | ||
|
|
59ecff8701 | ||
|
|
80e91cde70 | ||
|
|
b1c2c79d80 | ||
|
|
5b2576ace4 | ||
|
|
da9f370924 | ||
|
|
e56daf07d4 | ||
|
|
9e3caa57ec | ||
|
|
4fd7b9ef1f | ||
|
|
12235cc853 | ||
|
|
56e4264394 | ||
|
|
baf8203b80 | ||
|
|
41d966895f | ||
|
|
551792274f | ||
|
|
c7abdb61e8 | ||
|
|
72ebe7df58 | ||
|
|
97f3c214e8 | ||
|
|
47842bac1c | ||
|
|
ede498fa52 | ||
|
|
c8a06416e7 | ||
|
|
5fea7f2bc4 | ||
|
|
15420137fa | ||
|
|
73791e0e50 | ||
|
|
7af0f12e6b | ||
|
|
895f9c911b | ||
|
|
204f47265b | ||
|
|
b240b4821a | ||
|
|
249f6e4f27 | ||
|
|
32facd2877 | ||
|
|
fd3cb6e2ca | ||
|
|
1df6d857e5 | ||
|
|
3b9c2fd4d2 | ||
|
|
baa130e646 | ||
|
|
c3ec40189f | ||
|
|
686ae5b213 | ||
|
|
5c19a8bb70 | ||
|
|
efa7b7b0b5 | ||
|
|
e466c31bb3 | ||
|
|
942acf5940 | ||
|
|
b38d8d8640 | ||
|
|
c4c8a96f25 | ||
|
|
65377d881c | ||
|
|
b847e0c65e | ||
|
|
6ab884a95c | ||
|
|
4b17a485ae | ||
|
|
e5a4715161 | ||
|
|
53b7b14467 | ||
|
|
5eeb012557 | ||
|
|
cf9884aaba | ||
|
|
d811cab57e | ||
|
|
d5a3443076 | ||
|
|
bd11d3a34f | ||
|
|
5f6a202d65 | ||
|
|
6db3f7023f | ||
|
|
8a64898798 | ||
|
|
6ad14d63d6 | ||
|
|
a26150ca40 | ||
|
|
57c9066f81 | ||
|
|
fe3c3b8946 | ||
|
|
76686c22fc | ||
|
|
bba4057616 | ||
|
|
10a1f34164 | ||
|
|
4516a5def5 | ||
|
|
4ef4af452a | ||
|
|
8f1e95a0ce | ||
|
|
824aea0c7b | ||
|
|
7d54f0e336 | ||
|
|
961b47239b | ||
|
|
248f19a550 | ||
|
|
29e6529884 | ||
|
|
cb4f822a19 | ||
|
|
2159fefc87 | ||
|
|
d341bf14ff | ||
|
|
c793ac9c6c | ||
|
|
289589654b | ||
|
|
8cb9c9faf9 | ||
|
|
1c0069e95e | ||
|
|
8f23642e7d | ||
|
|
b59758379e | ||
|
|
d91267ee91 | ||
|
|
6196bc7b2e | ||
|
|
adfe5c123c | ||
|
|
573726d0b2 | ||
|
|
5cb95b6e55 | ||
|
|
4ce79955f8 | ||
|
|
3d320b4e61 | ||
|
|
420feb3a52 | ||
|
|
4d425cf7b8 | ||
|
|
f716300152 | ||
|
|
fea625e443 | ||
|
|
068fdbd3f9 | ||
|
|
fd2cbc13ff | ||
|
|
6a70b9c4b4 | ||
|
|
4ee7d16d3e | ||
|
|
6fd25da80d | ||
|
|
2fb286cd43 | ||
|
|
3a75b2571a | ||
|
|
66fe821b04 | ||
|
|
ba478c6cbb | ||
|
|
7b1145c75e | ||
|
|
b45630080b | ||
|
|
170ddbc76f | ||
|
|
91b364f531 | ||
|
|
aadcc85e73 | ||
|
|
548b1ea7ec | ||
|
|
463696e6a8 | ||
|
|
66f7ae3773 | ||
|
|
cc2b836646 | ||
|
|
816a4c1887 | ||
|
|
604da64f52 | ||
|
|
247edc89f0 | ||
|
|
6a1f5fb4cd | ||
|
|
01c11f9fa0 | ||
|
|
9434851c54 | ||
|
|
a331e4b49c | ||
|
|
772f5cb1a8 | ||
|
|
0fa9d71da8 | ||
|
|
3872c3c49b | ||
|
|
018b53db1b | ||
|
|
33ed2c8851 | ||
|
|
c75e30fd28 | ||
|
|
7cc940a16c | ||
|
|
ba853be00d | ||
|
|
a720ed6a77 | ||
|
|
d84ca511d4 | ||
|
|
95fe803bf6 | ||
|
|
87c364a104 | ||
|
|
7233695824 | ||
|
|
3d3300ac32 | ||
|
|
ef7775b658 | ||
|
|
a8776603c3 | ||
|
|
ee92ab1136 | ||
|
|
83c88ca472 | ||
|
|
6f22297158 | ||
|
|
df87599f1b | ||
|
|
367b5e73cb | ||
|
|
d6cbea97f9 | ||
|
|
e43ab23a45 | ||
|
|
706b1f4622 | ||
|
|
5b75321a31 | ||
|
|
2f2b5b90ea | ||
|
|
cf3dedf4bb | ||
|
|
d874145f52 | ||
|
|
800f4deb90 | ||
|
|
c22af0a782 | ||
|
|
8465b8cfca | ||
|
|
6ae65e0ab4 | ||
|
|
6a64bf6fc1 | ||
|
|
f038bb90bb | ||
|
|
a5ba14e446 | ||
|
|
70f657beff | ||
|
|
b1085c36ff | ||
|
|
1896aa7da1 | ||
|
|
087856c535 | ||
|
|
27f1364caa | ||
|
|
bec5365ac9 | ||
|
|
0db14d6f74 | ||
|
|
ccb44e1bd1 | ||
|
|
8c3db627e4 | ||
|
|
56204f8497 | ||
|
|
fc75e88cb7 | ||
|
|
d2719b5309 | ||
|
|
e5254abfcd | ||
|
|
96b8d35a00 | ||
|
|
035b0a449b | ||
|
|
6b20177b09 | ||
|
|
15861ea41a | ||
|
|
d836756e19 | ||
|
|
191d9a7b2b | ||
|
|
0394cd7631 | ||
|
|
c1ddff4ee5 | ||
|
|
55cc428cd3 | ||
|
|
39505c9764 | ||
|
|
887a2f9be1 | ||
|
|
1328fa9e5e | ||
|
|
513830f319 | ||
|
|
440abcda7c | ||
|
|
6f3fdbecea | ||
|
|
3a46cc33ee | ||
|
|
ae3eb84e01 | ||
|
|
7a73e18596 | ||
|
|
c89779e8fc | ||
|
|
5ca8d10d04 | ||
|
|
1ce6aef57c | ||
|
|
49b23aafa3 | ||
|
|
f888647b12 | ||
|
|
065ff6eaf5 | ||
|
|
bf976fe188 | ||
|
|
5ce73574c8 | ||
|
|
5d94a983d2 | ||
|
|
25366af4bb | ||
|
|
f397c5472c | ||
|
|
6273eb0061 | ||
|
|
734fd7c0fc | ||
|
|
8797a58efc | ||
|
|
5306d58991 | ||
|
|
dcb17f2f21 | ||
|
|
37b3a30619 | ||
|
|
b0fed61a01 | ||
|
|
4a471138b2 | ||
|
|
41540a3a5f | ||
|
|
26784a5325 |
3
.github/workflows/ansible.yml
vendored
3
.github/workflows/ansible.yml
vendored
@@ -47,8 +47,9 @@ jobs:
|
|||||||
molecule converge
|
molecule converge
|
||||||
molecule idempotence
|
molecule idempotence
|
||||||
molecule verify
|
molecule verify
|
||||||
molecule destroy
|
|
||||||
working-directory: "${{ github.repository }}"
|
working-directory: "${{ github.repository }}"
|
||||||
|
env:
|
||||||
|
TARGET_GITHUB_SHA: "${{ github.event.pull_request.head.sha }}"
|
||||||
# # https://galaxy.ansible.com/docs/contributing/importing.html
|
# # https://galaxy.ansible.com/docs/contributing/importing.html
|
||||||
# release:
|
# release:
|
||||||
# runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
|
|||||||
46
.github/workflows/ci.yml
vendored
46
.github/workflows/ci.yml
vendored
@@ -20,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
|
||||||
@@ -49,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:
|
||||||
@@ -76,7 +109,7 @@ 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 unpaper tesseract-ocr imagemagick ghostscript optipng
|
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript optipng
|
||||||
@@ -87,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'
|
||||||
@@ -130,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:
|
||||||
-
|
-
|
||||||
@@ -240,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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
6
Pipfile
6
Pipfile
@@ -9,12 +9,12 @@ verify_ssl = true
|
|||||||
name = "piwheels"
|
name = "piwheels"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
dateparser = "~=0.7.6"
|
dateparser = "~=1.0.0"
|
||||||
django = "~=3.1.3"
|
django = "~=3.1.3"
|
||||||
django-cors-headers = "*"
|
django-cors-headers = "*"
|
||||||
django-extensions = "*"
|
django-extensions = "*"
|
||||||
django-filter = "~=2.4.0"
|
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 = {extras = ["speedup"], version = "*"}
|
fuzzywuzzy = {extras = ["speedup"], version = "*"}
|
||||||
@@ -53,7 +53,7 @@ concurrent-log-handler = "*"
|
|||||||
# uvloop 0.15+ incompatible with python 3.6
|
# uvloop 0.15+ incompatible with python 3.6
|
||||||
uvloop = "~=0.14.0"
|
uvloop = "~=0.14.0"
|
||||||
# TODO: keep an eye on piwheel builds and update this once available (https://www.piwheels.org/project/cryptography/)
|
# TODO: keep an eye on piwheel builds and update this once available (https://www.piwheels.org/project/cryptography/)
|
||||||
cryptography = "~=3.3.2"
|
cryptography = "~=3.4"
|
||||||
"pdfminer.six" = "*"
|
"pdfminer.six" = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|||||||
123
Pipfile.lock
generated
123
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "71959eb287fc97969263be5e3a1b1f4f369b7a5ace85bd1947a25b9b92e17e8a"
|
"sha256": "49f8a0718a7982bc4ca7e1315c17849523b0d29c38f13cff88f8f883e0e9943a"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {},
|
"requires": {},
|
||||||
@@ -28,11 +28,11 @@
|
|||||||
},
|
},
|
||||||
"arrow": {
|
"arrow": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5",
|
"sha256:7909d9fd30d32fa8fd173fdeb3f7125aaf6dedd1a837276fe1d8cea2c0e86d76",
|
||||||
"sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4"
|
"sha256:b1e106a0ab754e540f4aeb08747900830b688adef02d81240e65afc0e781a870"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==0.17.0"
|
"version": "==1.0.1"
|
||||||
},
|
},
|
||||||
"asgiref": {
|
"asgiref": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -60,11 +60,11 @@
|
|||||||
},
|
},
|
||||||
"autobahn": {
|
"autobahn": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:41a3a3f89cde48643baf4e105d9491c566295f9abee951379e59121784044b8b",
|
"sha256:884f79c50fdc55ade2c315946a9caa145e8b10075eee9d2c2594ea5e8f5226aa",
|
||||||
"sha256:7e6b1bf95196b733978bab2d54a7ab8899c16ce11be369dc58422c07b7eea726"
|
"sha256:bf7a9d302a34d0f719d43c57f65ca1f2f5c982dd6ea0c11e1e190ef6f43710fe"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.6'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==21.2.1"
|
"version": "==21.2.2"
|
||||||
},
|
},
|
||||||
"automat": {
|
"automat": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -76,10 +76,10 @@
|
|||||||
},
|
},
|
||||||
"blessed": {
|
"blessed": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0a74a8d3f0366db600d061273df77d44f0db07daade7bb7a4d49c8bc22ed9f74",
|
"sha256:1312879f971330a1b7f2c6341f2ae7e2cbac244bfc9d0ecfbbecd4b0293bc755",
|
||||||
"sha256:580429e7e0c6f6a42ea81b0ae5a4993b6205c6ccbb635d034b4277af8175753e"
|
"sha256:5b5e2f0563d5a668c282f3f5946f7b1abb70c85829461900e607e74d7725106e"
|
||||||
],
|
],
|
||||||
"version": "==1.17.12"
|
"version": "==1.18.0"
|
||||||
},
|
},
|
||||||
"certifi": {
|
"certifi": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -190,24 +190,22 @@
|
|||||||
},
|
},
|
||||||
"cryptography": {
|
"cryptography": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0d7b69674b738068fa6ffade5c962ecd14969690585aaca0a1b1fc9058938a72",
|
"sha256:066bc53f052dfeda2f2d7c195cf16fb3e5ff13e1b6b7415b468514b40b381a5b",
|
||||||
"sha256:1bd0ccb0a1ed775cd7e2144fe46df9dc03eefd722bbcf587b3e0616ea4a81eff",
|
"sha256:0923ba600d00718d63a3976f23cab19aef10c1765038945628cd9be047ad0336",
|
||||||
"sha256:3c284fc1e504e88e51c428db9c9274f2da9f73fdf5d7e13a36b8ecb039af6e6c",
|
"sha256:2d32223e5b0ee02943f32b19245b61a62db83a882f0e76cc564e1cec60d48f87",
|
||||||
"sha256:49570438e60f19243e7e0d504527dd5fe9b4b967b5a1ff21cc12b57602dd85d3",
|
"sha256:4169a27b818de4a1860720108b55a2801f32b6ae79e7f99c00d79f2a2822eeb7",
|
||||||
"sha256:541dd758ad49b45920dda3b5b48c968f8b2533d8981bcdb43002798d8f7a89ed",
|
"sha256:57ad77d32917bc55299b16d3b996ffa42a1c73c6cfa829b14043c561288d2799",
|
||||||
"sha256:5a60d3780149e13b7a6ff7ad6526b38846354d11a15e21068e57073e29e19bed",
|
"sha256:5ecf2bcb34d17415e89b546dbb44e73080f747e504273e4d4987630493cded1b",
|
||||||
"sha256:7951a966613c4211b6612b0352f5bf29989955ee592c4a885d8c7d0f830d0433",
|
"sha256:600cf9bfe75e96d965509a4c0b2b183f74a4fa6f5331dcb40fb7b77b7c2484df",
|
||||||
"sha256:922f9602d67c15ade470c11d616f2b2364950602e370c76f0c94c94ae672742e",
|
"sha256:66b57a9ca4b3221d51b237094b0303843b914b7d5afd4349970bb26518e350b0",
|
||||||
"sha256:a0f0b96c572fc9f25c3f4ddbf4688b9b38c69836713fb255f4a2715d93cbaf44",
|
"sha256:926ae3dfd160050158b9ca25d419fb7ee658974564b01aa10c059a75dffab7e8",
|
||||||
"sha256:a777c096a49d80f9d2979695b835b0f9c9edab73b59e4ceb51f19724dda887ed",
|
"sha256:93cfe5b7ff006de13e1e89830810ecbd014791b042cbe5eec253be11ac2b28f3",
|
||||||
"sha256:a9a4ac9648d39ce71c2f63fe7dc6db144b9fa567ddfc48b9fde1b54483d26042",
|
"sha256:9e98b452132963678e3ac6c73f7010fe53adf72209a32854d55690acac3f6724",
|
||||||
"sha256:aa4969f24d536ae2268c902b2c3d62ab464b5a66bcb247630d208a79a8098e9b",
|
"sha256:df186fcbf86dc1ce56305becb8434e4b6b7504bc724b71ad7a3239e0c9d14ef2",
|
||||||
"sha256:c7390f9b2119b2b43160abb34f63277a638504ef8df99f11cb52c1fda66a2e6f",
|
"sha256:fec7fb46b10da10d9e1d078d1ff8ed9e05ae14f431fdbd11145edd0550b9a964"
|
||||||
"sha256:ddd06e71c449a4fe10d0c60846280ee35d69ce49e3e413ce46d5f129e1468083",
|
|
||||||
"sha256:e18e6ab84dfb0ab997faf8cca25a86ff15dfea4027b986322026cc99e0a892da"
|
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==3.3.2"
|
"version": "==3.4.6"
|
||||||
},
|
},
|
||||||
"daphne": {
|
"daphne": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -219,11 +217,11 @@
|
|||||||
},
|
},
|
||||||
"dateparser": {
|
"dateparser": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:7552c994f893b5cb8fcf103b4cd2ff7f57aab9bfd2619fdf0cf571c0740fd90b",
|
"sha256:159cc4e01a593706a15cd4e269a0b3345edf3aef8bf9278a57dac8adf5bf1e4a",
|
||||||
"sha256:e875efd8c57c85c2d02b238239878db59ff1971f5a823457fcc69e493bf6ebfa"
|
"sha256:17202df32c7a36e773136ff353aa3767e987f8b3e27374c39fd21a30a803d6f8"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.7.6"
|
"version": "==1.0.0"
|
||||||
},
|
},
|
||||||
"django": {
|
"django": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -415,11 +413,11 @@
|
|||||||
},
|
},
|
||||||
"imap-tools": {
|
"imap-tools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0eaa9b990fae336601dd44f353fac2d35ea25ca3b1b682a83700511635fc30ae",
|
"sha256:44641716f0c5c07df78c5713405675fac50220d4f8e2906c9ed4aabd9c356fa8",
|
||||||
"sha256:1c809e286d439e41fbe796c522ad4e565fd47a4260253343fa1b1045b6bfe8b1"
|
"sha256:bab9a6edf848a58bdd3959ba55e0eb75039ff188a3779371d0b8db674a4945e8"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==0.37.0"
|
"version": "==0.38.1"
|
||||||
},
|
},
|
||||||
"img2pdf": {
|
"img2pdf": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -591,11 +589,11 @@
|
|||||||
},
|
},
|
||||||
"ocrmypdf": {
|
"ocrmypdf": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0f624456a50be0b0bc8c0b59704d159f637616c093a1cabe8bb383706561bcf7",
|
"sha256:2fca4046e3a33e5902dd89c3003a3f31c7d584dc43f14c7c97f57fce20d2e469",
|
||||||
"sha256:b829ad640a6160423162012e094ee2f7cd074ec99efadd7f7486954ec9182985"
|
"sha256:5c386fcf2c0f2635533c2bad0edcd2d455f667a134d180dc61cbb3d4d5f0c8e2"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==11.6.2"
|
"version": "==11.7.0"
|
||||||
},
|
},
|
||||||
"pathvalidate": {
|
"pathvalidate": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -784,14 +782,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": "==2.20"
|
"version": "==2.20"
|
||||||
},
|
},
|
||||||
"pyhamcrest": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316",
|
|
||||||
"sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '3.5'",
|
|
||||||
"version": "==2.0.2"
|
|
||||||
},
|
|
||||||
"pyopenssl": {
|
"pyopenssl": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4c231c759543ba02560fcd2480c48dcec4dae34c9da7d3747c508227e0624b51",
|
"sha256:4c231c759543ba02560fcd2480c48dcec4dae34c9da7d3747c508227e0624b51",
|
||||||
@@ -1097,45 +1087,22 @@
|
|||||||
},
|
},
|
||||||
"tqdm": {
|
"tqdm": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:65185676e9fdf20d154cffd1c5de8e39ef9696ff7e59fe0156b1b08e468736af",
|
"sha256:2c44efa73b8914dba7807aefd09653ac63c22b5b4ea34f7a80973f418f1a3089",
|
||||||
"sha256:70657337ec104eb4f3fb229285358f23f045433f6aea26846cdd55f0fd68945c"
|
"sha256:c23ac707e8e8aabb825e4d91f8e17247f9cc14b0d64dd9e97be0781e9e525bba"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==4.57.0"
|
"version": "==4.58.0"
|
||||||
},
|
},
|
||||||
"twisted": {
|
"twisted": {
|
||||||
"extras": [
|
"extras": [
|
||||||
"tls"
|
"tls"
|
||||||
],
|
],
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0150dae5adc962d15e00054cc6926f1e64763fb8dd26e1632593ac06e592104b",
|
"sha256:77544a8945cf69b98d2946689bbe0c75de7d145cdf11f391dd487eae8fc95a12",
|
||||||
"sha256:040eb6641125d2a9a09cf198ec7b83dd8858c6f51f6770325ed9959c00f5098f",
|
"sha256:aab38085ea6cda5b378b519a0ec99986874921ee8881318626b0a3414bb2631e"
|
||||||
"sha256:147780b8caf21ba2aef3688628eaf13d7e7fe02a86747cd54bfaf2140538f042",
|
|
||||||
"sha256:158ddb80719a4813d292293ac44ba41d8b56555ed009d90994a278237ee63d2c",
|
|
||||||
"sha256:15e52271f08f62e2230ff093e0278aa01c9dac057c4557cadadd2429eed86a3e",
|
|
||||||
"sha256:2182000d6ffc05d269e6c03bfcec8b57e20259ca1086180edaedec3f1e689292",
|
|
||||||
"sha256:25ffcf37944bdad4a99981bc74006d735a678d2b5c193781254fbbb6d69e3b22",
|
|
||||||
"sha256:3281d9ce889f7b21bdb73658e887141aa45a102baf3b2320eafcfba954fcefec",
|
|
||||||
"sha256:356e8d8dd3590e790e3dba4db139eb8a17aca64b46629c622e1b1597a4a92478",
|
|
||||||
"sha256:70952c56e4965b9f53b180daecf20a9595cf22b8d0935cd3bd664c90273c3ab2",
|
|
||||||
"sha256:7408c6635ee1b96587289283ebe90ee15dbf9614b05857b446055116bc822d29",
|
|
||||||
"sha256:7c547fd0215db9da8a1bc23182b309e84a232364cc26d829e9ee196ce840b114",
|
|
||||||
"sha256:894f6f3cfa57a15ea0d0714e4283913a5f2511dbd18653dd148eba53b3919797",
|
|
||||||
"sha256:94ac3d55a58c90e2075c5fe1853f2aa3892b73e3bf56395f743aefde8605eeaa",
|
|
||||||
"sha256:a58e61a2a01e5bcbe3b575c0099a2bcb8d70a75b1a087338e0c48dd6e01a5f15",
|
|
||||||
"sha256:c09c47ff9750a8e3aa60ad169c4b95006d455a29b80ad0901f031a103b2991cd",
|
|
||||||
"sha256:ca3a0b8c9110800e576d89b5337373e52018b41069bc879f12fa42b7eb2d0274",
|
|
||||||
"sha256:cd1dc5c85b58494138a3917752b54bb1daa0045d234b7c132c37a61d5483ebad",
|
|
||||||
"sha256:cdbc4c7f0cd7a2218b575844e970f05a1be1861c607b0e048c9bceca0c4d42f7",
|
|
||||||
"sha256:d267125cc0f1e8a0eed6319ba4ac7477da9b78a535601c49ecd20c875576433a",
|
|
||||||
"sha256:d72c55b5d56e176563b91d11952d13b01af8725c623e498db5507b6614fc1e10",
|
|
||||||
"sha256:d95803193561a243cb0401b0567c6b7987d3f2a67046770e1dccd1c9e49a9780",
|
|
||||||
"sha256:e92703bed0cc21d6cb5c61d66922b3b1564015ca8a51325bd164a5e33798d504",
|
|
||||||
"sha256:f058bd0168271de4dcdc39845b52dd0a4a2fecf5f1246335f13f5e96eaebb467",
|
|
||||||
"sha256:f3c19e5bd42bbe4bf345704ad7c326c74d3fd7a1b3844987853bef180be638d4"
|
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
"markers": "python_full_version >= '3.5.4'",
|
||||||
"version": "==20.3.0"
|
"version": "==21.2.0"
|
||||||
},
|
},
|
||||||
"txaio": {
|
"txaio": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
@@ -1490,11 +1457,11 @@
|
|||||||
},
|
},
|
||||||
"faker": {
|
"faker": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:31a58ec5a8f4672f24da3b5ddea02c82a712de1de3179b432948e5c34d787aca",
|
"sha256:90b69e9e05d622edb2fa5ebfda7bef41c88675cace85e72689fde5b8723d00a3",
|
||||||
"sha256:aadfe0efe11ecbbbc5b3b0b0fab050c2acbd2d8e5201769546d43d236bfff663"
|
"sha256:da395fe545f40d4366b82b1a02448847a4586bd2b28af393b3edbd1e45d1e0fc"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.6'",
|
"markers": "python_version >= '3.6'",
|
||||||
"version": "==6.4.1"
|
"version": "==6.5.0"
|
||||||
},
|
},
|
||||||
"filelock": {
|
"filelock": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -1,5 +1,6 @@
|
|||||||
[](https://github.com/jonaswinkler/paperless-ng/actions)
|
[](https://github.com/jonaswinkler/paperless-ng/actions)
|
||||||

|

|
||||||
|
[](https://crowdin.com/project/paperless-ng)
|
||||||
[](https://paperless-ng.readthedocs.io/en/latest/?badge=latest)
|
[](https://paperless-ng.readthedocs.io/en/latest/?badge=latest)
|
||||||
[](https://gitter.im/paperless-ng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/paperless-ng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
[](https://hub.docker.com/r/jonaswinkler/paperless-ng)
|
[](https://hub.docker.com/r/jonaswinkler/paperless-ng)
|
||||||
@@ -51,7 +52,7 @@ 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 as important 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.
|
||||||
* Optimized for multi core systems: Paperless-ng consumes multiple documents in parallel.
|
* Optimized for multi core systems: Paperless-ng consumes multiple documents in parallel.
|
||||||
@@ -77,9 +78,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, Portuguese, Italian, and Romanian.
|
||||||
|
|
||||||
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 crowdin! 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
|
||||||
|
|
||||||
@@ -104,9 +105,13 @@ Paperless has been around a while now, and people are starting to build stuff on
|
|||||||
|
|
||||||
These projects also exist, but their status and compatibility with paperless-ng is unknown.
|
These projects also exist, but their status and compatibility with paperless-ng is unknown.
|
||||||
|
|
||||||
* [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): A desktop UI for your Paperless installation. Runs on Mac, Linux, and Windows.
|
|
||||||
* [paperless-cli](https://github.com/stgarf/paperless-cli): A golang command line binary to interact with a Paperless instance.
|
* [paperless-cli](https://github.com/stgarf/paperless-cli): A golang command line binary to interact with a Paperless instance.
|
||||||
|
|
||||||
|
This project also exists, but needs updates to be compatile with paperless-ng.
|
||||||
|
|
||||||
|
* [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): A desktop UI for your Paperless installation. Runs on Mac, Linux, and Windows.
|
||||||
|
Known issues on Mac: (Could not load reminders and documents)
|
||||||
|
|
||||||
# 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. 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.
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ paperlessng_directory: /opt/paperless-ng
|
|||||||
paperlessng_consumption_dir: "{{ paperlessng_directory }}/consumption"
|
paperlessng_consumption_dir: "{{ paperlessng_directory }}/consumption"
|
||||||
paperlessng_data_dir: "{{ paperlessng_directory }}/data"
|
paperlessng_data_dir: "{{ paperlessng_directory }}/data"
|
||||||
paperlessng_media_root: "{{ paperlessng_directory }}/media"
|
paperlessng_media_root: "{{ paperlessng_directory }}/media"
|
||||||
paperlessng_static_dir: "{{ paperlessng_directory }}/static"
|
paperlessng_staticdir: "{{ paperlessng_directory }}/static"
|
||||||
paperlessng_filename_format:
|
paperlessng_filename_format:
|
||||||
|
paperlessng_logging_dir: "{{ paperlessng_data_dir }}/log"
|
||||||
paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv"
|
paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv"
|
||||||
|
|
||||||
# Hosting & Security
|
# Hosting & Security
|
||||||
@@ -36,12 +37,15 @@ paperlessng_enable_http_remote_user: False
|
|||||||
paperlessng_ocr_languages:
|
paperlessng_ocr_languages:
|
||||||
- eng
|
- eng
|
||||||
paperlessng_ocr_mode: skip
|
paperlessng_ocr_mode: skip
|
||||||
|
paperlessng_ocr_clean: clean
|
||||||
|
paperlessng_ocr_deskew: True
|
||||||
|
paperlessng_ocr_rotate_pages: True
|
||||||
|
paperlessng_ocr_rotate_pages_threshold: 12
|
||||||
paperlessng_ocr_output_type: pdfa
|
paperlessng_ocr_output_type: pdfa
|
||||||
paperlessng_ocr_pages: 0
|
paperlessng_ocr_pages: 0
|
||||||
paperlessng_ocr_image_dpi:
|
paperlessng_ocr_image_dpi:
|
||||||
# see https://ocrmypdf.readthedocs.io/en/latest/api.html#ocrmypdf.ocr
|
# see https://ocrmypdf.readthedocs.io/en/latest/api.html#ocrmypdf.ocr
|
||||||
paperlessng_ocr_user_args:
|
paperlessng_ocr_user_args:
|
||||||
#- "deskew": True # https://github.com/jonaswinkler/paperless-ng/issues/231
|
|
||||||
- "optimize": 1
|
- "optimize": 1
|
||||||
paperlessng_use_jbig2enc: True
|
paperlessng_use_jbig2enc: True
|
||||||
paperlessng_big2enc_lossy: False
|
paperlessng_big2enc_lossy: False
|
||||||
@@ -57,10 +61,11 @@ paperlessng_consumer_polling: 0
|
|||||||
paperlessng_consumer_delete_duplicates: False
|
paperlessng_consumer_delete_duplicates: False
|
||||||
paperlessng_consumer_recursive: False
|
paperlessng_consumer_recursive: False
|
||||||
paperlessng_consumer_subdirs_as_tags: False
|
paperlessng_consumer_subdirs_as_tags: False
|
||||||
|
paperlessng_convert_memory_limit: 0
|
||||||
|
paperlessng_convert_tmpdir:
|
||||||
paperlessng_optimize_thumbnails: True
|
paperlessng_optimize_thumbnails: True
|
||||||
paperlessng_post_consume_script:
|
paperlessng_post_consume_script:
|
||||||
paperlessng_filename_date_order:
|
paperlessng_filename_date_order:
|
||||||
paperlessng_filename_parse_transforms:
|
|
||||||
paperlessng_thumbnail_font_name: /usr/share/fonts/liberation/LiberationSerif-Regular.ttf
|
paperlessng_thumbnail_font_name: /usr/share/fonts/liberation/LiberationSerif-Regular.ttf
|
||||||
paperlessng_ignore_dates: ""
|
paperlessng_ignore_dates: ""
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
tasks:
|
tasks:
|
||||||
- name: set current github commit as version when available
|
- name: set current github commit as version when available
|
||||||
set_fact:
|
set_fact:
|
||||||
paperlessng_version: "{{ lookup('env', 'GITHUB_SHA') | default('master', True) }}"
|
paperlessng_version: "{{ lookup('env', 'TARGET_GITHUB_SHA') | default('master', True) }}"
|
||||||
- name: update to newest paperless-ng release
|
- name: update to newest paperless-ng release
|
||||||
include_role:
|
include_role:
|
||||||
name: ansible
|
name: ansible
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
- name: install ocr languages
|
- name: install ocr languages
|
||||||
apt:
|
apt:
|
||||||
pkg: "{{ paperlessng_ocr_languages | map('regex_replace', '^(.*)$', 'tesseract-ocr-\\1') | list }}"
|
pkg: "{{ paperlessng_ocr_languages | map('regex_replace', '^(.*)$', 'tesseract-ocr-\\1') | map('replace', '_', '-') | list }}"
|
||||||
|
|
||||||
- name: set up notesalexp repository key (for jbig2enc)
|
- name: set up notesalexp repository key (for jbig2enc)
|
||||||
apt_key:
|
apt_key:
|
||||||
@@ -256,7 +256,7 @@
|
|||||||
- "{{ paperlessng_consumption_dir }}"
|
- "{{ paperlessng_consumption_dir }}"
|
||||||
- "{{ paperlessng_data_dir }}"
|
- "{{ paperlessng_data_dir }}"
|
||||||
- "{{ paperlessng_media_root }}"
|
- "{{ paperlessng_media_root }}"
|
||||||
- "{{ paperlessng_static_dir }}"
|
- "{{ paperlessng_staticdir }}"
|
||||||
|
|
||||||
- name: rename initial config
|
- name: rename initial config
|
||||||
command:
|
command:
|
||||||
@@ -280,9 +280,11 @@
|
|||||||
- regexp: PAPERLESS_MEDIA_ROOT
|
- regexp: PAPERLESS_MEDIA_ROOT
|
||||||
line: "PAPERLESS_MEDIA_ROOT={{ paperlessng_media_root }}"
|
line: "PAPERLESS_MEDIA_ROOT={{ paperlessng_media_root }}"
|
||||||
- regexp: PAPERLESS_STATICDIR
|
- regexp: PAPERLESS_STATICDIR
|
||||||
line: "PAPERLESS_STATICDIR={{ paperlessng_static_dir }}"
|
line: "PAPERLESS_STATICDIR={{ paperlessng_staticdir }}"
|
||||||
- regexp: PAPERLESS_FILENAME_FORMAT
|
- regexp: PAPERLESS_FILENAME_FORMAT
|
||||||
line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}"
|
line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}"
|
||||||
|
- regexp: PAPERLESS_LOGGING_DIR
|
||||||
|
line: "PAPERLESS_LOGGING_DIR={{ paperlessng_logging_dir }}"
|
||||||
# Hosting & Security
|
# Hosting & Security
|
||||||
- regexp: PAPERLESS_SECRET_KEY
|
- regexp: PAPERLESS_SECRET_KEY
|
||||||
line: "PAPERLESS_SECRET_KEY={{ paperlessng_secret_key }}"
|
line: "PAPERLESS_SECRET_KEY={{ paperlessng_secret_key }}"
|
||||||
@@ -302,9 +304,17 @@
|
|||||||
line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperlessng_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('+') | replace('-','_') }}"
|
||||||
- regexp: PAPERLESS_OCR_MODE
|
- regexp: PAPERLESS_OCR_MODE
|
||||||
line: "PAPERLESS_OCR_MODE={{ paperlessng_ocr_mode }}"
|
line: "PAPERLESS_OCR_MODE={{ paperlessng_ocr_mode }}"
|
||||||
|
- regexp: PAPERLESS_OCR_CLEAN
|
||||||
|
line: "PAPERLESS_OCR_CLEAN={{ paperlessng_ocr_clean }}"
|
||||||
|
- regexp: PAPERLESS_OCR_DESKEW
|
||||||
|
line: "PAPERLESS_OCR_DESKEW={{ paperlessng_ocr_deskew }}"
|
||||||
|
- regexp: PAPERLESS_OCR_ROTATE_PAGES
|
||||||
|
line: "PAPERLESS_OCR_ROTATE_PAGES={{ paperlessng_ocr_rotate_pages }}"
|
||||||
|
- regexp: PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD
|
||||||
|
line: "PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD={{ paperlessng_ocr_rotate_pages_threshold }}"
|
||||||
- regexp: PAPERLESS_OCR_OUTPUT_TYPE
|
- regexp: PAPERLESS_OCR_OUTPUT_TYPE
|
||||||
line: "PAPERLESS_OCR_OUTPUT_TYPE={{ paperlessng_ocr_output_type }}"
|
line: "PAPERLESS_OCR_OUTPUT_TYPE={{ paperlessng_ocr_output_type }}"
|
||||||
- regexp: PAPERLESS_OCR_PAGES
|
- regexp: PAPERLESS_OCR_PAGES
|
||||||
@@ -331,6 +341,10 @@
|
|||||||
line: "PAPERLESS_CONSUMER_RECURSIVE={{ paperlessng_consumer_recursive }}"
|
line: "PAPERLESS_CONSUMER_RECURSIVE={{ paperlessng_consumer_recursive }}"
|
||||||
- regexp: PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS
|
- regexp: PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS
|
||||||
line: "PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS={{ paperlessng_consumer_subdirs_as_tags }}"
|
line: "PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS={{ paperlessng_consumer_subdirs_as_tags }}"
|
||||||
|
- regexp: PAPERLESS_CONVERT_MEMORY_LIMIT
|
||||||
|
line: "PAPERLESS_CONVERT_MEMORY_LIMIT={{ paperlessng_convert_memory_limit }}"
|
||||||
|
- regexp: PAPERLESS_CONVERT_TMPDIR
|
||||||
|
line: "PAPERLESS_CONVERT_TMPDIR={{ paperlessng_convert_tmpdir }}"
|
||||||
- regexp: PAPERLESS_OPTIMIZE_THUMBNAILS
|
- regexp: PAPERLESS_OPTIMIZE_THUMBNAILS
|
||||||
line: "PAPERLESS_OPTIMIZE_THUMBNAILS={{ paperlessng_optimize_thumbnails }}"
|
line: "PAPERLESS_OPTIMIZE_THUMBNAILS={{ paperlessng_optimize_thumbnails }}"
|
||||||
- regexp: PAPERLESS_POST_CONSUME_SCRIPT
|
- regexp: PAPERLESS_POST_CONSUME_SCRIPT
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
commit_message: '[ci skip]'
|
||||||
files:
|
files:
|
||||||
- source: /src/locale/en_US/LC_MESSAGES/django.po
|
- source: /src/locale/en_US/LC_MESSAGES/django.po
|
||||||
translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po
|
translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po
|
||||||
|
|||||||
@@ -184,17 +184,17 @@ Downgrades are possible. However, some updates also contain database migrations
|
|||||||
In order to move back from a version that applied database migrations, you'll have to revert the database migration *before* downgrading,
|
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.
|
and then downgrade paperless.
|
||||||
|
|
||||||
This table lists the most recent database migrations for each versions:
|
This table lists the compatible versions for each database migration number.
|
||||||
|
|
||||||
+---------+-------------------------+
|
+------------------+-----------------+
|
||||||
| Version | Latest migration number |
|
| Migration number | Version range |
|
||||||
+---------+-------------------------+
|
+------------------+-----------------+
|
||||||
| 1.0.0 | 1011 |
|
| 1011 | 1.0.0 |
|
||||||
+---------+-------------------------+
|
+------------------+-----------------+
|
||||||
| 1.1.0 | 1011 |
|
| 1012 | 1.1.0 - 1.2.1 |
|
||||||
+---------+-------------------------+
|
+------------------+-----------------+
|
||||||
| 1.1.1 | 1012 |
|
| 1014 | 1.3.0 - current |
|
||||||
+---------+-------------------------+
|
+------------------+-----------------+
|
||||||
|
|
||||||
Execute the following management command to migrate your database:
|
Execute the following management command to migrate your database:
|
||||||
|
|
||||||
|
|||||||
50
docs/api.rst
50
docs/api.rst
@@ -284,3 +284,53 @@ The endpoint supports the following optional form fields:
|
|||||||
The endpoint will immediately return "OK" if the document consumption process
|
The endpoint will immediately return "OK" if the document consumption process
|
||||||
was started successfully. No additional status information about the consumption
|
was started successfully. No additional status information about the consumption
|
||||||
process itself is available, since that happens in a different process.
|
process itself is available, since that happens in a different process.
|
||||||
|
|
||||||
|
|
||||||
|
.. _api-versioning:
|
||||||
|
|
||||||
|
API Versioning
|
||||||
|
##############
|
||||||
|
|
||||||
|
The REST API is versioned since Paperless-ng 1.3.0.
|
||||||
|
|
||||||
|
* Versioning ensures that changes to the API don't break older clients.
|
||||||
|
* Clients specify the specific version of the API they wish to use with every request and Paperless will handle the request using the specified API version.
|
||||||
|
* Even if the underlying data model changes, older API versions will always serve compatible data.
|
||||||
|
* If no version is specified, Paperless will serve version 1 to ensure compatibility with older clients that do not request a specific API version.
|
||||||
|
|
||||||
|
API versions are specified by submitting an additional HTTP ``Accept`` header with every request:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
Accept: application/json; version=6
|
||||||
|
|
||||||
|
If an invalid version is specified, Paperless 1.3.0 will respond with "406 Not Acceptable" and an error message in the body.
|
||||||
|
Earlier versions of Paperless will serve API version 1 regardless of whether a version is specified via the ``Accept`` header.
|
||||||
|
|
||||||
|
If a client wishes to verify whether it is compatible with any given server, the following procedure should be performed:
|
||||||
|
|
||||||
|
1. Perform an *authenticated* request against any API endpoint. If the server is on version 1.3.0 or newer, the server will
|
||||||
|
add two custom headers to the response:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
X-Api-Version: 2
|
||||||
|
X-Version: 1.3.0
|
||||||
|
|
||||||
|
2. Determine whether the client is compatible with this server based on the presence/absence of these headers and their values if present.
|
||||||
|
|
||||||
|
|
||||||
|
API Changelog
|
||||||
|
=============
|
||||||
|
|
||||||
|
Version 1
|
||||||
|
---------
|
||||||
|
|
||||||
|
Initial API version.
|
||||||
|
|
||||||
|
Version 2
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Added field ``Tag.color``. This read/write string field contains a hex color such as ``#a6cee3``.
|
||||||
|
* Added read-only field ``Tag.text_color``. This field contains the text color to use for a specific tag, which is either black or white depending on the brightness of ``Tag.color``.
|
||||||
|
* Removed field ``Tag.colour``.
|
||||||
|
|||||||
@@ -5,6 +5,70 @@
|
|||||||
Changelog
|
Changelog
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
paperless-ng 1.3.1
|
||||||
|
##################
|
||||||
|
|
||||||
|
* Added translation into Spanish and Russian.
|
||||||
|
|
||||||
|
* Other changes
|
||||||
|
|
||||||
|
* ISO-8601 date format will now always show years with 4 digits.
|
||||||
|
|
||||||
|
* Added the ability to search for a document with a specific ASN.
|
||||||
|
|
||||||
|
* The document cards now display ASN, types and dates in a more organized way.
|
||||||
|
|
||||||
|
* Added document previews when hovering over the preview button.
|
||||||
|
|
||||||
|
* Fixes
|
||||||
|
|
||||||
|
* The startup check for write permissions now works properly on NFS shares.
|
||||||
|
|
||||||
|
* Fixed an issue with the search results score indicator.
|
||||||
|
|
||||||
|
* Paperless was unable to generate thumbnails for encrypted PDF files and failed. Paperless will now generate a default thumbnail for these files.
|
||||||
|
|
||||||
|
* Fixed ``AUTO_LOGIN_USERNAME``: Unable to perform POST/PUT/DELETE requests and unable to receive WebSocket messages.
|
||||||
|
|
||||||
|
paperless-ng 1.3.0
|
||||||
|
##################
|
||||||
|
|
||||||
|
This release contains new database migrations.
|
||||||
|
|
||||||
|
* Changes
|
||||||
|
|
||||||
|
* The REST API is versioned from this point onwards. This will allow me to make changes without breaking existing clients. See the documentation about :ref:`api-versioning` for details.
|
||||||
|
|
||||||
|
* Added a color picker for tag colors.
|
||||||
|
|
||||||
|
* Added the ability to use the filter for searching the document content as well.
|
||||||
|
|
||||||
|
* Added translations into Italian and Romanian. Thank you!
|
||||||
|
|
||||||
|
* Close individual documents from the sidebar. Thanks to `Michael Shamoon`_.
|
||||||
|
|
||||||
|
* `BolkoSchreiber <https://github.com/BolkoSchreiber>`_ added an option to disable/enable thumbnail inversion in dark mode.
|
||||||
|
|
||||||
|
* `Simon Taddiken <https://github.com/skuzzle>`_ added the ability to customize the header used for remote user authentication with SSO applications.
|
||||||
|
|
||||||
|
* Bug fixes
|
||||||
|
|
||||||
|
* Fixed an issue with the auto matching algorithm when more than 256 tags were used.
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
* Regression fix: Dates on the front end did not respect date locale settings in some cases.
|
||||||
|
|
||||||
paperless-ng 1.2.0
|
paperless-ng 1.2.0
|
||||||
##################
|
##################
|
||||||
|
|
||||||
|
|||||||
@@ -191,8 +191,28 @@ PAPERLESS_ENABLE_HTTP_REMOTE_USER=<bool>
|
|||||||
Allows authentication via HTTP_REMOTE_USER which is used by some SSO
|
Allows authentication via HTTP_REMOTE_USER which is used by some SSO
|
||||||
applications.
|
applications.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This will allow authentication by simply adding a ``Remote-User: <username>`` header
|
||||||
|
to a request. Use with care! You especially *must* ensure that any such header is not
|
||||||
|
passed from your proxy server to paperless.
|
||||||
|
|
||||||
|
If you're exposing paperless to the internet directly, do not use this.
|
||||||
|
|
||||||
|
Also see the warning `in the official documentation <https://docs.djangoproject.com/en/3.1/howto/auth-remote-user/#configuration>`.
|
||||||
|
|
||||||
Defaults to `false` which disables this feature.
|
Defaults to `false` which disables this feature.
|
||||||
|
|
||||||
|
PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME=<str>
|
||||||
|
If `PAPERLESS_ENABLE_HTTP_REMOTE_USER` is enabled, this property allows to
|
||||||
|
customize the name of the HTTP header from which the authenticated username
|
||||||
|
is extracted. Values are in terms of
|
||||||
|
[HttpRequest.META](https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpRequest.META).
|
||||||
|
Thus, the configured value must start with `HTTP_` followed by the
|
||||||
|
normalized actual header name.
|
||||||
|
|
||||||
|
Defaults to `HTTP_REMOTE_USER`.
|
||||||
|
|
||||||
.. _configuration-ocr:
|
.. _configuration-ocr:
|
||||||
|
|
||||||
OCR settings
|
OCR settings
|
||||||
@@ -278,7 +298,7 @@ PAPERLESS_OCR_DESKEW=<bool>
|
|||||||
PAPERLESS_OCR_ROTATE_PAGES=<bool>
|
PAPERLESS_OCR_ROTATE_PAGES=<bool>
|
||||||
Tells paperless to correct page rotation (90°, 180° and 270° rotation).
|
Tells paperless to correct page rotation (90°, 180° and 270° rotation).
|
||||||
|
|
||||||
If you notice that paperless is not rotating pages incorrectly rotated
|
If you notice that paperless is not rotating incorrectly rotated
|
||||||
pages (or vice versa), try adjusting the threshold up or down (see below).
|
pages (or vice versa), try adjusting the threshold up or down (see below).
|
||||||
|
|
||||||
Defaults to ``true``, which enables this feature.
|
Defaults to ``true``, which enables this feature.
|
||||||
@@ -287,7 +307,7 @@ PAPERLESS_OCR_ROTATE_PAGES=<bool>
|
|||||||
PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD=<num>
|
PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD=<num>
|
||||||
Adjust the threshold for automatic page rotation by ``PAPERLESS_OCR_ROTATE_PAGES``.
|
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,
|
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 correctly rotated pages
|
whereas "2" is a very aggressive option and will often result in correctly rotated pages
|
||||||
being rotated as well.
|
being rotated as well.
|
||||||
|
|
||||||
Defaults to "12".
|
Defaults to "12".
|
||||||
@@ -338,7 +358,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::
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Paperless development
|
|||||||
|
|
||||||
This section describes the steps you need to take to start development on paperless-ng.
|
This section describes the steps you need to take to start development on paperless-ng.
|
||||||
|
|
||||||
1. Check out the source from github. The repository is organized in the following way:
|
Check out the source from github. The repository is organized in the following way:
|
||||||
|
|
||||||
* ``master`` always represents the latest release and will only see changes
|
* ``master`` always represents the latest release and will only see changes
|
||||||
when a new release is made.
|
when a new release is made.
|
||||||
@@ -13,6 +13,8 @@ This section describes the steps you need to take to start development on paperl
|
|||||||
* ``feature-X`` contain bigger changes that will be in some release, but not
|
* ``feature-X`` contain bigger changes that will be in some release, but not
|
||||||
necessarily the next one.
|
necessarily the next one.
|
||||||
|
|
||||||
|
When making functional changes to paperless, *always* make your changes on the ``dev`` branch.
|
||||||
|
|
||||||
Apart from that, the folder structure is as follows:
|
Apart from that, the folder structure is as follows:
|
||||||
|
|
||||||
* ``docs/`` - Documentation.
|
* ``docs/`` - Documentation.
|
||||||
@@ -21,13 +23,64 @@ This section describes the steps you need to take to start development on paperl
|
|||||||
* ``scripts/`` - Various scripts that help with different parts of development.
|
* ``scripts/`` - Various scripts that help with different parts of development.
|
||||||
* ``docker/`` - Files required to build the docker image.
|
* ``docker/`` - Files required to build the docker image.
|
||||||
|
|
||||||
2. Install some dependencies.
|
Initial setup and first start
|
||||||
|
=============================
|
||||||
|
|
||||||
* Python 3.6.
|
After you forked and cloned the code from github you need to perform a first-time setup.
|
||||||
* All dependencies listed in the :ref:`Bare metal route <setup-bare_metal>`
|
To do the setup you need to perform the steps from the following chapters in a certain order:
|
||||||
* redis. You can either install redis or use the included scritps/start-services.sh
|
|
||||||
to use docker to fire up a redis instance (and some other services such as tika,
|
1. Install prerequisites + pipenv as mentioned in :ref:`Bare metal route <setup-bare_metal>`
|
||||||
gotenberg and a postgresql server).
|
2. Copy ``paperless.conf.example`` to ``paperless.conf`` and enable debug mode.
|
||||||
|
3. Install the Angular CLI interface:
|
||||||
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
$ npm install -g @angular/cli
|
||||||
|
|
||||||
|
4. Create ``consume`` and ``media`` folders in the cloned root folder.
|
||||||
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
mkdir -p consume media
|
||||||
|
|
||||||
|
5. You can now either ...
|
||||||
|
|
||||||
|
* install redis or
|
||||||
|
* use the included scripts/start-services.sh to use docker to fire up a redis instance (and some other services such as tika, gotenberg and a postgresql server) or
|
||||||
|
* spin up a bare redis container
|
||||||
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
docker run -d -p 6379:6379 -restart unless-stopped redis:latest
|
||||||
|
|
||||||
|
6. Install the python dependencies by performing in the src/ directory.
|
||||||
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
pipenv install --dev
|
||||||
|
|
||||||
|
7. Generate the static UI so you can perform a login to get session that is required for frontend development (this needs to be done one time only). From root folder:
|
||||||
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
compile-frontend.sh
|
||||||
|
|
||||||
|
8. Apply migrations and create a superuser for your dev instance:
|
||||||
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
python3 manage.py migrate
|
||||||
|
python3 manage.py createsuperuser
|
||||||
|
|
||||||
|
9. Now spin up the dev backend. Depending on which part of paperless you're developing for, you need to have some or all of them running.
|
||||||
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
python3 manage.py runserver & python3 manage.py document_consumer & python3 manage.py qcluster
|
||||||
|
|
||||||
|
10. Login with the superuser credentials provided in step 8 at ``http://localhost:8000`` to create a session that enables you to use the backend.
|
||||||
|
|
||||||
|
Backend development environment is now ready, to start Frontend development go to ``/src-ui`` and run ``ng serve``. From there you can use ``http://localhost:4200`` for a preview.
|
||||||
|
|
||||||
Back end development
|
Back end development
|
||||||
====================
|
====================
|
||||||
@@ -35,21 +88,18 @@ Back end development
|
|||||||
The backend is a django application. I use PyCharm for development, but you can use whatever
|
The backend is a django application. I use PyCharm for development, but you can use whatever
|
||||||
you want.
|
you want.
|
||||||
|
|
||||||
Install the python dependencies by performing ``pipenv install --dev`` in the src/ directory.
|
|
||||||
This will also create a virtual environment, which you can enter with ``pipenv shell`` or
|
|
||||||
execute one-shot commands in with ``pipenv run``.
|
|
||||||
|
|
||||||
Copy ``paperless.conf.example`` to ``paperless.conf`` and enable debug mode.
|
|
||||||
|
|
||||||
Configure the IDE to use the src/ folder as the base source folder. Configure the following
|
Configure the IDE to use the src/ folder as the base source folder. Configure the following
|
||||||
launch configurations in your IDE:
|
launch configurations in your IDE:
|
||||||
|
|
||||||
* python3 manage.py runserver
|
* python3 manage.py runserver
|
||||||
* python3 manage.py qcluster
|
* python3 manage.py qcluster
|
||||||
* python3 manage.py consumer
|
* python3 manage.py document_consumer
|
||||||
|
|
||||||
Depending on which part of paperless you're developing for, you need to have some or all of
|
To start them all:
|
||||||
them running.
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
python3 manage.py runserver & python3 manage.py document_consumer & python3 manage.py qcluster
|
||||||
|
|
||||||
Testing and code style:
|
Testing and code style:
|
||||||
|
|
||||||
@@ -109,6 +159,92 @@ 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.
|
||||||
|
|
||||||
|
|
||||||
|
Localization
|
||||||
|
============
|
||||||
|
|
||||||
|
Paperless is available in many different languages. Since paperless consists both of a django
|
||||||
|
application and an Angular front end, both these parts have to be translated separately.
|
||||||
|
|
||||||
|
Front end localization
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
* The Angular front end does localization according to the `Angular documentation <https://angular.io/guide/i18n>`_.
|
||||||
|
* The source language of the project is "en_US".
|
||||||
|
* The source strings end up in the file "src-ui/messages.xlf".
|
||||||
|
* The translated strings need to be placed in the "src-ui/src/locale/" folder.
|
||||||
|
* In order to extract added or changed strings from the source files, call ``ng xi18n --ivy``.
|
||||||
|
|
||||||
|
Adding new languages requires adding the translated files in the "src-ui/src/locale/" folder and adjusting a couple files.
|
||||||
|
|
||||||
|
1. Adjust "src-ui/angular.json":
|
||||||
|
|
||||||
|
.. code:: json
|
||||||
|
|
||||||
|
"i18n": {
|
||||||
|
"sourceLocale": "en-US",
|
||||||
|
"locales": {
|
||||||
|
"de": "src/locale/messages.de.xlf",
|
||||||
|
"nl-NL": "src/locale/messages.nl_NL.xlf",
|
||||||
|
"fr": "src/locale/messages.fr.xlf",
|
||||||
|
"en-GB": "src/locale/messages.en_GB.xlf",
|
||||||
|
"pt-BR": "src/locale/messages.pt_BR.xlf",
|
||||||
|
"language-code": "language-file"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
2. Add the language to the available options in "src-ui/src/app/services/settings.service.ts":
|
||||||
|
|
||||||
|
.. code:: typescript
|
||||||
|
|
||||||
|
getLanguageOptions(): LanguageOption[] {
|
||||||
|
return [
|
||||||
|
{code: "en-us", name: $localize`English (US)`, englishName: "English (US)", dateInputFormat: "mm/dd/yyyy"},
|
||||||
|
{code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)", dateInputFormat: "dd/mm/yyyy"},
|
||||||
|
{code: "de", name: $localize`German`, englishName: "German", dateInputFormat: "dd.mm.yyyy"},
|
||||||
|
{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"}
|
||||||
|
// Add your new language here
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
``dateInputFormat`` is a special string that defines the behavior of the date input fields and absolutely needs to contain "dd", "mm" and "yyyy".
|
||||||
|
|
||||||
|
3. Import and register the Angular data for this locale in "src-ui/src/app/app.module.ts":
|
||||||
|
|
||||||
|
.. code:: typescript
|
||||||
|
|
||||||
|
import localeDe from '@angular/common/locales/de';
|
||||||
|
registerLocaleData(localeDe)
|
||||||
|
|
||||||
|
Back end localization
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
A majority of the strings that appear in the back end appear only when the admin is used. However,
|
||||||
|
some of these are still shown on the front end (such as error messages).
|
||||||
|
|
||||||
|
* The django application does localization according to the `django documentation <https://docs.djangoproject.com/en/3.1/topics/i18n/translation/>`_.
|
||||||
|
* The source language of the project is "en_US".
|
||||||
|
* Localization files end up in the folder "src/locale/".
|
||||||
|
* In order to extract strings from the application, call ``python3 manage.py makemessages -l en_US``. This is important after making changes to translatable strings.
|
||||||
|
* The message files need to be compiled for them to show up in the application. Call ``python3 manage.py compilemessages`` to do this. The generated files don't get
|
||||||
|
committed into git, since these are derived artifacts. The build pipeline takes care of executing this command.
|
||||||
|
|
||||||
|
Adding new languages requires adding the translated files in the "src/locale/" folder and adjusting the file "src/paperless/settings.py" to include the new language:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
LANGUAGES = [
|
||||||
|
("en-us", _("English (US)")),
|
||||||
|
("en-gb", _("English (GB)")),
|
||||||
|
("de", _("German")),
|
||||||
|
("nl-nl", _("Dutch")),
|
||||||
|
("fr", _("French")),
|
||||||
|
("pt-br", _("Portuguese (Brazil)")),
|
||||||
|
# Add language here.
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
Building the documentation
|
Building the documentation
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
@@ -133,6 +269,23 @@ this is how you do it:
|
|||||||
This will build the HTML documentation, and put the resulting files in the ``_build/html``
|
This will build the HTML documentation, and put the resulting files in the ``_build/html``
|
||||||
directory.
|
directory.
|
||||||
|
|
||||||
|
Building the Docker image
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Building the docker image from source requires the following two steps:
|
||||||
|
|
||||||
|
1. Build the front end.
|
||||||
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
./compile-frontend.sh
|
||||||
|
|
||||||
|
2. Build the docker image.
|
||||||
|
|
||||||
|
.. code:: shell-session
|
||||||
|
|
||||||
|
docker build . -t <your-tag>
|
||||||
|
|
||||||
Extending Paperless
|
Extending Paperless
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
|||||||
@@ -284,6 +284,12 @@ writing. Windows is not and will never be supported.
|
|||||||
* ``libmagic-dev`` for mime type detection
|
* ``libmagic-dev`` for mime type detection
|
||||||
* ``mime-support`` for mime type detection
|
* ``mime-support`` for mime type detection
|
||||||
|
|
||||||
|
Use this list for your preferred package management:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
python3 python3-pip python3-dev imagemagick fonts-liberation optipng gnupg libpq-dev libmagic-dev mime-support
|
||||||
|
|
||||||
These dependencies are required for OCRmyPDF, which is used for text recognition.
|
These dependencies are required for OCRmyPDF, which is used for text recognition.
|
||||||
|
|
||||||
* ``unpaper``
|
* ``unpaper``
|
||||||
@@ -297,6 +303,12 @@ writing. Windows is not and will never be supported.
|
|||||||
* ``tesseract-ocr`` >= 4.0.0 for OCR
|
* ``tesseract-ocr`` >= 4.0.0 for OCR
|
||||||
* ``tesseract-ocr`` language packs (``tesseract-ocr-eng``, ``tesseract-ocr-deu``, etc)
|
* ``tesseract-ocr`` language packs (``tesseract-ocr-eng``, ``tesseract-ocr-deu``, etc)
|
||||||
|
|
||||||
|
Use this list for your preferred package management:
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
unpaper ghostscript icc-profiles-free qpdf liblept5 libxml2 pngquant zlib1g tesseract-ocr
|
||||||
|
|
||||||
On Raspberry Pi, these libraries are required as well:
|
On Raspberry Pi, these libraries are required as well:
|
||||||
|
|
||||||
* ``libatlas-base-dev``
|
* ``libatlas-base-dev``
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
ask() {
|
ask() {
|
||||||
while true ; do
|
while true ; do
|
||||||
if [[ -z $3 ]] ; then
|
if [[ -z $3 ]] ; then
|
||||||
@@ -64,6 +62,19 @@ if [[ -z $(which docker-compose) ]] ; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check if user has permissions to run Docker by trying to get the status of Docker (docker status).
|
||||||
|
# If this fails, the user probably does not have permissions for Docker.
|
||||||
|
docker stats --no-stream 2>/dev/null 1>&2
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
echo ""
|
||||||
|
echo "WARN: It look like the current user does not have Docker permissions."
|
||||||
|
echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user."
|
||||||
|
echo ""
|
||||||
|
sleep 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "############################################"
|
echo "############################################"
|
||||||
echo "### Paperless-ng docker installation ###"
|
echo "### Paperless-ng docker installation ###"
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
-i https://pypi.python.org/simple
|
-i https://pypi.python.org/simple
|
||||||
--extra-index-url https://www.piwheels.org/simple
|
--extra-index-url https://www.piwheels.org/simple
|
||||||
aioredis==1.3.1
|
aioredis==1.3.1
|
||||||
arrow==0.17.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
arrow==1.0.1; python_version >= '3.6'
|
||||||
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==21.2.1; python_version >= '3.6'
|
autobahn==21.2.2; python_version >= '3.7'
|
||||||
automat==20.2.0
|
automat==20.2.0
|
||||||
blessed==1.17.12
|
blessed==1.18.0
|
||||||
certifi==2020.12.5
|
certifi==2020.12.5
|
||||||
cffi==1.14.5
|
cffi==1.14.5
|
||||||
channels-redis==3.2.0
|
channels-redis==3.2.0
|
||||||
@@ -24,9 +24,9 @@ click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2,
|
|||||||
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
|
concurrent-log-handler==0.9.19
|
||||||
constantly==15.1.0
|
constantly==15.1.0
|
||||||
cryptography==3.3.2
|
cryptography==3.4.6
|
||||||
daphne==3.0.1; python_version >= '3.6'
|
daphne==3.0.1; python_version >= '3.6'
|
||||||
dateparser==0.7.6
|
dateparser==1.0.0
|
||||||
django-cors-headers==3.7.0
|
django-cors-headers==3.7.0
|
||||||
django-extensions==3.1.1
|
django-extensions==3.1.1
|
||||||
django-filter==2.4.0
|
django-filter==2.4.0
|
||||||
@@ -43,7 +43,7 @@ httptools==0.1.1
|
|||||||
humanfriendly==9.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
humanfriendly==9.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||||
hyperlink==21.0.0
|
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.38.1
|
||||||
img2pdf==0.4.0
|
img2pdf==0.4.0
|
||||||
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'
|
||||||
@@ -53,7 +53,7 @@ langdetect==1.0.8
|
|||||||
lxml==4.6.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
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
|
numpy==1.19.5
|
||||||
ocrmypdf==11.6.2
|
ocrmypdf==11.7.0
|
||||||
pathvalidate==2.3.2
|
pathvalidate==2.3.2
|
||||||
pdfminer.six==20201018
|
pdfminer.six==20201018
|
||||||
pikepdf==2.5.2
|
pikepdf==2.5.2
|
||||||
@@ -64,7 +64,6 @@ psycopg2-binary==2.8.6
|
|||||||
pyasn1-modules==0.2.8
|
pyasn1-modules==0.2.8
|
||||||
pyasn1==0.4.8
|
pyasn1==0.4.8
|
||||||
pycparser==2.20; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
pycparser==2.20; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||||
pyhamcrest==2.0.2; python_version >= '3.5'
|
|
||||||
pyopenssl==20.0.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
pyopenssl==20.0.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||||
python-dateutil==2.8.1
|
python-dateutil==2.8.1
|
||||||
python-dotenv==0.15.0
|
python-dotenv==0.15.0
|
||||||
@@ -85,8 +84,8 @@ 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.57.0
|
tqdm==4.58.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]==21.2.0; python_full_version >= '3.5.4'
|
||||||
txaio==21.2.1; python_version >= '3.6'
|
txaio==21.2.1; python_version >= '3.6'
|
||||||
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'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
docker run -p 5432:5432 -v paperless_pgdata:/var/lib/postgresql/data -d postgres:13
|
docker run -p 5432:5432 -e POSTGRES_PASSWORD=password -v paperless_pgdata:/var/lib/postgresql/data -d postgres:13
|
||||||
docker run -d -p 6379:6379 redis:latest
|
docker run -d -p 6379:6379 redis:latest
|
||||||
docker run -p 3000:3000 -d thecodingmachine/gotenberg
|
docker run -p 3000:3000 -d thecodingmachine/gotenberg
|
||||||
docker run -p 9998:9998 -d apache/tika
|
docker run -p 9998:9998 -d apache/tika
|
||||||
|
|||||||
@@ -16,10 +16,15 @@
|
|||||||
"i18n": {
|
"i18n": {
|
||||||
"sourceLocale": "en-US",
|
"sourceLocale": "en-US",
|
||||||
"locales": {
|
"locales": {
|
||||||
"de": "src/locale/messages.de.xlf",
|
"de-DE": "src/locale/messages.de_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-FR": "src/locale/messages.fr_FR.xlf",
|
||||||
"en-GB": "src/locale/messages.en_GB.xlf"
|
"en-GB": "src/locale/messages.en_GB.xlf",
|
||||||
|
"pt-BR": "src/locale/messages.pt_BR.xlf",
|
||||||
|
"it-IT": "src/locale/messages.it_IT.xlf",
|
||||||
|
"ro-RO": "src/locale/messages.ro_RO.xlf",
|
||||||
|
"ru-RU": "src/locale/messages.ru_RU.xlf",
|
||||||
|
"es-ES": "src/locale/messages.es_ES.xlf"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"architect": {
|
"architect": {
|
||||||
@@ -36,6 +41,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",
|
||||||
@@ -100,7 +106,8 @@
|
|||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
"options": {
|
"options": {
|
||||||
"browserTarget": "paperless-ui:build"
|
"browserTarget": "paperless-ui:build",
|
||||||
|
"ivy": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
@@ -112,6 +119,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"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -419,7 +419,7 @@
|
|||||||
<source>Do you really want to delete the tag "<x id="PH" equiv-text="object.name"/>"?</source>
|
<source>Do you really want to delete the tag "<x id="PH" equiv-text="object.name"/>"?</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.ts</context>
|
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.ts</context>
|
||||||
<context context-type="linenumber">30</context>
|
<context context-type="linenumber">26</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="cafc87479686947e2590b9f588a88040aeaf660b" datatype="html">
|
<trans-unit id="cafc87479686947e2590b9f588a88040aeaf660b" datatype="html">
|
||||||
@@ -517,42 +517,35 @@
|
|||||||
<source>Saved view "<x id="PH" equiv-text="savedView.name"/>" deleted.</source>
|
<source>Saved view "<x id="PH" equiv-text="savedView.name"/>" deleted.</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">67</context>
|
<context context-type="linenumber">68</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5647210819299459618" datatype="html">
|
<trans-unit id="5647210819299459618" datatype="html">
|
||||||
<source>Settings saved successfully.</source>
|
<source>Settings saved successfully.</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">87</context>
|
<context context-type="linenumber">89</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6839066544204061364" datatype="html">
|
<trans-unit id="6839066544204061364" datatype="html">
|
||||||
<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">94</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">96</context>
|
<context context-type="linenumber">100</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/components/manage/settings/settings.component.ts</context>
|
|
||||||
<context context-type="linenumber">97</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">114</context>
|
<context context-type="linenumber">117</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="121cc5391cd2a5115bc2b3160379ee5b36cd7716" datatype="html">
|
<trans-unit id="121cc5391cd2a5115bc2b3160379ee5b36cd7716" datatype="html">
|
||||||
@@ -573,14 +566,14 @@
|
|||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<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">115</context>
|
<context context-type="linenumber">116</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="99dee94e92dbd9e21a008d4569f9719ed206ae37" datatype="html">
|
<trans-unit id="99dee94e92dbd9e21a008d4569f9719ed206ae37" datatype="html">
|
||||||
<source>Saved views</source>
|
<source>Saved views</source>
|
||||||
<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">133</context>
|
<context context-type="linenumber">134</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13" datatype="html">
|
<trans-unit id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13" datatype="html">
|
||||||
@@ -688,102 +681,109 @@
|
|||||||
<context context-type="linenumber">98</context>
|
<context context-type="linenumber">98</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="71bad20b37410c8972c9aa0f7c62996534b84339" datatype="html">
|
||||||
|
<source>Invert thumbnails in dark mode</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">99</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="3863a86cd9e69a61d143d3daf51df44203df4a82" datatype="html">
|
<trans-unit id="3863a86cd9e69a61d143d3daf51df44203df4a82" datatype="html">
|
||||||
<source>Bulk editing</source>
|
<source>Bulk editing</source>
|
||||||
<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">102</context>
|
<context context-type="linenumber">103</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586" datatype="html">
|
<trans-unit id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586" datatype="html">
|
||||||
<source>Show confirmation dialogs</source>
|
<source>Show confirmation dialogs</source>
|
||||||
<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">106</context>
|
<context context-type="linenumber">107</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a" datatype="html">
|
<trans-unit id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a" datatype="html">
|
||||||
<source>Deleting documents will always ask for confirmation.</source>
|
<source>Deleting documents will always ask for confirmation.</source>
|
||||||
<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">106</context>
|
<context context-type="linenumber">107</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8cfddc13e04f5545ac63f419ef363505d6f78c2e" datatype="html">
|
<trans-unit id="8cfddc13e04f5545ac63f419ef363505d6f78c2e" datatype="html">
|
||||||
<source>Apply on close</source>
|
<source>Apply on close</source>
|
||||||
<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">107</context>
|
<context context-type="linenumber">108</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8680abbea249ebe9c2fe35556559c8e1a9eb5841" datatype="html">
|
<trans-unit id="8680abbea249ebe9c2fe35556559c8e1a9eb5841" datatype="html">
|
||||||
<source>Document processing</source>
|
<source>Document processing</source>
|
||||||
<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">118</context>
|
<context context-type="linenumber">119</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2ad4d76b36341c589d94004ad2a213fd4d6f5ca0" datatype="html">
|
<trans-unit id="2ad4d76b36341c589d94004ad2a213fd4d6f5ca0" datatype="html">
|
||||||
<source>Show notifications when new documents are detected</source>
|
<source>Show notifications when new documents are detected</source>
|
||||||
<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">122</context>
|
<context context-type="linenumber">123</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="e775f4f7c40249d31426ae61a21616a0c9d8e84f" datatype="html">
|
<trans-unit id="e775f4f7c40249d31426ae61a21616a0c9d8e84f" datatype="html">
|
||||||
<source>Show notifications when document processing completes successfully</source>
|
<source>Show notifications when document processing completes successfully</source>
|
||||||
<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">123</context>
|
<context context-type="linenumber">124</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="e3844dd174d8e817ddb551fae28f14ae80ca36b6" datatype="html">
|
<trans-unit id="e3844dd174d8e817ddb551fae28f14ae80ca36b6" datatype="html">
|
||||||
<source>Show notifications when document processing fails</source>
|
<source>Show notifications when document processing fails</source>
|
||||||
<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">124</context>
|
<context context-type="linenumber">125</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="af113f7c9f7e13145c3461f61a1aedf12d57bd71" datatype="html">
|
<trans-unit id="af113f7c9f7e13145c3461f61a1aedf12d57bd71" datatype="html">
|
||||||
<source>Suppress notifications on dashboard</source>
|
<source>Suppress notifications on dashboard</source>
|
||||||
<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">125</context>
|
<context context-type="linenumber">126</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="e27bd3804d2936a6897e81c2e52e294490e5e5a8" datatype="html">
|
<trans-unit id="e27bd3804d2936a6897e81c2e52e294490e5e5a8" datatype="html">
|
||||||
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
||||||
<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">125</context>
|
<context context-type="linenumber">126</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8cb90334f5dfd7fc67205085f59381e2a334ccfc" datatype="html">
|
<trans-unit id="8cb90334f5dfd7fc67205085f59381e2a334ccfc" datatype="html">
|
||||||
<source>Appears on</source>
|
<source>Appears on</source>
|
||||||
<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">145</context>
|
<context context-type="linenumber">146</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5" datatype="html">
|
<trans-unit id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5" datatype="html">
|
||||||
<source>Show on dashboard</source>
|
<source>Show on dashboard</source>
|
||||||
<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">148</context>
|
<context context-type="linenumber">149</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="541bfc5b123b3f8867fd681eaceefb663a811973" datatype="html">
|
<trans-unit id="541bfc5b123b3f8867fd681eaceefb663a811973" datatype="html">
|
||||||
<source>Show in sidebar</source>
|
<source>Show in sidebar</source>
|
||||||
<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">152</context>
|
<context context-type="linenumber">153</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="abba764a7a595d04dc8c3b26e04b3780d4fdb540" datatype="html">
|
<trans-unit id="abba764a7a595d04dc8c3b26e04b3780d4fdb540" datatype="html">
|
||||||
<source>No saved views defined.</source>
|
<source>No saved views defined.</source>
|
||||||
<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">162</context>
|
<context context-type="linenumber">163</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ef60a738a565f498b858e903e42bc5ffc3cc1299" datatype="html">
|
<trans-unit id="ef60a738a565f498b858e903e42bc5ffc3cc1299" datatype="html">
|
||||||
@@ -874,28 +874,28 @@
|
|||||||
<source>Create new tag</source>
|
<source>Create new tag</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.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="5872175735754226507" datatype="html">
|
<trans-unit id="5872175735754226507" datatype="html">
|
||||||
<source>Edit tag</source>
|
<source>Edit tag</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.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="f2a30b4e1a89a8a0db0bd147b54d6626b9a9bc42" datatype="html">
|
<trans-unit id="f2a30b4e1a89a8a0db0bd147b54d6626b9a9bc42" datatype="html">
|
||||||
<source>Inbox tag</source>
|
<source>Inbox tag</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">21</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5e2f1a4ea12a1b8606ee3f0548d0ba64bf266077" datatype="html">
|
<trans-unit id="5e2f1a4ea12a1b8606ee3f0548d0ba64bf266077" datatype="html">
|
||||||
<source>Inbox tags are automatically assigned to all consumed documents.</source>
|
<source>Inbox tags are automatically assigned to all consumed documents.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">21</context>
|
<context context-type="linenumber">13</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6672809941092516947" datatype="html">
|
<trans-unit id="6672809941092516947" datatype="html">
|
||||||
@@ -926,32 +926,32 @@
|
|||||||
<context context-type="linenumber">4</context>
|
<context context-type="linenumber">4</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2abff6a01d9b342a5a14b7fb90309a95ce934f8e" datatype="html">
|
<trans-unit id="f7f2e30106223a69bcf0f8e586e6be9dc4e72226" datatype="html">
|
||||||
<source>Showing documents similar to <x id="START_LINK" equiv-text="<a routerLink="/documents/{{more_like}}">{{more_like_doc?.original_file_name}}"/><x id="INTERPOLATION" equiv-text="{{more_like_doc?.original_file_name}}</a>"/><x id="CLOSE_LINK" equiv-text="</a>"/></source>
|
<source>Showing documents similar to <x id="START_LINK" equiv-text="<a routerLink="/documents/{{more_like}}">{{more_like_doc?.original_file_name}}"/><x id="INTERPOLATION" equiv-text="{{more_like_doc?.original_file_name}}</a>"/><x id="CLOSE_LINK" equiv-text="</a>"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
|
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
|
||||||
<context context-type="linenumber">7</context>
|
<context context-type="linenumber">6</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6e0b0a1ea16f18f2fb1586c53d99d2f22e1aee2e" datatype="html">
|
<trans-unit id="6e0b0a1ea16f18f2fb1586c53d99d2f22e1aee2e" datatype="html">
|
||||||
<source>Search query: <x id="START_ITALIC_TEXT" equiv-text="<i>{{query}}"/><x id="INTERPOLATION" equiv-text="{{query}}</i>"/><x id="CLOSE_ITALIC_TEXT" equiv-text="</i>"/></source>
|
<source>Search query: <x id="START_ITALIC_TEXT" equiv-text="<i>{{query}}"/><x id="INTERPOLATION" equiv-text="{{query}}</i>"/><x id="CLOSE_ITALIC_TEXT" equiv-text="</i>"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
|
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
|
||||||
<context context-type="linenumber">11</context>
|
<context context-type="linenumber">9</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="afa760e48c97d64d19c1455d18b7834a2256e23f" datatype="html">
|
<trans-unit id="afa760e48c97d64d19c1455d18b7834a2256e23f" datatype="html">
|
||||||
<source>Did you mean "<x id="START_LINK" equiv-text="<a [routerLink]="" (click)="searchCorrectedQuery()">{{correctedQuery}}"/><x id="INTERPOLATION" equiv-text="{{correctedQuery}}</a>"/><x id="CLOSE_LINK" equiv-text="</a>"/>"?</source>
|
<source>Did you mean "<x id="START_LINK" equiv-text="<a [routerLink]="" (click)="searchCorrectedQuery()">{{correctedQuery}}"/><x id="INTERPOLATION" equiv-text="{{correctedQuery}}</a>"/><x id="CLOSE_LINK" equiv-text="</a>"/>"?</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
|
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
|
||||||
<context context-type="linenumber">13</context>
|
<context context-type="linenumber">11</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="fe6ced3fcc803bba5a2e6c1a067b9ce62542500e" datatype="html">
|
<trans-unit id="fe6ced3fcc803bba5a2e6c1a067b9ce62542500e" datatype="html">
|
||||||
<source>{VAR_PLURAL, plural, =0 {No results} =1 {One result} other {<x id="INTERPOLATION"/> results}}</source>
|
<source>{VAR_PLURAL, plural, =0 {No results} =1 {One result} other {<x id="INTERPOLATION"/> results}}</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
|
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
|
||||||
<context context-type="linenumber">18</context>
|
<context context-type="linenumber">16</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="41147374f427980a9f1a8cd5e3f4b1666e6f2418" datatype="html">
|
<trans-unit id="41147374f427980a9f1a8cd5e3f4b1666e6f2418" datatype="html">
|
||||||
@@ -980,42 +980,42 @@
|
|||||||
<source>Manage</source>
|
<source>Manage</source>
|
||||||
<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">107</context>
|
<context context-type="linenumber">112</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="408cb6073e60c5d966296a3207fc596adca75e01" datatype="html">
|
<trans-unit id="408cb6073e60c5d966296a3207fc596adca75e01" datatype="html">
|
||||||
<source>Admin</source>
|
<source>Admin</source>
|
||||||
<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">149</context>
|
<context context-type="linenumber">154</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="321e4419a943044e674beb55b8039f42a9761ca5" datatype="html">
|
<trans-unit id="321e4419a943044e674beb55b8039f42a9761ca5" datatype="html">
|
||||||
<source>Info</source>
|
<source>Info</source>
|
||||||
<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">155</context>
|
<context context-type="linenumber">160</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="fcfd4675b4c90f08d18d3abede9a9a4dff4cfdc7" datatype="html">
|
<trans-unit id="fcfd4675b4c90f08d18d3abede9a9a4dff4cfdc7" datatype="html">
|
||||||
<source>Documentation</source>
|
<source>Documentation</source>
|
||||||
<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">162</context>
|
<context context-type="linenumber">167</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="355a222236bc01b9a8cd3cb9ecf76891125aed69" datatype="html">
|
<trans-unit id="355a222236bc01b9a8cd3cb9ecf76891125aed69" datatype="html">
|
||||||
<source>GitHub</source>
|
<source>GitHub</source>
|
||||||
<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">170</context>
|
<context context-type="linenumber">175</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="ea3a452c5238897cabc5781308cceb2d37dcf258" datatype="html">
|
<trans-unit id="ea3a452c5238897cabc5781308cceb2d37dcf258" datatype="html">
|
||||||
<source>Suggest an idea</source>
|
<source>Suggest an idea</source>
|
||||||
<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">176</context>
|
<context context-type="linenumber">181</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="af665f8de8fabe306aaf27443957e69bcbbce63c" datatype="html">
|
<trans-unit id="af665f8de8fabe306aaf27443957e69bcbbce63c" datatype="html">
|
||||||
@@ -1036,84 +1036,112 @@
|
|||||||
<source>Close all</source>
|
<source>Close all</source>
|
||||||
<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">101</context>
|
<context context-type="linenumber">106</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5701618810648052610" datatype="html">
|
||||||
|
<source>Title</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">77</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="3100631071441658964" datatype="html">
|
||||||
|
<source>Title & content</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">78</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="7517688192215738656" datatype="html">
|
||||||
|
<source>ASN</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">79</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5195932016807797291" datatype="html">
|
<trans-unit id="5195932016807797291" datatype="html">
|
||||||
<source>Correspondent: <x id="PH" equiv-text="this.correspondents.find(c => c.id == +rule.value)?.name"/></source>
|
<source>Correspondent: <x id="PH" equiv-text="this.correspondents.find(c => c.id == +rule.value)?.name"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||||
<context context-type="linenumber">29</context>
|
<context context-type="linenumber">33</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8170755470576301659" datatype="html">
|
<trans-unit id="8170755470576301659" datatype="html">
|
||||||
<source>Without correspondent</source>
|
<source>Without correspondent</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||||
<context context-type="linenumber">31</context>
|
<context context-type="linenumber">35</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8705701325879965907" datatype="html">
|
<trans-unit id="8705701325879965907" datatype="html">
|
||||||
<source>Type: <x id="PH" equiv-text="this.documentTypes.find(dt => dt.id == +rule.value)?.name"/></source>
|
<source>Type: <x id="PH" equiv-text="this.documentTypes.find(dt => dt.id == +rule.value)?.name"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||||
<context context-type="linenumber">36</context>
|
<context context-type="linenumber">40</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4362173610367509215" datatype="html">
|
<trans-unit id="4362173610367509215" datatype="html">
|
||||||
<source>Without document type</source>
|
<source>Without document type</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||||
<context context-type="linenumber">38</context>
|
<context context-type="linenumber">42</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8180755793012580465" datatype="html">
|
<trans-unit id="8180755793012580465" datatype="html">
|
||||||
<source>Tag: <x id="PH" equiv-text="this.tags.find(t => t.id == +rule.value)?.name"/></source>
|
<source>Tag: <x id="PH" equiv-text="this.tags.find(t => t.id == +rule.value)?.name"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||||
<context context-type="linenumber">42</context>
|
<context context-type="linenumber">46</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6494566478302448576" datatype="html">
|
<trans-unit id="6494566478302448576" datatype="html">
|
||||||
<source>Without any tag</source>
|
<source>Without any tag</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||||
<context context-type="linenumber">46</context>
|
<context context-type="linenumber">50</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6523384805359286307" datatype="html">
|
<trans-unit id="6523384805359286307" datatype="html">
|
||||||
<source>Title: <x id="PH" equiv-text="rule.value"/></source>
|
<source>Title: <x id="PH" equiv-text="rule.value"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||||
<context context-type="linenumber">50</context>
|
<context context-type="linenumber">54</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="1872523635812236432" datatype="html">
|
||||||
|
<source>ASN: <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">57</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</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">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||||
<context context-type="linenumber">12</context>
|
<context context-type="linenumber">20</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4b089ca12c472cf0b46167bb5afe4b527b301bbc" datatype="html">
|
<trans-unit id="4b089ca12c472cf0b46167bb5afe4b527b301bbc" datatype="html">
|
||||||
<source>Filter correspondents</source>
|
<source>Filter correspondents</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||||
<context context-type="linenumber">20</context>
|
<context context-type="linenumber">28</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="0ad509732aaf702b7ea8c771c7809fa84bc85908" datatype="html">
|
<trans-unit id="0ad509732aaf702b7ea8c771c7809fa84bc85908" datatype="html">
|
||||||
<source>Filter document types</source>
|
<source>Filter document types</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||||
<context context-type="linenumber">27</context>
|
<context context-type="linenumber">35</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2d9d55f1b70142ff4597ba32179d16888fd9c6b2" datatype="html">
|
<trans-unit id="2d9d55f1b70142ff4597ba32179d16888fd9c6b2" datatype="html">
|
||||||
<source>Reset filters</source>
|
<source>Reset filters</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||||
<context context-type="linenumber">50</context>
|
<context context-type="linenumber">58</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7593728289020204896" datatype="html">
|
<trans-unit id="7593728289020204896" datatype="html">
|
||||||
@@ -1135,28 +1163,28 @@
|
|||||||
<source>Last 7 days</source>
|
<source>Last 7 days</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">24</context>
|
<context context-type="linenumber">34</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4463380307954693363" datatype="html">
|
<trans-unit id="4463380307954693363" datatype="html">
|
||||||
<source>Last month</source>
|
<source>Last month</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">25</context>
|
<context context-type="linenumber">35</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8697368973702409683" datatype="html">
|
<trans-unit id="8697368973702409683" datatype="html">
|
||||||
<source>Last 3 months</source>
|
<source>Last 3 months</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">26</context>
|
<context context-type="linenumber">36</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3566342898065860218" datatype="html">
|
<trans-unit id="3566342898065860218" datatype="html">
|
||||||
<source>Last year</source>
|
<source>Last year</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">27</context>
|
<context context-type="linenumber">37</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="be2add3a3d9e4e2556b8f9048a15a9c0f00bf1ad" datatype="html">
|
<trans-unit id="be2add3a3d9e4e2556b8f9048a15a9c0f00bf1ad" datatype="html">
|
||||||
@@ -1170,7 +1198,7 @@
|
|||||||
<source>Before</source>
|
<source>Before</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/date-dropdown/date-dropdown.component.html</context>
|
||||||
<context context-type="linenumber">29</context>
|
<context context-type="linenumber">38</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="99ee4faa69cd2ea8e3678c1f557c0ff1f05aae46" datatype="html">
|
<trans-unit id="99ee4faa69cd2ea8e3678c1f557c0ff1f05aae46" datatype="html">
|
||||||
@@ -1184,14 +1212,14 @@
|
|||||||
<source>View</source>
|
<source>View</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
||||||
<context context-type="linenumber">50</context>
|
<context context-type="linenumber">52</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="849b42384616374df49bd8b3711ec159cb10b845" datatype="html">
|
<trans-unit id="849b42384616374df49bd8b3711ec159cb10b845" datatype="html">
|
||||||
<source>Created: <x id="INTERPOLATION" equiv-text="{{document.created | customDate}}"/></source>
|
<source>Created: <x id="INTERPOLATION" equiv-text="{{document.created | customDate}}"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
||||||
<context context-type="linenumber">67</context>
|
<context context-type="linenumber">71</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="cd6f3fd48957e1fea6545c2b2defc7b2435ebfa8" datatype="html">
|
<trans-unit id="cd6f3fd48957e1fea6545c2b2defc7b2435ebfa8" datatype="html">
|
||||||
@@ -1212,14 +1240,7 @@
|
|||||||
<source>Score:</source>
|
<source>Score:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
||||||
<context context-type="linenumber">62</context>
|
<context context-type="linenumber">66</context>
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="2840db547019ce8c76b2cdbe3a1653c5b68b06af" datatype="html">
|
|
||||||
<source>View in browser</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
|
|
||||||
<context context-type="linenumber">40</context>
|
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7985804062689412812" datatype="html">
|
<trans-unit id="7985804062689412812" datatype="html">
|
||||||
@@ -1605,6 +1626,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">14</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">
|
||||||
@@ -1630,35 +1658,77 @@
|
|||||||
<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">90</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6987083569809053351" datatype="html">
|
<trans-unit id="6987083569809053351" datatype="html">
|
||||||
<source>English (GB)</source>
|
<source>English (GB)</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">91</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">84</context>
|
<context context-type="linenumber">92</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">85</context>
|
<context context-type="linenumber">93</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">86</context>
|
<context context-type="linenumber">94</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">95</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2935232983274991580" datatype="html">
|
||||||
|
<source>Italian</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||||
|
<context context-type="linenumber">96</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="8118856427047826368" datatype="html">
|
||||||
|
<source>Romanian</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||||
|
<context context-type="linenumber">97</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="7137419789978325708" datatype="html">
|
||||||
|
<source>Russian</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||||
|
<context context-type="linenumber">98</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="5190825892106392539" datatype="html">
|
||||||
|
<source>Spanish</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||||
|
<context context-type="linenumber">99</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">105</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2119857572761283468" datatype="html">
|
<trans-unit id="2119857572761283468" datatype="html">
|
||||||
@@ -1770,13 +1840,6 @@
|
|||||||
<context context-type="linenumber">39</context>
|
<context context-type="linenumber">39</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7517688192215738656" datatype="html">
|
|
||||||
<source>ASN</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
|
||||||
<context context-type="linenumber">17</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="2691296884221415710" datatype="html">
|
<trans-unit id="2691296884221415710" datatype="html">
|
||||||
<source>Correspondent</source>
|
<source>Correspondent</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
@@ -1784,13 +1847,6 @@
|
|||||||
<context context-type="linenumber">18</context>
|
<context context-type="linenumber">18</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5701618810648052610" datatype="html">
|
|
||||||
<source>Title</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
|
||||||
<context context-type="linenumber">19</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="5066119607229701477" datatype="html">
|
<trans-unit id="5066119607229701477" datatype="html">
|
||||||
<source>Document type</source>
|
<source>Document type</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
@@ -1819,97 +1875,6 @@
|
|||||||
<context context-type="linenumber">23</context>
|
<context context-type="linenumber">23</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2056433880533904076" datatype="html">
|
|
||||||
<source>Light blue</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">6</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="4082253113407591781" datatype="html">
|
|
||||||
<source>Blue</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">7</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="1143414876575720034" datatype="html">
|
|
||||||
<source>Light green</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">8</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="119581980963263815" datatype="html">
|
|
||||||
<source>Green</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">9</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="3250646524116252719" datatype="html">
|
|
||||||
<source>Light red</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">10</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="1628552745302385832" datatype="html">
|
|
||||||
<source>Red </source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">11</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="5479028842846122610" datatype="html">
|
|
||||||
<source>Light orange</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">12</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="8598918991528773310" datatype="html">
|
|
||||||
<source>Orange</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">13</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="1789283185177957430" datatype="html">
|
|
||||||
<source>Light violet</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">14</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="2682868487071320453" datatype="html">
|
|
||||||
<source>Violet</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">15</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="1449010446077321264" datatype="html">
|
|
||||||
<source>Brown</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">16</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="30300572504753589" datatype="html">
|
|
||||||
<source>Black</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">17</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="461048771215121187" datatype="html">
|
|
||||||
<source>Light grey</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/data/paperless-tag.ts</context>
|
|
||||||
<context context-type="linenumber">18</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="4561076822163447092" datatype="html">
|
<trans-unit id="4561076822163447092" datatype="html">
|
||||||
<source>Create new item</source>
|
<source>Create new item</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
|
|||||||
26
src-ui/package-lock.json
generated
26
src-ui/package-lock.json
generated
@@ -2035,6 +2035,11 @@
|
|||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@ctrl/tinycolor": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ=="
|
||||||
|
},
|
||||||
"@istanbuljs/schema": {
|
"@istanbuljs/schema": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
|
||||||
@@ -2055,9 +2060,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"
|
||||||
}
|
}
|
||||||
@@ -7895,6 +7900,11 @@
|
|||||||
"object-visit": "^1.0.0"
|
"object-visit": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"material-colors": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||||
|
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
|
||||||
|
},
|
||||||
"md5.js": {
|
"md5.js": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||||
@@ -8333,6 +8343,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ngx-color": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-n04tcMnCpOgmI24egST94YwHmnSoAxK8O1T2t3nGrTwWbvw5XBRJvImNFnoNrriBXzc4Gx4hFehH5MU8CZxp1w==",
|
||||||
|
"requires": {
|
||||||
|
"@ctrl/tinycolor": "^3.1.6",
|
||||||
|
"material-colors": "^1.2.6",
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ngx-cookie-service": {
|
"ngx-cookie-service": {
|
||||||
"version": "10.1.1",
|
"version": "10.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-10.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-10.1.1.tgz",
|
||||||
|
|||||||
@@ -20,12 +20,13 @@
|
|||||||
"@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",
|
"file-saver": "^2.0.5",
|
||||||
"ng-bootstrap": "^1.6.3",
|
"ng-bootstrap": "^1.6.3",
|
||||||
"ng2-pdf-viewer": "^6.3.2",
|
"ng2-pdf-viewer": "^6.3.2",
|
||||||
|
"ngx-color": "^6.2.0",
|
||||||
"ngx-cookie-service": "^10.1.1",
|
"ngx-cookie-service": "^10.1.1",
|
||||||
"ngx-file-drop": "^10.0.0",
|
"ngx-file-drop": "^10.0.0",
|
||||||
"ngx-infinite-scroll": "^9.1.0",
|
"ngx-infinite-scroll": "^9.1.0",
|
||||||
|
|||||||
@@ -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';
|
||||||
@@ -60,16 +59,33 @@ 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 { ApiVersionInterceptor } from './interceptors/api-version.interceptor';
|
||||||
|
import { ColorSliderModule } from 'ngx-color/slider';
|
||||||
|
import { ColorComponent } from './components/common/input/color/color.component';
|
||||||
|
|
||||||
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';
|
||||||
|
import localeIt from '@angular/common/locales/it';
|
||||||
import localeEnGb from '@angular/common/locales/en-GB';
|
import localeEnGb from '@angular/common/locales/en-GB';
|
||||||
|
import localeRo from '@angular/common/locales/ro';
|
||||||
|
import localeRu from '@angular/common/locales/ru';
|
||||||
|
import localeEs from '@angular/common/locales/es';
|
||||||
|
|
||||||
|
|
||||||
registerLocaleData(localeFr)
|
registerLocaleData(localeFr)
|
||||||
registerLocaleData(localeNl)
|
registerLocaleData(localeNl)
|
||||||
registerLocaleData(localeDe)
|
registerLocaleData(localeDe)
|
||||||
|
registerLocaleData(localePt, "pt-BR")
|
||||||
|
registerLocaleData(localeIt)
|
||||||
registerLocaleData(localeEnGb)
|
registerLocaleData(localeEnGb)
|
||||||
|
registerLocaleData(localeRo)
|
||||||
|
registerLocaleData(localeRu)
|
||||||
|
registerLocaleData(localeEs)
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -104,7 +120,6 @@ registerLocaleData(localeEnGb)
|
|||||||
SelectComponent,
|
SelectComponent,
|
||||||
CheckComponent,
|
CheckComponent,
|
||||||
SaveViewConfigDialogComponent,
|
SaveViewConfigDialogComponent,
|
||||||
DateTimeComponent,
|
|
||||||
TagsComponent,
|
TagsComponent,
|
||||||
SortableDirective,
|
SortableDirective,
|
||||||
SavedViewWidgetComponent,
|
SavedViewWidgetComponent,
|
||||||
@@ -120,7 +135,9 @@ registerLocaleData(localeEnGb)
|
|||||||
SelectDialogComponent,
|
SelectDialogComponent,
|
||||||
NumberComponent,
|
NumberComponent,
|
||||||
SafePipe,
|
SafePipe,
|
||||||
CustomDatePipe
|
CustomDatePipe,
|
||||||
|
DateComponent,
|
||||||
|
ColorComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@@ -132,7 +149,8 @@ registerLocaleData(localeEnGb)
|
|||||||
NgxFileDropModule,
|
NgxFileDropModule,
|
||||||
InfiniteScrollModule,
|
InfiniteScrollModule,
|
||||||
PdfViewerModule,
|
PdfViewerModule,
|
||||||
NgSelectModule
|
NgSelectModule,
|
||||||
|
ColorSliderModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
DatePipe,
|
DatePipe,
|
||||||
@@ -140,9 +158,15 @@ registerLocaleData(localeEnGb)
|
|||||||
provide: HTTP_INTERCEPTORS,
|
provide: HTTP_INTERCEPTORS,
|
||||||
useClass: CsrfInterceptor,
|
useClass: CsrfInterceptor,
|
||||||
multi: true
|
multi: true
|
||||||
|
},{
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: ApiVersionInterceptor,
|
||||||
|
multi: true
|
||||||
},
|
},
|
||||||
FilterPipe,
|
FilterPipe,
|
||||||
DocumentTitlePipe
|
DocumentTitlePipe,
|
||||||
|
{provide: NgbDateAdapter, useClass: ISODateTimeAdapter},
|
||||||
|
{provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter}
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<div ngbDropdownMenu class="dropdown-menu-right shadow mr-2" aria-labelledby="userDropdown">
|
<div ngbDropdownMenu class="dropdown-menu-right shadow mr-2" aria-labelledby="userDropdown">
|
||||||
<div *ngIf="displayName" class="d-sm-none">
|
<div *ngIf="displayName" class="d-sm-none">
|
||||||
<p class="small mb-0 px-3" i18n>Logged in as {{displayName}}</p>
|
<p class="small mb-0 px-3 text-muted" i18n>Logged in as {{displayName}}</p>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
</div>
|
</div>
|
||||||
<a ngbDropdownItem class="nav-link" routerLink="settings" (click)="closeMenu()">
|
<a ngbDropdownItem class="nav-link" routerLink="settings" (click)="closeMenu()">
|
||||||
@@ -92,9 +92,14 @@
|
|||||||
<svg class="sidebaricon" fill="currentColor">
|
<svg class="sidebaricon" fill="currentColor">
|
||||||
<use xlink:href="assets/bootstrap-icons.svg#file-text"/>
|
<use xlink:href="assets/bootstrap-icons.svg#file-text"/>
|
||||||
</svg> {{d.title | documentTitle}}
|
</svg> {{d.title | documentTitle}}
|
||||||
|
<span class="close bg-light" (click)="closeDocument(d); $event.preventDefault()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-x" viewBox="0 0 16 16">
|
||||||
|
<use xlink:href="assets/bootstrap-icons.svg#x"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item w-100" *ngIf="openDocuments.length > 1">
|
<li class="nav-item w-100" *ngIf="openDocuments.length >= 1">
|
||||||
<a class="nav-link text-truncate" [routerLink]="" (click)="closeAll()">
|
<a class="nav-link text-truncate" [routerLink]="" (click)="closeAll()">
|
||||||
<svg class="sidebaricon" fill="currentColor">
|
<svg class="sidebaricon" fill="currentColor">
|
||||||
<use xlink:href="assets/bootstrap-icons.svg#x"/>
|
<use xlink:href="assets/bootstrap-icons.svg#x"/>
|
||||||
|
|||||||
@@ -62,7 +62,34 @@
|
|||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item .nav-link-additional {
|
.nav-item {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover .close {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 1;
|
||||||
|
top: 0;
|
||||||
|
padding: .25rem .3rem 0;
|
||||||
|
right: .4rem;
|
||||||
|
width: 1.8rem;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover svg {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link-additional {
|
||||||
margin-top: 0.2rem;
|
margin-top: 0.2rem;
|
||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
@@ -71,6 +98,8 @@
|
|||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Navbar
|
* Navbar
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router, Params } from '@angular/router';
|
||||||
import { from, Observable, Subscription } from 'rxjs';
|
import { from, Observable, Subscription, BehaviorSubject } from 'rxjs';
|
||||||
import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
|
import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
|
||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
||||||
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
|
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
|
||||||
@@ -16,7 +16,7 @@ import { Meta } from '@angular/platform-browser';
|
|||||||
templateUrl: './app-frame.component.html',
|
templateUrl: './app-frame.component.html',
|
||||||
styleUrls: ['./app-frame.component.scss']
|
styleUrls: ['./app-frame.component.scss']
|
||||||
})
|
})
|
||||||
export class AppFrameComponent implements OnInit, OnDestroy {
|
export class AppFrameComponent implements OnInit {
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
public router: Router,
|
public router: Router,
|
||||||
@@ -39,9 +39,9 @@ export class AppFrameComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
searchField = new FormControl('')
|
searchField = new FormControl('')
|
||||||
|
|
||||||
openDocuments: PaperlessDocument[] = []
|
get openDocuments(): PaperlessDocument[] {
|
||||||
|
return this.openDocumentsService.getOpenDocuments()
|
||||||
openDocumentsSubscription: Subscription
|
}
|
||||||
|
|
||||||
searchAutoComplete = (text$: Observable<string>) =>
|
searchAutoComplete = (text$: Observable<string>) =>
|
||||||
text$.pipe(
|
text$.pipe(
|
||||||
@@ -77,12 +77,24 @@ export class AppFrameComponent implements OnInit, OnDestroy {
|
|||||||
this.router.navigate(['search'], {queryParams: {query: this.searchField.value}})
|
this.router.navigate(['search'], {queryParams: {query: this.searchField.value}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closeDocument(d: PaperlessDocument) {
|
||||||
|
this.closeMenu()
|
||||||
|
this.openDocumentsService.closeDocument(d)
|
||||||
|
|
||||||
|
let route = this.activatedRoute.snapshot
|
||||||
|
while (route.firstChild) {
|
||||||
|
route = route.firstChild
|
||||||
|
}
|
||||||
|
if (route.component == DocumentDetailComponent && route.params['id'] == d.id) {
|
||||||
|
this.router.navigate([""])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
closeAll() {
|
closeAll() {
|
||||||
this.closeMenu()
|
this.closeMenu()
|
||||||
this.openDocumentsService.closeAll()
|
this.openDocumentsService.closeAll()
|
||||||
|
|
||||||
// TODO: is there a better way to do this?
|
let route = this.activatedRoute.snapshot
|
||||||
let route = this.activatedRoute
|
|
||||||
while (route.firstChild) {
|
while (route.firstChild) {
|
||||||
route = route.firstChild
|
route = route.firstChild
|
||||||
}
|
}
|
||||||
@@ -92,13 +104,6 @@ export class AppFrameComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.openDocuments = this.openDocumentsService.getOpenDocuments()
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
if (this.openDocumentsSubscription) {
|
|
||||||
this.openDocumentsSubscription.unsubscribe()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get displayName() {
|
get displayName() {
|
||||||
|
|||||||
@@ -20,9 +20,18 @@
|
|||||||
</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" (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">
|
||||||
|
|
||||||
<div class="mb-2 d-flex flex-row w-100 justify-content-between small">
|
<div class="mb-2 d-flex flex-row w-100 justify-content-between small">
|
||||||
@@ -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" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()"
|
||||||
</div>
|
[(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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<div class="form-group">
|
||||||
|
<label [for]="inputId">{{title}}</label>
|
||||||
|
|
||||||
|
<div class="input-group" [class.is-invalid]="error">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text" [style.background-color]="value"> </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #popContent>
|
||||||
|
<div style="min-width: 200px;" class="pb-3">
|
||||||
|
<color-slider [color]="value" (onChangeComplete)="colorChanged($event.color.hex)"></color-slider>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<input class="form-control" [class.is-invalid]="error" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" [autoClose]="'outside'" [ngbPopover]="popContent" placement="bottom" popoverClass="shadow">
|
||||||
|
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-outline-secondary" type="button" (click)="randomize()">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dice-5" viewBox="0 0 16 16">
|
||||||
|
<path d="M13 1a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h10zM3 0a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V3a3 3 0 0 0-3-3H3z"/>
|
||||||
|
<path d="M5.5 4a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm8 0a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0 8a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm-8 0a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm4-4a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<small *ngIf="hint" class="form-text text-muted">{{hint}}</small>
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
{{error}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -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 { ColorComponent } from './color.component';
|
||||||
|
|
||||||
describe('DateTimeComponent', () => {
|
describe('ColorComponent', () => {
|
||||||
let component: DateTimeComponent;
|
let component: ColorComponent;
|
||||||
let fixture: ComponentFixture<DateTimeComponent>;
|
let fixture: ComponentFixture<ColorComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ DateTimeComponent ]
|
declarations: [ ColorComponent ]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(DateTimeComponent);
|
fixture = TestBed.createComponent(ColorComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { Component, forwardRef } from '@angular/core';
|
||||||
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import { randomColor } from 'src/app/utils/color';
|
||||||
|
import { AbstractInputComponent } from '../abstract-input';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
providers: [{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => ColorComponent),
|
||||||
|
multi: true
|
||||||
|
}],
|
||||||
|
selector: 'app-input-color',
|
||||||
|
templateUrl: './color.component.html',
|
||||||
|
styleUrls: ['./color.component.scss']
|
||||||
|
})
|
||||||
|
export class ColorComponent extends AbstractInputComponent<string> {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
randomize() {
|
||||||
|
this.colorChanged(randomColor())
|
||||||
|
}
|
||||||
|
|
||||||
|
colorChanged(value) {
|
||||||
|
this.value = value
|
||||||
|
this.onChange(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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> -->
|
|
||||||
@@ -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"))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<div class="form-group">
|
||||||
|
<label [for]="inputId">{{title}}</label>
|
||||||
|
<div class="input-group" [class.is-invalid]="error">
|
||||||
|
<input class="form-control" [class.is-invalid]="error" [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>
|
||||||
|
<div class="invalid-feedback" i18n>Invalid date.</div>
|
||||||
|
<small *ngIf="hint" class="form-text text-muted">{{hint}}</small>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { DateComponent } from './date.component';
|
||||||
|
|
||||||
|
describe('DateComponent', () => {
|
||||||
|
let component: DateComponent;
|
||||||
|
let fixture: ComponentFixture<DateComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ DateComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DateComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
<span *ngIf="!clickable" class="badge" [style.background]="getColour().value" [style.color]="getColour().textColor">{{tag.name}}</span>
|
<span *ngIf="!clickable" class="badge" [style.background]="tag.color" [style.color]="tag.text_color">{{tag.name}}</span>
|
||||||
<a [routerLink]="" [title]="linkTitle" *ngIf="clickable" class="badge" [style.background]="getColour().value" [style.color]="getColour().textColor">{{tag.name}}</a>
|
<a [routerLink]="" [title]="linkTitle" *ngIf="clickable" class="badge" [style.background]="tag.color" [style.color]="tag.text_color">{{tag.name}}</a>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { TAG_COLOURS, PaperlessTag } from 'src/app/data/paperless-tag';
|
import { PaperlessTag } from 'src/app/data/paperless-tag';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tag',
|
selector: 'app-tag',
|
||||||
@@ -22,8 +22,4 @@ export class TagComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
getColour() {
|
|
||||||
return TAG_COLOURS.find(c => c.id == this.tag.colour)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ form {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .progress {
|
.progress {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|||||||
@@ -58,7 +58,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"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="card mb-3 shadow-sm" [class.card-selected]="selected" [class.document-card]="selectable">
|
<div class="card mb-3 shadow-sm" [class.card-selected]="selected" [class.document-card]="selectable" [class.popover-hidden]="popoverHidden" (mouseleave)="mouseLeaveCard()">
|
||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
<div class="col-md-2 d-none d-lg-block doc-img-background rounded-left" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
|
<div class="col-md-2 d-none d-lg-block doc-img-background rounded-left" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
|
||||||
<img [src]="getThumbUrl()" class="card-img doc-img border-right rounded-left">
|
<img [src]="getThumbUrl()" class="card-img doc-img border-right rounded-left" [class.inverted]="getIsThumbInverted()">
|
||||||
|
|
||||||
<div style="top: 0; left: 0" class="position-absolute border-right border-bottom bg-light p-1" [class.document-card-check]="!selected">
|
<div style="top: 0; left: 0" class="position-absolute border-right border-bottom bg-light p-1" [class.document-card-check]="!selected">
|
||||||
<div class="custom-control custom-checkbox">
|
<div class="custom-control custom-checkbox">
|
||||||
@@ -23,7 +23,6 @@
|
|||||||
{{document.title | documentTitle}}
|
{{document.title | documentTitle}}
|
||||||
<app-tag [tag]="t" linkTitle="Filter by tag" i18n-linkTitle *ngFor="let t of document.tags$ | async" class="ml-1" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="clickTag.observers.length"></app-tag>
|
<app-tag [tag]="t" linkTitle="Filter by tag" i18n-linkTitle *ngFor="let t of document.tags$ | async" class="ml-1" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="clickTag.observers.length"></app-tag>
|
||||||
</h5>
|
</h5>
|
||||||
<h5 class="card-title" *ngIf="document.archive_serial_number">#{{document.archive_serial_number}}</h5>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="card-text">
|
<p class="card-text">
|
||||||
<app-result-highlight *ngIf="getDetailsAsHighlight()" class="result-content" [highlights]="getDetailsAsHighlight()"></app-result-highlight>
|
<app-result-highlight *ngIf="getDetailsAsHighlight()" class="result-content" [highlights]="getDetailsAsHighlight()"></app-result-highlight>
|
||||||
@@ -43,28 +42,51 @@
|
|||||||
<path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
|
<path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
|
||||||
</svg> <span class="d-block d-md-inline" i18n>Edit</span>
|
</svg> <span class="d-block d-md-inline" i18n>Edit</span>
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-sm btn-outline-secondary" [href]="getPreviewUrl()">
|
<a class="btn btn-sm btn-outline-secondary" [href]="previewUrl"
|
||||||
|
[ngbPopover]="previewContent" [popoverTitle]="document.title | documentTitle"
|
||||||
|
autoClose="true" popoverClass="shadow" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
|
||||||
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
|
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
|
||||||
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
|
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
|
||||||
</svg> <span class="d-block d-md-inline" i18n>View</span>
|
</svg> <span class="d-block d-md-inline" i18n>View</span>
|
||||||
</a>
|
</a>
|
||||||
|
<ng-template #previewContent>
|
||||||
|
<object [data]="previewUrl | safe" class="preview" width="100%"></object>
|
||||||
|
</ng-template>
|
||||||
<a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">
|
<a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">
|
||||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-download" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-download" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
<path fill-rule="evenodd" d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
||||||
<path fill-rule="evenodd" d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
|
<path fill-rule="evenodd" d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
|
||||||
</svg> <span class="d-block d-md-inline" i18n>Download</span>
|
</svg> <span class="d-block d-md-inline" i18n>Download</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="searchScore" class="d-flex align-items-center ml-md-auto mt-2 mt-md-0">
|
<div class="list-group list-group-horizontal border-0 card-info ml-md-auto mt-2 mt-md-0">
|
||||||
|
<div *ngIf="searchScore" class="list-group-item bg-light text-dark p-1 mr-5 border-0 d-flex search-score">
|
||||||
<small class="text-muted" i18n>Score:</small>
|
<small class="text-muted" i18n>Score:</small>
|
||||||
|
<ngb-progressbar [type]="searchScoreClass" [value]="searchScore" class="search-score-bar mx-2 mt-1" [max]="1"></ngb-progressbar>
|
||||||
<ngb-progressbar [type]="searchScoreClass" [value]="searchScore" class="search-score-bar mx-2" [max]="1"></ngb-progressbar>
|
</div>
|
||||||
|
<button *ngIf="document.document_type" type="button" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 mr-2" title="Filter by document type"
|
||||||
|
(click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
|
||||||
|
<svg class="metadata-icon mr-2 text-muted bi bi-file-earmark" viewBox="0 0 16 16" fill="currentColor">
|
||||||
|
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
|
||||||
|
</svg>
|
||||||
|
<small>{{(document.document_type$ | async)?.name}}</small>
|
||||||
|
</button>
|
||||||
|
<div *ngIf="document.archive_serial_number" class="list-group-item mr-2 bg-light text-dark p-1 border-0">
|
||||||
|
<svg class="metadata-icon mr-2 text-muted bi bi-upc-scan" viewBox="0 0 16 16" fill="currentColor">
|
||||||
|
<path d="M1.5 1a.5.5 0 0 0-.5.5v3a.5.5 0 0 1-1 0v-3A1.5 1.5 0 0 1 1.5 0h3a.5.5 0 0 1 0 1h-3zM11 .5a.5.5 0 0 1 .5-.5h3A1.5 1.5 0 0 1 16 1.5v3a.5.5 0 0 1-1 0v-3a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 1-.5-.5zM.5 11a.5.5 0 0 1 .5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 1 0 1h-3A1.5 1.5 0 0 1 0 14.5v-3a.5.5 0 0 1 .5-.5zm15 0a.5.5 0 0 1 .5.5v3a1.5 1.5 0 0 1-1.5 1.5h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 1 .5-.5zM3 4.5a.5.5 0 0 1 1 0v7a.5.5 0 0 1-1 0v-7zm2 0a.5.5 0 0 1 1 0v7a.5.5 0 0 1-1 0v-7zm2 0a.5.5 0 0 1 1 0v7a.5.5 0 0 1-1 0v-7zm2 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-7zm3 0a.5.5 0 0 1 1 0v7a.5.5 0 0 1-1 0v-7z"/>
|
||||||
|
</svg>
|
||||||
|
<small>#{{document.archive_serial_number}}</small>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item bg-light text-dark p-1 border-0" ngbTooltip="Added: {{document.added | customDate:'shortDate'}} Created: {{document.created | customDate:'shortDate'}}">
|
||||||
|
<svg class="metadata-icon mr-2 text-muted bi bi-calendar-event" viewBox="0 0 16 16" fill="currentColor">
|
||||||
|
<path d="M11 6.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1z"/>
|
||||||
|
<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>
|
||||||
|
<small>{{document.created | customDate:'mediumDate'}}</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<small class="text-muted" [class.ml-auto]="!searchScore" i18n>Created: {{document.created | customDate}}</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,3 +37,25 @@
|
|||||||
.doc-img-background-selected {
|
.doc-img-background-selected {
|
||||||
background-color: $primaryFaded;
|
background-color: $primaryFaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-info {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
button {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-icon {
|
||||||
|
width: 0.8rem;
|
||||||
|
height: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-score {
|
||||||
|
padding-top: 0.35rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service';
|
import { DocumentService } from 'src/app/services/rest/document.service';
|
||||||
|
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
|
||||||
|
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-document-card-large',
|
selector: 'app-document-card-large',
|
||||||
templateUrl: './document-card-large.component.html',
|
templateUrl: './document-card-large.component.html',
|
||||||
styleUrls: ['./document-card-large.component.scss']
|
styleUrls: ['./document-card-large.component.scss', '../popover-preview/popover-preview.scss']
|
||||||
})
|
})
|
||||||
export class DocumentCardLargeComponent implements OnInit {
|
export class DocumentCardLargeComponent implements OnInit {
|
||||||
|
|
||||||
constructor(private documentService: DocumentService, private sanitizer: DomSanitizer) { }
|
constructor(private documentService: DocumentService, private sanitizer: DomSanitizer, private settingsService: SettingsService) { }
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
selected = false
|
selected = false
|
||||||
@@ -37,9 +39,17 @@ export class DocumentCardLargeComponent implements OnInit {
|
|||||||
@Output()
|
@Output()
|
||||||
clickCorrespondent = new EventEmitter<number>()
|
clickCorrespondent = new EventEmitter<number>()
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
clickDocumentType = new EventEmitter<number>()
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
searchScore: number
|
searchScore: number
|
||||||
|
|
||||||
|
@ViewChild('popover') popover: NgbPopover
|
||||||
|
|
||||||
|
mouseOnPreview = false
|
||||||
|
popoverHidden = true
|
||||||
|
|
||||||
get searchScoreClass() {
|
get searchScoreClass() {
|
||||||
if (this.searchScore > 0.7) {
|
if (this.searchScore > 0.7) {
|
||||||
return "success"
|
return "success"
|
||||||
@@ -53,6 +63,10 @@ export class DocumentCardLargeComponent implements OnInit {
|
|||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIsThumbInverted() {
|
||||||
|
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
|
||||||
|
}
|
||||||
|
|
||||||
getDetailsAsString() {
|
getDetailsAsString() {
|
||||||
if (typeof this.details === 'string') {
|
if (typeof this.details === 'string') {
|
||||||
return this.details.substring(0, 500)
|
return this.details.substring(0, 500)
|
||||||
@@ -74,7 +88,32 @@ export class DocumentCardLargeComponent implements OnInit {
|
|||||||
return this.documentService.getDownloadUrl(this.document.id)
|
return this.documentService.getDownloadUrl(this.document.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
getPreviewUrl() {
|
get previewUrl() {
|
||||||
return this.documentService.getPreviewUrl(this.document.id)
|
return this.documentService.getPreviewUrl(this.document.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mouseEnterPreview() {
|
||||||
|
this.mouseOnPreview = true
|
||||||
|
if (!this.popover.isOpen()) {
|
||||||
|
// we're going to open but hide to pre-load content during hover delay
|
||||||
|
this.popover.open()
|
||||||
|
this.popoverHidden = true
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.mouseOnPreview) {
|
||||||
|
// show popover
|
||||||
|
this.popoverHidden = false
|
||||||
|
} else {
|
||||||
|
this.popover.close()
|
||||||
|
}
|
||||||
|
}, 600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeavePreview() {
|
||||||
|
this.mouseOnPreview = false
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeaveCard() {
|
||||||
|
this.popover.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="col p-2 h-100">
|
<div class="col p-2 h-100">
|
||||||
<div class="card h-100 shadow-sm document-card" [class.card-selected]="selected">
|
<div class="card h-100 shadow-sm document-card" [class.card-selected]="selected" [class.popover-hidden]="popoverHidden" (mouseleave)="mouseLeaveCard()">
|
||||||
<div class="border-bottom doc-img-container" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
|
<div class="border-bottom doc-img-container" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
|
||||||
<img class="card-img doc-img rounded-top" [src]="getThumbUrl()">
|
<img class="card-img doc-img rounded-top" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
|
||||||
|
|
||||||
<div class="border-right border-bottom bg-light p-1 rounded document-card-check">
|
<div class="border-right border-bottom bg-light p-1 rounded document-card-check">
|
||||||
<div class="custom-control custom-checkbox">
|
<div class="custom-control custom-checkbox">
|
||||||
@@ -25,24 +25,52 @@
|
|||||||
<ng-container *ngIf="document.correspondent">
|
<ng-container *ngIf="document.correspondent">
|
||||||
<a [routerLink]="" title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="font-weight-bold">{{(document.correspondent$ | async)?.name}}</a>:
|
<a [routerLink]="" title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="font-weight-bold">{{(document.correspondent$ | async)?.name}}</a>:
|
||||||
</ng-container>
|
</ng-container>
|
||||||
{{document.title | documentTitle}} <span *ngIf="document.archive_serial_number">(#{{document.archive_serial_number}})</span>
|
{{document.title | documentTitle}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer pt-0 pb-2 px-2">
|
||||||
|
<div class="list-group list-group-flush border-0 pt-1 pb-2 card-info">
|
||||||
<div class="d-flex justify-content-between align-items-center mx-n2">
|
<button *ngIf="document.document_type" type="button" class="list-group-item list-group-item-action bg-transparent pl-0 p-1 border-0" title="Filter by document type"
|
||||||
<div class="btn-group">
|
(click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
|
||||||
|
<svg class="metadata-icon mr-2 text-muted bi bi-file-earmark" viewBox="0 0 16 16" fill="currentColor">
|
||||||
|
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
|
||||||
|
</svg>
|
||||||
|
<small>{{(document.document_type$ | async)?.name}}</small>
|
||||||
|
</button>
|
||||||
|
<div class="list-group-item bg-transparent p-0 border-0 d-flex flex-wrap-reverse justify-content-between">
|
||||||
|
<div class="pl-0 p-1" placement="top" ngbTooltip="Added: {{document.added | customDate:'mediumDate'}} Created: {{document.created | customDate:'mediumDate'}}">
|
||||||
|
<svg class="metadata-icon mr-2 text-muted bi bi-calendar-event" viewBox="0 0 16 16" fill="currentColor">
|
||||||
|
<path d="M11 6.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-1z"/>
|
||||||
|
<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>
|
||||||
|
<small>{{document.created | customDate:'mediumDate'}}</small>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="document.archive_serial_number" class="pl-0 p-1">
|
||||||
|
<svg class="metadata-icon mr-2 text-muted bi bi-upc-scan" viewBox="0 0 16 16" fill="currentColor">
|
||||||
|
<path d="M1.5 1a.5.5 0 0 0-.5.5v3a.5.5 0 0 1-1 0v-3A1.5 1.5 0 0 1 1.5 0h3a.5.5 0 0 1 0 1h-3zM11 .5a.5.5 0 0 1 .5-.5h3A1.5 1.5 0 0 1 16 1.5v3a.5.5 0 0 1-1 0v-3a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 1-.5-.5zM.5 11a.5.5 0 0 1 .5.5v3a.5.5 0 0 0 .5.5h3a.5.5 0 0 1 0 1h-3A1.5 1.5 0 0 1 0 14.5v-3a.5.5 0 0 1 .5-.5zm15 0a.5.5 0 0 1 .5.5v3a1.5 1.5 0 0 1-1.5 1.5h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 0 .5-.5v-3a.5.5 0 0 1 .5-.5zM3 4.5a.5.5 0 0 1 1 0v7a.5.5 0 0 1-1 0v-7zm2 0a.5.5 0 0 1 1 0v7a.5.5 0 0 1-1 0v-7zm2 0a.5.5 0 0 1 1 0v7a.5.5 0 0 1-1 0v-7zm2 0a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5v-7zm3 0a.5.5 0 0 1 1 0v7a.5.5 0 0 1-1 0v-7z"/>
|
||||||
|
</svg>
|
||||||
|
<small>#{{document.archive_serial_number}}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div class="btn-group w-100">
|
||||||
<a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary" title="Edit" i18n-title>
|
<a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary" title="Edit" i18n-title>
|
||||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
|
<path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
<a [href]="getPreviewUrl()" class="btn btn-sm btn-outline-secondary" title="View in browser" i18n-title>
|
<a [href]="previewUrl" target="_blank" class="btn btn-sm btn-outline-secondary"
|
||||||
|
[ngbPopover]="previewContent" [popoverTitle]="document.title | documentTitle"
|
||||||
|
autoClose="true" popoverClass="shadow" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
|
||||||
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
|
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
|
||||||
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
|
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
<ng-template #previewContent>
|
||||||
|
<object [data]="previewUrl | safe" class="preview" width="100%"></object>
|
||||||
|
</ng-template>
|
||||||
<a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" (click)="$event.stopPropagation()" i18n-title>
|
<a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" (click)="$event.stopPropagation()" i18n-title>
|
||||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-download" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-download" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
<path fill-rule="evenodd" d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
||||||
@@ -50,9 +78,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<small class="text-muted pl-1">{{document.created | customDate:'shortDate'}}</small>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
@import "/src/theme";
|
@import "/src/theme";
|
||||||
|
|
||||||
|
.card-text {
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
.doc-img {
|
.doc-img {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
object-position: top left;
|
object-position: top left;
|
||||||
height: 200px;
|
height: 175px;
|
||||||
mix-blend-mode: multiply;
|
mix-blend-mode: multiply;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,3 +38,31 @@
|
|||||||
.doc-img-background-selected {
|
.doc-img-background-selected {
|
||||||
background-color: $primaryFaded;
|
background-color: $primaryFaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-info {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
button {
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-color: transparent !important;
|
||||||
|
color: $primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.metadata-icon {
|
||||||
|
width: 0.8rem;
|
||||||
|
height: 0.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer .btn {
|
||||||
|
padding-top: .10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .tooltip-inner {
|
||||||
|
text-align: left !important;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
import { PaperlessDocument } from 'src/app/data/paperless-document';
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service';
|
import { DocumentService } from 'src/app/services/rest/document.service';
|
||||||
|
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
|
||||||
|
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-document-card-small',
|
selector: 'app-document-card-small',
|
||||||
templateUrl: './document-card-small.component.html',
|
templateUrl: './document-card-small.component.html',
|
||||||
styleUrls: ['./document-card-small.component.scss']
|
styleUrls: ['./document-card-small.component.scss', '../popover-preview/popover-preview.scss']
|
||||||
})
|
})
|
||||||
export class DocumentCardSmallComponent implements OnInit {
|
export class DocumentCardSmallComponent implements OnInit {
|
||||||
|
|
||||||
constructor(private documentService: DocumentService) { }
|
constructor(private documentService: DocumentService, private settingsService: SettingsService) { }
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
selected = false
|
selected = false
|
||||||
@@ -27,11 +29,23 @@ export class DocumentCardSmallComponent implements OnInit {
|
|||||||
@Output()
|
@Output()
|
||||||
clickCorrespondent = new EventEmitter<number>()
|
clickCorrespondent = new EventEmitter<number>()
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
clickDocumentType = new EventEmitter<number>()
|
||||||
|
|
||||||
moreTags: number = null
|
moreTags: number = null
|
||||||
|
|
||||||
|
@ViewChild('popover') popover: NgbPopover
|
||||||
|
|
||||||
|
mouseOnPreview = false
|
||||||
|
popoverHidden = true
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getIsThumbInverted() {
|
||||||
|
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
|
||||||
|
}
|
||||||
|
|
||||||
getThumbUrl() {
|
getThumbUrl() {
|
||||||
return this.documentService.getThumbUrl(this.document.id)
|
return this.documentService.getThumbUrl(this.document.id)
|
||||||
}
|
}
|
||||||
@@ -40,7 +54,7 @@ export class DocumentCardSmallComponent implements OnInit {
|
|||||||
return this.documentService.getDownloadUrl(this.document.id)
|
return this.documentService.getDownloadUrl(this.document.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
getPreviewUrl() {
|
get previewUrl() {
|
||||||
return this.documentService.getPreviewUrl(this.document.id)
|
return this.documentService.getPreviewUrl(this.document.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,4 +71,28 @@ export class DocumentCardSmallComponent implements OnInit {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mouseEnterPreview() {
|
||||||
|
this.mouseOnPreview = true
|
||||||
|
if (!this.popover.isOpen()) {
|
||||||
|
// we're going to open but hide to pre-load content during hover delay
|
||||||
|
this.popover.open()
|
||||||
|
this.popoverHidden = true
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.mouseOnPreview) {
|
||||||
|
// show popover
|
||||||
|
this.popoverHidden = false
|
||||||
|
} else {
|
||||||
|
this.popover.close()
|
||||||
|
}
|
||||||
|
}, 600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeavePreview() {
|
||||||
|
this.mouseOnPreview = false
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseLeaveCard() {
|
||||||
|
this.popover.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="displayMode == 'largeCards'">
|
<div *ngIf="displayMode == 'largeCards'">
|
||||||
<app-document-card-large [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" *ngFor="let d of list.documents; trackBy: trackByDocumentId" [document]="d" [details]="d.content" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)">
|
<app-document-card-large [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" *ngFor="let d of list.documents; trackBy: trackByDocumentId" [document]="d" [details]="d.content" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickDocumentType)="clickDocumentType($event)">
|
||||||
</app-document-card-large>
|
</app-document-card-large>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -170,5 +170,5 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div class="m-n2 row row-cols-paperless-cards" *ngIf="displayMode == 'smallCards'">
|
<div class="m-n2 row row-cols-paperless-cards" *ngIf="displayMode == 'smallCards'">
|
||||||
<app-document-card-small [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" [document]="d" *ngFor="let d of list.documents; trackBy: trackByDocumentId" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)"></app-document-card-small>
|
<app-document-card-small [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" [document]="d" *ngFor="let d of list.documents; trackBy: trackByDocumentId" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickDocumentType)="clickDocumentType($event)"></app-document-card-small>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,15 @@
|
|||||||
<div class="col mb-2 mb-xl-0">
|
<div class="col mb-2 mb-xl-0">
|
||||||
<div class="form-inline d-flex align-items-center">
|
<div class="form-inline d-flex align-items-center">
|
||||||
<label class="text-muted mr-2 mb-0" i18n>Filter by:</label>
|
<label class="text-muted mr-2 mb-0" i18n>Filter by:</label>
|
||||||
<input class="form-control form-control-sm flex-fill w-auto" type="text" [(ngModel)]="titleFilter" placeholder="Title" i18n-placeholder>
|
<div class="input-group input-group-sm flex-fill w-auto">
|
||||||
|
<div class="input-group-prepend" ngbDropdown>
|
||||||
|
<button class="btn btn-outline-primary" ngbDropdownToggle>{{textFilterTargetName}}</button>
|
||||||
|
<div class="dropdown-menu shadow" ngbDropdownMenu>
|
||||||
|
<button *ngFor="let t of textFilterTargets" ngbDropdownItem [class.active]="textFilterTarget == t.id" (click)="changeTextFilterTarget(t.id)">{{t.name}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input class="form-control form-control-sm" type="text" [(ngModel)]="textFilter">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-100 d-xl-none"></div>
|
<div class="w-100 d-xl-none"></div>
|
||||||
|
|||||||
@@ -8,10 +8,14 @@ import { DocumentTypeService } from 'src/app/services/rest/document-type.service
|
|||||||
import { TagService } from 'src/app/services/rest/tag.service';
|
import { TagService } from 'src/app/services/rest/tag.service';
|
||||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
|
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
|
||||||
import { FilterRule } from 'src/app/data/filter-rule';
|
import { FilterRule } from 'src/app/data/filter-rule';
|
||||||
import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_ANY_TAG, FILTER_HAS_TAG, FILTER_TITLE } from 'src/app/data/filter-rule-type';
|
import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_ASN, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_ANY_TAG, FILTER_HAS_TAG, FILTER_TITLE, FILTER_TITLE_CONTENT } from 'src/app/data/filter-rule-type';
|
||||||
import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component';
|
import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component';
|
||||||
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component';
|
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component';
|
||||||
|
|
||||||
|
const TEXT_FILTER_TARGET_TITLE = "title"
|
||||||
|
const TEXT_FILTER_TARGET_TITLE_CONTENT = "title-content"
|
||||||
|
const TEXT_FILTER_TARGET_ASN = "asn"
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-filter-editor',
|
selector: 'app-filter-editor',
|
||||||
templateUrl: './filter-editor.component.html',
|
templateUrl: './filter-editor.component.html',
|
||||||
@@ -48,6 +52,9 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
case FILTER_TITLE:
|
case FILTER_TITLE:
|
||||||
return $localize`Title: ${rule.value}`
|
return $localize`Title: ${rule.value}`
|
||||||
|
|
||||||
|
case FILTER_ASN:
|
||||||
|
return $localize`ASN: ${rule.value}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +71,20 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
correspondents: PaperlessCorrespondent[] = []
|
correspondents: PaperlessCorrespondent[] = []
|
||||||
documentTypes: PaperlessDocumentType[] = []
|
documentTypes: PaperlessDocumentType[] = []
|
||||||
|
|
||||||
_titleFilter = ""
|
_textFilter = ""
|
||||||
|
|
||||||
|
textFilterTargets = [
|
||||||
|
{id: TEXT_FILTER_TARGET_TITLE, name: $localize`Title`},
|
||||||
|
{id: TEXT_FILTER_TARGET_TITLE_CONTENT, name: $localize`Title & content`},
|
||||||
|
{id: TEXT_FILTER_TARGET_ASN, name: $localize`ASN`}
|
||||||
|
]
|
||||||
|
|
||||||
|
textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||||
|
|
||||||
|
get textFilterTargetName() {
|
||||||
|
return this.textFilterTargets.find(t => t.id == this.textFilterTarget)?.name
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
tagSelectionModel = new FilterableDropdownSelectionModel()
|
tagSelectionModel = new FilterableDropdownSelectionModel()
|
||||||
correspondentSelectionModel = new FilterableDropdownSelectionModel()
|
correspondentSelectionModel = new FilterableDropdownSelectionModel()
|
||||||
@@ -80,7 +100,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
this.documentTypeSelectionModel.clear(false)
|
this.documentTypeSelectionModel.clear(false)
|
||||||
this.tagSelectionModel.clear(false)
|
this.tagSelectionModel.clear(false)
|
||||||
this.correspondentSelectionModel.clear(false)
|
this.correspondentSelectionModel.clear(false)
|
||||||
this._titleFilter = null
|
this._textFilter = null
|
||||||
this.dateAddedBefore = null
|
this.dateAddedBefore = null
|
||||||
this.dateAddedAfter = null
|
this.dateAddedAfter = null
|
||||||
this.dateCreatedBefore = null
|
this.dateCreatedBefore = null
|
||||||
@@ -89,7 +109,16 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
value.forEach(rule => {
|
value.forEach(rule => {
|
||||||
switch (rule.rule_type) {
|
switch (rule.rule_type) {
|
||||||
case FILTER_TITLE:
|
case FILTER_TITLE:
|
||||||
this._titleFilter = rule.value
|
this._textFilter = rule.value
|
||||||
|
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE
|
||||||
|
break
|
||||||
|
case FILTER_TITLE_CONTENT:
|
||||||
|
this._textFilter = rule.value
|
||||||
|
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||||
|
break
|
||||||
|
case FILTER_ASN:
|
||||||
|
this._textFilter = rule.value
|
||||||
|
this.textFilterTarget = TEXT_FILTER_TARGET_ASN
|
||||||
break
|
break
|
||||||
case FILTER_CREATED_AFTER:
|
case FILTER_CREATED_AFTER:
|
||||||
this.dateCreatedAfter = rule.value
|
this.dateCreatedAfter = rule.value
|
||||||
@@ -121,8 +150,14 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
get filterRules(): FilterRule[] {
|
get filterRules(): FilterRule[] {
|
||||||
let filterRules: FilterRule[] = []
|
let filterRules: FilterRule[] = []
|
||||||
if (this._titleFilter) {
|
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT) {
|
||||||
filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter})
|
filterRules.push({rule_type: FILTER_TITLE_CONTENT, value: this._textFilter})
|
||||||
|
}
|
||||||
|
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE) {
|
||||||
|
filterRules.push({rule_type: FILTER_TITLE, value: this._textFilter})
|
||||||
|
}
|
||||||
|
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_ASN) {
|
||||||
|
filterRules.push({rule_type: FILTER_ASN, value: this._textFilter})
|
||||||
}
|
}
|
||||||
if (this.tagSelectionModel.isNoneSelected()) {
|
if (this.tagSelectionModel.isNoneSelected()) {
|
||||||
filterRules.push({rule_type: FILTER_HAS_ANY_TAG, value: "false"})
|
filterRules.push({rule_type: FILTER_HAS_ANY_TAG, value: "false"})
|
||||||
@@ -165,15 +200,15 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
this.filterRulesChange.next(this.filterRules)
|
this.filterRulesChange.next(this.filterRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
get titleFilter() {
|
get textFilter() {
|
||||||
return this._titleFilter
|
return this._textFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
set titleFilter(value) {
|
set textFilter(value) {
|
||||||
this.titleFilterDebounce.next(value)
|
this.textFilterDebounce.next(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
titleFilterDebounce: Subject<string>
|
textFilterDebounce: Subject<string>
|
||||||
subscription: Subscription
|
subscription: Subscription
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -181,19 +216,19 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
|
this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
|
||||||
this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
|
this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
|
||||||
|
|
||||||
this.titleFilterDebounce = new Subject<string>()
|
this.textFilterDebounce = new Subject<string>()
|
||||||
|
|
||||||
this.subscription = this.titleFilterDebounce.pipe(
|
this.subscription = this.textFilterDebounce.pipe(
|
||||||
debounceTime(400),
|
debounceTime(400),
|
||||||
distinctUntilChanged()
|
distinctUntilChanged()
|
||||||
).subscribe(title => {
|
).subscribe(text => {
|
||||||
this._titleFilter = title
|
this._textFilter = text
|
||||||
this.updateRules()
|
this.updateRules()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.titleFilterDebounce.complete()
|
this.textFilterDebounce.complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
resetSelected() {
|
resetSelected() {
|
||||||
@@ -223,4 +258,9 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
|||||||
onDocumentTypeDropdownOpen() {
|
onDocumentTypeDropdownOpen() {
|
||||||
this.documentTypeSelectionModel.apply()
|
this.documentTypeSelectionModel.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeTextFilterTarget(target) {
|
||||||
|
this.textFilterTarget = target
|
||||||
|
this.updateRules()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
::ng-deep .popover {
|
||||||
|
max-width: 40rem;
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
min-width: 30rem;
|
||||||
|
min-height: 18rem;
|
||||||
|
max-height: 35rem;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-border {
|
||||||
|
position: absolute;
|
||||||
|
top: 4rem;
|
||||||
|
left: calc(50% - 0.5rem);
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .popover-hidden .popover {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -14,5 +14,5 @@
|
|||||||
<div class="bg-dark p-3 mb-3 text-light text-monospace log-container">
|
<div class="bg-dark p-3 mb-3 text-light text-monospace log-container">
|
||||||
<p
|
<p
|
||||||
class="m-0 p-0 log-entry-{{getLogLevel(log)}}"
|
class="m-0 p-0 log-entry-{{getLogLevel(log)}}"
|
||||||
*ngFor="let log of logs" style="white-space: pre;">{{log}}</p>
|
*ngFor="let log of logs">{{log}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,9 +16,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.log-container {
|
.log-container {
|
||||||
|
overflow-y: scroll;
|
||||||
overflow: scroll;
|
|
||||||
|
|
||||||
height: calc(100vh - 190px);
|
height: calc(100vh - 190px);
|
||||||
top: 70px;
|
top: 70px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -96,6 +96,7 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<app-input-check i18n-title title="Use system settings" formControlName="darkModeUseSystem"></app-input-check>
|
<app-input-check i18n-title title="Use system settings" formControlName="darkModeUseSystem"></app-input-check>
|
||||||
<app-input-check [hidden]="settingsForm.value.darkModeUseSystem" i18n-title title="Enable dark mode" formControlName="darkModeEnabled"></app-input-check>
|
<app-input-check [hidden]="settingsForm.value.darkModeUseSystem" i18n-title title="Enable dark mode" formControlName="darkModeEnabled"></app-input-check>
|
||||||
|
<app-input-check i18n-title title="Invert thumbnails in dark mode" formControlName="darkModeInvertThumbs"></app-input-check>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export class SettingsComponent implements OnInit {
|
|||||||
'documentListItemPerPage': new FormControl(this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)),
|
'documentListItemPerPage': new FormControl(this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)),
|
||||||
'darkModeUseSystem': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM)),
|
'darkModeUseSystem': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM)),
|
||||||
'darkModeEnabled': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED)),
|
'darkModeEnabled': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED)),
|
||||||
|
'darkModeInvertThumbs': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)),
|
||||||
'useNativePdfViewer': new FormControl(this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)),
|
'useNativePdfViewer': new FormControl(this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)),
|
||||||
'savedViews': this.savedViewGroup,
|
'savedViews': this.savedViewGroup,
|
||||||
'displayLanguage': new FormControl(this.settings.getLanguage()),
|
'displayLanguage': new FormControl(this.settings.getLanguage()),
|
||||||
@@ -74,6 +75,7 @@ export class SettingsComponent implements OnInit {
|
|||||||
this.settings.set(SETTINGS_KEYS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage)
|
this.settings.set(SETTINGS_KEYS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage)
|
||||||
this.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem)
|
this.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem)
|
||||||
this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString())
|
this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString())
|
||||||
|
this.settings.set(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED, (this.settingsForm.value.darkModeInvertThumbs == true).toString())
|
||||||
this.settings.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, this.settingsForm.value.useNativePdfViewer)
|
this.settings.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, this.settingsForm.value.useNativePdfViewer)
|
||||||
this.settings.set(SETTINGS_KEYS.DATE_LOCALE, this.settingsForm.value.dateLocale)
|
this.settings.set(SETTINGS_KEYS.DATE_LOCALE, this.settingsForm.value.dateLocale)
|
||||||
this.settings.set(SETTINGS_KEYS.DATE_FORMAT, this.settingsForm.value.dateFormat)
|
this.settings.set(SETTINGS_KEYS.DATE_FORMAT, this.settingsForm.value.dateFormat)
|
||||||
@@ -88,14 +90,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 [
|
return [
|
||||||
{code: "", name: $localize`Use date format of display language`},
|
{code: "", name: $localize`Use date format of display language`}
|
||||||
{code: "iso-8601", name: $localize`ISO 8601`}
|
].concat(this.settings.getDateLocaleOptions())
|
||||||
].concat(this.settings.getLanguageOptions())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get today() {
|
get today() {
|
||||||
|
|||||||
@@ -8,19 +8,11 @@
|
|||||||
<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-color i18n-title title="Color" formControlName="color" [error]="error?.color"></app-input-color>
|
||||||
<div class="form-group paperless-input-select">
|
|
||||||
<label for="colour" i18n>Color</label>
|
|
||||||
<ng-select name="colour" formControlName="colour" [items]="getColours()" bindValue="id" bindLabel="name" [clearable]="false">
|
|
||||||
<ng-template ng-option-tmp ng-label-tmp let-item="item">
|
|
||||||
<span class="badge" [style.background]="item.value" [style.color]="item.textColor">{{item.name}}</span>
|
|
||||||
</ng-template>
|
|
||||||
</ng-select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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">
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import { Component } from '@angular/core';
|
|||||||
import { FormControl, FormGroup } from '@angular/forms';
|
import { FormControl, FormGroup } from '@angular/forms';
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
|
||||||
import { TAG_COLOURS, PaperlessTag } from 'src/app/data/paperless-tag';
|
import { PaperlessTag } from 'src/app/data/paperless-tag';
|
||||||
import { TagService } from 'src/app/services/rest/tag.service';
|
import { TagService } from 'src/app/services/rest/tag.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
|
import { randomColor } from 'src/app/utils/color';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tag-edit-dialog',
|
selector: 'app-tag-edit-dialog',
|
||||||
@@ -28,7 +29,7 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
|
|||||||
getForm(): FormGroup {
|
getForm(): FormGroup {
|
||||||
return new FormGroup({
|
return new FormGroup({
|
||||||
name: new FormControl(''),
|
name: new FormControl(''),
|
||||||
colour: new FormControl(1),
|
color: new FormControl(randomColor()),
|
||||||
is_inbox_tag: new FormControl(false),
|
is_inbox_tag: new FormControl(false),
|
||||||
matching_algorithm: new FormControl(1),
|
matching_algorithm: new FormControl(1),
|
||||||
match: new FormControl(""),
|
match: new FormControl(""),
|
||||||
@@ -36,12 +37,4 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getColours() {
|
|
||||||
return TAG_COLOURS
|
|
||||||
}
|
|
||||||
|
|
||||||
getColor(id: number) {
|
|
||||||
return TAG_COLOURS.find(c => c.id == id)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let tag of data">
|
<tr *ngFor="let tag of data">
|
||||||
<td scope="row">{{ tag.name }}</td>
|
<td scope="row">{{ tag.name }}</td>
|
||||||
<td scope="row"><span class="badge" [style.color]="getColor(tag.colour).textColor"
|
<td scope="row"><span class="badge" [style.color]="tag.text_color"
|
||||||
[style.background-color]="getColor(tag.colour).value">{{ getColor(tag.colour).name }}</span></td>
|
[style.background-color]="tag.color">{{tag.color}}</span></td>
|
||||||
<td scope="row">{{ getMatching(tag) }}</td>
|
<td scope="row">{{ getMatching(tag) }}</td>
|
||||||
<td scope="row">{{ tag.document_count }}</td>
|
<td scope="row">{{ tag.document_count }}</td>
|
||||||
<td scope="row">
|
<td scope="row">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { FILTER_HAS_TAG } from 'src/app/data/filter-rule-type';
|
import { FILTER_HAS_TAG } from 'src/app/data/filter-rule-type';
|
||||||
import { TAG_COLOURS, PaperlessTag } from 'src/app/data/paperless-tag';
|
import { PaperlessTag } from 'src/app/data/paperless-tag';
|
||||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
|
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
|
||||||
import { TagService } from 'src/app/services/rest/tag.service';
|
import { TagService } from 'src/app/services/rest/tag.service';
|
||||||
import { ToastService } from 'src/app/services/toast.service';
|
import { ToastService } from 'src/app/services/toast.service';
|
||||||
@@ -22,10 +22,6 @@ export class TagListComponent extends GenericListComponent<PaperlessTag> {
|
|||||||
super(tagService, modalService, TagEditDialogComponent, toastService)
|
super(tagService, modalService, TagEditDialogComponent, toastService)
|
||||||
}
|
}
|
||||||
|
|
||||||
getColor(id) {
|
|
||||||
return TAG_COLOURS.find(c => c.id == id)
|
|
||||||
}
|
|
||||||
|
|
||||||
getDeleteMessage(object: PaperlessTag) {
|
getDeleteMessage(object: PaperlessTag) {
|
||||||
return $localize`Do you really want to delete the tag "${object.name}"?`
|
return $localize`Do you really want to delete the tag "${object.name}"?`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
<div *ngIf="errorMessage" class="alert alert-danger" i18n>Invalid search query: {{errorMessage}}</div>
|
<div *ngIf="errorMessage" class="alert alert-danger" i18n>Invalid search query: {{errorMessage}}</div>
|
||||||
|
|
||||||
<p *ngIf="more_like" i18n>
|
<p *ngIf="more_like" i18n>Showing documents similar to <a routerLink="/documents/{{more_like}}">{{more_like_doc?.original_file_name}}</a></p>
|
||||||
Showing documents similar to <a routerLink="/documents/{{more_like}}">{{more_like_doc?.original_file_name}}</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p *ngIf="query">
|
<p *ngIf="query">
|
||||||
<ng-container i18n>Search query: <i>{{query}}</i></ng-container>
|
<ng-container i18n>Search query: <i>{{query}}</i></ng-container>
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export const FILTER_DOES_NOT_HAVE_TAG = 17
|
|||||||
|
|
||||||
export const FILTER_ASN_ISNULL = 18
|
export const FILTER_ASN_ISNULL = 18
|
||||||
|
|
||||||
|
export const FILTER_TITLE_CONTENT = 19
|
||||||
|
|
||||||
export const FILTER_RULE_TYPES: FilterRuleType[] = [
|
export const FILTER_RULE_TYPES: FilterRuleType[] = [
|
||||||
|
|
||||||
{id: FILTER_TITLE, filtervar: "title__icontains", datatype: "string", multi: false, default: ""},
|
{id: FILTER_TITLE, filtervar: "title__icontains", datatype: "string", multi: false, default: ""},
|
||||||
@@ -47,7 +49,9 @@ export const FILTER_RULE_TYPES: FilterRuleType[] = [
|
|||||||
|
|
||||||
{id: FILTER_MODIFIED_BEFORE, filtervar: "modified__date__lt", datatype: "date", multi: false},
|
{id: FILTER_MODIFIED_BEFORE, filtervar: "modified__date__lt", datatype: "date", multi: false},
|
||||||
{id: FILTER_MODIFIED_AFTER, filtervar: "modified__date__gt", datatype: "date", multi: false},
|
{id: FILTER_MODIFIED_AFTER, filtervar: "modified__date__gt", datatype: "date", multi: false},
|
||||||
{id: FILTER_ASN_ISNULL, filtervar: "archive_serial_number__isnull", datatype: "boolean", multi: false}
|
{id: FILTER_ASN_ISNULL, filtervar: "archive_serial_number__isnull", datatype: "boolean", multi: false},
|
||||||
|
|
||||||
|
{id: FILTER_TITLE_CONTENT, filtervar: "title_content", datatype: "string", multi: false}
|
||||||
]
|
]
|
||||||
|
|
||||||
export interface FilterRuleType {
|
export interface FilterRuleType {
|
||||||
|
|||||||
@@ -1,26 +1,10 @@
|
|||||||
import { MatchingModel } from './matching-model';
|
import { MatchingModel } from "./matching-model";
|
||||||
import { ObjectWithId } from './object-with-id';
|
|
||||||
|
|
||||||
|
|
||||||
export const TAG_COLOURS = [
|
|
||||||
{id: 1, value: "#a6cee3", name: $localize`Light blue`, textColor: "#000000"},
|
|
||||||
{id: 2, value: "#1f78b4", name: $localize`Blue`, textColor: "#ffffff"},
|
|
||||||
{id: 3, value: "#b2df8a", name: $localize`Light green`, textColor: "#000000"},
|
|
||||||
{id: 4, value: "#33a02c", name: $localize`Green`, textColor: "#ffffff"},
|
|
||||||
{id: 5, value: "#fb9a99", name: $localize`Light red`, textColor: "#000000"},
|
|
||||||
{id: 6, value: "#e31a1c", name: $localize`Red `, textColor: "#ffffff"},
|
|
||||||
{id: 7, value: "#fdbf6f", name: $localize`Light orange`, textColor: "#000000"},
|
|
||||||
{id: 8, value: "#ff7f00", name: $localize`Orange`, textColor: "#000000"},
|
|
||||||
{id: 9, value: "#cab2d6", name: $localize`Light violet`, textColor: "#000000"},
|
|
||||||
{id: 10, value: "#6a3d9a", name: $localize`Violet`, textColor: "#ffffff"},
|
|
||||||
{id: 11, value: "#b15928", name: $localize`Brown`, textColor: "#ffffff"},
|
|
||||||
{id: 12, value: "#000000", name: $localize`Black`, textColor: "#ffffff"},
|
|
||||||
{id: 13, value: "#cccccc", name: $localize`Light grey`, textColor: "#000000"}
|
|
||||||
]
|
|
||||||
|
|
||||||
export interface PaperlessTag extends MatchingModel {
|
export interface PaperlessTag extends MatchingModel {
|
||||||
|
|
||||||
colour?: number
|
color?: string
|
||||||
|
|
||||||
|
text_color?: string
|
||||||
|
|
||||||
is_inbox_tag?: boolean
|
is_inbox_tag?: boolean
|
||||||
|
|
||||||
|
|||||||
16
src-ui/src/app/interceptors/api-version.interceptor.spec.ts
Normal file
16
src-ui/src/app/interceptors/api-version.interceptor.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ApiVersionInterceptor } from './api-version.interceptor';
|
||||||
|
|
||||||
|
describe('ApiVersionInterceptor', () => {
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
ApiVersionInterceptor
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const interceptor: ApiVersionInterceptor = TestBed.inject(ApiVersionInterceptor);
|
||||||
|
expect(interceptor).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
25
src-ui/src/app/interceptors/api-version.interceptor.ts
Normal file
25
src-ui/src/app/interceptors/api-version.interceptor.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
HttpRequest,
|
||||||
|
HttpHandler,
|
||||||
|
HttpEvent,
|
||||||
|
HttpInterceptor
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { environment } from 'src/environments/environment';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ApiVersionInterceptor implements HttpInterceptor {
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||||
|
request = request.clone({
|
||||||
|
setHeaders: {
|
||||||
|
'Accept': `application/json; version=${environment.apiVersion}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return next.handle(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,8 +4,8 @@ import { SettingsService, SETTINGS_KEYS } from '../services/settings.service';
|
|||||||
|
|
||||||
const FORMAT_TO_ISO_FORMAT = {
|
const FORMAT_TO_ISO_FORMAT = {
|
||||||
"longDate": "y-MM-dd",
|
"longDate": "y-MM-dd",
|
||||||
"mediumDate": "yy-MM-dd",
|
"mediumDate": "y-MM-dd",
|
||||||
"shortDate": "yy-MM-dd"
|
"shortDate": "y-MM-dd"
|
||||||
}
|
}
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
@@ -13,17 +13,20 @@ const FORMAT_TO_ISO_FORMAT = {
|
|||||||
})
|
})
|
||||||
export class CustomDatePipe extends DatePipe implements PipeTransform {
|
export class CustomDatePipe extends DatePipe implements PipeTransform {
|
||||||
|
|
||||||
|
private defaultLocale: string
|
||||||
|
|
||||||
constructor(@Inject(LOCALE_ID) locale: string, private settings: SettingsService) {
|
constructor(@Inject(LOCALE_ID) locale: string, private settings: SettingsService) {
|
||||||
super(locale)
|
super(locale)
|
||||||
|
this.defaultLocale = locale
|
||||||
}
|
}
|
||||||
|
|
||||||
transform(value: any, format?: string, timezone?: string, locale?: string): string | null {
|
transform(value: any, format?: string, timezone?: string, locale?: string): string | null {
|
||||||
let l = locale || this.settings.get(SETTINGS_KEYS.DATE_LOCALE)
|
let l = locale || this.settings.get(SETTINGS_KEYS.DATE_LOCALE) || this.defaultLocale
|
||||||
let f = format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT)
|
let f = format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT)
|
||||||
if (l == "iso-8601") {
|
if (l == "iso-8601") {
|
||||||
return super.transform(value, FORMAT_TO_ISO_FORMAT[f], timezone)
|
return super.transform(value, FORMAT_TO_ISO_FORMAT[f], timezone)
|
||||||
} else {
|
} else {
|
||||||
return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, locale)
|
return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 = {
|
||||||
@@ -21,6 +26,7 @@ export const SETTINGS_KEYS = {
|
|||||||
DOCUMENT_LIST_SIZE: 'general-settings:documentListSize',
|
DOCUMENT_LIST_SIZE: 'general-settings:documentListSize',
|
||||||
DARK_MODE_USE_SYSTEM: 'general-settings:dark-mode:use-system',
|
DARK_MODE_USE_SYSTEM: 'general-settings:dark-mode:use-system',
|
||||||
DARK_MODE_ENABLED: 'general-settings:dark-mode:enabled',
|
DARK_MODE_ENABLED: 'general-settings:dark-mode:enabled',
|
||||||
|
DARK_MODE_THUMB_INVERTED: 'general-settings:dark-mode:thumb-inverted',
|
||||||
USE_NATIVE_PDF_VIEWER: 'general-settings:document-details:native-pdf-viewer',
|
USE_NATIVE_PDF_VIEWER: 'general-settings:document-details:native-pdf-viewer',
|
||||||
DATE_LOCALE: 'general-settings:date-display:date-locale',
|
DATE_LOCALE: 'general-settings:date-display:date-locale',
|
||||||
DATE_FORMAT: 'general-settings:date-display:date-format',
|
DATE_FORMAT: 'general-settings:date-display:date-format',
|
||||||
@@ -36,6 +42,7 @@ const SETTINGS: PaperlessSettings[] = [
|
|||||||
{key: SETTINGS_KEYS.DOCUMENT_LIST_SIZE, type: "number", default: 50},
|
{key: SETTINGS_KEYS.DOCUMENT_LIST_SIZE, type: "number", default: 50},
|
||||||
{key: SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, type: "boolean", default: true},
|
{key: SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, type: "boolean", default: true},
|
||||||
{key: SETTINGS_KEYS.DARK_MODE_ENABLED, type: "boolean", default: false},
|
{key: SETTINGS_KEYS.DARK_MODE_ENABLED, type: "boolean", default: false},
|
||||||
|
{key: SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED, type: "boolean", default: true},
|
||||||
{key: SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, type: "boolean", default: false},
|
{key: SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, type: "boolean", default: false},
|
||||||
{key: SETTINGS_KEYS.DATE_LOCALE, type: "string", default: ""},
|
{key: SETTINGS_KEYS.DATE_LOCALE, type: "string", default: ""},
|
||||||
{key: SETTINGS_KEYS.DATE_FORMAT, type: "string", default: "mediumDate"},
|
{key: SETTINGS_KEYS.DATE_FORMAT, type: "string", default: "mediumDate"},
|
||||||
@@ -56,7 +63,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);
|
||||||
|
|
||||||
@@ -79,14 +87,25 @@ 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: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)"},
|
{code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)", dateInputFormat: "dd/mm/yyyy"},
|
||||||
{code: "de", name: $localize`German`, englishName: "German"},
|
{code: "de-de", name: $localize`German`, englishName: "German", dateInputFormat: "dd.mm.yyyy"},
|
||||||
{code: "nl", name: $localize`Dutch`, englishName: "Dutch"},
|
{code: "nl-nl", name: $localize`Dutch`, englishName: "Dutch", dateInputFormat: "dd-mm-yyyy"},
|
||||||
{code: "fr", name: $localize`French`, englishName: "French"}
|
{code: "fr-fr", name: $localize`French`, englishName: "French", dateInputFormat: "dd/mm/yyyy"},
|
||||||
|
{code: "pt-br", name: $localize`Portuguese (Brazil)`, englishName: "Portuguese (Brazil)", dateInputFormat: "dd/mm/yyyy"},
|
||||||
|
{code: "it-it", name: $localize`Italian`, englishName: "Italian", dateInputFormat: "dd/mm/yyyy"},
|
||||||
|
{code: "ro-ro", name: $localize`Romanian`, englishName: "Romanian", dateInputFormat: "dd.mm.yyyy"},
|
||||||
|
{code: "ru-ru", name: $localize`Russian`, englishName: "Russian", dateInputFormat: "dd.mm.yyyy"},
|
||||||
|
{code: "es-es", name: $localize`Spanish`, englishName: "Spanish", 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')) {
|
||||||
@@ -107,6 +126,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)
|
||||||
|
|
||||||
|
|||||||
48
src-ui/src/app/utils/color.ts
Normal file
48
src-ui/src/app/utils/color.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
function componentToHex(c) {
|
||||||
|
var hex = Math.floor(c).toString(16)
|
||||||
|
return hex.length == 1 ? "0" + hex : hex
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
|
||||||
|
*
|
||||||
|
* Converts an HSL color value to RGB. Conversion formula
|
||||||
|
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
||||||
|
* Assumes h, s, and l are contained in the set [0, 1] and
|
||||||
|
* returns r, g, and b in the set [0, 255].
|
||||||
|
*
|
||||||
|
* @param Number h The hue
|
||||||
|
* @param Number s The saturation
|
||||||
|
* @param Number l The lightness
|
||||||
|
* @return Array The RGB representation
|
||||||
|
*/
|
||||||
|
function hslToRgb(h, s, l){
|
||||||
|
var r, g, b
|
||||||
|
|
||||||
|
if(s == 0){
|
||||||
|
r = g = b = l // achromatic
|
||||||
|
}else{
|
||||||
|
function hue2rgb(p, q, t){
|
||||||
|
if(t < 0) t += 1
|
||||||
|
if(t > 1) t -= 1
|
||||||
|
if(t < 1/6) return p + (q - p) * 6 * t
|
||||||
|
if(t < 1/2) return q
|
||||||
|
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
var q = l < 0.5 ? l * (1 + s) : l + s - l * s
|
||||||
|
var p = 2 * l - q
|
||||||
|
r = hue2rgb(p, q, h + 1/3)
|
||||||
|
g = hue2rgb(p, q, h)
|
||||||
|
b = hue2rgb(p, q, h - 1/3)
|
||||||
|
}
|
||||||
|
|
||||||
|
return [r * 255, g * 255, b * 255]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function randomColor() {
|
||||||
|
let rgb = hslToRgb(Math.random(), 0.6, Math.random() * 0.4 + 0.4)
|
||||||
|
return `#${componentToHex(rgb[0])}${componentToHex(rgb[1])}${componentToHex(rgb[2])}`
|
||||||
|
}
|
||||||
59
src-ui/src/app/utils/ngb-date-parser-formatter.ts
Normal file
59
src-ui/src/app/utils/ngb-date-parser-formatter.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src-ui/src/app/utils/ngb-iso-date-adapter.ts
Normal file
27
src-ui/src/app/utils/ngb-iso-date-adapter.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src-ui/src/app/utils/ngb-iso-date-time-adapter.ts
Normal file
23
src-ui/src/app/utils/ngb-iso-date-time-adapter.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src-ui/src/apple-touch-icon.png
Normal file
BIN
src-ui/src/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
@@ -1,8 +1,9 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: true,
|
production: true,
|
||||||
apiBaseUrl: "/api/",
|
apiBaseUrl: "/api/",
|
||||||
|
apiVersion: "2",
|
||||||
appTitle: "Paperless-ng",
|
appTitle: "Paperless-ng",
|
||||||
version: "1.2.0",
|
version: "1.3.1",
|
||||||
webSocketHost: window.location.host,
|
webSocketHost: window.location.host,
|
||||||
webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:")
|
webSocketProtocol: (window.location.protocol == "https:" ? "wss:" : "ws:")
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
apiBaseUrl: "http://localhost:8000/api/",
|
apiBaseUrl: "http://localhost:8000/api/",
|
||||||
|
apiVersion: "2",
|
||||||
appTitle: "Paperless-ng",
|
appTitle: "Paperless-ng",
|
||||||
version: "DEVELOPMENT",
|
version: "DEVELOPMENT",
|
||||||
webSocketHost: "localhost:8000",
|
webSocketHost: "localhost:8000",
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
2298
src-ui/src/locale/messages.cs_CZ.xlf
Normal file
2298
src-ui/src/locale/messages.cs_CZ.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2298
src-ui/src/locale/messages.de_DE.xlf
Normal file
2298
src-ui/src/locale/messages.de_DE.xlf
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2298
src-ui/src/locale/messages.es_ES.xlf
Normal file
2298
src-ui/src/locale/messages.es_ES.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2298
src-ui/src/locale/messages.fr_FR.xlf
Normal file
2298
src-ui/src/locale/messages.fr_FR.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2298
src-ui/src/locale/messages.hu_HU.xlf
Normal file
2298
src-ui/src/locale/messages.hu_HU.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2298
src-ui/src/locale/messages.it_IT.xlf
Normal file
2298
src-ui/src/locale/messages.it_IT.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2290
src-ui/src/locale/messages.la_LA.xlf
Normal file
2290
src-ui/src/locale/messages.la_LA.xlf
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2298
src-ui/src/locale/messages.pt_BR.xlf
Normal file
2298
src-ui/src/locale/messages.pt_BR.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2298
src-ui/src/locale/messages.pt_PT.xlf
Normal file
2298
src-ui/src/locale/messages.pt_PT.xlf
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2298
src-ui/src/locale/messages.ru_RU.xlf
Normal file
2298
src-ui/src/locale/messages.ru_RU.xlf
Normal file
File diff suppressed because it is too large
Load Diff
2290
src-ui/src/locale/messages.th_TH.xlf
Normal file
2290
src-ui/src/locale/messages.th_TH.xlf
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2298
src-ui/src/locale/messages.zh_CN.xlf
Normal file
2298
src-ui/src/locale/messages.zh_CN.xlf
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
|||||||
$primary-dark-mode: #45973a;
|
$primary-dark-mode: #45973a;
|
||||||
$danger-dark-mode: #b71631;
|
$danger-dark-mode: #b71631;
|
||||||
$bg-dark-mode: #161618;
|
$bg-dark-mode: #161618;
|
||||||
|
$bg-dark-mode-accent: #21262d;
|
||||||
$bg-light-dark-mode: #1c1c1f;
|
$bg-light-dark-mode: #1c1c1f;
|
||||||
$text-color-dark-mode: #abb2bf;
|
$text-color-dark-mode: #abb2bf;
|
||||||
$text-color-dark-mode-accent: lighten($text-color-dark-mode, 10%);
|
$text-color-dark-mode-accent: lighten($text-color-dark-mode, 10%);
|
||||||
@@ -63,6 +64,10 @@ $border-color-dark-mode: #47494f;
|
|||||||
background-color: $bg-dark-mode;
|
background-color: $bg-dark-mode;
|
||||||
color: $text-color-dark-mode;
|
color: $text-color-dark-mode;
|
||||||
border-color: $border-color-dark-mode $border-color-dark-mode $bg-dark-mode;
|
border-color: $border-color-dark-mode $border-color-dark-mode $bg-dark-mode;
|
||||||
|
|
||||||
|
.close {
|
||||||
|
background-color: inherit !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -71,6 +76,10 @@ $border-color-dark-mode: #47494f;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-item.active .page-link {
|
||||||
|
background-color: darken($primary-dark-mode, 10%);
|
||||||
|
}
|
||||||
|
|
||||||
.nav-tabs {
|
.nav-tabs {
|
||||||
border-color: $border-color-dark-mode;
|
border-color: $border-color-dark-mode;
|
||||||
|
|
||||||
@@ -139,15 +148,18 @@ $border-color-dark-mode: #47494f;
|
|||||||
|
|
||||||
.doc-img {
|
.doc-img {
|
||||||
mix-blend-mode: normal;
|
mix-blend-mode: normal;
|
||||||
filter: invert(95%) hue-rotate(180deg);
|
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border-color: $bg-dark-mode;
|
border-color: $bg-dark-mode;
|
||||||
|
filter: invert(10%);
|
||||||
&.border-right {
|
&.border-right {
|
||||||
border-right: none !important;
|
border-right: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.doc-img.inverted {
|
||||||
|
filter: invert(95%) hue-rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
.card-selected .doc-img {
|
.card-selected .doc-img {
|
||||||
mix-blend-mode: luminosity;
|
mix-blend-mode: luminosity;
|
||||||
}
|
}
|
||||||
@@ -218,7 +230,7 @@ $border-color-dark-mode: #47494f;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-outline-secondary {
|
.btn-outline-secondary {
|
||||||
border-color: $text-color-dark-mode;
|
border-color: darken($text-color-dark-mode, 30%);
|
||||||
color: $text-color-dark-mode;
|
color: $text-color-dark-mode;
|
||||||
|
|
||||||
&:not(:disabled):not(.disabled):hover {
|
&:not(:disabled):not(.disabled):hover {
|
||||||
@@ -246,6 +258,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;
|
||||||
}
|
}
|
||||||
@@ -261,6 +283,10 @@ $border-color-dark-mode: #47494f;
|
|||||||
background-color: $bg-dark-mode !important;
|
background-color: $bg-dark-mode !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-footer button:hover {
|
||||||
|
color: $primary-dark-mode !important;
|
||||||
|
}
|
||||||
|
|
||||||
.form-control:not(.is-invalid):not(.btn),
|
.form-control:not(.is-invalid):not(.btn),
|
||||||
input:not(.is-invalid),
|
input:not(.is-invalid),
|
||||||
textarea:not(.is-invalid) {
|
textarea:not(.is-invalid) {
|
||||||
@@ -366,6 +392,34 @@ $border-color-dark-mode: #47494f;
|
|||||||
.progress-bar.bg-primary {
|
.progress-bar.bg-primary {
|
||||||
background-color: darken($primary-dark-mode, 5%) !important;
|
background-color: darken($primary-dark-mode, 5%) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popover {
|
||||||
|
.popover-header,
|
||||||
|
.popover-body {
|
||||||
|
background-color: $bg-dark-mode-accent;
|
||||||
|
border-color: $border-color-dark-mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$placements: 'top', 'right', 'bottom', 'left';
|
||||||
|
|
||||||
|
@each $placement in $placements {
|
||||||
|
.bs-popover-#{$placement} > .arrow::after,
|
||||||
|
.bs-popover-auto[x-placement^=#{$placement}] > .arrow::after {
|
||||||
|
border-#{$placement}-color: $bg-dark-mode-accent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bs-popover-bottom .popover-header::before,
|
||||||
|
.bs-popover-auto[x-placement^=bottom] .popover-header::before {
|
||||||
|
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
BIN
src/clash.pdf
Binary file not shown.
@@ -19,12 +19,12 @@ class TagAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = (
|
list_display = (
|
||||||
"name",
|
"name",
|
||||||
"colour",
|
"color",
|
||||||
"match",
|
"match",
|
||||||
"matching_algorithm"
|
"matching_algorithm"
|
||||||
)
|
)
|
||||||
list_filter = ("colour", "matching_algorithm")
|
list_filter = ("color", "matching_algorithm")
|
||||||
list_editable = ("colour", "match", "matching_algorithm")
|
list_editable = ("color", "match", "matching_algorithm")
|
||||||
|
|
||||||
|
|
||||||
class DocumentTypeAdmin(admin.ModelAdmin):
|
class DocumentTypeAdmin(admin.ModelAdmin):
|
||||||
|
|||||||
@@ -123,10 +123,11 @@ class DocumentClassifier(object):
|
|||||||
m.update(y.to_bytes(4, 'little', signed=True))
|
m.update(y.to_bytes(4, 'little', signed=True))
|
||||||
labels_correspondent.append(y)
|
labels_correspondent.append(y)
|
||||||
|
|
||||||
tags = [tag.pk for tag in doc.tags.filter(
|
tags = sorted([tag.pk for tag in doc.tags.filter(
|
||||||
matching_algorithm=MatchingModel.MATCH_AUTO
|
matching_algorithm=MatchingModel.MATCH_AUTO
|
||||||
)]
|
)])
|
||||||
m.update(bytearray(tags))
|
for tag in tags:
|
||||||
|
m.update(tag.to_bytes(4, 'little', signed=True))
|
||||||
labels_tags.append(tags)
|
labels_tags.append(tags)
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from django.db.models import Q
|
||||||
from django_filters.rest_framework import BooleanFilter, FilterSet, Filter
|
from django_filters.rest_framework import BooleanFilter, FilterSet, Filter
|
||||||
|
|
||||||
from .models import Correspondent, Document, Tag, DocumentType, Log
|
from .models import Correspondent, Document, Tag, DocumentType, Log
|
||||||
@@ -70,6 +71,16 @@ class InboxFilter(Filter):
|
|||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class TitleContentFilter(Filter):
|
||||||
|
|
||||||
|
def filter(self, qs, value):
|
||||||
|
if value:
|
||||||
|
return qs.filter(Q(title__icontains=value) |
|
||||||
|
Q(content__icontains=value))
|
||||||
|
else:
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
class DocumentFilterSet(FilterSet):
|
class DocumentFilterSet(FilterSet):
|
||||||
|
|
||||||
is_tagged = BooleanFilter(
|
is_tagged = BooleanFilter(
|
||||||
@@ -85,6 +96,8 @@ class DocumentFilterSet(FilterSet):
|
|||||||
|
|
||||||
is_in_inbox = InboxFilter()
|
is_in_inbox = InboxFilter()
|
||||||
|
|
||||||
|
title_content = TitleContentFilter()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Document
|
model = Document
|
||||||
fields = {
|
fields = {
|
||||||
|
|||||||
70
src/documents/migrations/1013_migrate_tag_colour.py
Normal file
70
src/documents/migrations/1013_migrate_tag_colour.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# Generated by Django 3.1.4 on 2020-12-02 21:43
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
COLOURS_OLD = {
|
||||||
|
1: "#a6cee3",
|
||||||
|
2: "#1f78b4",
|
||||||
|
3: "#b2df8a",
|
||||||
|
4: "#33a02c",
|
||||||
|
5: "#fb9a99",
|
||||||
|
6: "#e31a1c",
|
||||||
|
7: "#fdbf6f",
|
||||||
|
8: "#ff7f00",
|
||||||
|
9: "#cab2d6",
|
||||||
|
10: "#6a3d9a",
|
||||||
|
11: "#b15928",
|
||||||
|
12: "#000000",
|
||||||
|
13: "#cccccc",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
Tag = apps.get_model('documents', 'Tag')
|
||||||
|
|
||||||
|
for tag in Tag.objects.all():
|
||||||
|
colour_old_id = tag.colour_old
|
||||||
|
rgb = COLOURS_OLD[colour_old_id]
|
||||||
|
tag.color = rgb
|
||||||
|
tag.save()
|
||||||
|
|
||||||
|
|
||||||
|
def reverse(apps, schema_editor):
|
||||||
|
Tag = apps.get_model('documents', 'Tag')
|
||||||
|
|
||||||
|
def _get_colour_id(rdb):
|
||||||
|
for idx, rdbx in COLOURS_OLD.items():
|
||||||
|
if rdbx == rdb:
|
||||||
|
return idx
|
||||||
|
# Return colour 1 if we can't match anything
|
||||||
|
return 1
|
||||||
|
|
||||||
|
for tag in Tag.objects.all():
|
||||||
|
colour_id = _get_colour_id(tag.color)
|
||||||
|
tag.colour_old = colour_id
|
||||||
|
tag.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('documents', '1012_fix_archive_files'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='tag',
|
||||||
|
old_name='colour',
|
||||||
|
new_name='colour_old',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tag',
|
||||||
|
name='color',
|
||||||
|
field=models.CharField(default='#a6cee3', max_length=7, verbose_name='color'),
|
||||||
|
),
|
||||||
|
migrations.RunPython(forward, reverse),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='tag',
|
||||||
|
name='colour_old',
|
||||||
|
)
|
||||||
|
]
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user