mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-02-28 14:26:24 +00:00
Compare commits
567 Commits
feature-ac
...
v2.20.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47f9f642a9 | ||
|
|
8bfebc3b9b | ||
|
|
c7f83212a3 | ||
|
|
b010f65ae7 | ||
|
|
89d3a53603 | ||
|
|
9601b3d597 | ||
|
|
13e07844fe | ||
|
|
be82fcb70a | ||
|
|
98298e37cd | ||
|
|
35be0850ec | ||
|
|
1bb4b9b473 | ||
|
|
f85094dc2b | ||
|
|
65ca78e9e7 | ||
|
|
5c1bbcd06d | ||
|
|
bc734798e3 | ||
|
|
5ecbfc9df7 | ||
|
|
e63b62d531 | ||
|
|
dd3ec83569 | ||
|
|
7a23356898 | ||
|
|
afaf39e43a | ||
|
|
0dc58cf686 | ||
|
|
505ff31748 | ||
|
|
3c51b3f9cd | ||
|
|
5b45b89d35 | ||
|
|
1188a89369 | ||
|
|
5b9bb147cf | ||
|
|
c278f52fb2 | ||
|
|
d27a5f688a | ||
|
|
c5bb5b237b | ||
|
|
11a5714cba | ||
|
|
4363567fa7 | ||
|
|
3e41d99a82 | ||
|
|
5cc3c087d9 | ||
|
|
c8c4c7c749 | ||
|
|
836c81e037 | ||
|
|
e4b861d76f | ||
|
|
6913f9d79c | ||
|
|
891f4a2faf | ||
|
|
2312314aa7 | ||
|
|
72e8b73108 | ||
|
|
5c9ff367e3 | ||
|
|
94f6b8d36d | ||
|
|
32d04e1fd3 | ||
|
|
56c744fd56 | ||
|
|
1f432a3378 | ||
|
|
d1aa76e4ce | ||
|
|
5381bc5907 | ||
|
|
16cc704539 | ||
|
|
245d9fb4a1 | ||
|
|
771f3f150a | ||
|
|
ecfeff5054 | ||
|
|
37477d391e | ||
|
|
2f1cd31e31 | ||
|
|
742c136773 | ||
|
|
71ecdc528e | ||
|
|
00ec8a577b | ||
|
|
3618c50b62 | ||
|
|
6f4497185e | ||
|
|
e816269db5 | ||
|
|
d4e60e13bf | ||
|
|
cb091665e2 | ||
|
|
00bb92e3e1 | ||
|
|
11ec676909 | ||
|
|
7c457466b7 | ||
|
|
65aed2405c | ||
|
|
985dc9be31 | ||
|
|
305d764805 | ||
|
|
eca2ba3657 | ||
|
|
220c70b27d | ||
|
|
ccaebabe0a | ||
|
|
598540fda0 | ||
|
|
b1a75b0166 | ||
|
|
bf38ae98f1 | ||
|
|
84c59f45da | ||
|
|
d8397ac77e | ||
|
|
7c0a13b339 | ||
|
|
7c8db78a62 | ||
|
|
04f81bf17d | ||
|
|
f96a29db5d | ||
|
|
5af3039d62 | ||
|
|
078cba4bd1 | ||
|
|
43e29598b3 | ||
|
|
d9a596d67a | ||
|
|
a1026f03db | ||
|
|
6c8a9b0373 | ||
|
|
7130c0bd06 | ||
|
|
d391fdec64 | ||
|
|
4d7aa8e1a2 | ||
|
|
9bdbfd362f | ||
|
|
9ba1d93e15 | ||
|
|
a9c73e2846 | ||
|
|
332136df8b | ||
|
|
3a1d33225e | ||
|
|
e770ff572e | ||
|
|
402f2ead59 | ||
|
|
3b4d958b97 | ||
|
|
3f81b432ec | ||
|
|
66d363bdc5 | ||
|
|
c845cf0a19 | ||
|
|
317f239d09 | ||
|
|
128c3539d5 | ||
|
|
26975868a0 | ||
|
|
f3fc3febf1 | ||
|
|
8efc998687 | ||
|
|
3f47900f06 | ||
|
|
963a519e5c | ||
|
|
59e5d15cf0 | ||
|
|
ef2f65fcb8 | ||
|
|
555ba8bb19 | ||
|
|
01992bb5c6 | ||
|
|
21032ac008 | ||
|
|
b63e095a60 | ||
|
|
ce642409e8 | ||
|
|
2e5bd02e7e | ||
|
|
7032da53c5 | ||
|
|
6f3451bce0 | ||
|
|
8c5b5cd77b | ||
|
|
919c54c6ba | ||
|
|
4632ad3a36 | ||
|
|
0c43b50f01 | ||
|
|
67d079fe14 | ||
|
|
ca674e5a02 | ||
|
|
71e08a1e98 | ||
|
|
1e61a6cd6a | ||
|
|
a76731ca89 | ||
|
|
ffc56bddda | ||
|
|
4c2cc373f2 | ||
|
|
76bb6d3422 | ||
|
|
85a2a0a416 | ||
|
|
5036aa1ea3 | ||
|
|
f7da273ab7 | ||
|
|
93338a0a82 | ||
|
|
a96db50b0a | ||
|
|
c5e80a7e4f | ||
|
|
bc622d67fc | ||
|
|
4a8d3c858c | ||
|
|
8c335321cd | ||
|
|
27966858fd | ||
|
|
d3bfb186e0 | ||
|
|
cf5ac596ed | ||
|
|
25b5e8fede | ||
|
|
80be6793cf | ||
|
|
7b175ec1b3 | ||
|
|
36d45ecf4d | ||
|
|
4bf681387a | ||
|
|
c05d75dab0 | ||
|
|
7a50157164 | ||
|
|
a93d83119e | ||
|
|
addaf92a61 | ||
|
|
8c7fa4e165 | ||
|
|
22a47a28dc | ||
|
|
20d921142e | ||
|
|
1ed8f1d086 | ||
|
|
46853e10dc | ||
|
|
c31c244b54 | ||
|
|
56493d6640 | ||
|
|
f7f94762b6 | ||
|
|
beb5fe2232 | ||
|
|
c924213f32 | ||
|
|
b053b35332 | ||
|
|
a45692aa0f | ||
|
|
c3ac102eba | ||
|
|
0e5ab7f3e0 | ||
|
|
533b64cb70 | ||
|
|
b3d6359afc | ||
|
|
b6e3827ab1 | ||
|
|
7cd802cf48 | ||
|
|
7470b799a3 | ||
|
|
b5df90156e | ||
|
|
733d2e19a0 | ||
|
|
fe7419484b | ||
|
|
0d827e8511 | ||
|
|
69514d8d70 | ||
|
|
dd6f7fad32 | ||
|
|
c5ad148dc7 | ||
|
|
b12f1e757c | ||
|
|
0cbab1ae80 | ||
|
|
0219df5b67 | ||
|
|
005ef4fce6 | ||
|
|
44f0191bfb | ||
|
|
e9f846ca24 | ||
|
|
2049497b76 | ||
|
|
2a9d1fce0d | ||
|
|
808c074f48 | ||
|
|
7927e5c436 | ||
|
|
cac48c9855 | ||
|
|
3fda648f37 | ||
|
|
95736eebc4 | ||
|
|
85027dbffd | ||
|
|
74f72e417d | ||
|
|
fe3c424d7d | ||
|
|
a0172a2754 | ||
|
|
810bf3d612 | ||
|
|
846cc47565 | ||
|
|
1d396d9160 | ||
|
|
2a4e8f9acd | ||
|
|
a9dfe8f3f7 | ||
|
|
906e841ded | ||
|
|
6684e80ffc | ||
|
|
3dc7cf3da1 | ||
|
|
819f606335 | ||
|
|
ad45e3f747 | ||
|
|
74b10db028 | ||
|
|
cffb9c34f0 | ||
|
|
6f52614817 | ||
|
|
a0d3527d20 | ||
|
|
4e64ca7ca6 | ||
|
|
e9511bd3da | ||
|
|
8b9ca75a90 | ||
|
|
9f0a4ac19d | ||
|
|
8f969ecab5 | ||
|
|
245e52a4eb | ||
|
|
a8c75d95d8 | ||
|
|
d6e2456baf | ||
|
|
3b75d3271e | ||
|
|
e88816d141 | ||
|
|
e5bd4713ac | ||
|
|
b9aced07fb | ||
|
|
6b55740f56 | ||
|
|
9aee063347 | ||
|
|
7fe411bb1a | ||
|
|
34b5f4c565 | ||
|
|
3808a4e14a | ||
|
|
3bd4135aba | ||
|
|
b60fb8ed82 | ||
|
|
3f32ed319a | ||
|
|
03e6d58f86 | ||
|
|
c197487374 | ||
|
|
d718d7d29f | ||
|
|
ce112cda0e | ||
|
|
d904aaef60 | ||
|
|
35bc673648 | ||
|
|
d0bd111eab | ||
|
|
cd81f750b4 | ||
|
|
48d21da13b | ||
|
|
701aafce06 | ||
|
|
cbe8bc35d6 | ||
|
|
1c4fa7237c | ||
|
|
63dab0ab09 | ||
|
|
276dc31abe | ||
|
|
a11a2ec13f | ||
|
|
df9136e7d4 | ||
|
|
1d8fadcb3c | ||
|
|
4e85262781 | ||
|
|
7e5d80fa38 | ||
|
|
3cfd64b77a | ||
|
|
0fc595a16a | ||
|
|
91e2220f23 | ||
|
|
893c05dfdc | ||
|
|
faf3e8dc0d | ||
|
|
41b9fff407 | ||
|
|
26f61c900f | ||
|
|
8d0e07e931 | ||
|
|
bf9e3fca48 | ||
|
|
144dd8cdf3 | ||
|
|
13161ebb01 | ||
|
|
0ebd9f24b5 | ||
|
|
c9f49f390a | ||
|
|
31cee7481b | ||
|
|
78893292f8 | ||
|
|
e4ac079cd7 | ||
|
|
597c2629dd | ||
|
|
264166810c | ||
|
|
a8b42a3c7a | ||
|
|
af7bac03f1 | ||
|
|
363fd5a97f | ||
|
|
fcae006afa | ||
|
|
a206ac78dd | ||
|
|
4456be72c6 | ||
|
|
557612f7a2 | ||
|
|
bf18eaa5c2 | ||
|
|
d1c11bcf08 | ||
|
|
15e6809a71 | ||
|
|
7326224888 | ||
|
|
04a01fb9f4 | ||
|
|
340754d865 | ||
|
|
39c429bb87 | ||
|
|
8686f264cf | ||
|
|
f6c004183e | ||
|
|
d394053ddc | ||
|
|
a36c28418c | ||
|
|
f0d1c75fac | ||
|
|
495159f0b2 | ||
|
|
33fd8a6579 | ||
|
|
e08e34fb90 | ||
|
|
6164bac66e | ||
|
|
df86882e8e | ||
|
|
79b30fbade | ||
|
|
d609b386fe | ||
|
|
502bbb2420 | ||
|
|
27574009e1 | ||
|
|
bd73555ecc | ||
|
|
613c922dd2 | ||
|
|
1659aa08e4 | ||
|
|
68dfb4a930 | ||
|
|
3c439b970f | ||
|
|
962f7994d1 | ||
|
|
93eea80f3e | ||
|
|
5bc27eb4b2 | ||
|
|
b19701cb96 | ||
|
|
9c552bc2d7 | ||
|
|
80fabb0b56 | ||
|
|
af1c235af5 | ||
|
|
92ee906701 | ||
|
|
d6710de486 | ||
|
|
f71b13b82a | ||
|
|
3df43d828a | ||
|
|
643e2b4a8e | ||
|
|
6fa896df39 | ||
|
|
6aeb5a5503 | ||
|
|
86dbeb3a27 | ||
|
|
e97217f267 | ||
|
|
05d5d7e796 | ||
|
|
ab7875cc76 | ||
|
|
e8957de4a7 | ||
|
|
1717517e70 | ||
|
|
af544177d4 | ||
|
|
766af6a48a | ||
|
|
e985051890 | ||
|
|
764ad059d1 | ||
|
|
5e47069934 | ||
|
|
4ff09c4cf4 | ||
|
|
53b393dab5 | ||
|
|
0114993ac6 | ||
|
|
6119c215e7 | ||
|
|
8d1f23e9d6 | ||
|
|
c8850fa752 | ||
|
|
19a54b3b23 | ||
|
|
1cdd8d9ba8 | ||
|
|
4449dbadb5 | ||
|
|
43b4f36026 | ||
|
|
0e35acaef5 | ||
|
|
19ff339804 | ||
|
|
6b868a5ecb | ||
|
|
6231211f9b | ||
|
|
6dbd32759d | ||
|
|
e0512e35a2 | ||
|
|
4cff907ba0 | ||
|
|
4b32c3228e | ||
|
|
4ddac79f0f | ||
|
|
d4be3bd31d | ||
|
|
d5aba09de9 | ||
|
|
f2ef9af291 | ||
|
|
4905edbf79 | ||
|
|
feb5d534b5 | ||
|
|
d230514dd3 | ||
|
|
1709aee903 | ||
|
|
3e4aa87cc5 | ||
|
|
fc95d42b35 | ||
|
|
c4346124c3 | ||
|
|
44b8c4881a | ||
|
|
d3d8eef0b6 | ||
|
|
a283c1c320 | ||
|
|
f3220ce981 | ||
|
|
2dc4f1f49b | ||
|
|
17509171bb | ||
|
|
9e11e7fd05 | ||
|
|
84942a4e69 | ||
|
|
48168df320 | ||
|
|
cec665f8d5 | ||
|
|
8adc26e09d | ||
|
|
84d85d7a23 | ||
|
|
71f20f62d0 | ||
|
|
a94a8e4c6f | ||
|
|
7a1aae7749 | ||
|
|
894939e492 | ||
|
|
f431578f43 | ||
|
|
1b18c14188 | ||
|
|
d721a88a2f | ||
|
|
f7b4d38e39 | ||
|
|
46cf6b4583 | ||
|
|
2d701c5c1b | ||
|
|
1123d845ec | ||
|
|
dfa6308ca4 | ||
|
|
b5a17a8d11 | ||
|
|
cfac74319f | ||
|
|
f9f069b092 | ||
|
|
b2703b4605 | ||
|
|
852eb0ef36 | ||
|
|
0870d42eae | ||
|
|
e2cf95f8af | ||
|
|
a79c8dc51c | ||
|
|
4b95c2f0e5 | ||
|
|
e1c8cd779b | ||
|
|
cc7c7f31ba | ||
|
|
1d30ce2afa | ||
|
|
5aa86f8755 | ||
|
|
de2ddad5ee | ||
|
|
d2064a2535 | ||
|
|
cc621cf729 | ||
|
|
fc4134e15c | ||
|
|
ac1b420966 | ||
|
|
80595899c1 | ||
|
|
9463a8fd26 | ||
|
|
58ab137282 | ||
|
|
05c216b2a8 | ||
|
|
d6db2d3fce | ||
|
|
a6e41b4145 | ||
|
|
cb927c5b22 | ||
|
|
107374af71 | ||
|
|
a77141e133 | ||
|
|
117dfb83fe | ||
|
|
fdef774a16 | ||
|
|
08887cb8e3 | ||
|
|
7b679e11bc | ||
|
|
dbbebaeb89 | ||
|
|
d9459ac37f | ||
|
|
4e0f5dff95 | ||
|
|
10ccccc987 | ||
|
|
27d72ebb18 | ||
|
|
909ccebb34 | ||
|
|
4275e18c10 | ||
|
|
0088333360 | ||
|
|
ed1d488d6e | ||
|
|
b25b15ba32 | ||
|
|
f2fabc81d4 | ||
|
|
f94c3eeea8 | ||
|
|
bf468ac64f | ||
|
|
22064ed004 | ||
|
|
23daa0b974 | ||
|
|
7b63f5a98c | ||
|
|
7c76377477 | ||
|
|
56c70bf177 | ||
|
|
daf47f377b | ||
|
|
64f31cac0c | ||
|
|
dcc503c35f | ||
|
|
a583cff21c | ||
|
|
bfd468103b | ||
|
|
be0c1fd1ed | ||
|
|
82370963da | ||
|
|
0fdfa42a83 | ||
|
|
0f0ba92e15 | ||
|
|
5f0281e427 | ||
|
|
a0c7785881 | ||
|
|
349fbce579 | ||
|
|
217b004884 | ||
|
|
29c36542fa | ||
|
|
d5b87aeffb | ||
|
|
9225a38458 | ||
|
|
3fa89b85d7 | ||
|
|
5e6b49971f | ||
|
|
be63c79db1 | ||
|
|
26c70b69c4 | ||
|
|
e0b0dd8548 | ||
|
|
1bbac9948a | ||
|
|
ca9b5d9586 | ||
|
|
521fd1c957 | ||
|
|
f00a565130 | ||
|
|
d878bc153a | ||
|
|
f5e6951910 | ||
|
|
91102d0335 | ||
|
|
82ec1be622 | ||
|
|
01a8cf6f36 | ||
|
|
6bdb365f87 | ||
|
|
696e591a3b | ||
|
|
3c2782c3a9 | ||
|
|
a68800d53c | ||
|
|
52a937cdcc | ||
|
|
00e629d957 | ||
|
|
243b3bc812 | ||
|
|
0ccc2da9bb | ||
|
|
b6dbbec019 | ||
|
|
b1c406680f | ||
|
|
42bdbc1b2d | ||
|
|
2f529a9500 | ||
|
|
ee6b700243 | ||
|
|
b1a84c65ed | ||
|
|
edb8c06e2a | ||
|
|
1b6ec65f6e | ||
|
|
6d72ee795f | ||
|
|
6730896894 | ||
|
|
5d6ea70434 | ||
|
|
fac1ee4283 | ||
|
|
1bee1495cf | ||
|
|
6dca4daea5 | ||
|
|
54e2b916e6 | ||
|
|
ea62e30c90 | ||
|
|
91511b45cd | ||
|
|
b5dd751b67 | ||
|
|
07c298523a | ||
|
|
0ea159683d | ||
|
|
f0b6e79d14 | ||
|
|
302cb22ec6 | ||
|
|
4210addb46 | ||
|
|
06746b4b31 | ||
|
|
2f5533a179 | ||
|
|
2f267341f8 | ||
|
|
88befee527 | ||
|
|
c4a7186cd2 | ||
|
|
d974f092aa | ||
|
|
23501b9060 | ||
|
|
f09965464a | ||
|
|
ae5bd2d2fd | ||
|
|
2b73007e7e | ||
|
|
8505fa3e54 | ||
|
|
a51093afc2 | ||
|
|
2fdae59288 | ||
|
|
4637f5c5e5 | ||
|
|
5e7ee924ff | ||
|
|
fded55dc70 | ||
|
|
20da51278e | ||
|
|
293c84d871 | ||
|
|
1fe8599266 | ||
|
|
5410074062 | ||
|
|
4b8f6ed643 | ||
|
|
f8689c4819 | ||
|
|
cebc227701 | ||
|
|
814df94e8d | ||
|
|
fa496dfc8d | ||
|
|
924471b59c | ||
|
|
feb320cae9 | ||
|
|
9178af5fb2 | ||
|
|
850444c2fc | ||
|
|
90baba2cec | ||
|
|
9889c59d3d | ||
|
|
3d2a3ede71 | ||
|
|
bc019fab96 | ||
|
|
1cd21d0f38 | ||
|
|
f940ed0b7b | ||
|
|
3180ccf4cb | ||
|
|
43abb0541b | ||
|
|
a3a405354f | ||
|
|
09e98d600e | ||
|
|
01a39b9bb4 | ||
|
|
3b0b40f071 | ||
|
|
6dce83865f | ||
|
|
18252a19d7 | ||
|
|
733a9674d6 | ||
|
|
f3b6e15321 | ||
|
|
6591d5da63 | ||
|
|
c974dc9400 | ||
|
|
1671d49d44 | ||
|
|
6b248ef140 | ||
|
|
735681d294 | ||
|
|
a9085c65c5 | ||
|
|
e312425b1c | ||
|
|
13fe064f6e | ||
|
|
958f98d7e5 | ||
|
|
dfad3c4d8e | ||
|
|
37267f3f04 | ||
|
|
b34538d991 | ||
|
|
fc97bd1315 | ||
|
|
dbf3721ec2 | ||
|
|
59afbe09b1 | ||
|
|
bfeaa1b119 | ||
|
|
e1c3124698 | ||
|
|
f2e22e103b | ||
|
|
dda94f013e | ||
|
|
caf00e7ead | ||
|
|
f214440d2e | ||
|
|
240c9ac511 | ||
|
|
a2c9bc346a | ||
|
|
b1cbb1c73a | ||
|
|
434b1e3245 | ||
|
|
497fdcaf4e | ||
|
|
52b95f2b62 | ||
|
|
83391af866 | ||
|
|
0a1786f39b | ||
|
|
3b069ac034 | ||
|
|
07882b918b | ||
|
|
cc5ba71f06 | ||
|
|
f16b8fbe2a | ||
|
|
fe54d99356 | ||
|
|
a49efb07ea | ||
|
|
e4fd008441 | ||
|
|
de12023311 | ||
|
|
60ebdc0ad6 |
@@ -10,10 +10,8 @@ component_management:
|
||||
paths:
|
||||
- src-ui/**
|
||||
# https://docs.codecov.com/docs/pull-request-comments
|
||||
# codecov will only comment if coverage changes
|
||||
comment:
|
||||
layout: "header, diff, components, flags, files"
|
||||
require_changes: true
|
||||
# https://docs.codecov.com/docs/javascript-bundle-analysis
|
||||
require_bundle_changes: true
|
||||
bundle_change_threshold: "50Kb"
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[codespell]
|
||||
write-changes = True
|
||||
ignore-words-list = criterias,afterall,valeu,ureue,equest,ure,assertIn
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
FROM --platform=$BUILDPLATFORM docker.io/node:20-bookworm-slim as main-app
|
||||
FROM --platform=$BUILDPLATFORM docker.io/node:20-trixie-slim as main-app
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -8,16 +8,17 @@ ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG TARGETARCH
|
||||
|
||||
# Can be workflow provided, defaults set for manual building
|
||||
ARG JBIG2ENC_VERSION=0.29
|
||||
ARG QPDF_VERSION=11.9.0
|
||||
ARG GS_VERSION=10.03.1
|
||||
ARG JBIG2ENC_VERSION=0.30
|
||||
|
||||
# Set Python environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
# Ignore warning from Whitenoise
|
||||
PYTHONWARNINGS="ignore:::django.http.response:517" \
|
||||
PNGX_CONTAINERIZED=1
|
||||
PNGX_CONTAINERIZED=1 \
|
||||
# https://docs.astral.sh/uv/reference/settings/#link-mode
|
||||
UV_LINK_MODE=copy \
|
||||
UV_CACHE_DIR=/cache/uv/
|
||||
|
||||
#
|
||||
# Begin installation and configuration
|
||||
@@ -83,37 +84,15 @@ RUN set -eux \
|
||||
&& apt-get update \
|
||||
&& apt-get install --yes --quiet ${PYTHON_PACKAGES}
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.7.8 /uv /bin/uv
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.9.10 /uv /bin/uv
|
||||
|
||||
|
||||
RUN set -eux \
|
||||
&& echo "Installing pre-built updates" \
|
||||
&& echo "Installing qpdf ${QPDF_VERSION}" \
|
||||
&& curl --fail --silent --show-error --location \
|
||||
--output libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& curl --fail --silent --show-error --location \
|
||||
--output qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& dpkg --install ./libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& dpkg --install ./qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& echo "Installing Ghostscript ${GS_VERSION}" \
|
||||
&& curl --fail --silent --show-error --location \
|
||||
--output libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
&& curl --fail --silent --show-error --location \
|
||||
--output ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
&& curl --fail --silent --show-error --location \
|
||||
--output libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
|
||||
&& dpkg --install ./libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
|
||||
&& dpkg --install ./libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
&& dpkg --install ./ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
&& echo "Installing jbig2enc" \
|
||||
&& curl --fail --silent --show-error --location \
|
||||
--output jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/jbig2enc-${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/jbig2enc-trixie-v${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& dpkg --install ./jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb
|
||||
|
||||
# setup docker-specific things
|
||||
@@ -127,6 +106,7 @@ COPY [ \
|
||||
|
||||
RUN set -eux \
|
||||
&& echo "Configuring ImageMagick" \
|
||||
&& mkdir -p /etc/ImageMagick-6 \
|
||||
&& mv paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
||||
|
||||
|
||||
@@ -142,7 +122,7 @@ ARG BUILD_PACKAGES="\
|
||||
pkg-config"
|
||||
|
||||
# hadolint ignore=DL3042
|
||||
RUN --mount=type=cache,target=/root/.cache/uv,id=pip-cache \
|
||||
RUN --mount=type=cache,target=/cache/uv/,id=uv-cache \
|
||||
set -eux \
|
||||
&& echo "Installing build system packages" \
|
||||
&& apt-get update \
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml",
|
||||
"service": "paperless-development",
|
||||
"workspaceFolder": "/usr/src/paperless/paperless-ngx",
|
||||
"postCreateCommand": "/bin/bash -c 'uv sync --group dev && uv run pre-commit install'",
|
||||
"postCreateCommand": "/bin/bash -c 'rm -rf .venv/.* && uv sync --group dev && uv run pre-commit install'",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#
|
||||
# This file is intended only to be used through VSCOde devcontainers. See README.md
|
||||
# in the folder .devcontainer.
|
||||
|
||||
services:
|
||||
broker:
|
||||
image: docker.io/library/redis:7
|
||||
@@ -50,7 +49,6 @@ services:
|
||||
- ./data:/usr/src/paperless/paperless-ngx/data
|
||||
- ./media:/usr/src/paperless/paperless-ngx/media
|
||||
- ./consume:/usr/src/paperless/paperless-ngx/consume
|
||||
- ~/.gitconfig:/usr/src/paperless/.gitconfig:ro
|
||||
environment:
|
||||
PAPERLESS_REDIS: redis://broker:6379
|
||||
PAPERLESS_TIKA_ENABLED: 1
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
{
|
||||
"python.testing.pytestArgs": [
|
||||
"src"
|
||||
],
|
||||
"python.testing.pytestArgs": [],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true,
|
||||
"files.watcherExclude": {
|
||||
"**/.venv/**": true,
|
||||
"**/pytest_cache/**": true
|
||||
}
|
||||
},
|
||||
"python.testing.cwd": "${workspaceFolder}/src"
|
||||
}
|
||||
|
||||
@@ -116,9 +116,9 @@
|
||||
},
|
||||
{
|
||||
"label": "Maintenance: Build Documentation",
|
||||
"description": "Build the documentation with MkDocs",
|
||||
"description": "Build the documentation with Zensical",
|
||||
"type": "shell",
|
||||
"command": "uv run mkdocs build --config-file mkdocs.yml && uv run mkdocs serve",
|
||||
"command": "uv run zensical build && uv run zensical serve",
|
||||
"group": "none",
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
|
||||
@@ -11,6 +11,10 @@ end_of_line = lf
|
||||
charset = utf-8
|
||||
max_line_length = 79
|
||||
|
||||
[*.sh]
|
||||
indent_style = tab
|
||||
indent_size = 1
|
||||
|
||||
[{*.html,*.css,*.js}]
|
||||
max_line_length = off
|
||||
|
||||
|
||||
55
.github/DISCUSSION_TEMPLATE/support.yml
vendored
Normal file
55
.github/DISCUSSION_TEMPLATE/support.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
title: "[Support] "
|
||||
body:
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: What's your question or issue?
|
||||
description: Provide a clear and concise description of what you're trying to do, and what's going wrong.
|
||||
placeholder: |
|
||||
I'm trying to...
|
||||
|
||||
[Include screenshots if helpful]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: What have you tried?
|
||||
description: Describe any steps you've already taken to troubleshoot or solve the issue.
|
||||
placeholder: |
|
||||
- I checked the logs and saw...
|
||||
- I followed the install guide and tried...
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Paperless-ngx version
|
||||
placeholder: e.g. 1.14.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: host-os
|
||||
attributes:
|
||||
label: Host OS
|
||||
description: Include architecture if relevant.
|
||||
placeholder: e.g. Ubuntu 22.04 / Raspberry Pi arm64
|
||||
- type: dropdown
|
||||
id: install-method
|
||||
attributes:
|
||||
label: Installation method
|
||||
options:
|
||||
- Docker - official image
|
||||
- Docker - linuxserver.io image
|
||||
- Bare metal
|
||||
- Other (please describe above)
|
||||
- type: textarea
|
||||
id: system-status
|
||||
attributes:
|
||||
label: System status
|
||||
description: If available, copy & paste the system status output from Settings > System Status > Copy
|
||||
render: json
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant logs or output
|
||||
description: If you have logs, errors that might help, paste it here. For example other containers or services (database, redis, etc).
|
||||
render: bash
|
||||
10
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
10
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -6,8 +6,8 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### ⚠️ Please remember: issues are for *bugs*
|
||||
That is, something you believe affects every single user of Paperless-ngx, not just you. If you're not sure, start with one of the other options below.
|
||||
### ⚠️ Please remember: issues are for *bugs* only! ⚠️
|
||||
That is, something you believe affects every single user of Paperless-ngx (and the demo, for example), not just you. If you are not sure, start with one of the other options below.
|
||||
|
||||
Also, note that **Paperless-ngx does not perform OCR or archive file creation itself**, those are handled by other tools. Problems with OCR or archive versions of specific files should likely be raised 'upstream', see https://github.com/ocrmypdf/OCRmyPDF/issues or https://github.com/tesseract-ocr/tesseract/issues
|
||||
- type: markdown
|
||||
@@ -59,6 +59,12 @@ body:
|
||||
label: Browser logs
|
||||
description: Logs from the web browser related to your issue, if needed
|
||||
render: bash
|
||||
- type: textarea
|
||||
id: logs_services
|
||||
attributes:
|
||||
label: Services logs
|
||||
description: Logs from other services (or containers) related to your issue, if needed. For example, the database or redis logs.
|
||||
render: bash
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -35,8 +35,8 @@ NOTE: PRs that do not address the following will not be merged, please do not sk
|
||||
|
||||
- [ ] I have read & agree with the [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md).
|
||||
- [ ] If applicable, I have included testing coverage for new code in this PR, for [backend](https://docs.paperless-ngx.com/development/#testing) and / or [front-end](https://docs.paperless-ngx.com/development/#testing-and-code-style) changes.
|
||||
- [ ] If applicable, I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
|
||||
- [ ] If applicable, I have tested my code for breaking changes & regressions on both mobile & desktop devices, using the latest version of major browsers.
|
||||
- [ ] If applicable, I have checked that all tests pass, see [documentation](https://docs.paperless-ngx.com/development/#back-end-development).
|
||||
- [ ] I have run all `pre-commit` hooks, see [documentation](https://docs.paperless-ngx.com/development/#code-formatting-with-pre-commit-hooks).
|
||||
- [ ] I have made corresponding changes to the documentation as needed.
|
||||
- [ ] I have checked my modifications for any breaking changes.
|
||||
- [ ] In the description of the PR above I have disclosed the use of AI tools in the coding of this PR.
|
||||
|
||||
53
.github/dependabot.yml
vendored
53
.github/dependabot.yml
vendored
@@ -1,6 +1,5 @@
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
# Required for uv support for now
|
||||
enable-beta-ecosystems: true
|
||||
@@ -42,30 +41,56 @@ updates:
|
||||
- "backend"
|
||||
- "dependencies"
|
||||
groups:
|
||||
# Development & CI/CD Tooling
|
||||
development:
|
||||
patterns:
|
||||
- "*pytest*"
|
||||
- "ruff"
|
||||
- "mkdocs-material"
|
||||
- "zensical"
|
||||
- "pre-commit*"
|
||||
django:
|
||||
# Django & DRF Ecosystem
|
||||
django-ecosystem:
|
||||
patterns:
|
||||
- "*django*"
|
||||
- "drf-*"
|
||||
major-versions:
|
||||
- "djangorestframework"
|
||||
- "whitenoise"
|
||||
- "bleach"
|
||||
- "jinja2"
|
||||
# Async, Task Queuing & Caching
|
||||
async-tasks:
|
||||
patterns:
|
||||
- "celery*"
|
||||
- "channels*"
|
||||
- "flower"
|
||||
- "redis"
|
||||
# Document, PDF, and OCR Processing
|
||||
document-processing:
|
||||
patterns:
|
||||
- "ocrmypdf"
|
||||
- "pdf2image"
|
||||
- "pyzbar"
|
||||
- "zxing-cpp"
|
||||
- "tika-client"
|
||||
- "gotenberg-client"
|
||||
- "python-magic"
|
||||
- "python-gnupg"
|
||||
# Data, NLP, and Search
|
||||
data-nlp-search:
|
||||
patterns:
|
||||
- "nltk"
|
||||
- "scikit-learn"
|
||||
- "langdetect"
|
||||
- "rapidfuzz"
|
||||
- "whoosh-reloaded"
|
||||
# Utilities (Patch Updates)
|
||||
utilities-patch:
|
||||
update-types:
|
||||
- "major"
|
||||
small-changes:
|
||||
- "patch"
|
||||
# Utilities (Minor Updates)
|
||||
utilities-minor:
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
exclude-patterns:
|
||||
- "*django*"
|
||||
- "drf-*"
|
||||
pre-built:
|
||||
patterns:
|
||||
- psycopg*
|
||||
- zxing-cpp
|
||||
# Enable updates for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
target-branch: "dev"
|
||||
|
||||
1
.github/release-drafter.yml
vendored
1
.github/release-drafter.yml
vendored
@@ -44,6 +44,7 @@ include-labels:
|
||||
- 'notable'
|
||||
exclude-labels:
|
||||
- 'skip-changelog'
|
||||
filter-by-commitish: true
|
||||
category-template: '### $TITLE'
|
||||
change-template: '- $TITLE @$AUTHOR ([#$NUMBER]($URL))'
|
||||
change-title-escapes: '\<*_&#@'
|
||||
|
||||
230
.github/workflows/ci.yml
vendored
230
.github/workflows/ci.yml
vendored
@@ -11,42 +11,89 @@ on:
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- 'translations**'
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
env:
|
||||
DEFAULT_UV_VERSION: "0.7.x"
|
||||
DEFAULT_UV_VERSION: "0.9.x"
|
||||
# This is the default version of Python to use in most steps which aren't specific
|
||||
DEFAULT_PYTHON_VERSION: "3.11"
|
||||
NLTK_DATA: "/usr/share/nltk_data"
|
||||
jobs:
|
||||
detect-duplicate:
|
||||
name: Detect Duplicate Run
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
should_run: ${{ steps.check.outputs.should_run }}
|
||||
steps:
|
||||
- name: Check if workflow should run
|
||||
id: check
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
if (context.eventName !== 'push') {
|
||||
core.info('Not a push event; running workflow.');
|
||||
core.setOutput('should_run', 'true');
|
||||
return;
|
||||
}
|
||||
|
||||
const ref = context.ref || '';
|
||||
if (!ref.startsWith('refs/heads/')) {
|
||||
core.info('Push is not to a branch; running workflow.');
|
||||
core.setOutput('should_run', 'true');
|
||||
return;
|
||||
}
|
||||
|
||||
const branch = ref.substring('refs/heads/'.length);
|
||||
const { owner, repo } = context.repo;
|
||||
const prs = await github.paginate(github.rest.pulls.list, {
|
||||
owner,
|
||||
repo,
|
||||
state: 'open',
|
||||
head: `${owner}:${branch}`,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
if (prs.length === 0) {
|
||||
core.info(`No open PR found for ${branch}; running workflow.`);
|
||||
core.setOutput('should_run', 'true');
|
||||
} else {
|
||||
core.info(`Found ${prs.length} open PR(s) for ${branch}; skipping duplicate push run.`);
|
||||
core.setOutput('should_run', 'false');
|
||||
}
|
||||
pre-commit:
|
||||
# We want to run on external PRs, but not on our own internal PRs as they'll be run
|
||||
# by the push to the branch. Without this if check, checks are duplicated since
|
||||
# internal PRs match both the push and pull_request events.
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
|
||||
needs:
|
||||
- detect-duplicate
|
||||
if: needs.detect-duplicate.outputs.should_run == 'true'
|
||||
name: Linting Checks
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Install python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||
- name: Check files
|
||||
uses: pre-commit/action@v3.0.1
|
||||
documentation:
|
||||
name: "Build & Deploy Documentation"
|
||||
name: "Build Documentation"
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- pre-commit
|
||||
steps:
|
||||
- uses: actions/configure-pages@v5
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Set up Python
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||
enable-cache: true
|
||||
@@ -60,24 +107,30 @@ jobs:
|
||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||
--dev \
|
||||
--frozen \
|
||||
mkdocs build --config-file ./mkdocs.yml
|
||||
- name: Deploy documentation
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
run: |
|
||||
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
|
||||
git config --global user.name "${{ github.actor }}"
|
||||
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
||||
uv run \
|
||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||
--dev \
|
||||
--frozen \
|
||||
mkdocs gh-deploy --force --no-history
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
zensical build --clean
|
||||
- name: Upload documentation artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: documentation
|
||||
path: site/
|
||||
retention-days: 7
|
||||
- uses: actions/upload-pages-artifact@v4
|
||||
with:
|
||||
path: site
|
||||
name: github-pages-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
deploy-documentation:
|
||||
name: "Deploy Documentation"
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- documentation
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- uses: actions/deploy-pages@v4
|
||||
id: deployment
|
||||
with:
|
||||
artifact_name: github-pages-${{ github.run_id }}-${{ github.run_attempt }}
|
||||
tests-backend:
|
||||
name: "Backend Tests (Python ${{ matrix.python-version }})"
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -89,18 +142,18 @@ jobs:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Start containers
|
||||
run: |
|
||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml pull --quiet
|
||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml up --detach
|
||||
- name: Set up Python
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "${{ matrix.python-version }}"
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||
enable-cache: true
|
||||
@@ -121,8 +174,11 @@ jobs:
|
||||
- name: List installed Python dependencies
|
||||
run: |
|
||||
uv pip list
|
||||
- name: Install or update NLTK dependencies
|
||||
run: uv run python -m nltk.downloader punkt punkt_tab snowball_data stopwords -d ${{ env.NLTK_DATA }}
|
||||
- name: Tests
|
||||
env:
|
||||
NLTK_DATA: ${{ env.NLTK_DATA }}
|
||||
PAPERLESS_CI_TEST: 1
|
||||
# Enable paperless_mail testing against real server
|
||||
PAPERLESS_MAIL_TEST_HOST: ${{ secrets.TEST_MAIL_HOST }}
|
||||
@@ -136,15 +192,14 @@ jobs:
|
||||
pytest
|
||||
- name: Upload backend test results to Codecov
|
||||
if: always()
|
||||
uses: codecov/test-results-action@v1
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: backend-python-${{ matrix.python-version }}
|
||||
files: junit.xml
|
||||
report_type: test_results
|
||||
- name: Upload backend coverage to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: backend-python-${{ matrix.python-version }}
|
||||
files: coverage.xml
|
||||
- name: Stop containers
|
||||
@@ -158,13 +213,13 @@ jobs:
|
||||
needs:
|
||||
- pre-commit
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20.x
|
||||
cache: 'pnpm'
|
||||
@@ -191,13 +246,13 @@ jobs:
|
||||
shard-index: [1, 2, 3, 4]
|
||||
shard-count: [4]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20.x
|
||||
cache: 'pnpm'
|
||||
@@ -217,23 +272,26 @@ jobs:
|
||||
- name: Run Jest unit tests
|
||||
run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||
- name: Upload frontend test results to Codecov
|
||||
uses: codecov/test-results-action@v1
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: frontend-node-${{ matrix.node-version }}
|
||||
directory: src-ui/
|
||||
report_type: test_results
|
||||
- name: Upload frontend coverage to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: frontend-node-${{ matrix.node-version }}
|
||||
directory: src-ui/coverage/
|
||||
tests-frontend-e2e:
|
||||
name: "Frontend E2E Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
|
||||
runs-on: ubuntu-24.04
|
||||
container: mcr.microsoft.com/playwright:v1.57.0-noble
|
||||
needs:
|
||||
- install-frontend-dependencies
|
||||
env:
|
||||
PLAYWRIGHT_BROWSERS_PATH: /ms-playwright
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -241,13 +299,13 @@ jobs:
|
||||
shard-index: [1, 2]
|
||||
shard-count: [2]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20.x
|
||||
cache: 'pnpm'
|
||||
@@ -262,19 +320,8 @@ jobs:
|
||||
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
||||
- name: Re-link Angular cli
|
||||
run: cd src-ui && pnpm link @angular/cli
|
||||
- name: Cache Playwright browsers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: ${{ runner.os }}-playwright-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-playwright-
|
||||
- name: Install Playwright system dependencies
|
||||
run: npx playwright install-deps
|
||||
- name: Install dependencies
|
||||
run: cd src-ui && pnpm install --no-frozen-lockfile
|
||||
- name: Install Playwright
|
||||
run: cd src-ui && pnpm exec playwright install
|
||||
- name: Run Playwright e2e tests
|
||||
run: cd src-ui && pnpm exec playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||
frontend-bundle-analysis:
|
||||
@@ -284,13 +331,13 @@ jobs:
|
||||
- tests-frontend
|
||||
- tests-frontend-e2e
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20.x
|
||||
cache: 'pnpm'
|
||||
@@ -310,9 +357,12 @@ jobs:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
run: cd src-ui && pnpm run build --configuration=production
|
||||
build-docker-image:
|
||||
name: Build Docker image for ${{ github.ref_name }}
|
||||
name: Build Docker image for ${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }}
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || startsWith(github.ref, 'refs/heads/fix-') || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/beta' || contains(github.ref, 'beta.rc') || startsWith(github.ref, 'refs/tags/v'))
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
if: (github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || startsWith(github.ref, 'refs/heads/fix-') || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/beta' || contains(github.ref, 'beta.rc') || startsWith(github.ref, 'refs/tags/v') || startsWith(github.ref, 'refs/heads/l10n_'))) || (github.event_name == 'pull_request' && (startsWith(github.head_ref, 'feature-') || startsWith(github.head_ref, 'fix-') || github.head_ref == 'dev' || github.head_ref == 'beta' || contains(github.head_ref, 'beta.rc') || startsWith(github.head_ref, 'l10n_')))
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-build-docker-image-${{ github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
@@ -321,6 +371,23 @@ jobs:
|
||||
- tests-frontend
|
||||
- tests-frontend-e2e
|
||||
steps:
|
||||
- name: Prepare build variables
|
||||
id: build-vars
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const isPR = context.eventName === 'pull_request';
|
||||
const defaultRefName = context.ref.replace('refs/heads/', '');
|
||||
const headRef = isPR ? context.payload.pull_request.head.ref : defaultRefName;
|
||||
const buildRef = isPR ? `refs/heads/${headRef}` : context.ref;
|
||||
const buildCacheKey = headRef.split('/').join('-');
|
||||
const canPush = context.eventName === 'push' || (isPR && context.payload.pull_request.head.repo.full_name === `${context.repo.owner}/${context.repo.repo}`);
|
||||
|
||||
core.setOutput('build-ref', buildRef);
|
||||
core.setOutput('build-ref-name', headRef);
|
||||
core.setOutput('build-cache-key', buildCacheKey);
|
||||
core.setOutput('can-push', canPush ? 'true' : 'false');
|
||||
- name: Check pushing to Docker Hub
|
||||
id: push-other-places
|
||||
# Only push to Dockerhub from the main repo AND the ref is either:
|
||||
@@ -329,8 +396,11 @@ jobs:
|
||||
# beta
|
||||
# a tag
|
||||
# Otherwise forks would require a Docker Hub account and secrets setup
|
||||
env:
|
||||
BUILD_REF: ${{ steps.build-vars.outputs.build-ref }}
|
||||
BUILD_REF_NAME: ${{ steps.build-vars.outputs.build-ref-name }}
|
||||
run: |
|
||||
if [[ ${{ github.repository_owner }} == "paperless-ngx" && ( ${{ github.ref_name }} == "dev" || ${{ github.ref_name }} == "beta" || ${{ startsWith(github.ref, 'refs/tags/v') }} == "true" ) ]] ; then
|
||||
if [[ ${{ github.repository_owner }} == "paperless-ngx" && ( "$BUILD_REF_NAME" == "dev" || "$BUILD_REF_NAME" == "beta" || $BUILD_REF == refs/tags/v* || $BUILD_REF == *beta.rc* ) ]] ; then
|
||||
echo "Enabling DockerHub image push"
|
||||
echo "enable=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
@@ -354,12 +424,14 @@ jobs:
|
||||
tags: |
|
||||
# Tag branches with branch name
|
||||
type=ref,event=branch
|
||||
# Pull requests need a sanitized branch tag for pushing images
|
||||
type=raw,value=${{ steps.build-vars.outputs.build-cache-key }},enable=${{ github.event_name == 'pull_request' }}
|
||||
# Process semver tags
|
||||
# For a tag x.y.z or vX.Y.Z, output an x.y.z and x.y image tag
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
# If https://github.com/docker/buildx/issues/1044 is resolved,
|
||||
# the append input with a native arm64 arch could be used to
|
||||
# significantly speed up building
|
||||
@@ -396,7 +468,7 @@ jobs:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
push: ${{ steps.build-vars.outputs.can-push == 'true' }}
|
||||
tags: ${{ steps.docker-meta.outputs.tags }}
|
||||
labels: ${{ steps.docker-meta.outputs.labels }}
|
||||
build-args: |
|
||||
@@ -404,19 +476,21 @@ jobs:
|
||||
# Get cache layers from this branch, then dev
|
||||
# This allows new branches to get at least some cache benefits, generally from dev
|
||||
cache-from: |
|
||||
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
||||
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ steps.build-vars.outputs.build-cache-key }}
|
||||
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:dev
|
||||
cache-to: |
|
||||
type=registry,mode=max,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
||||
cache-to: ${{ steps.build-vars.outputs.can-push == 'true' && format('type=registry,mode=max,ref=ghcr.io/{0}/builder/cache/app:{1}', steps.set-ghcr-repository.outputs.ghcr-repository, steps.build-vars.outputs.build-cache-key) || '' }}
|
||||
- name: Inspect image
|
||||
if: steps.build-vars.outputs.can-push == 'true'
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
||||
- name: Export frontend artifact from docker
|
||||
if: steps.build-vars.outputs.can-push == 'true'
|
||||
run: |
|
||||
docker create --name frontend-extract ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
||||
docker cp frontend-extract:/usr/src/paperless/src/documents/static/frontend src/documents/static/frontend/
|
||||
- name: Upload frontend artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: steps.build-vars.outputs.can-push == 'true'
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: frontend-compiled
|
||||
path: src/documents/static/frontend/
|
||||
@@ -426,17 +500,18 @@ jobs:
|
||||
needs:
|
||||
- build-docker-image
|
||||
- documentation
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Set up Python
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||
enable-cache: true
|
||||
@@ -449,12 +524,12 @@ jobs:
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq --no-install-recommends gettext liblept5
|
||||
- name: Download frontend artifact
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: frontend-compiled
|
||||
path: src/documents/static/frontend/
|
||||
- name: Download documentation artifact
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: documentation
|
||||
path: docs/_build/html/
|
||||
@@ -517,7 +592,7 @@ jobs:
|
||||
sudo chown -R 1000:1000 paperless-ngx/
|
||||
tar -cJf paperless-ngx.tar.xz paperless-ngx/
|
||||
- name: Upload release artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: release
|
||||
path: dist/paperless-ngx.tar.xz
|
||||
@@ -525,6 +600,8 @@ jobs:
|
||||
publish-release:
|
||||
name: "Publish Release"
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
prerelease: ${{ steps.get_version.outputs.prerelease }}
|
||||
changelog: ${{ steps.create-release.outputs.body }}
|
||||
@@ -534,7 +611,7 @@ jobs:
|
||||
if: github.ref_type == 'tag' && (startsWith(github.ref_name, 'v') || contains(github.ref_name, '-beta.rc'))
|
||||
steps:
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v6
|
||||
with:
|
||||
name: release
|
||||
path: ./
|
||||
@@ -556,6 +633,7 @@ jobs:
|
||||
version: ${{ steps.get_version.outputs.version }}
|
||||
prerelease: ${{ steps.get_version.outputs.prerelease }}
|
||||
publish: true # ensures release is not marked as draft
|
||||
commitish: main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload release archive
|
||||
@@ -570,21 +648,25 @@ jobs:
|
||||
append-changelog:
|
||||
name: "Append Changelog"
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
needs:
|
||||
- publish-release
|
||||
if: needs.publish-release.outputs.prerelease == 'false'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: main
|
||||
- name: Set up Python
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||
enable-cache: true
|
||||
@@ -612,7 +694,7 @@ jobs:
|
||||
git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA"
|
||||
git push origin ${{ needs.publish-release.outputs.version }}-changelog
|
||||
- name: Create Pull Request
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { repo, owner } = context.repo;
|
||||
|
||||
12
.github/workflows/cleanup-tags.yml
vendored
12
.github/workflows/cleanup-tags.yml
vendored
@@ -4,13 +4,11 @@
|
||||
# Requires a PAT with the correct scope set in the secrets.
|
||||
#
|
||||
# This workflow will not trigger runs on forked repos.
|
||||
|
||||
name: Cleanup Image Tags
|
||||
on:
|
||||
delete:
|
||||
push:
|
||||
paths:
|
||||
- ".github/workflows/cleanup-tags.yml"
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0'
|
||||
concurrency:
|
||||
group: registry-tags-cleanup
|
||||
cancel-in-progress: false
|
||||
@@ -29,7 +27,7 @@ jobs:
|
||||
steps:
|
||||
- name: Clean temporary images
|
||||
if: "${{ env.TOKEN != '' }}"
|
||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.10.0
|
||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.12.0
|
||||
with:
|
||||
token: "${{ env.TOKEN }}"
|
||||
owner: "${{ github.repository_owner }}"
|
||||
@@ -55,7 +53,7 @@ jobs:
|
||||
steps:
|
||||
- name: Clean untagged images
|
||||
if: "${{ env.TOKEN != '' }}"
|
||||
uses: stumpylog/image-cleaner-action/untagged@v0.10.0
|
||||
uses: stumpylog/image-cleaner-action/untagged@v0.12.0
|
||||
with:
|
||||
token: "${{ env.TOKEN }}"
|
||||
owner: "${{ github.repository_owner }}"
|
||||
|
||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -34,10 +34,10 @@ jobs:
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -45,4 +45,4 @@ jobs:
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
2
.github/workflows/crowdin.yml
vendored
2
.github/workflows/crowdin.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.PNGX_BOT_PAT }}
|
||||
- name: crowdin action
|
||||
|
||||
10
.github/workflows/pr-bot.yml
vendored
10
.github/workflows/pr-bot.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
steps:
|
||||
- name: Label PR by file path or branch name
|
||||
# see .github/labeler.yml for the labeler config
|
||||
uses: actions/labeler@v5
|
||||
uses: actions/labeler@v6
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Label by size
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
fail_if_xl: 'false'
|
||||
excluded_files: /\.lock$/ /\.txt$/ ^src-ui/pnpm-lock\.yaml$ ^src-ui/messages\.xlf$ ^src/locale/en_US/LC_MESSAGES/django\.po$
|
||||
- name: Label by PR title
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
labels.push('bug');
|
||||
} else if (/^feature/i.test(title)) {
|
||||
labels.push('enhancement');
|
||||
} else if (!/^(dependabot)/i.test(title)) {
|
||||
} else if (!/^(dependabot)/i.test(title) && !/^(chore)/i.test(title)) {
|
||||
labels.push('enhancement'); // Default fallback
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
}
|
||||
- name: Label bot-generated PRs
|
||||
if: ${{ contains(github.actor, 'dependabot') || contains(github.actor, 'crowdin-bot') }}
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
}
|
||||
- name: Welcome comment
|
||||
if: ${{ !contains(github.actor, 'bot') }}
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
|
||||
20
.github/workflows/repo-maintenance.yml
vendored
20
.github/workflows/repo-maintenance.yml
vendored
@@ -15,16 +15,23 @@ jobs:
|
||||
if: github.repository_owner == 'paperless-ngx'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
days-before-stale: 7
|
||||
days-before-close: 14
|
||||
any-of-labels: 'stale,cant-reproduce,not a bug'
|
||||
any-of-issue-labels: 'cant-reproduce,not a bug'
|
||||
stale-issue-label: stale
|
||||
stale-pr-label: stale
|
||||
stale-issue-message: >
|
||||
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||
|
||||
days-before-pr-stale: 14
|
||||
days-before-pr-close: 7
|
||||
stale-pr-message: ""
|
||||
stale-pr-label: stale
|
||||
exempt-pr-labels: 'notable'
|
||||
close-pr-message: >
|
||||
This pull request has been automatically closed because it has not had recent activity. Thank you for your contributions. Please open a new pull request or discussion if you would like to continue working on this change.
|
||||
|
||||
lock-threads:
|
||||
name: 'Lock Old Threads'
|
||||
if: github.repository_owner == 'paperless-ngx'
|
||||
@@ -50,7 +57,7 @@ jobs:
|
||||
if: github.repository_owner == 'paperless-ngx'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
- uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
function sleep(ms) {
|
||||
@@ -107,7 +114,7 @@ jobs:
|
||||
if: github.repository_owner == 'paperless-ngx'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
- uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
function sleep(ms) {
|
||||
@@ -199,7 +206,7 @@ jobs:
|
||||
if: github.repository_owner == 'paperless-ngx'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
- uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
function sleep(ms) {
|
||||
@@ -234,6 +241,7 @@ jobs:
|
||||
) {
|
||||
nodes {
|
||||
id,
|
||||
createdAt,
|
||||
number,
|
||||
updatedAt,
|
||||
upvoteCount,
|
||||
|
||||
10
.github/workflows/translate-strings.yml
vendored
10
.github/workflows/translate-strings.yml
vendored
@@ -11,19 +11,19 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.PNGX_BOT_PAT }}
|
||||
ref: ${{ github.head_ref }}
|
||||
- name: Set up Python
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -qq --no-install-recommends gettext
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@v7
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Install backend python dependencies
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
with:
|
||||
version: 10
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20.x
|
||||
cache: 'pnpm'
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
cd src-ui
|
||||
pnpm run ng extract-i18n
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
uses: stefanzweifel/git-auto-commit-action@v7
|
||||
with:
|
||||
file_pattern: 'src-ui/messages.xlf src/locale/en_US/LC_MESSAGES/django.po'
|
||||
commit_message: "Auto translate strings"
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -53,7 +53,7 @@ junit.xml
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# MkDocs documentation
|
||||
# Zensical documentation
|
||||
site/
|
||||
|
||||
# PyBuilder
|
||||
@@ -107,3 +107,6 @@ celerybeat-schedule*
|
||||
/.devcontainer/data/
|
||||
/.devcontainer/media/
|
||||
/.devcontainer/redisdata/
|
||||
|
||||
# ignore pnpm package store folder created when setting up the devcontainer
|
||||
.pnpm-store/
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# This file configures pre-commit hooks.
|
||||
# See https://pre-commit.com/ for general information
|
||||
# See https://pre-commit.com/hooks.html for a listing of possible hooks
|
||||
|
||||
repos:
|
||||
# General hooks
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-docstring-first
|
||||
- id: check-json
|
||||
@@ -19,7 +18,7 @@ repos:
|
||||
exclude_types:
|
||||
- svg
|
||||
- pofile
|
||||
exclude: "(^LICENSE$)"
|
||||
exclude: "(^LICENSE$|^src/documents/static/bootstrap.min.css$)"
|
||||
- id: mixed-line-ending
|
||||
args:
|
||||
- "--fix=lf"
|
||||
@@ -29,16 +28,16 @@ repos:
|
||||
- id: check-case-conflict
|
||||
- id: detect-private-key
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.4.0
|
||||
rev: v2.4.1
|
||||
hooks:
|
||||
- id: codespell
|
||||
exclude: "(^src-ui/src/locale/)|(^src-ui/pnpm-lock.yaml)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)"
|
||||
additional_dependencies: [tomli]
|
||||
exclude_types:
|
||||
- pofile
|
||||
- json
|
||||
# See https://github.com/prettier/prettier/issues/15742 for the fork reason
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: 'v3.3.3'
|
||||
rev: 'v3.6.2'
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or:
|
||||
@@ -50,34 +49,36 @@ repos:
|
||||
- 'prettier-plugin-organize-imports@4.1.0'
|
||||
# Python hooks
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.9.9
|
||||
rev: v0.14.5
|
||||
hooks:
|
||||
- id: ruff
|
||||
- id: ruff-check
|
||||
- id: ruff-format
|
||||
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||
rev: "v2.5.1"
|
||||
rev: "v2.11.1"
|
||||
hooks:
|
||||
- id: pyproject-fmt
|
||||
# Dockerfile hooks
|
||||
- repo: https://github.com/AleksaC/hadolint-py
|
||||
rev: v2.12.0.3
|
||||
rev: v2.14.0
|
||||
hooks:
|
||||
- id: hadolint
|
||||
# Shell script hooks
|
||||
- repo: https://github.com/lovesegfault/beautysh
|
||||
rev: v6.2.1
|
||||
rev: v6.4.2
|
||||
hooks:
|
||||
- id: beautysh
|
||||
additional_dependencies:
|
||||
- setuptools
|
||||
types: [file]
|
||||
files: (\.sh$|/run$|/finish$)
|
||||
args:
|
||||
- "--tab"
|
||||
- repo: https://github.com/shellcheck-py/shellcheck-py
|
||||
rev: "v0.10.0.1"
|
||||
rev: "v0.11.0.1"
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
- repo: https://github.com/google/yamlfmt
|
||||
rev: v0.14.0
|
||||
rev: v0.20.0
|
||||
hooks:
|
||||
- id: yamlfmt
|
||||
exclude: "^src-ui/pnpm-lock.yaml"
|
||||
types:
|
||||
- yaml
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
If you feel like contributing to the project, please do! Bug fixes and improvements are always welcome.
|
||||
|
||||
⚠️ Please note: Pull requests that implement a new feature or enhancement _should almost always target an existing feature request_ with evidence of community interest and discussion. This is in order to balance the work of implementing and maintaining new features / enhancements. Pull requests that are opened without meeting this requirement may not be merged.
|
||||
|
||||
If you want to implement something big:
|
||||
|
||||
- Please start a discussion about that in the issues! Maybe something similar is already in development and we can make it happen together.
|
||||
- As above, please start with a discussion! Maybe something similar is already in development and we can make it happen together.
|
||||
- When making additions to the project, consider if the majority of users will benefit from your change. If not, you're probably better of forking the project.
|
||||
- Also consider if your change will get in the way of other users. A good change is a change that enhances the experience of some users who want that change and does not affect users who do not care about the change.
|
||||
- Please see the [paperless-ngx merge process](#merging-prs) below.
|
||||
@@ -37,6 +39,8 @@ Before you can run `pytest`, ensure to [properly set up your local environment](
|
||||
|
||||
Once you have submitted a **P**ull **R**equest it will be reviewed, approved, and merged by one or more community members of any team. Automated code tests and formatting checks must be passed.
|
||||
|
||||
Important: Pull requests that implement a new feature or enhancement _should almost always target an existing feature request_ with evidence of community interest and discussion. This is in order to balance the work of implementing and maintaining new features / enhancements. Instead of opening a PR which does not meet this requirement, please open a feature request instead, to gather feedback from both users and the project maintainers.
|
||||
|
||||
## Non-Trivial Requests
|
||||
|
||||
PRs deemed `non-trivial` will go through a stricter review process before being merged into `dev`. This is to ensure code quality and complete functionality (free of side effects).
|
||||
@@ -109,28 +113,12 @@ Paperless-ngx is a community project. We do our best to delegate permission and
|
||||
|
||||
## Structure
|
||||
|
||||
As of writing, there are 21 members in paperless-ngx. 4 of these people have complete administrative privileges to the repo:
|
||||
There are currently 2 members in paperless-ngx with complete administrative privileges to the repo:
|
||||
|
||||
- [@shamoon](https://github.com/shamoon)
|
||||
- [@bauerj](https://github.com/bauerj)
|
||||
- [@qcasey](https://github.com/qcasey)
|
||||
- [@FrankStrieter](https://github.com/FrankStrieter)
|
||||
- [@stumpylog](https://github.com/stumpylog)
|
||||
|
||||
There are 5 teams collaborating on specific tasks within paperless-ngx:
|
||||
|
||||
- @paperless-ngx/backend (Python / django)
|
||||
- @paperless-ngx/frontend (JavaScript / Typescript)
|
||||
- @paperless-ngx/ci-cd (GitHub Actions / Deployment)
|
||||
- @paperless-ngx/issues (Issue triage)
|
||||
- @paperless-ngx/test (General testing for larger PRs)
|
||||
|
||||
## Permissions
|
||||
|
||||
All team members are notified when mentioned or assigned to a relevant issue or pull request. Additionally, each team has slightly different access to paperless-ngx:
|
||||
|
||||
- The **test** team has no special permissions.
|
||||
- The **issues** team has `triage` access. This means they can organize issues and pull requests.
|
||||
- The **backend**, **frontend**, and **ci-cd** teams have `write` access. This means they can approve PRs and push code, containers, releases, and more.
|
||||
There are other members who occasionally contribute but we are actively seeking more dedicated maintainers of the project. Please reach out if you are interested.
|
||||
|
||||
## Joining
|
||||
|
||||
@@ -141,13 +129,13 @@ The admins occasionally invite contributors directly if we believe having them o
|
||||
# Automatic Repository Maintenance
|
||||
|
||||
The Paperless-ngx team appreciates all effort and interest from the community in filing bug reports, creating feature requests, sharing ideas and helping other
|
||||
community members. That said, in an effort to keep the repository organized and managebale the project uses automatic handling of certain areas:
|
||||
community members. That said, in an effort to keep the repository organized and manageable the project uses automatic handling of certain areas:
|
||||
|
||||
- Issues that cannot be reproduced will be marked 'stale' after 7 days of inactivity and closed after 14 further days of inactivity.
|
||||
- Issues, pull requests and discussions that are closed will be locked after 30 days of inactivity.
|
||||
- Discussions with a marked answer will be automatically closed.
|
||||
- Discussions in the 'General' or 'Support' categories will be closed after 180 days of inactivity.
|
||||
- Feature requests that do not meet the following thresholds will be closed: 180 days of inactivity, < 5 "up-votes" after 180 days, < 20 "up-votes" after 1 year or < 80 "up-votes" at 2 years.
|
||||
- Feature requests that do not meet the following thresholds will be closed: 180 days of inactivity with less than 80 "up-votes", < 5 "up-votes" after 180 days, < 20 "up-votes" after 1 year or < 40 "up-votes" at 2 years.
|
||||
|
||||
In all cases, threads can be re-opened by project maintainers and, of course, users can always create a new discussion for related concerns.
|
||||
Finally, remember that all information remains searchable and 'closed' feature requests can still serve as inspiration for new features.
|
||||
|
||||
29
Dockerfile
29
Dockerfile
@@ -5,7 +5,7 @@
|
||||
# Purpose: Compiles the frontend
|
||||
# Notes:
|
||||
# - Does PNPM stuff with Typescript and such
|
||||
FROM --platform=$BUILDPLATFORM docker.io/node:20-bookworm-slim AS compile-frontend
|
||||
FROM --platform=$BUILDPLATFORM docker.io/node:20-trixie-slim AS compile-frontend
|
||||
|
||||
COPY ./src-ui /src/src-ui
|
||||
|
||||
@@ -21,7 +21,7 @@ ARG PNGX_TAG_VERSION=
|
||||
RUN set -eux && \
|
||||
case "${PNGX_TAG_VERSION}" in \
|
||||
dev|beta|fix*|feature*) \
|
||||
sed -i -E "s/version: '([0-9\.]+)'/version: '\1 #${PNGX_TAG_VERSION}'/g" /src/src-ui/src/environments/environment.prod.ts \
|
||||
sed -i -E "s/tag: '([a-z\.]+)'/tag: '${PNGX_TAG_VERSION}'/g" /src/src-ui/src/environments/environment.prod.ts \
|
||||
;; \
|
||||
esac
|
||||
|
||||
@@ -32,7 +32,7 @@ RUN set -eux \
|
||||
# Purpose: Installs s6-overlay and rootfs
|
||||
# Comments:
|
||||
# - Don't leave anything extra in here either
|
||||
FROM ghcr.io/astral-sh/uv:0.7.9-python3.12-bookworm-slim AS s6-overlay-base
|
||||
FROM ghcr.io/astral-sh/uv:0.9.15-python3.12-trixie-slim AS s6-overlay-base
|
||||
|
||||
WORKDIR /usr/src/s6
|
||||
|
||||
@@ -102,8 +102,6 @@ ARG TARGETARCH
|
||||
|
||||
# Can be workflow provided, defaults set for manual building
|
||||
ARG JBIG2ENC_VERSION=0.30
|
||||
ARG QPDF_VERSION=11.9.0
|
||||
ARG GS_VERSION=10.03.1
|
||||
|
||||
# Set Python environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
@@ -170,20 +168,8 @@ RUN set -eux \
|
||||
&& apt-get update \
|
||||
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES} \
|
||||
&& echo "Installing pre-built updates" \
|
||||
&& curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \
|
||||
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
|
||||
https://github.com/paperless-ngx/builder/releases/download/jbig2enc-${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& echo "Installing qpdf ${QPDF_VERSION}" \
|
||||
&& dpkg --install ./libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& dpkg --install ./qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& echo "Installing Ghostscript ${GS_VERSION}" \
|
||||
&& dpkg --install ./libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
|
||||
&& dpkg --install ./libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
&& dpkg --install ./ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
|
||||
&& curl --fail --silent --no-progress-meter --show-error --location --remote-name-all \
|
||||
https://github.com/paperless-ngx/builder/releases/download/jbig2enc-trixie-v${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& echo "Installing jbig2enc" \
|
||||
&& dpkg --install ./jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
|
||||
&& echo "Configuring imagemagick" \
|
||||
@@ -254,7 +240,8 @@ RUN set -eux \
|
||||
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless \
|
||||
&& echo "Collecting static files" \
|
||||
&& s6-setuidgid paperless python3 manage.py collectstatic --clear --no-input --link \
|
||||
&& s6-setuidgid paperless python3 manage.py compilemessages
|
||||
&& s6-setuidgid paperless python3 manage.py compilemessages \
|
||||
&& /usr/local/bin/deduplicate.py --verbose /usr/src/paperless/static/
|
||||
|
||||
VOLUME ["/usr/src/paperless/data", \
|
||||
"/usr/src/paperless/media", \
|
||||
@@ -265,4 +252,4 @@ ENTRYPOINT ["/init"]
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000" ]
|
||||
HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "-L", "--max-time", "2", "http://localhost:8000" ]
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# Docker Compose file for running paperless testing with actual gotenberg
|
||||
# Docker Compose file for running paperless testing with actual Gotenberg
|
||||
# and Tika containers for a more end to end test of the Tika related functionality
|
||||
# Can be used locally or by the CI to start the necessary containers with the
|
||||
# correct networking for the tests
|
||||
|
||||
services:
|
||||
gotenberg:
|
||||
image: docker.io/gotenberg/gotenberg:8.20
|
||||
image: docker.io/gotenberg/gotenberg:8.25
|
||||
hostname: gotenberg
|
||||
container_name: gotenberg
|
||||
network_mode: host
|
||||
|
||||
@@ -32,6 +32,6 @@
|
||||
# Note that this is different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines
|
||||
# the language used for OCR.
|
||||
# The container installs English, German, Italian, Spanish and French by default.
|
||||
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster
|
||||
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names
|
||||
# for available languages.
|
||||
#PAPERLESS_OCR_LANGUAGES=tur ces
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
# - Instead of SQLite (default), MariaDB is used as the database server.
|
||||
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
||||
# is configured to use these services. These provide support for consuming
|
||||
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
|
||||
# parts.
|
||||
# Office documents (Word, Excel, PowerPoint and their LibreOffice counter-
|
||||
# parts).
|
||||
#
|
||||
# To install and update paperless with this file, do the following:
|
||||
#
|
||||
@@ -25,11 +25,9 @@
|
||||
# and '.env' into a folder.
|
||||
# - Run 'docker compose pull'.
|
||||
# - Run 'docker compose up -d'.
|
||||
|
||||
#
|
||||
# For more extensive installation and update instructions, refer to the
|
||||
# documentation.
|
||||
|
||||
services:
|
||||
broker:
|
||||
image: docker.io/library/redis:8
|
||||
@@ -37,7 +35,7 @@ services:
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
db:
|
||||
image: docker.io/library/mariadb:11
|
||||
image: docker.io/library/mariadb:12
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- dbdata:/var/lib/mysql
|
||||
@@ -74,7 +72,7 @@ services:
|
||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||
gotenberg:
|
||||
image: docker.io/gotenberg/gotenberg:8.20
|
||||
image: docker.io/gotenberg/gotenberg:8.25
|
||||
restart: unless-stopped
|
||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
||||
# want to allow external content like tracking pixels or even javascript.
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#
|
||||
# For more extensive installation and update instructions, refer to the
|
||||
# documentation.
|
||||
|
||||
services:
|
||||
broker:
|
||||
image: docker.io/library/redis:8
|
||||
@@ -32,7 +31,7 @@ services:
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
db:
|
||||
image: docker.io/library/mariadb:11
|
||||
image: docker.io/library/mariadb:12
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- dbdata:/var/lib/mysql
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#
|
||||
# For more extensive installation and update instructions, refer to the
|
||||
# documentation.
|
||||
|
||||
services:
|
||||
broker:
|
||||
image: docker.io/library/redis:8
|
||||
@@ -33,10 +32,10 @@ services:
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
db:
|
||||
image: docker.io/library/postgres:17
|
||||
image: docker.io/library/postgres:18
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
- pgdata:/var/lib/postgresql
|
||||
environment:
|
||||
POSTGRES_DB: paperless
|
||||
POSTGRES_USER: paperless
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
# - Instead of SQLite (default), PostgreSQL is used as the database server.
|
||||
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
||||
# is configured to use these services. These provide support for consuming
|
||||
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
|
||||
# parts.
|
||||
# Office documents (Word, Excel, PowerPoint and their LibreOffice counter-
|
||||
# parts).
|
||||
#
|
||||
# To install and update paperless with this file, do the following:
|
||||
#
|
||||
@@ -28,7 +28,6 @@
|
||||
#
|
||||
# For more extensive installation and update instructions, refer to the
|
||||
# documentation.
|
||||
|
||||
services:
|
||||
broker:
|
||||
image: docker.io/library/redis:8
|
||||
@@ -36,10 +35,10 @@ services:
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
db:
|
||||
image: docker.io/library/postgres:17
|
||||
image: docker.io/library/postgres:18
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
- pgdata:/var/lib/postgresql
|
||||
environment:
|
||||
POSTGRES_DB: paperless
|
||||
POSTGRES_USER: paperless
|
||||
@@ -67,7 +66,7 @@ services:
|
||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||
gotenberg:
|
||||
image: docker.io/gotenberg/gotenberg:8.20
|
||||
image: docker.io/gotenberg/gotenberg:8.25
|
||||
restart: unless-stopped
|
||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
||||
# want to allow external content like tracking pixels or even javascript.
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#
|
||||
# For more extensive installation and update instructions, refer to the
|
||||
# documentation.
|
||||
|
||||
services:
|
||||
broker:
|
||||
image: docker.io/library/redis:8
|
||||
@@ -32,10 +31,10 @@ services:
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
db:
|
||||
image: docker.io/library/postgres:17
|
||||
image: docker.io/library/postgres:18
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
- pgdata:/var/lib/postgresql
|
||||
environment:
|
||||
POSTGRES_DB: paperless
|
||||
POSTGRES_USER: paperless
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#
|
||||
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
||||
# is configured to use these services. These provide support for consuming
|
||||
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
|
||||
# parts.
|
||||
# Office documents (Word, Excel, PowerPoint and their LibreOffice counter-
|
||||
# parts).
|
||||
#
|
||||
# To install and update paperless with this file, do the following:
|
||||
#
|
||||
@@ -28,7 +28,6 @@
|
||||
#
|
||||
# For more extensive installation and update instructions, refer to the
|
||||
# documentation.
|
||||
|
||||
services:
|
||||
broker:
|
||||
image: docker.io/library/redis:8
|
||||
@@ -56,7 +55,7 @@ services:
|
||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||
gotenberg:
|
||||
image: docker.io/gotenberg/gotenberg:8.20
|
||||
image: docker.io/gotenberg/gotenberg:8.25
|
||||
restart: unless-stopped
|
||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
||||
# want to allow external content like tracking pixels or even javascript.
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#
|
||||
# For more extensive installation and update instructions, refer to the
|
||||
# documentation.
|
||||
|
||||
services:
|
||||
broker:
|
||||
image: docker.io/library/redis:8
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py management_command "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py management_command "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py management_command "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -29,5 +29,5 @@ if find /run/s6/container_environment/*"_FILE" -maxdepth 1 > /dev/null 2>&1; the
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "${log_prefix} No *_FILE environment found"
|
||||
echo "${log_prefix} No *_FILE environment found"
|
||||
fi
|
||||
|
||||
@@ -13,8 +13,8 @@ echo $(date +%s) > /var/run/s6/container_environment/PAPERLESS_START_TIME_S
|
||||
# Check if we're starting as a non-root user
|
||||
if [ "$(id --user)" != "0" ]; then
|
||||
printf "true" > /var/run/s6/container_environment/USER_IS_NON_ROOT
|
||||
echo "${log_prefix} paperless-ngx docker container running under a user ($(id --user):$(id --group))"
|
||||
echo "${log_prefix} paperless-ngx docker container running under a user ($(id --user):$(id --group))"
|
||||
else
|
||||
printf "/usr/src/paperless" > /var/run/s6/container_environment/HOME
|
||||
echo "${log_prefix} paperless-ngx docker container starting init as root"
|
||||
echo "${log_prefix} paperless-ngx docker container starting init as root"
|
||||
fi
|
||||
|
||||
@@ -1,70 +1,66 @@
|
||||
#!/command/with-contenv /usr/bin/bash
|
||||
# shellcheck shell=bash
|
||||
# vim: set ft=bash ts=4 sw=4 sts=4 et :
|
||||
|
||||
declare -r log_prefix="[init-db-wait]"
|
||||
set -euo pipefail
|
||||
|
||||
declare -r LOG_PREFIX="[init-db-wait]"
|
||||
|
||||
declare -ri TIMEOUT=60
|
||||
declare -i ATTEMPT=0
|
||||
declare -i DELAY=0
|
||||
declare -i STARTED_AT=${EPOCHSECONDS:?EPOCHSECONDS var unset}
|
||||
|
||||
delay_next_attempt() {
|
||||
local -i elapsed=$(( EPOCHSECONDS - STARTED_AT ))
|
||||
local -ri remaining=$(( TIMEOUT - elapsed ))
|
||||
|
||||
if (( remaining <= 0 )); then
|
||||
echo "${LOG_PREFIX} Unable to connect after $elapsed seconds."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DELAY+=1
|
||||
|
||||
# clamp to remaining time
|
||||
if (( DELAY > remaining )); then
|
||||
DELAY=$remaining
|
||||
fi
|
||||
|
||||
ATTEMPT+=1
|
||||
echo "${LOG_PREFIX} Attempt $ATTEMPT failed! Trying again in $DELAY seconds..."
|
||||
sleep "$DELAY"
|
||||
}
|
||||
|
||||
wait_for_postgres() {
|
||||
local attempt_num=1
|
||||
local -r max_attempts=5
|
||||
|
||||
echo "${log_prefix} Waiting for PostgreSQL to start..."
|
||||
echo "${LOG_PREFIX} Waiting for PostgreSQL to start..."
|
||||
|
||||
local -r host="${PAPERLESS_DBHOST:-localhost}"
|
||||
local -r port="${PAPERLESS_DBPORT:-5432}"
|
||||
local -r user="${PAPERLESS_DBUSER:-paperless}"
|
||||
|
||||
# Disable warning, host and port can't have spaces
|
||||
# shellcheck disable=SC2086
|
||||
while [ ! "$(pg_isready -h ${host} -p ${port} --username ${user})" ]; do
|
||||
|
||||
if [ $attempt_num -eq $max_attempts ]; then
|
||||
echo "${log_prefix} Unable to connect to database."
|
||||
exit 1
|
||||
else
|
||||
echo "${log_prefix} Attempt $attempt_num failed! Trying again in 5 seconds..."
|
||||
fi
|
||||
|
||||
attempt_num=$(("$attempt_num" + 1))
|
||||
sleep 5
|
||||
while ! pg_isready -h "${host}" -p "${port}" --username "${user}"; do
|
||||
delay_next_attempt
|
||||
done
|
||||
# Extra in case this is a first start
|
||||
sleep 5
|
||||
echo "Connected to PostgreSQL"
|
||||
echo "${LOG_PREFIX} Connected to PostgreSQL"
|
||||
}
|
||||
|
||||
wait_for_mariadb() {
|
||||
echo "${log_prefix} Waiting for MariaDB to start..."
|
||||
echo "${LOG_PREFIX} Waiting for MariaDB to start..."
|
||||
|
||||
local -r host="${PAPERLESS_DBHOST:=localhost}"
|
||||
local -r port="${PAPERLESS_DBPORT:=3306}"
|
||||
local -r host="${PAPERLESS_DBHOST:-localhost}"
|
||||
local -r port="${PAPERLESS_DBPORT:-3306}"
|
||||
|
||||
local attempt_num=1
|
||||
local -r max_attempts=5
|
||||
|
||||
# Disable warning, host and port can't have spaces
|
||||
# shellcheck disable=SC2086
|
||||
while ! true > /dev/tcp/$host/$port; do
|
||||
|
||||
if [ $attempt_num -eq $max_attempts ]; then
|
||||
echo "${log_prefix} Unable to connect to database."
|
||||
exit 1
|
||||
else
|
||||
echo "${log_prefix} Attempt $attempt_num failed! Trying again in 5 seconds..."
|
||||
|
||||
fi
|
||||
|
||||
attempt_num=$(("$attempt_num" + 1))
|
||||
sleep 5
|
||||
while ! mariadb-admin --host="${host}" --port="${port}" --skip-ssl ping --silent >/dev/null 2>&1; do
|
||||
delay_next_attempt
|
||||
done
|
||||
echo "Connected to MariaDB"
|
||||
echo "${LOG_PREFIX} Connected to MariaDB"
|
||||
}
|
||||
|
||||
if [[ "${PAPERLESS_DBENGINE}" == "mariadb" ]]; then
|
||||
echo "${log_prefix} Waiting for MariaDB to report ready"
|
||||
if [[ "${PAPERLESS_DBENGINE:-}" == "mariadb" ]]; then
|
||||
wait_for_mariadb
|
||||
elif [[ -n "${PAPERLESS_DBHOST}" ]]; then
|
||||
echo "${log_prefix} Waiting for postgresql to report ready"
|
||||
elif [[ -n "${PAPERLESS_DBHOST:-}" ]]; then
|
||||
wait_for_postgres
|
||||
fi
|
||||
|
||||
echo "${log_prefix} Database is ready"
|
||||
echo "${LOG_PREFIX} Database is ready"
|
||||
|
||||
@@ -10,11 +10,11 @@ export GRANIAN_WORKERS=${GRANIAN_WORKERS:-${PAPERLESS_WEBSERVER_WORKERS:-1}}
|
||||
|
||||
# Only set GRANIAN_URL_PATH_PREFIX if PAPERLESS_FORCE_SCRIPT_NAME is set
|
||||
if [[ -n "${PAPERLESS_FORCE_SCRIPT_NAME}" ]]; then
|
||||
export GRANIAN_URL_PATH_PREFIX=${PAPERLESS_FORCE_SCRIPT_NAME}
|
||||
export GRANIAN_URL_PATH_PREFIX=${PAPERLESS_FORCE_SCRIPT_NAME}
|
||||
fi
|
||||
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
exec granian --interface asginl --ws --loop uvloop "paperless.asgi:application"
|
||||
exec granian --interface asginl --ws --loop uvloop "paperless.asgi:application"
|
||||
else
|
||||
exec s6-setuidgid paperless granian --interface asginl --ws --loop uvloop "paperless.asgi:application"
|
||||
exec s6-setuidgid paperless granian --interface asginl --ws --loop uvloop "paperless.asgi:application"
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py convert_mariadb_uuid "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py convert_mariadb_uuid "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py convert_mariadb_uuid "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py createsuperuser "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py createsuperuser "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py createsuperuser "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py decrypt_documents "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py decrypt_documents "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py decrypt_documents "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
167
docker/rootfs/usr/local/bin/deduplicate.py
Executable file
167
docker/rootfs/usr/local/bin/deduplicate.py
Executable file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
File deduplication script that replaces identical files with symlinks.
|
||||
Uses SHA256 hashing to identify duplicate files.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
import humanize
|
||||
|
||||
|
||||
def calculate_sha256(filepath: Path) -> str | None:
|
||||
sha256_hash = hashlib.sha256()
|
||||
try:
|
||||
with filepath.open("rb") as f:
|
||||
# Read file in chunks to handle large files efficiently
|
||||
while chunk := f.read(65536): # 64KB chunks
|
||||
sha256_hash.update(chunk)
|
||||
return sha256_hash.hexdigest()
|
||||
except OSError as e:
|
||||
click.echo(f"Error reading {filepath}: {e}", err=True)
|
||||
return None
|
||||
|
||||
|
||||
def find_duplicate_files(directory: Path) -> dict[str, list[Path]]:
|
||||
"""
|
||||
Recursively scan directory and group files by their SHA256 hash.
|
||||
Returns a dictionary mapping hash -> list of file paths.
|
||||
"""
|
||||
hash_to_files: dict[str, list[Path]] = defaultdict(list)
|
||||
|
||||
for filepath in directory.rglob("*"):
|
||||
# Skip symlinks
|
||||
if filepath.is_symlink():
|
||||
continue
|
||||
|
||||
# Skip if not a regular file
|
||||
if not filepath.is_file():
|
||||
continue
|
||||
|
||||
file_hash = calculate_sha256(filepath)
|
||||
if file_hash:
|
||||
hash_to_files[file_hash].append(filepath)
|
||||
|
||||
# Filter to only return hashes with duplicates
|
||||
return {h: files for h, files in hash_to_files.items() if len(files) > 1}
|
||||
|
||||
|
||||
def replace_with_symlinks(
|
||||
duplicate_groups: dict[str, list[Path]],
|
||||
*,
|
||||
dry_run: bool = False,
|
||||
) -> tuple[int, int]:
|
||||
"""
|
||||
Replace duplicate files with symlinks to the first occurrence.
|
||||
Returns (number_of_files_replaced, space_saved_in_bytes).
|
||||
"""
|
||||
total_duplicates = 0
|
||||
space_saved = 0
|
||||
|
||||
for file_hash, file_list in duplicate_groups.items():
|
||||
# Keep the first file as the original, replace others with symlinks
|
||||
original_file = file_list[0]
|
||||
duplicates = file_list[1:]
|
||||
|
||||
click.echo(f"Found {len(duplicates)} duplicate(s) of: {original_file}")
|
||||
|
||||
for duplicate in duplicates:
|
||||
try:
|
||||
# Get file size before deletion
|
||||
file_size = duplicate.stat().st_size
|
||||
|
||||
if dry_run:
|
||||
click.echo(f" [DRY RUN] Would replace: {duplicate}")
|
||||
else:
|
||||
# Remove the duplicate file
|
||||
duplicate.unlink()
|
||||
|
||||
# Create relative symlink if possible, otherwise absolute
|
||||
try:
|
||||
# Try to create a relative symlink
|
||||
rel_path = original_file.relative_to(duplicate.parent)
|
||||
duplicate.symlink_to(rel_path)
|
||||
click.echo(f" Replaced: {duplicate} -> {rel_path}")
|
||||
except ValueError:
|
||||
# Fall back to absolute path
|
||||
duplicate.symlink_to(original_file.resolve())
|
||||
click.echo(f" Replaced: {duplicate} -> {original_file}")
|
||||
|
||||
space_saved += file_size
|
||||
|
||||
total_duplicates += 1
|
||||
|
||||
except OSError as e:
|
||||
click.echo(f" Error replacing {duplicate}: {e}", err=True)
|
||||
|
||||
return total_duplicates, space_saved
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument(
|
||||
"directory",
|
||||
type=click.Path(
|
||||
exists=True,
|
||||
file_okay=False,
|
||||
dir_okay=True,
|
||||
readable=True,
|
||||
path_type=Path,
|
||||
),
|
||||
)
|
||||
@click.option(
|
||||
"--dry-run",
|
||||
is_flag=True,
|
||||
help="Show what would be done without making changes",
|
||||
)
|
||||
@click.option("--verbose", "-v", is_flag=True, help="Show verbose output")
|
||||
def deduplicate(directory: Path, *, dry_run: bool, verbose: bool) -> None:
|
||||
"""
|
||||
Recursively search DIRECTORY for identical files and replace them with symlinks.
|
||||
|
||||
Uses SHA256 hashing to identify duplicate files. The first occurrence of each
|
||||
unique file is kept, and all duplicates are replaced with symlinks pointing to it.
|
||||
"""
|
||||
directory = directory.resolve()
|
||||
|
||||
click.echo(f"Scanning directory: {directory}")
|
||||
if dry_run:
|
||||
click.echo("Running in DRY RUN mode - no changes will be made")
|
||||
|
||||
# Find all duplicate files
|
||||
click.echo("Calculating file hashes...")
|
||||
duplicate_groups = find_duplicate_files(directory)
|
||||
|
||||
if not duplicate_groups:
|
||||
click.echo("No duplicate files found!")
|
||||
return
|
||||
|
||||
total_files = sum(len(files) - 1 for files in duplicate_groups.values())
|
||||
click.echo(
|
||||
f"Found {len(duplicate_groups)} group(s) of duplicates "
|
||||
f"({total_files} files to deduplicate)",
|
||||
)
|
||||
|
||||
if verbose:
|
||||
for file_hash, files in duplicate_groups.items():
|
||||
click.echo(f"Hash: {file_hash}")
|
||||
for f in files:
|
||||
click.echo(f" - {f}")
|
||||
|
||||
# Replace duplicates with symlinks
|
||||
click.echo("Processing duplicates...")
|
||||
num_replaced, space_saved = replace_with_symlinks(duplicate_groups, dry_run=dry_run)
|
||||
|
||||
# Summary
|
||||
click.echo(
|
||||
f"{'Would replace' if dry_run else 'Replaced'} "
|
||||
f"{num_replaced} duplicate file(s)",
|
||||
)
|
||||
if not dry_run:
|
||||
click.echo(f"Space saved: {humanize.naturalsize(space_saved, binary=True)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
deduplicate()
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_archiver "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_archiver "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_archiver "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_create_classifier "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_create_classifier "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_create_classifier "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_exporter "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_exporter "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_exporter "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_fuzzy_match "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_fuzzy_match "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_fuzzy_match "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_importer "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_importer "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_importer "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_index "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_index "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_index "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_renamer "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_renamer "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_renamer "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_retagger "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_retagger "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_retagger "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_sanity_checker "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_sanity_checker "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_sanity_checker "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py document_thumbnails "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py document_thumbnails "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py document_thumbnails "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py mail_fetcher "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py mail_fetcher "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py mail_fetcher "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py manage_superuser "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py manage_superuser "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py manage_superuser "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -5,10 +5,13 @@ set -e
|
||||
|
||||
cd "${PAPERLESS_SRC_DIR}"
|
||||
|
||||
if [[ $(id -u) == 0 ]]; then
|
||||
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
|
||||
python3 manage.py prune_audit_logs "$@"
|
||||
elif [[ $(id -u) == 0 ]]; then
|
||||
s6-setuidgid paperless python3 manage.py prune_audit_logs "$@"
|
||||
elif [[ $(id -un) == "paperless" ]]; then
|
||||
python3 manage.py prune_audit_logs "$@"
|
||||
else
|
||||
echo "Unknown user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -179,10 +179,14 @@ following:
|
||||
|
||||
### Database Upgrades
|
||||
|
||||
In general, paperless does not require a specific version of PostgreSQL or MariaDB and it is
|
||||
Paperless-ngx is compatible with Django-supported versions of PostgreSQL and MariaDB and it is generally
|
||||
safe to update them to newer versions. However, you should always take a backup and follow
|
||||
the instructions from your database's documentation for how to upgrade between major versions.
|
||||
|
||||
!!! note
|
||||
|
||||
As of Paperless-ngx v2.18, the minimum supported version of PostgreSQL is 14.
|
||||
|
||||
For PostgreSQL, refer to [Upgrading a PostgreSQL Cluster](https://www.postgresql.org/docs/current/upgrading.html).
|
||||
|
||||
For MariaDB, refer to [Upgrading MariaDB](https://mariadb.com/kb/en/upgrading/)
|
||||
@@ -306,7 +310,7 @@ in dedicated folders according to their nature: `archive`, `originals`,
|
||||
If `-sm` or `--split-manifest` is provided, information about document
|
||||
will be placed in individual json files, instead of a single JSON file. The main
|
||||
manifest.json will still contain application wide information (e.g. tags, correspondent,
|
||||
documenttype, etc)
|
||||
document type, etc)
|
||||
|
||||
If `-z` or `--zip` is provided, the export will be a zip file
|
||||
in the target directory, named according to the current local date or the
|
||||
@@ -457,6 +461,22 @@ of the index and usually makes queries faster and also ensures that the
|
||||
autocompletion works properly. This command is regularly invoked by the
|
||||
task scheduler.
|
||||
|
||||
### Clearing the database read cache
|
||||
|
||||
If the database read cache is enabled, **you must run this command** after making any changes to the database outside the application context.
|
||||
This includes operations such as restoring a database backup or executing SQL statements like UPDATE, INSERT, DELETE, ALTER, CREATE, or DROP.
|
||||
|
||||
Failing to invalidate the cache after such modifications can lead to stale data being served from the cache, and **may cause data corruption** or inconsistent behavior in the application.
|
||||
|
||||
Use the following management command to clear the cache:
|
||||
|
||||
```
|
||||
python3 manage.py invalidate_cachalot
|
||||
```
|
||||
|
||||
!!! info
|
||||
The database read cache is based on Django-Cachalot. You can refer to their [documentation](https://django-cachalot.readthedocs.io/en/latest/quickstart.html#manage-py-command).
|
||||
|
||||
### Managing filenames {#renamer}
|
||||
|
||||
If you use paperless' feature to
|
||||
|
||||
@@ -431,8 +431,138 @@ This allows for complex logic to be included in the format, including [logical s
|
||||
and [filters](https://jinja.palletsprojects.com/en/3.1.x/templates/#id11) to manipulate the [variables](#filename-format-variables)
|
||||
provided. The template is provided as a string, potentially multiline, and rendered into a single line.
|
||||
|
||||
In addition, the entire Document instance is available to be utilized in a more advanced way, as well as some variables which only make sense to be accessed
|
||||
with more complex logic.
|
||||
In addition, a limited `document` object is available for advanced templates.
|
||||
This object includes common metadata fields such as `id`, `pk`, `title`, `content`, `page_count`, `created`, `added`, `modified`, `mime_type`,
|
||||
`checksum`, `archive_checksum`, `archive_serial_number`, `filename`, `archive_filename`, and `original_filename`.
|
||||
Related values are available as nested objects with limited fields, for example document.correspondent.name, etc.
|
||||
|
||||
#### Custom Jinja2 Filters
|
||||
|
||||
##### Custom Field Access
|
||||
|
||||
The `get_cf_value` filter retrieves a value from custom field data with optional default fallback.
|
||||
|
||||
###### Syntax
|
||||
|
||||
```jinja2
|
||||
{{ custom_fields | get_cf_value('field_name') }}
|
||||
{{ custom_fields | get_cf_value('field_name', 'default_value') }}
|
||||
```
|
||||
|
||||
###### Parameters
|
||||
|
||||
- `custom_fields`: This _must_ be the provided custom field data
|
||||
- `name` (str): Name of the custom field to retrieve
|
||||
- `default` (str, optional): Default value to return if field is not found or has no value
|
||||
|
||||
###### Returns
|
||||
|
||||
- `str | None`: The field value, default value, or `None` if neither exists
|
||||
|
||||
###### Examples
|
||||
|
||||
```jinja2
|
||||
<!-- Basic usage -->
|
||||
{{ custom_fields | get_cf_value('department') }}
|
||||
|
||||
<!-- With default value -->
|
||||
{{ custom_fields | get_cf_value('phone', 'Not provided') }}
|
||||
```
|
||||
|
||||
##### Datetime Formatting
|
||||
|
||||
The `datetime` filter formats a datetime string or datetime object using Python's strftime formatting.
|
||||
|
||||
###### Syntax
|
||||
|
||||
```jinja2
|
||||
{{ datetime_value | datetime('%Y-%m-%d %H:%M:%S') }}
|
||||
```
|
||||
|
||||
###### Parameters
|
||||
|
||||
- `value` (str | datetime): Date/time value to format (strings will be parsed automatically)
|
||||
- `format` (str): Python strftime format string
|
||||
|
||||
###### Returns
|
||||
|
||||
- `str`: Formatted datetime string
|
||||
|
||||
###### Examples
|
||||
|
||||
```jinja2
|
||||
<!-- Format datetime object -->
|
||||
{{ created | datetime('%B %d, %Y at %I:%M %p') }}
|
||||
<!-- Output: "January 15, 2024 at 02:30 PM" -->
|
||||
|
||||
<!-- Custom formatting -->
|
||||
{{ custom_fields | get_cf_value('Date Field') | datetime('%A, %B %d, %Y') }}
|
||||
<!-- Output: "Monday, January 15, 2024" -->
|
||||
```
|
||||
|
||||
See the [strftime format code documentation](https://docs.python.org/3.13/library/datetime.html#strftime-and-strptime-format-codes)
|
||||
for the possible codes and their meanings.
|
||||
|
||||
##### Date Localization
|
||||
|
||||
The `localize_date` filter formats a date or datetime object into a localized string using Babel internationalization.
|
||||
This takes into account the provided locale for translation. Since this must be used on a date or datetime object,
|
||||
you must access the field directly, i.e. `document.created`.
|
||||
An ISO string can also be provided to control the output format.
|
||||
|
||||
###### Syntax
|
||||
|
||||
```jinja2
|
||||
{{ date_value | localize_date('medium', 'en_US') }}
|
||||
{{ datetime_value | localize_date('short', 'fr_FR') }}
|
||||
```
|
||||
|
||||
###### Parameters
|
||||
|
||||
- `value` (date | datetime | str): Date, datetime object or ISO string to format (datetime should be timezone-aware)
|
||||
- `format` (str): Format type - either a Babel preset ('short', 'medium', 'long', 'full') or custom pattern
|
||||
- `locale` (str): Locale code for localization (e.g., 'en_US', 'fr_FR', 'de_DE')
|
||||
|
||||
###### Returns
|
||||
|
||||
- `str`: Localized, formatted date string
|
||||
|
||||
###### Examples
|
||||
|
||||
```jinja2
|
||||
<!-- Preset formats -->
|
||||
{{ document.created | localize_date('short', 'en_US') }}
|
||||
<!-- Output: "1/15/24" -->
|
||||
|
||||
{{ document.created | localize_date('medium', 'en_US') }}
|
||||
<!-- Output: "Jan 15, 2024" -->
|
||||
|
||||
{{ document.created | localize_date('long', 'en_US') }}
|
||||
<!-- Output: "January 15, 2024" -->
|
||||
|
||||
{{ document.created | localize_date('full', 'en_US') }}
|
||||
<!-- Output: "Monday, January 15, 2024" -->
|
||||
|
||||
<!-- Different locales -->
|
||||
{{ document.created | localize_date('medium', 'fr_FR') }}
|
||||
<!-- Output: "15 janv. 2024" -->
|
||||
|
||||
{{ document.created | localize_date('medium', 'de_DE') }}
|
||||
<!-- Output: "15.01.2024" -->
|
||||
|
||||
<!-- Custom patterns -->
|
||||
{{ document.created | localize_date('dd/MM/yyyy', 'en_GB') }}
|
||||
<!-- Output: "15/01/2024" -->
|
||||
```
|
||||
|
||||
See the [supported format codes](https://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns) for more options.
|
||||
|
||||
### Format Presets
|
||||
|
||||
- **short**: Abbreviated format (e.g., "1/15/24")
|
||||
- **medium**: Medium-length format (e.g., "Jan 15, 2024")
|
||||
- **long**: Long format with full month name (e.g., "January 15, 2024")
|
||||
- **full**: Full format including day of week (e.g., "Monday, January 15, 2024")
|
||||
|
||||
#### Additional Variables
|
||||
|
||||
|
||||
18
docs/api.md
18
docs/api.md
@@ -1,4 +1,4 @@
|
||||
# The REST API
|
||||
# REST API
|
||||
|
||||
Paperless-ngx now ships with a fully-documented REST API and a browsable
|
||||
web interface to explore it. The API browsable interface is available at
|
||||
@@ -192,8 +192,8 @@ The endpoint supports the following optional form fields:
|
||||
- `tags`: Similar to correspondent. Specify this multiple times to
|
||||
have multiple tags added to the document.
|
||||
- `archive_serial_number`: An optional archive serial number to set.
|
||||
- `custom_fields`: An array of custom field ids to assign (with an empty
|
||||
value) to the document.
|
||||
- `custom_fields`: Either an array of custom field ids to assign (with an empty
|
||||
value) to the document or an object mapping field id -> value.
|
||||
|
||||
The endpoint will immediately return HTTP 200 if the document consumption
|
||||
process was started successfully, with the UUID of the consumption task
|
||||
@@ -282,6 +282,18 @@ The following methods are supported:
|
||||
- `"merge": true or false` (defaults to false)
|
||||
- The `merge` flag determines if the supplied permissions will overwrite all existing permissions (including
|
||||
removing them) or be merged with existing permissions.
|
||||
- `edit_pdf`
|
||||
- Requires `parameters`:
|
||||
- `"doc_ids": [DOCUMENT_ID]` A list of a single document ID to edit.
|
||||
- `"operations": [OPERATION, ...]` A list of operations to perform on the documents. Each operation is a dictionary
|
||||
with the following keys:
|
||||
- `"page": PAGE_NUMBER` The page number to edit (1-based).
|
||||
- `"rotate": DEGREES` Optional rotation in degrees (90, 180, 270).
|
||||
- `"doc": OUTPUT_DOCUMENT_INDEX` Optional index of the output document for split operations.
|
||||
- Optional `parameters`:
|
||||
- `"delete_original": true` to delete the original documents after editing.
|
||||
- `"update_document": true` to update the existing document with the edited PDF.
|
||||
- `"include_metadata": true` to copy metadata from the original document to the edited document.
|
||||
- `merge`
|
||||
- No additional `parameters` required.
|
||||
- The ordering of the merged document is determined by the list of IDs.
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
:root > * {
|
||||
--md-primary-fg-color: #17541f;
|
||||
--md-primary-fg-color--dark: #17541f;
|
||||
--md-primary-fg-color--light: #17541f;
|
||||
--md-accent-fg-color: #2b8a38;
|
||||
:root>* {
|
||||
--paperless-green: #17541f;
|
||||
--paperless-green-accent: #2b8a38;
|
||||
--md-primary-fg-color: var(--paperless-green);
|
||||
--md-primary-fg-color--dark: var(--paperless-green);
|
||||
--md-primary-fg-color--light: var(--paperless-green-accent);
|
||||
--md-accent-fg-color: var(--paperless-green-accent);
|
||||
--md-typeset-a-color: #21652a;
|
||||
}
|
||||
|
||||
.md-header,
|
||||
.md-tabs {
|
||||
background-color: var(--paperless-green);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.md-tabs__link {
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
}
|
||||
|
||||
.md-tabs__link:hover,
|
||||
.md-tabs__link--active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
[data-md-color-scheme="slate"] {
|
||||
--md-hue: 222;
|
||||
--md-default-bg-color: hsla(var(--md-hue), 15%, 10%, 1);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
@@ -69,8 +87,8 @@ h4 code {
|
||||
}
|
||||
|
||||
/* Hide config vars from sidebar, toc and move the border on mobile case their hidden */
|
||||
.md-nav.md-nav--secondary .md-nav__item .md-nav__link[href*="PAPERLESS_"],
|
||||
.md-nav.md-nav--secondary .md-nav__item .md-nav__link[href*="USERMAP_"] {
|
||||
.md-nav.md-nav--secondary .md-nav__item:has(> .md-nav__link[href*="PAPERLESS_"]),
|
||||
.md-nav.md-nav--secondary .md-nav__item:has(> .md-nav__link[href*="USERMAP_"]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -83,18 +101,3 @@ h4 code {
|
||||
border-top: .05rem solid var(--md-default-fg-color--lightest);
|
||||
}
|
||||
}
|
||||
|
||||
/* Show search shortcut key */
|
||||
[data-md-toggle="search"]:not(:checked) ~ .md-header .md-search__form::after {
|
||||
position: absolute;
|
||||
top: .3rem;
|
||||
right: .3rem;
|
||||
display: block;
|
||||
padding: .1rem .4rem;
|
||||
color: var(--md-default-fg-color--lighter);
|
||||
font-weight: bold;
|
||||
font-size: .8rem;
|
||||
border: .05rem solid var(--md-default-fg-color--lighter);
|
||||
border-radius: .1rem;
|
||||
content: "/";
|
||||
}
|
||||
|
||||
@@ -1,5 +1,899 @@
|
||||
# Changelog
|
||||
|
||||
## paperless-ngx 2.20.8
|
||||
|
||||
### Security
|
||||
|
||||
- Resolve [GHSA-7qqc-wrcw-2fj9](https://github.com/paperless-ngx/paperless-ngx/security/advisories/GHSA-7qqc-wrcw-2fj9)
|
||||
|
||||
## paperless-ngx 2.20.7
|
||||
|
||||
### Security
|
||||
|
||||
- Resolve [GHSA-x395-6h48-wr8v](https://github.com/paperless-ngx/paperless-ngx/security/advisories/GHSA-x395-6h48-wr8v)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Performance fix: use subqueries to improve object retrieval in large installs [@shamoon](https://github.com/shamoon) ([#11950](https://github.com/paperless-ngx/paperless-ngx/pull/11950))
|
||||
- Fix: correct user dropdown button icon styling [@shamoon](https://github.com/shamoon) ([#12092](https://github.com/paperless-ngx/paperless-ngx/issues/12092))
|
||||
- Fix: fix broken docker create_classifier command in 2.20.6 [@shamoon](https://github.com/shamoon) ([#11965](https://github.com/paperless-ngx/paperless-ngx/issues/11965))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>3 changes</summary>
|
||||
|
||||
- Performance fix: use subqueries to improve object retrieval in large installs [@shamoon](https://github.com/shamoon) ([#11950](https://github.com/paperless-ngx/paperless-ngx/pull/11950))
|
||||
- Fix: correct user dropdown button icon styling [@shamoon](https://github.com/shamoon) ([#12092](https://github.com/paperless-ngx/paperless-ngx/issues/12092))
|
||||
- Fix: fix broken docker create_classifier command in 2.20.6 [@shamoon](https://github.com/shamoon) ([#11965](https://github.com/paperless-ngx/paperless-ngx/issues/11965))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.20.6
|
||||
|
||||
### Security
|
||||
|
||||
- Resolve [GHSA-jqwv-hx7q-fxh3](https://github.com/paperless-ngx/paperless-ngx/security/advisories/GHSA-jqwv-hx7q-fxh3) and [GHSA-w47q-3m69-84v8](https://github.com/paperless-ngx/paperless-ngx/security/advisories/GHSA-w47q-3m69-84v8)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: extract all ids for nested tags [@shamoon](https://github.com/shamoon) ([#11888](https://github.com/paperless-ngx/paperless-ngx/pull/11888))
|
||||
- Fixhancement: change date calculation for 'this year' to include future documents [@shamoon](https://github.com/shamoon) ([#11884](https://github.com/paperless-ngx/paperless-ngx/pull/11884))
|
||||
- Fix: Running management scripts under rootless could fail [@stumpylog](https://github.com/stumpylog) ([#11870](https://github.com/paperless-ngx/paperless-ngx/pull/11870))
|
||||
- Fix: use correct field id for overrides [@shamoon](https://github.com/shamoon) ([#11869](https://github.com/paperless-ngx/paperless-ngx/pull/11869))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>3 changes</summary>
|
||||
|
||||
- Fix: extract all ids for nested tags [@shamoon](https://github.com/shamoon) ([#11888](https://github.com/paperless-ngx/paperless-ngx/pull/11888))
|
||||
- Fixhancement: change date calculation for 'this year' to include future documents [@shamoon](https://github.com/shamoon) ([#11884](https://github.com/paperless-ngx/paperless-ngx/pull/11884))
|
||||
- Fix: use correct field id for overrides [@shamoon](https://github.com/shamoon) ([#11869](https://github.com/paperless-ngx/paperless-ngx/pull/11869))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.20.5
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: ensure horizontal scroll for long tag names in list, wrap tags without parent [@shamoon](https://github.com/shamoon) ([#11811](https://github.com/paperless-ngx/paperless-ngx/pull/11811))
|
||||
- Fix: use explicit order field for workflow actions [@shamoon](https://github.com/shamoon) [@stumpylog](https://github.com/stumpylog) ([#11781](https://github.com/paperless-ngx/paperless-ngx/pull/11781))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>2 changes</summary>
|
||||
|
||||
- Fix: ensure horizontal scroll for long tag names in list, wrap tags without parent [@shamoon](https://github.com/shamoon) ([#11811](https://github.com/paperless-ngx/paperless-ngx/pull/11811))
|
||||
- Fix: use explicit order field for workflow actions [@shamoon](https://github.com/shamoon) [@stumpylog](https://github.com/stumpylog) ([#11781](https://github.com/paperless-ngx/paperless-ngx/pull/11781))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.20.4
|
||||
|
||||
### Security
|
||||
|
||||
- Resolve [GHSA-28cf-xvcf-hw6m](https://github.com/paperless-ngx/paperless-ngx/security/advisories/GHSA-28cf-xvcf-hw6m)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: propagate metadata override created value [@shamoon](https://github.com/shamoon) ([#11659](https://github.com/paperless-ngx/paperless-ngx/pull/11659))
|
||||
- Fix: support ordering by storage path name [@shamoon](https://github.com/shamoon) ([#11661](https://github.com/paperless-ngx/paperless-ngx/pull/11661))
|
||||
- Fix: validate cf integer values within PostgreSQL range [@shamoon](https://github.com/shamoon) ([#11666](https://github.com/paperless-ngx/paperless-ngx/pull/11666))
|
||||
- Fixhancement: add error handling and retry when opening index [@shamoon](https://github.com/shamoon) ([#11731](https://github.com/paperless-ngx/paperless-ngx/pull/11731))
|
||||
- Fix: fix recurring workflow to respect latest run time [@shamoon](https://github.com/shamoon) ([#11735](https://github.com/paperless-ngx/paperless-ngx/pull/11735))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>5 changes</summary>
|
||||
|
||||
- Fix: propagate metadata override created value [@shamoon](https://github.com/shamoon) ([#11659](https://github.com/paperless-ngx/paperless-ngx/pull/11659))
|
||||
- Fix: support ordering by storage path name [@shamoon](https://github.com/shamoon) ([#11661](https://github.com/paperless-ngx/paperless-ngx/pull/11661))
|
||||
- Fix: validate cf integer values within PostgreSQL range [@shamoon](https://github.com/shamoon) ([#11666](https://github.com/paperless-ngx/paperless-ngx/pull/11666))
|
||||
- Fixhancement: add error handling and retry when opening index [@shamoon](https://github.com/shamoon) ([#11731](https://github.com/paperless-ngx/paperless-ngx/pull/11731))
|
||||
- Fix: fix recurring workflow to respect latest run time [@shamoon](https://github.com/shamoon) ([#11735](https://github.com/paperless-ngx/paperless-ngx/pull/11735))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.20.3
|
||||
|
||||
### Security
|
||||
|
||||
- Resolve [GHSA-7cq3-mhxq-w946](https://github.com/paperless-ngx/paperless-ngx/security/advisories/GHSA-7cq3-mhxq-w946)
|
||||
|
||||
## paperless-ngx 2.20.2
|
||||
|
||||
### Security
|
||||
|
||||
- Resolve [GHSA-6653-vcx4-69mc](https://github.com/paperless-ngx/paperless-ngx/security/advisories/GHSA-6653-vcx4-69mc)
|
||||
- Resolve [GHSA-24x5-wp64-9fcc](https://github.com/paperless-ngx/paperless-ngx/security/advisories/GHSA-24x5-wp64-9fcc)
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- Tweakhancement: dim inactive users in users-groups list [@shamoon](https://github.com/shamoon) ([#11537](https://github.com/paperless-ngx/paperless-ngx/pull/11537))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: Expanded SVG validation whitelist and additional checks [@stumpylog](https://github.com/stumpylog) ([#11590](https://github.com/paperless-ngx/paperless-ngx/pull/11590))
|
||||
- Fix: normalize allowed SVG tag and attribute names, add version [@shamoon](https://github.com/shamoon) ([#11586](https://github.com/paperless-ngx/paperless-ngx/pull/11586))
|
||||
- Fix: pass additional arguments to TagSerializer for permissions [@shamoon](https://github.com/shamoon) ([#11576](https://github.com/paperless-ngx/paperless-ngx/pull/11576))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Chore(deps): Bump actions/checkout from 5 to 6 in the actions group @[dependabot[bot]](https://github.com/apps/dependabot) ([#11515](https://github.com/paperless-ngx/paperless-ngx/pull/11515))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>6 changes</summary>
|
||||
|
||||
- Chore: update Angular dependencies to 20.3.15 [@shamoon](https://github.com/shamoon) ([#11568](https://github.com/paperless-ngx/paperless-ngx/pull/11568))
|
||||
- Chore(deps): Bump actions/checkout from 5 to 6 in the actions group @[dependabot[bot]](https://github.com/apps/dependabot) ([#11515](https://github.com/paperless-ngx/paperless-ngx/pull/11515))
|
||||
- Chore(deps-dev): Bump webpack from 5.102.1 to 5.103.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11513](https://github.com/paperless-ngx/paperless-ngx/pull/11513))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.56.1 to 1.57.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11514](https://github.com/paperless-ngx/paperless-ngx/pull/11514))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11512](https://github.com/paperless-ngx/paperless-ngx/pull/11512))
|
||||
- docker(deps): bump astral-sh/uv from 0.9.14-python3.12-trixie-slim to 0.9.15-python3.12-trixie-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#11533](https://github.com/paperless-ngx/paperless-ngx/pull/11533))
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>12 changes</summary>
|
||||
|
||||
- Fix: Expanded SVG validation whitelist and additional checks [@stumpylog](https://github.com/stumpylog) ([#11590](https://github.com/paperless-ngx/paperless-ngx/pull/11590))
|
||||
- Fixhancement: pass ordering to tag children [@shamoon](https://github.com/shamoon) ([#11556](https://github.com/paperless-ngx/paperless-ngx/pull/11556))
|
||||
- Performance: avoid unnecessary filename operations on bulk custom field updates [@shamoon](https://github.com/shamoon) ([#11558](https://github.com/paperless-ngx/paperless-ngx/pull/11558))
|
||||
- Fix: normalize allowed SVG tag and attribute names, add version [@shamoon](https://github.com/shamoon) ([#11586](https://github.com/paperless-ngx/paperless-ngx/pull/11586))
|
||||
- Chore: refactor workflows code [@shamoon](https://github.com/shamoon) ([#11563](https://github.com/paperless-ngx/paperless-ngx/pull/11563))
|
||||
- Fix: pass additional arguments to TagSerializer for permissions [@shamoon](https://github.com/shamoon) ([#11576](https://github.com/paperless-ngx/paperless-ngx/pull/11576))
|
||||
- Chore: update Angular dependencies to 20.3.15 [@shamoon](https://github.com/shamoon) ([#11568](https://github.com/paperless-ngx/paperless-ngx/pull/11568))
|
||||
- Chore(deps-dev): Bump webpack from 5.102.1 to 5.103.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11513](https://github.com/paperless-ngx/paperless-ngx/pull/11513))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.56.1 to 1.57.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11514](https://github.com/paperless-ngx/paperless-ngx/pull/11514))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11512](https://github.com/paperless-ngx/paperless-ngx/pull/11512))
|
||||
- Tweakhancement: dim inactive users in users-groups list [@shamoon](https://github.com/shamoon) ([#11537](https://github.com/paperless-ngx/paperless-ngx/pull/11537))
|
||||
- Chore: add some output of social login errors [@shamoon](https://github.com/shamoon) ([#11527](https://github.com/paperless-ngx/paperless-ngx/pull/11527))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.20.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: set search term when using advanced search from global search [@shamoon](https://github.com/shamoon) ([#11503](https://github.com/paperless-ngx/paperless-ngx/pull/11503))
|
||||
- Fix: change async handling of select custom field updates [@shamoon](https://github.com/shamoon) ([#11490](https://github.com/paperless-ngx/paperless-ngx/pull/11490))
|
||||
- Fix: skip SSL for MariaDB ping in init script [@danielrheinbay](https://github.com/danielrheinbay) ([#11491](https://github.com/paperless-ngx/paperless-ngx/pull/11491))
|
||||
- Fix: handle allauth groups location breaking change [@shamoon](https://github.com/shamoon) ([#11471](https://github.com/paperless-ngx/paperless-ngx/pull/11471))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- docker(deps): Bump astral-sh/uv from 0.9.10-python3.12-trixie-slim to 0.9.11-python3.12-trixie-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#11450](https://github.com/paperless-ngx/paperless-ngx/pull/11450))
|
||||
- Chore(deps): Bump @angular/common from 20.3.12 to 20.3.14 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11481](https://github.com/paperless-ngx/paperless-ngx/pull/11481))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>4 changes</summary>
|
||||
|
||||
- Fix: set search term when using advanced search from global search [@shamoon](https://github.com/shamoon) ([#11503](https://github.com/paperless-ngx/paperless-ngx/pull/11503))
|
||||
- Fix: change async handling of select custom field updates [@shamoon](https://github.com/shamoon) ([#11490](https://github.com/paperless-ngx/paperless-ngx/pull/11490))
|
||||
- Chore(deps): Bump @angular/common from 20.3.12 to 20.3.14 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11481](https://github.com/paperless-ngx/paperless-ngx/pull/11481))
|
||||
- Fix: handle allauth groups location breaking change [@shamoon](https://github.com/shamoon) ([#11471](https://github.com/paperless-ngx/paperless-ngx/pull/11471))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.20.0
|
||||
|
||||
### Notable Changes
|
||||
|
||||
- Feature: Upgrade underlying Docker image to Trixie [@stumpylog](https://github.com/stumpylog) ([#10562](https://github.com/paperless-ngx/paperless-ngx/pull/10562))
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- Feature: Upgrade underlying Docker image to Trixie [@stumpylog](https://github.com/stumpylog) ([#10562](https://github.com/paperless-ngx/paperless-ngx/pull/10562))
|
||||
- Fixhancement: more log viewer improvements [@shamoon](https://github.com/shamoon) ([#11426](https://github.com/paperless-ngx/paperless-ngx/pull/11426))
|
||||
- Performance: Replace duplicated static files with symlinks [@stumpylog](https://github.com/stumpylog) ([#11418](https://github.com/paperless-ngx/paperless-ngx/pull/11418))
|
||||
- Enhancement: add more relative dates, support modified [@shamoon](https://github.com/shamoon) ([#11411](https://github.com/paperless-ngx/paperless-ngx/pull/11411))
|
||||
- Performance: make move files after select custom field change async [@shamoon](https://github.com/shamoon) ([#11391](https://github.com/paperless-ngx/paperless-ngx/pull/11391))
|
||||
- Enhancement: Use a better check for the MariaDB server to be ready [@stumpylog](https://github.com/stumpylog) ([#11396](https://github.com/paperless-ngx/paperless-ngx/pull/11396))
|
||||
- Enhancement: speed-up docker container startup [@flrgh](https://github.com/flrgh) ([#11134](https://github.com/paperless-ngx/paperless-ngx/pull/11134))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: prevent focus loss from change detection in cf query dropdown [@shamoon](https://github.com/shamoon) ([#11409](https://github.com/paperless-ngx/paperless-ngx/pull/11409))
|
||||
- Fix: sort editing filterable dropdowns sooner [@shamoon](https://github.com/shamoon) ([#11404](https://github.com/paperless-ngx/paperless-ngx/pull/11404))
|
||||
- Fix: support for custom field ordering w advanced search [@shamoon](https://github.com/shamoon) ([#11383](https://github.com/paperless-ngx/paperless-ngx/pull/11383))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Chore(deps): Bump the actions group with 7 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11259](https://github.com/paperless-ngx/paperless-ngx/pull/11259))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>16 changes</summary>
|
||||
|
||||
- Chore: Upgrades psycopg to 3.2.12 [@stumpylog](https://github.com/stumpylog) ([#11420](https://github.com/paperless-ngx/paperless-ngx/pull/11420))
|
||||
- Chore(deps-dev): Bump glob from 10.4.1 to 10.5.0 in /src/paperless_mail/templates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11413](https://github.com/paperless-ngx/paperless-ngx/pull/11413))
|
||||
- docker-compose(deps): bump gotenberg/gotenberg from 8.24 to 8.25 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#11393](https://github.com/paperless-ngx/paperless-ngx/pull/11393))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 21 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11260](https://github.com/paperless-ngx/paperless-ngx/pull/11260))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.55.1 to 1.56.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11263](https://github.com/paperless-ngx/paperless-ngx/pull/11263))
|
||||
- Chore(deps-dev): Bump webpack from 5.102.0 to 5.102.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11264](https://github.com/paperless-ngx/paperless-ngx/pull/11264))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11262](https://github.com/paperless-ngx/paperless-ngx/pull/11262))
|
||||
- Chore(deps-dev): Bump jest-preset-angular from 15.0.2 to 15.0.3 in /src-ui in the frontend-jest-dependencies group @[dependabot[bot]](https://github.com/apps/dependabot) ([#11261](https://github.com/paperless-ngx/paperless-ngx/pull/11261))
|
||||
- Chore(deps-dev): Bump @types/node from 24.6.1 to 24.9.2 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11265](https://github.com/paperless-ngx/paperless-ngx/pull/11265))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 11 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11337](https://github.com/paperless-ngx/paperless-ngx/pull/11337))
|
||||
- Chore(deps): Bump django-auditlog from 3.2.1 to 3.3.0 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11021](https://github.com/paperless-ngx/paperless-ngx/pull/11021))
|
||||
- Chore(deps): Bump the actions group with 7 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11259](https://github.com/paperless-ngx/paperless-ngx/pull/11259))
|
||||
- Chore(deps): Bump drf-spectacular-sidecar from 2025.9.1 to 2025.10.1 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11019](https://github.com/paperless-ngx/paperless-ngx/pull/11019))
|
||||
- Chore(deps): Bump django-filter from 25.1 to 25.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11020](https://github.com/paperless-ngx/paperless-ngx/pull/11020))
|
||||
- Chore(deps): Update django-allauth[mfa,socialaccount] requirement from ~=65.4.0 to ~=65.12.1 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11198](https://github.com/paperless-ngx/paperless-ngx/pull/11198))
|
||||
- docker(deps): bump astral-sh/uv from 0.9.9-python3.12-bookworm-slim to 0.9.10-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#11394](https://github.com/paperless-ngx/paperless-ngx/pull/11394))
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>19 changes</summary>
|
||||
|
||||
- Fixhancement: more log viewer improvements [@shamoon](https://github.com/shamoon) ([#11426](https://github.com/paperless-ngx/paperless-ngx/pull/11426))
|
||||
- Chore: Upgrades psycopg to 3.2.12 [@stumpylog](https://github.com/stumpylog) ([#11420](https://github.com/paperless-ngx/paperless-ngx/pull/11420))
|
||||
- Enhancement: add more relative dates, support modified [@shamoon](https://github.com/shamoon) ([#11411](https://github.com/paperless-ngx/paperless-ngx/pull/11411))
|
||||
- Chore(deps-dev): Bump glob from 10.4.1 to 10.5.0 in /src/paperless_mail/templates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11413](https://github.com/paperless-ngx/paperless-ngx/pull/11413))
|
||||
- Performance: make move files after select custom field change async [@shamoon](https://github.com/shamoon) ([#11391](https://github.com/paperless-ngx/paperless-ngx/pull/11391))
|
||||
- Fix: prevent focus loss from change detection in cf query dropdown [@shamoon](https://github.com/shamoon) ([#11409](https://github.com/paperless-ngx/paperless-ngx/pull/11409))
|
||||
- Fix: sort editing filterable dropdowns sooner [@shamoon](https://github.com/shamoon) ([#11404](https://github.com/paperless-ngx/paperless-ngx/pull/11404))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 21 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11260](https://github.com/paperless-ngx/paperless-ngx/pull/11260))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.55.1 to 1.56.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11263](https://github.com/paperless-ngx/paperless-ngx/pull/11263))
|
||||
- Chore(deps-dev): Bump webpack from 5.102.0 to 5.102.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11264](https://github.com/paperless-ngx/paperless-ngx/pull/11264))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11262](https://github.com/paperless-ngx/paperless-ngx/pull/11262))
|
||||
- Chore(deps-dev): Bump jest-preset-angular from 15.0.2 to 15.0.3 in /src-ui in the frontend-jest-dependencies group @[dependabot[bot]](https://github.com/apps/dependabot) ([#11261](https://github.com/paperless-ngx/paperless-ngx/pull/11261))
|
||||
- Chore(deps-dev): Bump @types/node from 24.6.1 to 24.9.2 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#11265](https://github.com/paperless-ngx/paperless-ngx/pull/11265))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 11 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11337](https://github.com/paperless-ngx/paperless-ngx/pull/11337))
|
||||
- Chore(deps): Bump django-auditlog from 3.2.1 to 3.3.0 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11021](https://github.com/paperless-ngx/paperless-ngx/pull/11021))
|
||||
- Chore(deps): Bump drf-spectacular-sidecar from 2025.9.1 to 2025.10.1 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11019](https://github.com/paperless-ngx/paperless-ngx/pull/11019))
|
||||
- Chore(deps): Bump django-filter from 25.1 to 25.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11020](https://github.com/paperless-ngx/paperless-ngx/pull/11020))
|
||||
- Chore(deps): Update django-allauth[mfa,socialaccount] requirement from ~=65.4.0 to ~=65.12.1 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11198](https://github.com/paperless-ngx/paperless-ngx/pull/11198))
|
||||
- Fix: support for custom field ordering w advanced search [@shamoon](https://github.com/shamoon) ([#11383](https://github.com/paperless-ngx/paperless-ngx/pull/11383))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.19.6
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Chore: include password validation on user edit [@shamoon](https://github.com/shamoon) ([#11308](https://github.com/paperless-ngx/paperless-ngx/pull/11308))
|
||||
- Fix: include BASE_URL when constructing for workflows [@ebardsley](https://github.com/ebardsley) ([#11360](https://github.com/paperless-ngx/paperless-ngx/pull/11360))
|
||||
- Fixhancement: refactor email attachment logic [@shamoon](https://github.com/shamoon) ([#11336](https://github.com/paperless-ngx/paperless-ngx/pull/11336))
|
||||
- Fixhancement: trim whitespace for some text searches [@shamoon](https://github.com/shamoon) ([#11357](https://github.com/paperless-ngx/paperless-ngx/pull/11357))
|
||||
- Fix: update Outlook refresh token when refreshed [@shamoon](https://github.com/shamoon) ([#11341](https://github.com/paperless-ngx/paperless-ngx/pull/11341))
|
||||
- Fix: only cache remote version data for version checking [@shamoon](https://github.com/shamoon) ([#11320](https://github.com/paperless-ngx/paperless-ngx/pull/11320))
|
||||
- Fix: include replace none logic in storage path preview, improve jinja conditionals for empty metadata [@shamoon](https://github.com/shamoon) ([#11315](https://github.com/paperless-ngx/paperless-ngx/pull/11315))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- docker(deps): bump astral-sh/uv from 0.9.7-python3.12-bookworm-slim to 0.9.9-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#11338](https://github.com/paperless-ngx/paperless-ngx/pull/11338))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>7 changes</summary>
|
||||
|
||||
- Fix: include BASE_URL when constructing for workflows [@ebardsley](https://github.com/ebardsley) ([#11360](https://github.com/paperless-ngx/paperless-ngx/pull/11360))
|
||||
- Fixhancement: refactor email attachment logic [@shamoon](https://github.com/shamoon) ([#11336](https://github.com/paperless-ngx/paperless-ngx/pull/11336))
|
||||
- Fixhancement: trim whitespace for some text searches [@shamoon](https://github.com/shamoon) ([#11357](https://github.com/paperless-ngx/paperless-ngx/pull/11357))
|
||||
- Fix: update Outlook refresh token when refreshed [@shamoon](https://github.com/shamoon) ([#11341](https://github.com/paperless-ngx/paperless-ngx/pull/11341))
|
||||
- Fix: only cache remote version data for version checking [@shamoon](https://github.com/shamoon) ([#11320](https://github.com/paperless-ngx/paperless-ngx/pull/11320))
|
||||
- Fix: include replace none logic in storage path preview, improve jinja conditionals for empty metadata [@shamoon](https://github.com/shamoon) ([#11315](https://github.com/paperless-ngx/paperless-ngx/pull/11315))
|
||||
- Chore: include password validation on user edit [@shamoon](https://github.com/shamoon) ([#11308](https://github.com/paperless-ngx/paperless-ngx/pull/11308))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.19.5
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: ensure custom field query propagation, change detection [@shamoon](https://github.com/shamoon) ([#11291](https://github.com/paperless-ngx/paperless-ngx/pull/11291))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- docker(deps): Bump astral-sh/uv from 0.9.4-python3.12-bookworm-slim to 0.9.7-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#11283](https://github.com/paperless-ngx/paperless-ngx/pull/11283))
|
||||
|
||||
### All App Changes
|
||||
|
||||
- Fix: ensure custom field query propagation, change detection [@shamoon](https://github.com/shamoon) ([#11291](https://github.com/paperless-ngx/paperless-ngx/pull/11291))
|
||||
|
||||
## paperless-ngx 2.19.4
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: use original_file when attaching docs to workflow emails with added trigger [@shamoon](https://github.com/shamoon) ([#11266](https://github.com/paperless-ngx/paperless-ngx/pull/11266))
|
||||
- Fix: mark 'Select' button in doc list for translation [@shamoon](https://github.com/shamoon) ([#11278](https://github.com/paperless-ngx/paperless-ngx/pull/11278))
|
||||
- Fix: respect fields parameter for created field [@shamoon](https://github.com/shamoon) ([#11251](https://github.com/paperless-ngx/paperless-ngx/pull/11251))
|
||||
- Fix: improve legibility of processed mail error popover in light mode [@shamoon](https://github.com/shamoon) ([#11258](https://github.com/paperless-ngx/paperless-ngx/pull/11258))
|
||||
- Fixhancement: truncate large logs, improve auto-scroll [@shamoon](https://github.com/shamoon) ([#11239](https://github.com/paperless-ngx/paperless-ngx/pull/11239))
|
||||
- Chore: add max-height and overflow to processedmail error popover [@shamoon](https://github.com/shamoon) ([#11252](https://github.com/paperless-ngx/paperless-ngx/pull/11252))
|
||||
- Fix: delay iframe DOM removal, handle onafterprint error for print in FF [@shamoon](https://github.com/shamoon) ([#11237](https://github.com/paperless-ngx/paperless-ngx/pull/11237))
|
||||
- Fix: de-deduplicate children in tag list when filtering [@shamoon](https://github.com/shamoon) ([#11229](https://github.com/paperless-ngx/paperless-ngx/pull/11229))
|
||||
|
||||
### Performance
|
||||
|
||||
- Performance: re-enable virtual scroll, bump ng-select [@shamoon](https://github.com/shamoon) ([#11279](https://github.com/paperless-ngx/paperless-ngx/pull/11279))
|
||||
- Performance: use virtual scroll container and log level parsing for logs view [@MickLesk](https://github.com/MickLesk) ([#11233](https://github.com/paperless-ngx/paperless-ngx/pull/11233))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>11 changes</summary>
|
||||
|
||||
- Performance: re-enable virtual scroll, bump ng-select [@shamoon](https://github.com/shamoon) ([#11279](https://github.com/paperless-ngx/paperless-ngx/pull/11279))
|
||||
- Fix: use original_file when attaching docs to workflow emails with added trigger [@shamoon](https://github.com/shamoon) ([#11266](https://github.com/paperless-ngx/paperless-ngx/pull/11266))
|
||||
- Fix: mark 'Select' button in doc list for translation [@shamoon](https://github.com/shamoon) ([#11278](https://github.com/paperless-ngx/paperless-ngx/pull/11278))
|
||||
- Fix: respect fields parameter for created field [@shamoon](https://github.com/shamoon) ([#11251](https://github.com/paperless-ngx/paperless-ngx/pull/11251))
|
||||
- Fix: improve legibility of processed mail error popover in light mode [@shamoon](https://github.com/shamoon) ([#11258](https://github.com/paperless-ngx/paperless-ngx/pull/11258))
|
||||
- Fixhancement: truncate large logs, improve auto-scroll [@shamoon](https://github.com/shamoon) ([#11239](https://github.com/paperless-ngx/paperless-ngx/pull/11239))
|
||||
- Chore: add max-height and overflow to processedmail error popover [@shamoon](https://github.com/shamoon) ([#11252](https://github.com/paperless-ngx/paperless-ngx/pull/11252))
|
||||
- Fix: delay iframe DOM removal, handle onafterprint error for print in FF [@shamoon](https://github.com/shamoon) ([#11237](https://github.com/paperless-ngx/paperless-ngx/pull/11237))
|
||||
- Performance: use virtual scroll container and log level parsing for logs view [@MickLesk](https://github.com/MickLesk) ([#11233](https://github.com/paperless-ngx/paperless-ngx/pull/11233))
|
||||
- Chore: cache Github version check for 15 minutes [@shamoon](https://github.com/shamoon) ([#11235](https://github.com/paperless-ngx/paperless-ngx/pull/11235))
|
||||
- Fix: de-deduplicate children in tag list when filtering [@shamoon](https://github.com/shamoon) ([#11229](https://github.com/paperless-ngx/paperless-ngx/pull/11229))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.19.3
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: remove unnecessary permission requirements for new email endpoint [@shamoon](https://github.com/shamoon) ([#11215](https://github.com/paperless-ngx/paperless-ngx/pull/11215))
|
||||
- Fix: refactor nested sorting in filterable dropdowns [@shamoon](https://github.com/shamoon) ([#11214](https://github.com/paperless-ngx/paperless-ngx/pull/11214))
|
||||
- Fix: add root tag filtering for tag list page consistency, fix toggle all [@shamoon](https://github.com/shamoon) ([#11208](https://github.com/paperless-ngx/paperless-ngx/pull/11208))
|
||||
- Fix: support ConsumableDocument in email attachments [@shamoon](https://github.com/shamoon) ([#11196](https://github.com/paperless-ngx/paperless-ngx/pull/11196))
|
||||
- Fix: add missing import for ConfirmButtonComponent in user-edit-dialog [@shamoon](https://github.com/shamoon) ([#11167](https://github.com/paperless-ngx/paperless-ngx/pull/11167))
|
||||
- Fix: resolve migration warning in 2.19.2 [@shamoon](https://github.com/shamoon) ([#11157](https://github.com/paperless-ngx/paperless-ngx/pull/11157))
|
||||
|
||||
### Changes
|
||||
|
||||
- Change: make workflow action only title draggable [@shamoon](https://github.com/shamoon) ([#11209](https://github.com/paperless-ngx/paperless-ngx/pull/11209))
|
||||
- Change: change workflowrun to softdeletemodel [@shamoon](https://github.com/shamoon) ([#11194](https://github.com/paperless-ngx/paperless-ngx/pull/11194))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Chore(deps): Bump django from 5.2.6 to 5.2.7 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11200](https://github.com/paperless-ngx/paperless-ngx/pull/11200))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>9 changes</summary>
|
||||
|
||||
- Chore(deps): Bump django from 5.2.6 to 5.2.7 @[dependabot[bot]](https://github.com/apps/dependabot) ([#11200](https://github.com/paperless-ngx/paperless-ngx/pull/11200))
|
||||
- Fix: remove unnecessary permission requirements for new email endpoint [@shamoon](https://github.com/shamoon) ([#11215](https://github.com/paperless-ngx/paperless-ngx/pull/11215))
|
||||
- Fix: refactor nested sorting in filterable dropdowns [@shamoon](https://github.com/shamoon) ([#11214](https://github.com/paperless-ngx/paperless-ngx/pull/11214))
|
||||
- Fix: add root tag filtering for tag list page consistency, fix toggle all [@shamoon](https://github.com/shamoon) ([#11208](https://github.com/paperless-ngx/paperless-ngx/pull/11208))
|
||||
- Change: make workflow action only title draggable [@shamoon](https://github.com/shamoon) ([#11209](https://github.com/paperless-ngx/paperless-ngx/pull/11209))
|
||||
- Change: change workflowrun to softdeletemodel [@shamoon](https://github.com/shamoon) ([#11194](https://github.com/paperless-ngx/paperless-ngx/pull/11194))
|
||||
- Chore: Minor migration optimization for workflow titles [@stumpylog](https://github.com/stumpylog) ([#11197](https://github.com/paperless-ngx/paperless-ngx/pull/11197))
|
||||
- Fix: support ConsumableDocument in email attachments [@shamoon](https://github.com/shamoon) ([#11196](https://github.com/paperless-ngx/paperless-ngx/pull/11196))
|
||||
- Fix: add missing import for ConfirmButtonComponent in user-edit-dialog [@shamoon](https://github.com/shamoon) ([#11167](https://github.com/paperless-ngx/paperless-ngx/pull/11167))
|
||||
- Fix: resolve migration warning in 2.19.2 [@shamoon](https://github.com/shamoon) ([#11157](https://github.com/paperless-ngx/paperless-ngx/pull/11157))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.19.2
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- Fixhancement: display loading status for tags instead of 'Private' [@shamoon](https://github.com/shamoon) ([#11140](https://github.com/paperless-ngx/paperless-ngx/pull/11140))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: Remove edit requirement for bulk email, show based on setting [@shamoon](https://github.com/shamoon) ([#11149](https://github.com/paperless-ngx/paperless-ngx/pull/11149))
|
||||
- Fix: handle undefined IDs in getOriginalObject [@shamoon](https://github.com/shamoon) ([#11147](https://github.com/paperless-ngx/paperless-ngx/pull/11147))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>3 changes</summary>
|
||||
|
||||
- Fix: Remove edit requirement for bulk email, show based on setting [@shamoon](https://github.com/shamoon) ([#11149](https://github.com/paperless-ngx/paperless-ngx/pull/11149))
|
||||
- Fix: handle undefined IDs in getOriginalObject [@shamoon](https://github.com/shamoon) ([#11147](https://github.com/paperless-ngx/paperless-ngx/pull/11147))
|
||||
- Fixhancement: display loading status for tags instead of 'Private' [@shamoon](https://github.com/shamoon) ([#11140](https://github.com/paperless-ngx/paperless-ngx/pull/11140))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.19.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: skip workflow title migration for empty titles [@shamoon](https://github.com/shamoon) ([#11136](https://github.com/paperless-ngx/paperless-ngx/pull/11136))
|
||||
- Fix: restore workflow title migration [@shamoon](https://github.com/shamoon) ([#11131](https://github.com/paperless-ngx/paperless-ngx/pull/11131))
|
||||
- Fix: retrieve document_count for tag children [@shamoon](https://github.com/shamoon) ([#11125](https://github.com/paperless-ngx/paperless-ngx/pull/11125))
|
||||
- Fix: move hierarchical order logic in dropdown sorting [@shamoon](https://github.com/shamoon) ([#11128](https://github.com/paperless-ngx/paperless-ngx/pull/11128))
|
||||
- Fix: use original object for children in tag list [@shamoon](https://github.com/shamoon) ([#11127](https://github.com/paperless-ngx/paperless-ngx/pull/11127))
|
||||
- Fix: dont display or fetch users or groups with insufficient perms [@shamoon](https://github.com/shamoon) ([#11111](https://github.com/paperless-ngx/paperless-ngx/pull/11111))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>6 changes</summary>
|
||||
|
||||
- Fix: skip workflow title migration for empty titles [@shamoon](https://github.com/shamoon) ([#11136](https://github.com/paperless-ngx/paperless-ngx/pull/11136))
|
||||
- Fix: restore workflow title migration [@shamoon](https://github.com/shamoon) ([#11131](https://github.com/paperless-ngx/paperless-ngx/pull/11131))
|
||||
- Fix: retrieve document_count for tag children [@shamoon](https://github.com/shamoon) ([#11125](https://github.com/paperless-ngx/paperless-ngx/pull/11125))
|
||||
- Fix: move hierarchical order logic in dropdown sorting [@shamoon](https://github.com/shamoon) ([#11128](https://github.com/paperless-ngx/paperless-ngx/pull/11128))
|
||||
- Fix: use original object for children in tag list [@shamoon](https://github.com/shamoon) ([#11127](https://github.com/paperless-ngx/paperless-ngx/pull/11127))
|
||||
- Fix: dont display or fetch users or groups with insufficient perms [@shamoon](https://github.com/shamoon) ([#11111](https://github.com/paperless-ngx/paperless-ngx/pull/11111))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.19.0
|
||||
|
||||
### Notable Changes
|
||||
|
||||
- Feature: Advanced Workflow Filters [@shamoon](https://github.com/shamoon) ([#11029](https://github.com/paperless-ngx/paperless-ngx/pull/11029))
|
||||
- Feature: Nested Tags [@shamoon](https://github.com/shamoon) ([#10833](https://github.com/paperless-ngx/paperless-ngx/pull/10833))
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- docker(deps): bump astral-sh/uv from 0.9.2-python3.12-bookworm-slim to 0.9.4-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#11091](https://github.com/paperless-ngx/paperless-ngx/pull/11091))
|
||||
- Enhancement: use friendly file names when emailing documents [@JanKleine](https://github.com/JanKleine) ([#11055](https://github.com/paperless-ngx/paperless-ngx/pull/11055))
|
||||
- Feature: Advanced Workflow Filters [@shamoon](https://github.com/shamoon) ([#11029](https://github.com/paperless-ngx/paperless-ngx/pull/11029))
|
||||
- docker(deps): Bump astral-sh/uv from 0.8.22-python3.12-bookworm-slim to 0.9.2-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#11052](https://github.com/paperless-ngx/paperless-ngx/pull/11052))
|
||||
- Feature: add support for emailing multiple documents [@JanKleine](https://github.com/JanKleine) ([#10666](https://github.com/paperless-ngx/paperless-ngx/pull/10666))
|
||||
- Enhancement: ignore same files in sanity checker as consumer [@shamoon](https://github.com/shamoon) ([#10999](https://github.com/paperless-ngx/paperless-ngx/pull/10999))
|
||||
- Enhancement: open color picker on swatch button click [@shamoon](https://github.com/shamoon) ([#10994](https://github.com/paperless-ngx/paperless-ngx/pull/10994))
|
||||
- Performance: Cache django-guardian permissions when counting documents [@Merinorus](https://github.com/Merinorus) ([#10657](https://github.com/paperless-ngx/paperless-ngx/pull/10657))
|
||||
- Tweakhancement: reorganize some list \& bulk editing buttons [@shamoon](https://github.com/shamoon) ([#10944](https://github.com/paperless-ngx/paperless-ngx/pull/10944))
|
||||
- Enhancement: support workflow path matching of barcode-split documents [@DerRockWolf](https://github.com/DerRockWolf) ([#10723](https://github.com/paperless-ngx/paperless-ngx/pull/10723))
|
||||
- Feature: processed mail UI [@shamoon](https://github.com/shamoon) ([#10866](https://github.com/paperless-ngx/paperless-ngx/pull/10866))
|
||||
- Enhancement: support custom field values on post document [@shamoon](https://github.com/shamoon) ([#10859](https://github.com/paperless-ngx/paperless-ngx/pull/10859))
|
||||
- Feature: Nested Tags [@shamoon](https://github.com/shamoon) ([#10833](https://github.com/paperless-ngx/paperless-ngx/pull/10833))
|
||||
- Enhancement: long text custom field [@jojo2357](https://github.com/jojo2357) ([#10846](https://github.com/paperless-ngx/paperless-ngx/pull/10846))
|
||||
- Enhancement: Add print button [@mpaletti](https://github.com/mpaletti) ([#10626](https://github.com/paperless-ngx/paperless-ngx/pull/10626))
|
||||
- Enhancement: add storage path as workflow trigger filter @david-loe ([#10771](https://github.com/paperless-ngx/paperless-ngx/pull/10771))
|
||||
- Enhancement: jinja template support for workflow title assignment [@sidey79](https://github.com/sidey79) ([#10700](https://github.com/paperless-ngx/paperless-ngx/pull/10700))
|
||||
- Enhancement: Limit excessively long content length when computing suggestions [@Merinorus](https://github.com/Merinorus) ([#10656](https://github.com/paperless-ngx/paperless-ngx/pull/10656))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: remove obsolete warning for custom field value index [@shamoon](https://github.com/shamoon) ([#11083](https://github.com/paperless-ngx/paperless-ngx/pull/11083))
|
||||
- Fix: set min-height for drag-drop items container [@shamoon](https://github.com/shamoon) ([#11064](https://github.com/paperless-ngx/paperless-ngx/pull/11064))
|
||||
- Fix custom field query dropdown toggle corners [@shamoon](https://github.com/shamoon) ([#11028](https://github.com/paperless-ngx/paperless-ngx/pull/11028))
|
||||
- Fix: correct save hotkey action when no next document exists [@shamoon](https://github.com/shamoon) ([#11027](https://github.com/paperless-ngx/paperless-ngx/pull/11027))
|
||||
- Fix: require only change permissions for task dismissal, add frontend error handling [@shamoon](https://github.com/shamoon) ([#11023](https://github.com/paperless-ngx/paperless-ngx/pull/11023))
|
||||
- Chore(deps): Bulk upgrade backend dependencies [@stumpylog](https://github.com/stumpylog) ([#10971](https://github.com/paperless-ngx/paperless-ngx/pull/10971))
|
||||
- Chore: remove Codecov token from CI workflow [@shamoon](https://github.com/shamoon) ([#10941](https://github.com/paperless-ngx/paperless-ngx/pull/10941))
|
||||
- Fix: fix select option removal and pagination update [@shamoon](https://github.com/shamoon) ([#10933](https://github.com/paperless-ngx/paperless-ngx/pull/10933))
|
||||
- Fix: skip fuzzy matching for empty document content [@shamoon](https://github.com/shamoon) ([#10914](https://github.com/paperless-ngx/paperless-ngx/pull/10914))
|
||||
- Fix: add extra error handling to \_consume for file checks [@shamoon](https://github.com/shamoon) ([#10897](https://github.com/paperless-ngx/paperless-ngx/pull/10897))
|
||||
- Fix: restore str celery beat schedule filename [@shamoon](https://github.com/shamoon) ([#10893](https://github.com/paperless-ngx/paperless-ngx/pull/10893))
|
||||
- Fix: fix pdf editor hover rotate counterclockwise button [@shamoon](https://github.com/shamoon) ([#10848](https://github.com/paperless-ngx/paperless-ngx/pull/10848))
|
||||
- Fix: warp long words in toast content [@shamoon](https://github.com/shamoon) ([#10839](https://github.com/paperless-ngx/paperless-ngx/pull/10839))
|
||||
- Fix: fix error when bulk adding empty doc link custom fields [@shamoon](https://github.com/shamoon) ([#10832](https://github.com/paperless-ngx/paperless-ngx/pull/10832))
|
||||
- Fix: set match value for correspondents created by mail rule [@shamoon](https://github.com/shamoon) ([#10820](https://github.com/paperless-ngx/paperless-ngx/pull/10820))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Chore(deps): Bump the actions group with 5 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10978](https://github.com/paperless-ngx/paperless-ngx/pull/10978))
|
||||
- Chore: remove Codecov token from CI workflow [@shamoon](https://github.com/shamoon) ([#10941](https://github.com/paperless-ngx/paperless-ngx/pull/10941))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>29 changes</summary>
|
||||
|
||||
- docker(deps): bump astral-sh/uv from 0.9.2-python3.12-bookworm-slim to 0.9.4-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#11091](https://github.com/paperless-ngx/paperless-ngx/pull/11091))
|
||||
- docker-compose(deps): Bump gotenberg/gotenberg from 8.23 to 8.24 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#11050](https://github.com/paperless-ngx/paperless-ngx/pull/11050))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 8 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11065](https://github.com/paperless-ngx/paperless-ngx/pull/11065))
|
||||
- docker(deps): Bump astral-sh/uv from 0.8.22-python3.12-bookworm-slim to 0.9.2-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#11052](https://github.com/paperless-ngx/paperless-ngx/pull/11052))
|
||||
- Chore(deps): Bump the actions group with 5 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10978](https://github.com/paperless-ngx/paperless-ngx/pull/10978))
|
||||
- Chore(deps): Bump uuid from 11.1.0 to 13.0.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10983](https://github.com/paperless-ngx/paperless-ngx/pull/10983))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.55.0 to 1.55.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10982](https://github.com/paperless-ngx/paperless-ngx/pull/10982))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10981](https://github.com/paperless-ngx/paperless-ngx/pull/10981))
|
||||
- Chore(deps-dev): Bump webpack from 5.101.3 to 5.102.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10986](https://github.com/paperless-ngx/paperless-ngx/pull/10986))
|
||||
- Chore(deps-dev): Bump prettier-plugin-organize-imports from 4.2.0 to 4.3.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10985](https://github.com/paperless-ngx/paperless-ngx/pull/10985))
|
||||
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10980](https://github.com/paperless-ngx/paperless-ngx/pull/10980))
|
||||
- Chore(deps-dev): Bump @types/node from 24.3.0 to 24.6.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10984](https://github.com/paperless-ngx/paperless-ngx/pull/10984))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 21 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10979](https://github.com/paperless-ngx/paperless-ngx/pull/10979))
|
||||
- docker-compose(deps): Bump library/postgres from 17 to 18 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#10965](https://github.com/paperless-ngx/paperless-ngx/pull/10965))
|
||||
- Chore(deps): Bump the major-versions group with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10960](https://github.com/paperless-ngx/paperless-ngx/pull/10960))
|
||||
- Chore(deps): Bump types-colorama from 0.4.15.20240311 to 0.4.15.20250801 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10961](https://github.com/paperless-ngx/paperless-ngx/pull/10961))
|
||||
- Chore(deps): Bump django-guardian from 3.1.3 to 3.2.0 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10909](https://github.com/paperless-ngx/paperless-ngx/pull/10909))
|
||||
- Chore(deps): Bump django-soft-delete from 1.0.19 to 1.0.21 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10908](https://github.com/paperless-ngx/paperless-ngx/pull/10908))
|
||||
- Chore(deps): Bump whitenoise from 6.10.0 to 6.11.0 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10910](https://github.com/paperless-ngx/paperless-ngx/pull/10910))
|
||||
- Chore(deps): Bump django-cors-headers from 4.8.0 to 4.9.0 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10907](https://github.com/paperless-ngx/paperless-ngx/pull/10907))
|
||||
- docker(deps): bump astral-sh/uv from 0.8.17-python3.12-bookworm-slim to 0.8.19-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10906](https://github.com/paperless-ngx/paperless-ngx/pull/10906))
|
||||
- docker(deps): Bump astral-sh/uv from 0.8.15-python3.12-bookworm-slim to 0.8.17-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10864](https://github.com/paperless-ngx/paperless-ngx/pull/10864))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10880](https://github.com/paperless-ngx/paperless-ngx/pull/10880))
|
||||
- Chore(deps): Bump django-guardian from 3.1.2 to 3.1.3 in the django group @[dependabot[bot]](https://github.com/apps/dependabot) ([#10863](https://github.com/paperless-ngx/paperless-ngx/pull/10863))
|
||||
- Chore(deps): Bump pytest-cov from 6.2.1 to 7.0.0 in the development group across 1 directory @[dependabot[bot]](https://github.com/apps/dependabot) ([#10822](https://github.com/paperless-ngx/paperless-ngx/pull/10822))
|
||||
- Chore(deps): Bump the django group with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10811](https://github.com/paperless-ngx/paperless-ngx/pull/10811))
|
||||
- docker-compose(deps): Bump gotenberg/gotenberg from 8.22 to 8.23 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#10812](https://github.com/paperless-ngx/paperless-ngx/pull/10812))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 8 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10821](https://github.com/paperless-ngx/paperless-ngx/pull/10821))
|
||||
- docker(deps): Bump astral-sh/uv from 0.8.13-python3.12-bookworm-slim to 0.8.15-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10810](https://github.com/paperless-ngx/paperless-ngx/pull/10810))
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>51 changes</summary>
|
||||
|
||||
- Tweak: improve tag parent validation error handling [@shamoon](https://github.com/shamoon) ([#11096](https://github.com/paperless-ngx/paperless-ngx/pull/11096))
|
||||
- Fix: remove obsolete warning for custom field value index [@shamoon](https://github.com/shamoon) ([#11083](https://github.com/paperless-ngx/paperless-ngx/pull/11083))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 8 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#11065](https://github.com/paperless-ngx/paperless-ngx/pull/11065))
|
||||
- Enhancement: use friendly file names when emailing documents [@JanKleine](https://github.com/JanKleine) ([#11055](https://github.com/paperless-ngx/paperless-ngx/pull/11055))
|
||||
- Fix: set min-height for drag-drop items container [@shamoon](https://github.com/shamoon) ([#11064](https://github.com/paperless-ngx/paperless-ngx/pull/11064))
|
||||
- Feature: Advanced Workflow Filters [@shamoon](https://github.com/shamoon) ([#11029](https://github.com/paperless-ngx/paperless-ngx/pull/11029))
|
||||
- Feature: add support for emailing multiple documents [@JanKleine](https://github.com/JanKleine) ([#10666](https://github.com/paperless-ngx/paperless-ngx/pull/10666))
|
||||
- Fix custom field query dropdown toggle corners [@shamoon](https://github.com/shamoon) ([#11028](https://github.com/paperless-ngx/paperless-ngx/pull/11028))
|
||||
- Fix: correct save hotkey action when no next document exists [@shamoon](https://github.com/shamoon) ([#11027](https://github.com/paperless-ngx/paperless-ngx/pull/11027))
|
||||
- Fix: require only change permissions for task dismissal, add frontend error handling [@shamoon](https://github.com/shamoon) ([#11023](https://github.com/paperless-ngx/paperless-ngx/pull/11023))
|
||||
- Enhancement: ignore same files in sanity checker as consumer [@shamoon](https://github.com/shamoon) ([#10999](https://github.com/paperless-ngx/paperless-ngx/pull/10999))
|
||||
- Enhancement: open color picker on swatch button click [@shamoon](https://github.com/shamoon) ([#10994](https://github.com/paperless-ngx/paperless-ngx/pull/10994))
|
||||
- Chore(deps): Bump uuid from 11.1.0 to 13.0.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10983](https://github.com/paperless-ngx/paperless-ngx/pull/10983))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.55.0 to 1.55.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10982](https://github.com/paperless-ngx/paperless-ngx/pull/10982))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10981](https://github.com/paperless-ngx/paperless-ngx/pull/10981))
|
||||
- Chore(deps-dev): Bump webpack from 5.101.3 to 5.102.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10986](https://github.com/paperless-ngx/paperless-ngx/pull/10986))
|
||||
- Chore(deps-dev): Bump prettier-plugin-organize-imports from 4.2.0 to 4.3.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10985](https://github.com/paperless-ngx/paperless-ngx/pull/10985))
|
||||
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10980](https://github.com/paperless-ngx/paperless-ngx/pull/10980))
|
||||
- Chore(deps-dev): Bump @types/node from 24.3.0 to 24.6.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10984](https://github.com/paperless-ngx/paperless-ngx/pull/10984))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 21 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10979](https://github.com/paperless-ngx/paperless-ngx/pull/10979))
|
||||
- Performance: Cache django-guardian permissions when counting documents [@Merinorus](https://github.com/Merinorus) ([#10657](https://github.com/paperless-ngx/paperless-ngx/pull/10657))
|
||||
- Chore(deps): Bulk upgrade backend dependencies [@stumpylog](https://github.com/stumpylog) ([#10971](https://github.com/paperless-ngx/paperless-ngx/pull/10971))
|
||||
- Chore(deps): Bump the major-versions group with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10960](https://github.com/paperless-ngx/paperless-ngx/pull/10960))
|
||||
- Chore(deps): Bump types-colorama from 0.4.15.20240311 to 0.4.15.20250801 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10961](https://github.com/paperless-ngx/paperless-ngx/pull/10961))
|
||||
- Chore(deps): Bump django-guardian from 3.1.3 to 3.2.0 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10909](https://github.com/paperless-ngx/paperless-ngx/pull/10909))
|
||||
- Chore(deps): Bump django-soft-delete from 1.0.19 to 1.0.21 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10908](https://github.com/paperless-ngx/paperless-ngx/pull/10908))
|
||||
- Chore(deps): Bump whitenoise from 6.10.0 to 6.11.0 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10910](https://github.com/paperless-ngx/paperless-ngx/pull/10910))
|
||||
- Tweakhancement: reorganize some list \& bulk editing buttons [@shamoon](https://github.com/shamoon) ([#10944](https://github.com/paperless-ngx/paperless-ngx/pull/10944))
|
||||
- Chore(deps): Bump django-cors-headers from 4.8.0 to 4.9.0 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10907](https://github.com/paperless-ngx/paperless-ngx/pull/10907))
|
||||
- Fix: fix select option removal and pagination update [@shamoon](https://github.com/shamoon) ([#10933](https://github.com/paperless-ngx/paperless-ngx/pull/10933))
|
||||
- Enhancement: support workflow path matching of barcode-split documents [@DerRockWolf](https://github.com/DerRockWolf) ([#10723](https://github.com/paperless-ngx/paperless-ngx/pull/10723))
|
||||
- Fix: skip fuzzy matching for empty document content [@shamoon](https://github.com/shamoon) ([#10914](https://github.com/paperless-ngx/paperless-ngx/pull/10914))
|
||||
- Feature: processed mail UI [@shamoon](https://github.com/shamoon) ([#10866](https://github.com/paperless-ngx/paperless-ngx/pull/10866))
|
||||
- Fix: add extra error handling to \_consume for file checks [@shamoon](https://github.com/shamoon) ([#10897](https://github.com/paperless-ngx/paperless-ngx/pull/10897))
|
||||
- Fix: restore str celery beat schedule filename [@shamoon](https://github.com/shamoon) ([#10893](https://github.com/paperless-ngx/paperless-ngx/pull/10893))
|
||||
- Enhancement: support custom field values on post document [@shamoon](https://github.com/shamoon) ([#10859](https://github.com/paperless-ngx/paperless-ngx/pull/10859))
|
||||
- Feature: Nested Tags [@shamoon](https://github.com/shamoon) ([#10833](https://github.com/paperless-ngx/paperless-ngx/pull/10833))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10880](https://github.com/paperless-ngx/paperless-ngx/pull/10880))
|
||||
- Chore(deps): Bump django-guardian from 3.1.2 to 3.1.3 in the django group @[dependabot[bot]](https://github.com/apps/dependabot) ([#10863](https://github.com/paperless-ngx/paperless-ngx/pull/10863))
|
||||
- Enhancement: long text custom field [@jojo2357](https://github.com/jojo2357) ([#10846](https://github.com/paperless-ngx/paperless-ngx/pull/10846))
|
||||
- Fix: fix pdf editor hover rotate counterclockwise button [@shamoon](https://github.com/shamoon) ([#10848](https://github.com/paperless-ngx/paperless-ngx/pull/10848))
|
||||
- Fix: warp long words in toast content [@shamoon](https://github.com/shamoon) ([#10839](https://github.com/paperless-ngx/paperless-ngx/pull/10839))
|
||||
- Fix: fix error when bulk adding empty doc link custom fields [@shamoon](https://github.com/shamoon) ([#10832](https://github.com/paperless-ngx/paperless-ngx/pull/10832))
|
||||
- Enhancement: Add print button [@mpaletti](https://github.com/mpaletti) ([#10626](https://github.com/paperless-ngx/paperless-ngx/pull/10626))
|
||||
- Enhancement: add storage path as workflow trigger filter @david-loe ([#10771](https://github.com/paperless-ngx/paperless-ngx/pull/10771))
|
||||
- Enhancement: jinja template support for workflow title assignment [@sidey79](https://github.com/sidey79) ([#10700](https://github.com/paperless-ngx/paperless-ngx/pull/10700))
|
||||
- Chore(deps): Bump pytest-cov from 6.2.1 to 7.0.0 in the development group across 1 directory @[dependabot[bot]](https://github.com/apps/dependabot) ([#10822](https://github.com/paperless-ngx/paperless-ngx/pull/10822))
|
||||
- Chore(deps): Bump the django group with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10811](https://github.com/paperless-ngx/paperless-ngx/pull/10811))
|
||||
- Enhancement: Limit excessively long content length when computing suggestions [@Merinorus](https://github.com/Merinorus) ([#10656](https://github.com/paperless-ngx/paperless-ngx/pull/10656))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 8 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10821](https://github.com/paperless-ngx/paperless-ngx/pull/10821))
|
||||
- Fix: set match value for correspondents created by mail rule [@shamoon](https://github.com/shamoon) ([#10820](https://github.com/paperless-ngx/paperless-ngx/pull/10820))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.18.4
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- Enhancement: report websocket status [@shamoon](https://github.com/shamoon) ([#10777](https://github.com/paperless-ngx/paperless-ngx/pull/10777))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Revert "Performance: Enable virtual scrolling for large custom field … [@shamoon](https://github.com/shamoon) ([#10803](https://github.com/paperless-ngx/paperless-ngx/pull/10803))
|
||||
- Fixhancement: update sidebar view counts on save \& next also [@shamoon](https://github.com/shamoon) ([#10793](https://github.com/paperless-ngx/paperless-ngx/pull/10793))
|
||||
- Performance fix: add paging for custom field select options [@shamoon](https://github.com/shamoon) ([#10755](https://github.com/paperless-ngx/paperless-ngx/pull/10755))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>8 changes</summary>
|
||||
|
||||
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 2 updates [@shamoon](https://github.com/shamoon) ([#10770](https://github.com/paperless-ngx/paperless-ngx/pull/10770))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10745](https://github.com/paperless-ngx/paperless-ngx/pull/10745))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 22 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10744](https://github.com/paperless-ngx/paperless-ngx/pull/10744))
|
||||
- Chore(deps): Bump bootstrap from 5.3.7 to 5.3.8 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10740](https://github.com/paperless-ngx/paperless-ngx/pull/10740))
|
||||
- Chore(deps-dev): Bump @<!---->playwright/test from 1.54.2 to 1.55.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10743](https://github.com/paperless-ngx/paperless-ngx/pull/10743))
|
||||
- Chore(deps-dev): Bump webpack from 5.101.0 to 5.101.3 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10751](https://github.com/paperless-ngx/paperless-ngx/pull/10751))
|
||||
- Chore(deps-dev): Bump @<!---->types/node from 24.1.0 to 24.3.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10750](https://github.com/paperless-ngx/paperless-ngx/pull/10750))
|
||||
- Chore(deps): Bump the actions group with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10757](https://github.com/paperless-ngx/paperless-ngx/pull/10757))
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>13 changes</summary>
|
||||
|
||||
- Revert "Performance: Enable virtual scrolling for large custom field … @shamoon ([#10803](https://github.com/paperless-ngx/paperless-ngx/pull/10803))
|
||||
- Fixhancement: update sidebar view counts on save \& next also @shamoon ([#10793](https://github.com/paperless-ngx/paperless-ngx/pull/10793))
|
||||
- Enhancement: report websocket status @shamoon ([#10777](https://github.com/paperless-ngx/paperless-ngx/pull/10777))
|
||||
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 2 updates @shamoon ([#10770](https://github.com/paperless-ngx/paperless-ngx/pull/10770))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10745](https://github.com/paperless-ngx/paperless-ngx/pull/10745))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 22 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10744](https://github.com/paperless-ngx/paperless-ngx/pull/10744))
|
||||
- Chore(deps): Bump bootstrap from 5.3.7 to 5.3.8 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10740](https://github.com/paperless-ngx/paperless-ngx/pull/10740))
|
||||
- Chore(deps-dev): Bump @<!---->playwright/test from 1.54.2 to 1.55.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10743](https://github.com/paperless-ngx/paperless-ngx/pull/10743))
|
||||
- Chore(deps-dev): Bump webpack from 5.101.0 to 5.101.3 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10751](https://github.com/paperless-ngx/paperless-ngx/pull/10751))
|
||||
- Chore(deps-dev): Bump @<!---->types/node from 24.1.0 to 24.3.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10750](https://github.com/paperless-ngx/paperless-ngx/pull/10750))
|
||||
- Chore: switch from os.path to pathlib.Path @gothicVI ([#10539](https://github.com/paperless-ngx/paperless-ngx/pull/10539))
|
||||
- Performance fix: add paging for custom field select options @shamoon ([#10755](https://github.com/paperless-ngx/paperless-ngx/pull/10755))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.18.3
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: include application config language settings for dateparser auto-detection [@shamoon](https://github.com/shamoon) ([#10722](https://github.com/paperless-ngx/paperless-ngx/pull/10722))
|
||||
- Fix: hide sidebar counts during saved views organization [@shamoon](https://github.com/shamoon) ([#10716](https://github.com/paperless-ngx/paperless-ngx/pull/10716))
|
||||
- Fix: wrap long view titles in sidebar [@shamoon](https://github.com/shamoon) ([#10715](https://github.com/paperless-ngx/paperless-ngx/pull/10715))
|
||||
- Fixhancement: more saved view count refreshes [@shamoon](https://github.com/shamoon) ([#10694](https://github.com/paperless-ngx/paperless-ngx/pull/10694))
|
||||
- Fix: include pagination array items for valid openapi schema [@shamoon](https://github.com/shamoon) ([#10682](https://github.com/paperless-ngx/paperless-ngx/pull/10682))
|
||||
- Fix: prevent scroll for view name in sidebar [@shamoon](https://github.com/shamoon) ([#10676](https://github.com/paperless-ngx/paperless-ngx/pull/10676))
|
||||
- Tweak: center document close button in app frame [@shamoon](https://github.com/shamoon) ([#10661](https://github.com/paperless-ngx/paperless-ngx/pull/10661))
|
||||
- Performance: Enable virtual scrolling for large custom field selects @david-loe ([#10708](https://github.com/paperless-ngx/paperless-ngx/pull/10708))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>5 changes</summary>
|
||||
|
||||
- Chore(deps): Update granian[uvloop] requirement from ~=2.4.1 to ~=2.5.1 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10529](https://github.com/paperless-ngx/paperless-ngx/pull/10529))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10714](https://github.com/paperless-ngx/paperless-ngx/pull/10714))
|
||||
- docker-compose(deps): Bump library/mariadb from 11 to 12 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#10621](https://github.com/paperless-ngx/paperless-ngx/pull/10621))
|
||||
- docker-compose(deps): Bump gotenberg/gotenberg from 8.20 to 8.22 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#10687](https://github.com/paperless-ngx/paperless-ngx/pull/10687))
|
||||
- docker(deps): Bump astral-sh/uv from 0.8.8-python3.12-bookworm-slim to 0.8.13-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10685](https://github.com/paperless-ngx/paperless-ngx/pull/10685))
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>11 changes</summary>
|
||||
|
||||
- Fix: include application config language settings for dateparser auto-detection [@shamoon](https://github.com/shamoon) ([#10722](https://github.com/paperless-ngx/paperless-ngx/pull/10722))
|
||||
- Chore(deps): Update granian[uvloop] requirement from ~=2.4.1 to ~=2.5.1 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10529](https://github.com/paperless-ngx/paperless-ngx/pull/10529))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10714](https://github.com/paperless-ngx/paperless-ngx/pull/10714))
|
||||
- Fix: hide sidebar counts during saved views organization [@shamoon](https://github.com/shamoon) ([#10716](https://github.com/paperless-ngx/paperless-ngx/pull/10716))
|
||||
- Fix: wrap long view titles in sidebar [@shamoon](https://github.com/shamoon) ([#10715](https://github.com/paperless-ngx/paperless-ngx/pull/10715))
|
||||
- Performance: Enable virtual scrolling for large custom field selects @david-loe ([#10708](https://github.com/paperless-ngx/paperless-ngx/pull/10708))
|
||||
- Chore: refactor document details component [@shamoon](https://github.com/shamoon) ([#10662](https://github.com/paperless-ngx/paperless-ngx/pull/10662))
|
||||
- Fixhancement: more saved view count refreshes [@shamoon](https://github.com/shamoon) ([#10694](https://github.com/paperless-ngx/paperless-ngx/pull/10694))
|
||||
- Fix: include pagination array items for valid openapi schema [@shamoon](https://github.com/shamoon) ([#10682](https://github.com/paperless-ngx/paperless-ngx/pull/10682))
|
||||
- Fix: prevent scroll for view name in sidebar [@shamoon](https://github.com/shamoon) ([#10676](https://github.com/paperless-ngx/paperless-ngx/pull/10676))
|
||||
- Tweak: center document close button in app frame [@shamoon](https://github.com/shamoon) ([#10661](https://github.com/paperless-ngx/paperless-ngx/pull/10661))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.18.2
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: prevent loss of changes when switching between open docs [@shamoon](https://github.com/shamoon) ([#10659](https://github.com/paperless-ngx/paperless-ngx/pull/10659))
|
||||
- Fix: ignore incomplete tasks for system status 'last run' [@shamoon](https://github.com/shamoon) ([#10641](https://github.com/paperless-ngx/paperless-ngx/pull/10641))
|
||||
- Fix: increase legibility of date filter clear button in light mode [@shamoon](https://github.com/shamoon) ([#10649](https://github.com/paperless-ngx/paperless-ngx/pull/10649))
|
||||
- Fix: ensure saved view count is visible with long names [@shamoon](https://github.com/shamoon) ([#10616](https://github.com/paperless-ngx/paperless-ngx/pull/10616))
|
||||
- Tweak: improve dateparser auto-detection messages [@shamoon](https://github.com/shamoon) ([#10640](https://github.com/paperless-ngx/paperless-ngx/pull/10640))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Chore(deps): Bump the development group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10578](https://github.com/paperless-ngx/paperless-ngx/pull/10578))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>6 changes</summary>
|
||||
|
||||
- Fix: prevent loss of changes when switching between open docs [@shamoon](https://github.com/shamoon) ([#10659](https://github.com/paperless-ngx/paperless-ngx/pull/10659))
|
||||
- Fix: ignore incomplete tasks for system status 'last run' [@shamoon](https://github.com/shamoon) ([#10641](https://github.com/paperless-ngx/paperless-ngx/pull/10641))
|
||||
- Tweak: improve dateparser auto-detection messages [@shamoon](https://github.com/shamoon) ([#10640](https://github.com/paperless-ngx/paperless-ngx/pull/10640))
|
||||
- Fix: increase legibility of date filter clear button in light mode [@shamoon](https://github.com/shamoon) ([#10649](https://github.com/paperless-ngx/paperless-ngx/pull/10649))
|
||||
- Fix: ensure saved view count is visible with long names [@shamoon](https://github.com/shamoon) ([#10616](https://github.com/paperless-ngx/paperless-ngx/pull/10616))
|
||||
- Chore(deps): Bump the development group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10578](https://github.com/paperless-ngx/paperless-ngx/pull/10578))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.18.1
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- Tweak: fix some button consistency [@shamoon](https://github.com/shamoon) ([#10593](https://github.com/paperless-ngx/paperless-ngx/pull/10593))
|
||||
- Fixhancement: mobile layout improvements for pdf editor [@shamoon](https://github.com/shamoon) ([#10588](https://github.com/paperless-ngx/paperless-ngx/pull/10588))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: fix app logo validation with no file [@shamoon](https://github.com/shamoon) ([#10599](https://github.com/paperless-ngx/paperless-ngx/pull/10599))
|
||||
|
||||
### Documentation
|
||||
|
||||
- Documentation: fix filters docs [@shamoon](https://github.com/shamoon) ([#10600](https://github.com/paperless-ngx/paperless-ngx/pull/10600))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>4 changes</summary>
|
||||
|
||||
- Fix: fix app logo validation with no file [@shamoon](https://github.com/shamoon) ([#10599](https://github.com/paperless-ngx/paperless-ngx/pull/10599))
|
||||
- Tweak: fix some button consistency [@shamoon](https://github.com/shamoon) ([#10593](https://github.com/paperless-ngx/paperless-ngx/pull/10593))
|
||||
- Development: restore version tag display [@shamoon](https://github.com/shamoon) ([#10592](https://github.com/paperless-ngx/paperless-ngx/pull/10592))
|
||||
- Fixhancement: mobile layout improvements for pdf editor [@shamoon](https://github.com/shamoon) ([#10588](https://github.com/paperless-ngx/paperless-ngx/pull/10588))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.18.0
|
||||
|
||||
### Notable Changes
|
||||
|
||||
- Feature: PDF editor [@shamoon](https://github.com/shamoon) ([#10318](https://github.com/paperless-ngx/paperless-ngx/pull/10318))
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- Feature: Add filter to localize dates for filepath templating [@stumpylog](https://github.com/stumpylog) ([#10559](https://github.com/paperless-ngx/paperless-ngx/pull/10559))
|
||||
- Feature: PDF editor [@shamoon](https://github.com/shamoon) ([#10318](https://github.com/paperless-ngx/paperless-ngx/pull/10318))
|
||||
- Enhancement: support webhook restrictions [@shamoon](https://github.com/shamoon) ([#10555](https://github.com/paperless-ngx/paperless-ngx/pull/10555))
|
||||
- Performance: Classifier performance optimizations [@Merinorus](https://github.com/Merinorus) ([#10363](https://github.com/paperless-ngx/paperless-ngx/pull/10363))
|
||||
- Performance: add setting to enable DB connection pooling for PostgreSQL [@Merinorus](https://github.com/Merinorus) ([#10354](https://github.com/paperless-ngx/paperless-ngx/pull/10354))
|
||||
- Fixhancement: improve text thumbnail generation for large files [@shamoon](https://github.com/shamoon) ([#10483](https://github.com/paperless-ngx/paperless-ngx/pull/10483))
|
||||
- Enhancement: disable auto spellcheck on filtering dropdowns [@TheDodger](https://github.com/TheDodger) ([#10487](https://github.com/paperless-ngx/paperless-ngx/pull/10487))
|
||||
- Enhancement: display saved view counts [@shamoon](https://github.com/shamoon) ([#10246](https://github.com/paperless-ngx/paperless-ngx/pull/10246))
|
||||
- Fixhancement: add missing exact operator for boolean CF queries [@shamoon](https://github.com/shamoon) ([#10402](https://github.com/paperless-ngx/paperless-ngx/pull/10402))
|
||||
- Feature: add Vietnamese translation [@shamoon](https://github.com/shamoon) ([#10352](https://github.com/paperless-ngx/paperless-ngx/pull/10352))
|
||||
- Performance: Add support for configuring date parser languages [@Merinorus](https://github.com/Merinorus) ([#10181](https://github.com/paperless-ngx/paperless-ngx/pull/10181))
|
||||
- Enhancement: Add a database caching for improved performance [@Merinorus](https://github.com/Merinorus) ([#9784](https://github.com/paperless-ngx/paperless-ngx/pull/9784))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: include ignore for config logos in sanity checker [@shamoon](https://github.com/shamoon) ([#10473](https://github.com/paperless-ngx/paperless-ngx/pull/10473))
|
||||
- Fix: track and restore changed document fields from session storage [@shamoon](https://github.com/shamoon) ([#10468](https://github.com/paperless-ngx/paperless-ngx/pull/10468))
|
||||
- Fix: Make some natural keyword date searches timezone-aware [@shamoon](https://github.com/shamoon) ([#10416](https://github.com/paperless-ngx/paperless-ngx/pull/10416))
|
||||
- Fixhancement: follow redirects in curl health check [@V0idC0de](https://github.com/V0idC0de) ([#10415](https://github.com/paperless-ngx/paperless-ngx/pull/10415))
|
||||
- Fix: dont use translated verbose_name for getting object perms [@shamoon](https://github.com/shamoon) ([#10399](https://github.com/paperless-ngx/paperless-ngx/pull/10399))
|
||||
- Fix: fix date format for 'today' in DateComponent [@shamoon](https://github.com/shamoon) ([#10369](https://github.com/paperless-ngx/paperless-ngx/pull/10369))
|
||||
- Fix: default to empty permissions for group creation [@shamoon](https://github.com/shamoon) ([#10337](https://github.com/paperless-ngx/paperless-ngx/pull/10337))
|
||||
- Fix: correct api created coercion with timezone [@shamoon](https://github.com/shamoon) ([#10287](https://github.com/paperless-ngx/paperless-ngx/pull/10287))
|
||||
- Fix: reset search query for preview on reset filter [@shamoon](https://github.com/shamoon) ([#10279](https://github.com/paperless-ngx/paperless-ngx/pull/10279))
|
||||
- Chore: reject absurd max age values [@shamoon](https://github.com/shamoon) ([#10243](https://github.com/paperless-ngx/paperless-ngx/pull/10243))
|
||||
- Chore: add tasks task_id param to openapi spec [@shamoon](https://github.com/shamoon) ([#10469](https://github.com/paperless-ngx/paperless-ngx/pull/10469))
|
||||
- Chore: include advanced search query param in API spec [@shamoon](https://github.com/shamoon) ([#10449](https://github.com/paperless-ngx/paperless-ngx/pull/10449))
|
||||
|
||||
### Security
|
||||
|
||||
- Address XSS vulnerability GHSA-6p53-hqqw-8j62
|
||||
|
||||
### Maintenance
|
||||
|
||||
- docker(deps): Bump astral-sh/uv from 0.8.4-python3.12-bookworm-slim to 0.8.8-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10564](https://github.com/paperless-ngx/paperless-ngx/pull/10564))
|
||||
- docker(deps): Bump astral-sh/uv from 0.7.9-python3.12-bookworm-slim to 0.7.19-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10343](https://github.com/paperless-ngx/paperless-ngx/pull/10343))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 7 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10347](https://github.com/paperless-ngx/paperless-ngx/pull/10347))
|
||||
- Chore(deps-dev): Bump @types/node from 22.15.29 to 24.0.10 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10306](https://github.com/paperless-ngx/paperless-ngx/pull/10306))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 8 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10481](https://github.com/paperless-ngx/paperless-ngx/pull/10481))
|
||||
- docker(deps): bump astral-sh/uv from 0.7.19-python3.12-bookworm-slim to 0.8.3-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10465](https://github.com/paperless-ngx/paperless-ngx/pull/10465))
|
||||
- Chore: switch from os.path to pathlib.Path [@gothicVI](https://github.com/gothicVI) ([#10397](https://github.com/paperless-ngx/paperless-ngx/pull/10397))
|
||||
- Chore(deps): Bump the small-changes group with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10528](https://github.com/paperless-ngx/paperless-ngx/pull/10528))
|
||||
- Chore(deps): Bump the django group across 1 directory with 9 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10538](https://github.com/paperless-ngx/paperless-ngx/pull/10538))
|
||||
- Chore(deps): Bump stefanzweifel/git-auto-commit-action from 5 to 6 in the actions group @[dependabot[bot]](https://github.com/apps/dependabot) ([#10302](https://github.com/paperless-ngx/paperless-ngx/pull/10302))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>23 changes</summary>
|
||||
|
||||
- chore: Small targeted upgrades to dependencies [@stumpylog](https://github.com/stumpylog) ([#10561](https://github.com/paperless-ngx/paperless-ngx/pull/10561))
|
||||
- docker(deps): Bump astral-sh/uv from 0.8.4-python3.12-bookworm-slim to 0.8.8-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10564](https://github.com/paperless-ngx/paperless-ngx/pull/10564))
|
||||
- Chore(deps): Bump the django group across 1 directory with 9 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10538](https://github.com/paperless-ngx/paperless-ngx/pull/10538))
|
||||
- Chore(deps): Bump the small-changes group with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10528](https://github.com/paperless-ngx/paperless-ngx/pull/10528))
|
||||
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10497](https://github.com/paperless-ngx/paperless-ngx/pull/10497))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10498](https://github.com/paperless-ngx/paperless-ngx/pull/10498))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.53.2 to 1.54.2 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10499](https://github.com/paperless-ngx/paperless-ngx/pull/10499))
|
||||
- Chore(deps-dev): Bump webpack from 5.99.9 to 5.101.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10501](https://github.com/paperless-ngx/paperless-ngx/pull/10501))
|
||||
- Chore(deps-dev): Bump prettier-plugin-organize-imports from 4.1.0 to 4.2.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10500](https://github.com/paperless-ngx/paperless-ngx/pull/10500))
|
||||
- Chore(deps-dev): Bump @types/node from 24.0.10 to 24.1.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10502](https://github.com/paperless-ngx/paperless-ngx/pull/10502))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 16 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10496](https://github.com/paperless-ngx/paperless-ngx/pull/10496))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 8 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10481](https://github.com/paperless-ngx/paperless-ngx/pull/10481))
|
||||
- docker(deps): bump astral-sh/uv from 0.7.19-python3.12-bookworm-slim to 0.8.3-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10465](https://github.com/paperless-ngx/paperless-ngx/pull/10465))
|
||||
- docker(deps): Bump astral-sh/uv from 0.7.9-python3.12-bookworm-slim to 0.7.19-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10343](https://github.com/paperless-ngx/paperless-ngx/pull/10343))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 7 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10347](https://github.com/paperless-ngx/paperless-ngx/pull/10347))
|
||||
- Chore(deps): Bump stefanzweifel/git-auto-commit-action from 5 to 6 in the actions group @[dependabot[bot]](https://github.com/apps/dependabot) ([#10302](https://github.com/paperless-ngx/paperless-ngx/pull/10302))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group across 1 directory with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10311](https://github.com/paperless-ngx/paperless-ngx/pull/10311))
|
||||
- Chore(deps-dev): Bump @types/node from 22.15.29 to 24.0.10 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10306](https://github.com/paperless-ngx/paperless-ngx/pull/10306))
|
||||
- Chore(deps): Bump bootstrap from 5.3.6 to 5.3.7 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10308](https://github.com/paperless-ngx/paperless-ngx/pull/10308))
|
||||
- Chore(deps-dev): Bump webpack from 5.98.0 to 5.99.9 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10309](https://github.com/paperless-ngx/paperless-ngx/pull/10309))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.51.1 to 1.53.2 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10307](https://github.com/paperless-ngx/paperless-ngx/pull/10307))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 13 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10303](https://github.com/paperless-ngx/paperless-ngx/pull/10303))
|
||||
- Chore: update to Angular 20 [@shamoon](https://github.com/shamoon) ([#10273](https://github.com/paperless-ngx/paperless-ngx/pull/10273))
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>44 changes</summary>
|
||||
|
||||
- chore: Small targeted upgrades to dependencies [@stumpylog](https://github.com/stumpylog) ([#10561](https://github.com/paperless-ngx/paperless-ngx/pull/10561))
|
||||
- Feature: Add filter to localize dates for filepath templating [@stumpylog](https://github.com/stumpylog) ([#10559](https://github.com/paperless-ngx/paperless-ngx/pull/10559))
|
||||
- Chore: Removes duplication and spread out config for codespell [@stumpylog](https://github.com/stumpylog) ([#10560](https://github.com/paperless-ngx/paperless-ngx/pull/10560))
|
||||
- Chore(deps): Bump the django group across 1 directory with 9 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10538](https://github.com/paperless-ngx/paperless-ngx/pull/10538))
|
||||
- Feature: PDF editor [@shamoon](https://github.com/shamoon) ([#10318](https://github.com/paperless-ngx/paperless-ngx/pull/10318))
|
||||
- Enhancement: support webhook restrictions [@shamoon](https://github.com/shamoon) ([#10555](https://github.com/paperless-ngx/paperless-ngx/pull/10555))
|
||||
- Performance: Classifier performance optimizations [@Merinorus](https://github.com/Merinorus) ([#10363](https://github.com/paperless-ngx/paperless-ngx/pull/10363))
|
||||
- Chore: switch from os.path to pathlib.Path [@gothicVI](https://github.com/gothicVI) ([#10397](https://github.com/paperless-ngx/paperless-ngx/pull/10397))
|
||||
- Chore(deps): Bump the small-changes group with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10528](https://github.com/paperless-ngx/paperless-ngx/pull/10528))
|
||||
- Performance: add setting to enable DB connection pooling for PostgreSQL [@Merinorus](https://github.com/Merinorus) ([#10354](https://github.com/paperless-ngx/paperless-ngx/pull/10354))
|
||||
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10497](https://github.com/paperless-ngx/paperless-ngx/pull/10497))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10498](https://github.com/paperless-ngx/paperless-ngx/pull/10498))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.53.2 to 1.54.2 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10499](https://github.com/paperless-ngx/paperless-ngx/pull/10499))
|
||||
- Chore(deps-dev): Bump webpack from 5.99.9 to 5.101.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10501](https://github.com/paperless-ngx/paperless-ngx/pull/10501))
|
||||
- Chore(deps-dev): Bump prettier-plugin-organize-imports from 4.1.0 to 4.2.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10500](https://github.com/paperless-ngx/paperless-ngx/pull/10500))
|
||||
- Chore(deps-dev): Bump @types/node from 24.0.10 to 24.1.0 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10502](https://github.com/paperless-ngx/paperless-ngx/pull/10502))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 16 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10496](https://github.com/paperless-ngx/paperless-ngx/pull/10496))
|
||||
- Fixhancement: improve text thumbnail generation for large files [@shamoon](https://github.com/shamoon) ([#10483](https://github.com/paperless-ngx/paperless-ngx/pull/10483))
|
||||
- Enhancement: disable auto spellcheck on filtering dropdowns [@TheDodger](https://github.com/TheDodger) ([#10487](https://github.com/paperless-ngx/paperless-ngx/pull/10487))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 8 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10481](https://github.com/paperless-ngx/paperless-ngx/pull/10481))
|
||||
- Fix: include ignore for config logos in sanity checker [@shamoon](https://github.com/shamoon) ([#10473](https://github.com/paperless-ngx/paperless-ngx/pull/10473))
|
||||
- Chore: add tasks task_id param to openapi spec [@shamoon](https://github.com/shamoon) ([#10469](https://github.com/paperless-ngx/paperless-ngx/pull/10469))
|
||||
- Fix: track and restore changed document fields from session storage [@shamoon](https://github.com/shamoon) ([#10468](https://github.com/paperless-ngx/paperless-ngx/pull/10468))
|
||||
- Chore: include advanced search query param in API spec [@shamoon](https://github.com/shamoon) ([#10449](https://github.com/paperless-ngx/paperless-ngx/pull/10449))
|
||||
- Enhancement: display saved view counts [@shamoon](https://github.com/shamoon) ([#10246](https://github.com/paperless-ngx/paperless-ngx/pull/10246))
|
||||
- Fix: Make some natural keyword date searches timezone-aware [@shamoon](https://github.com/shamoon) ([#10416](https://github.com/paperless-ngx/paperless-ngx/pull/10416))
|
||||
- Fixhancement: add missing exact operator for boolean CF queries [@shamoon](https://github.com/shamoon) ([#10402](https://github.com/paperless-ngx/paperless-ngx/pull/10402))
|
||||
- Fix: dont use translated verbose_name for getting object perms [@shamoon](https://github.com/shamoon) ([#10399](https://github.com/paperless-ngx/paperless-ngx/pull/10399))
|
||||
- Fix: fix date format for 'today' in DateComponent [@shamoon](https://github.com/shamoon) ([#10369](https://github.com/paperless-ngx/paperless-ngx/pull/10369))
|
||||
- Feature: add Vietnamese translation [@shamoon](https://github.com/shamoon) ([#10352](https://github.com/paperless-ngx/paperless-ngx/pull/10352))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 7 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10347](https://github.com/paperless-ngx/paperless-ngx/pull/10347))
|
||||
- Fix: default to empty permissions for group creation [@shamoon](https://github.com/shamoon) ([#10337](https://github.com/paperless-ngx/paperless-ngx/pull/10337))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group across 1 directory with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10311](https://github.com/paperless-ngx/paperless-ngx/pull/10311))
|
||||
- Chore(deps-dev): Bump @types/node from 22.15.29 to 24.0.10 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10306](https://github.com/paperless-ngx/paperless-ngx/pull/10306))
|
||||
- Chore(deps): Bump bootstrap from 5.3.6 to 5.3.7 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10308](https://github.com/paperless-ngx/paperless-ngx/pull/10308))
|
||||
- Chore(deps-dev): Bump webpack from 5.98.0 to 5.99.9 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10309](https://github.com/paperless-ngx/paperless-ngx/pull/10309))
|
||||
- Chore(deps-dev): Bump @playwright/test from 1.51.1 to 1.53.2 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10307](https://github.com/paperless-ngx/paperless-ngx/pull/10307))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 13 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10303](https://github.com/paperless-ngx/paperless-ngx/pull/10303))
|
||||
- Performance: Add support for configuring date parser languages [@Merinorus](https://github.com/Merinorus) ([#10181](https://github.com/paperless-ngx/paperless-ngx/pull/10181))
|
||||
- Enhancement: Add a database caching for improved performance [@Merinorus](https://github.com/Merinorus) ([#9784](https://github.com/paperless-ngx/paperless-ngx/pull/9784))
|
||||
- Fix: correct api created coercion with timezone [@shamoon](https://github.com/shamoon) ([#10287](https://github.com/paperless-ngx/paperless-ngx/pull/10287))
|
||||
- Fix: reset search query for preview on reset filter [@shamoon](https://github.com/shamoon) ([#10279](https://github.com/paperless-ngx/paperless-ngx/pull/10279))
|
||||
- Chore: update to Angular 20 [@shamoon](https://github.com/shamoon) ([#10273](https://github.com/paperless-ngx/paperless-ngx/pull/10273))
|
||||
- Chore: reject absurd max age values [@shamoon](https://github.com/shamoon) ([#10243](https://github.com/paperless-ngx/paperless-ngx/pull/10243))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.17.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: correct PAPERLESS_EMPTY_TRASH_DIR to Path [@shamoon](https://github.com/shamoon) ([#10227](https://github.com/paperless-ngx/paperless-ngx/pull/10227))
|
||||
|
||||
### All App Changes
|
||||
|
||||
- Fix: correct PAPERLESS_EMPTY_TRASH_DIR to Path [@shamoon](https://github.com/shamoon) ([#10227](https://github.com/paperless-ngx/paperless-ngx/pull/10227))
|
||||
|
||||
## paperless-ngx 2.17.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Fix: restore expected pre-2.16 scheduled workflow offset behavior [@shamoon](https://github.com/shamoon) ([#10218](https://github.com/paperless-ngx/paperless-ngx/pull/10218))
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- QoL: log version at startup, show backend vs frontend mismatch in system status [@shamoon](https://github.com/shamoon) ([#10214](https://github.com/paperless-ngx/paperless-ngx/pull/10214))
|
||||
- Feature: add Persian translation [@shamoon](https://github.com/shamoon) ([#10183](https://github.com/paperless-ngx/paperless-ngx/pull/10183))
|
||||
- Enhancement: support import of zipped export [@kaerbr](https://github.com/kaerbr) ([#10073](https://github.com/paperless-ngx/paperless-ngx/pull/10073))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: more api fixes [@shamoon](https://github.com/shamoon) ([#10204](https://github.com/paperless-ngx/paperless-ngx/pull/10204))
|
||||
- Fix: restore expected pre-2.16 scheduled workflow offset behavior [@shamoon](https://github.com/shamoon) ([#10218](https://github.com/paperless-ngx/paperless-ngx/pull/10218))
|
||||
- Fix: fix some API crashes [@shamoon](https://github.com/shamoon) ([#10196](https://github.com/paperless-ngx/paperless-ngx/pull/10196))
|
||||
- Fix: remove duplicate base path in websocket urls [@robertmx](https://github.com/robertmx) ([#10194](https://github.com/paperless-ngx/paperless-ngx/pull/10194))
|
||||
- Fix: use hard delete for custom fields with workflow removal [@shamoon](https://github.com/shamoon) ([#10191](https://github.com/paperless-ngx/paperless-ngx/pull/10191))
|
||||
- Fix: fix mail account test api schema [@shamoon](https://github.com/shamoon) ([#10164](https://github.com/paperless-ngx/paperless-ngx/pull/10164))
|
||||
- Fix: correct api schema for mail_account process [@shamoon](https://github.com/shamoon) ([#10157](https://github.com/paperless-ngx/paperless-ngx/pull/10157))
|
||||
- Fix: correct api schema for next_asn [@shamoon](https://github.com/shamoon) ([#10151](https://github.com/paperless-ngx/paperless-ngx/pull/10151))
|
||||
- Fix: fix email and notes endpoints api spec [@shamoon](https://github.com/shamoon) ([#10148](https://github.com/paperless-ngx/paperless-ngx/pull/10148))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Chore: bump angular/common to 19.12.14 [@shamoon](https://github.com/shamoon) ([#10212](https://github.com/paperless-ngx/paperless-ngx/pull/10212))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>14 changes</summary>
|
||||
|
||||
- QoL: log version at startup, show backend vs frontend mismatch in system status [@shamoon](https://github.com/shamoon) ([#10214](https://github.com/paperless-ngx/paperless-ngx/pull/10214))
|
||||
- Fix: more api fixes [@shamoon](https://github.com/shamoon) ([#10204](https://github.com/paperless-ngx/paperless-ngx/pull/10204))
|
||||
- Fix: restore expected pre-2.16 scheduled workflow offset behavior [@shamoon](https://github.com/shamoon) ([#10218](https://github.com/paperless-ngx/paperless-ngx/pull/10218))
|
||||
- Chore: switch from os.path to pathlib.Path [@gothicVI](https://github.com/gothicVI) ([#9933](https://github.com/paperless-ngx/paperless-ngx/pull/9933))
|
||||
- Chore: bump angular/common to 19.12.14 [@shamoon](https://github.com/shamoon) ([#10212](https://github.com/paperless-ngx/paperless-ngx/pull/10212))
|
||||
- Fix: fix some API crashes [@shamoon](https://github.com/shamoon) ([#10196](https://github.com/paperless-ngx/paperless-ngx/pull/10196))
|
||||
- Fix: remove duplicate base path in websocket urls [@robertmx](https://github.com/robertmx) ([#10194](https://github.com/paperless-ngx/paperless-ngx/pull/10194))
|
||||
- Fix: use hard delete for custom fields with workflow removal [@shamoon](https://github.com/shamoon) ([#10191](https://github.com/paperless-ngx/paperless-ngx/pull/10191))
|
||||
- Feature: add Persian translation [@shamoon](https://github.com/shamoon) ([#10183](https://github.com/paperless-ngx/paperless-ngx/pull/10183))
|
||||
- Enhancement: support import of zipped export [@kaerbr](https://github.com/kaerbr) ([#10073](https://github.com/paperless-ngx/paperless-ngx/pull/10073))
|
||||
- Fix: fix mail account test api schema [@shamoon](https://github.com/shamoon) ([#10164](https://github.com/paperless-ngx/paperless-ngx/pull/10164))
|
||||
- Fix: correct api schema for mail_account process [@shamoon](https://github.com/shamoon) ([#10157](https://github.com/paperless-ngx/paperless-ngx/pull/10157))
|
||||
- Fix: correct api schema for next_asn [@shamoon](https://github.com/shamoon) ([#10151](https://github.com/paperless-ngx/paperless-ngx/pull/10151))
|
||||
- Fix: fix email and notes endpoints api spec [@shamoon](https://github.com/shamoon) ([#10148](https://github.com/paperless-ngx/paperless-ngx/pull/10148))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.16.3
|
||||
|
||||
### Features / Enhancements
|
||||
@@ -5364,9 +6258,6 @@ This release contains new database migrations.
|
||||
Paperless will continue to work with WSGI, but you will not get any
|
||||
status notifications.
|
||||
|
||||
Apache `mod_wsgi` users, see
|
||||
[this note](faq.md#how-do-i-get-websocket-support-with-apache-mod_wsgi).
|
||||
|
||||
- Paperless now offers suggestions for tags, correspondents and types
|
||||
on the document detail page.
|
||||
|
||||
@@ -5948,7 +6839,6 @@ primarily.
|
||||
a very good job at ocr'ing a document with the default
|
||||
language. Certain language specifics such as umlauts may not get
|
||||
picked up properly.
|
||||
- `PAPERLESS_DEBUG` defaults to `false`.
|
||||
- The presence of `PAPERLESS_DBHOST` now determines whether to use
|
||||
PostgreSQL or SQLite.
|
||||
- `PAPERLESS_OCR_THREADS` is gone and replaced with
|
||||
@@ -6169,11 +7059,12 @@ primarily.
|
||||
who are doing active development on Paperless using the Docker
|
||||
environment:
|
||||
[#376](https://github.com/the-paperless-project/paperless/pull/376).
|
||||
- You now also have the ability to customise the interface to your
|
||||
- ~~You now also have the ability to customise the interface to your
|
||||
heart's content by creating a file called `overrides.css` and/or
|
||||
`overrides.js` in the root of your media directory. Thanks to [Mark
|
||||
McFate](https://github.com/SummittDweller) for this idea:
|
||||
[#371](https://github.com/the-paperless-project/paperless/issues/371)
|
||||
[#371](https://github.com/the-paperless-project/paperless/issues/371)~~
|
||||
(Not supported by Paperless-ngx)
|
||||
|
||||
### 2.0.0
|
||||
|
||||
|
||||
@@ -159,6 +159,65 @@ Available options are `postgresql` and `mariadb`.
|
||||
|
||||
Defaults to unset, which uses Django’s built-in defaults.
|
||||
|
||||
#### [`PAPERLESS_DB_POOLSIZE=<int>`](#PAPERLESS_DB_POOLSIZE) {#PAPERLESS_DB_POOLSIZE}
|
||||
|
||||
: Defines the maximum number of database connections to keep in the pool.
|
||||
|
||||
Only applies to PostgreSQL. This setting is ignored for other database engines.
|
||||
|
||||
The value must be greater than or equal to 1 to be used.
|
||||
Defaults to unset, which disables connection pooling.
|
||||
|
||||
!!! note
|
||||
|
||||
A pool of 8-10 connections per worker is typically sufficient.
|
||||
If you encounter error messages such as `couldn't get a connection`
|
||||
or database connection timeouts, you probably need to increase the pool size.
|
||||
|
||||
!!! warning
|
||||
Make sure your PostgreSQL `max_connections` setting is large enough to handle the connection pools:
|
||||
`(NB_PAPERLESS_WORKERS + NB_CELERY_WORKERS) × POOL_SIZE + SAFETY_MARGIN`. For example, with
|
||||
4 Paperless workers and 2 Celery workers, and a pool size of 8:``(4 + 2) × 8 + 10 = 58`,
|
||||
so `max_connections = 60` (or even more) is appropriate.
|
||||
|
||||
This assumes only Paperless-ngx connects to your PostgreSQL instance. If you have other applications,
|
||||
you should increase `max_connections` accordingly.
|
||||
|
||||
#### [`PAPERLESS_DB_READ_CACHE_ENABLED=<bool>`](#PAPERLESS_DB_READ_CACHE_ENABLED) {#PAPERLESS_DB_READ_CACHE_ENABLED}
|
||||
|
||||
: Caches the database read query results into Redis. This can significantly improve application response times by caching database queries, at the cost of slightly increased memory usage.
|
||||
|
||||
Defaults to `false`.
|
||||
|
||||
!!! danger
|
||||
|
||||
**Do not modify the database outside the application while it is running.**
|
||||
This includes actions such as restoring a backup, upgrading the database, or performing manual inserts. All external modifications must be done **only when the application is stopped**.
|
||||
After making any such changes, you **must invalidate the DB read cache** using the `invalidate_cachalot` management command.
|
||||
|
||||
#### [`PAPERLESS_READ_CACHE_TTL=<int>`](#PAPERLESS_READ_CACHE_TTL) {#PAPERLESS_READ_CACHE_TTL}
|
||||
|
||||
: Specifies how long (in seconds) read data should be cached.
|
||||
|
||||
Allowed values are between `1` (one second) and `31536000` (one year). Defaults to `3600` (one hour).
|
||||
|
||||
!!! warning
|
||||
|
||||
A high TTL increases memory usage over time. Memory may be used until end of TTL, even if the cache is invalidated with the `invalidate_cachalot` command.
|
||||
|
||||
In case of an out-of-memory (OOM) situation, Redis may stop accepting new data — including cache entries, scheduled tasks, and documents to consume.
|
||||
If your system has limited RAM, consider configuring a dedicated Redis instance for the read cache, with a memory limit and the eviction policy set to `allkeys-lru`.
|
||||
For more details, refer to the [Redis eviction policy documentation](https://redis.io/docs/latest/develop/reference/eviction/), and see the `PAPERLESS_READ_CACHE_REDIS_URL` setting to specify a separate Redis broker.
|
||||
|
||||
#### [`PAPERLESS_READ_CACHE_REDIS_URL=<url>`](#PAPERLESS_READ_CACHE_REDIS_URL) {#PAPERLESS_READ_CACHE_REDIS_URL}
|
||||
|
||||
: Defines the Redis instance used for the read cache.
|
||||
|
||||
Defaults to `None`.
|
||||
|
||||
!!! Note
|
||||
If this value is not set, the same Redis instance used for scheduled tasks will be used for caching as well.
|
||||
|
||||
## Optional Services
|
||||
|
||||
### Tika {#tika}
|
||||
@@ -928,21 +987,10 @@ paperless will process in parallel on a single document.
|
||||
process very large documents faster, use a higher thread per worker
|
||||
count.
|
||||
|
||||
The default is a balance between the two, according to your CPU core
|
||||
count, with a slight favor towards threads per worker:
|
||||
|
||||
| CPU core count | Workers | Threads |
|
||||
| -------------- | ------- | ------- |
|
||||
| > 1 | > 1 | > 1 |
|
||||
| > 2 | > 2 | > 1 |
|
||||
| > 4 | > 2 | > 2 |
|
||||
| > 6 | > 2 | > 3 |
|
||||
| > 8 | > 2 | > 4 |
|
||||
| > 12 | > 3 | > 4 |
|
||||
| > 16 | > 4 | > 4 |
|
||||
|
||||
If you only specify PAPERLESS_TASK_WORKERS, paperless will adjust
|
||||
PAPERLESS_THREADS_PER_WORKER automatically.
|
||||
If unset, paperless uses `max(floor(cpu_count / PAPERLESS_TASK_WORKERS), 1)`
|
||||
threads per worker. The idea behind this is that as long as there are enough cores,
|
||||
the total number of threads should less than or equal to the total number of (logical)
|
||||
CPU cores.
|
||||
|
||||
#### [`PAPERLESS_WORKER_TIMEOUT=<num>`](#PAPERLESS_WORKER_TIMEOUT) {#PAPERLESS_WORKER_TIMEOUT}
|
||||
|
||||
@@ -966,7 +1014,23 @@ still perform some basic text pre-processing before matching.
|
||||
|
||||
: See also `PAPERLESS_NLTK_DIR`.
|
||||
|
||||
Defaults to 1.
|
||||
Defaults to true, enabling the feature.
|
||||
|
||||
#### [`PAPERLESS_DATE_PARSER_LANGUAGES=<lang>`](#PAPERLESS_DATE_PARSER_LANGUAGES) {#PAPERLESS_DATE_PARSER_LANGUAGES}
|
||||
|
||||
Specifies which language Paperless should use when parsing dates from documents.
|
||||
|
||||
This should be a language code supported by the dateparser library,
|
||||
for example: "en", or a combination such as "en+de".
|
||||
Locales are also supported (e.g., "en-AU").
|
||||
Multiple languages can be combined using "+", for example: "en+de" or "en-AU+de".
|
||||
For valid values, refer to the list of supported languages and locales in the [dateparser documentation](https://dateparser.readthedocs.io/en/latest/supported_locales.html).
|
||||
|
||||
Set this to match the languages in which most of your documents are written.
|
||||
If not set, Paperless will attempt to infer the language(s) from the OCR configuration (`PAPERLESS_OCR_LANGUAGE`).
|
||||
|
||||
!!! note
|
||||
This format differs from the `PAPERLESS_OCR_LANGUAGE` setting, which uses ISO 639-2 codes (3 letters, e.g., "eng+deu" for Tesseract OCR).
|
||||
|
||||
#### [`PAPERLESS_EMAIL_TASK_CRON=<cron expression>`](#PAPERLESS_EMAIL_TASK_CRON) {#PAPERLESS_EMAIL_TASK_CRON}
|
||||
|
||||
@@ -997,17 +1061,27 @@ should be a valid crontab(5) expression describing when to run.
|
||||
|
||||
#### [`PAPERLESS_SANITY_TASK_CRON=<cron expression>`](#PAPERLESS_SANITY_TASK_CRON) {#PAPERLESS_SANITY_TASK_CRON}
|
||||
|
||||
: Configures the scheduled sanity checker frequency.
|
||||
: Configures the scheduled sanity checker frequency. The value should be a
|
||||
valid crontab(5) expression describing when to run.
|
||||
|
||||
: If set to the string "disable", the sanity checker will not run automatically.
|
||||
|
||||
Defaults to `30 0 * * sun` or Sunday at 30 minutes past midnight.
|
||||
|
||||
#### [`PAPERLESS_WORKFLOW_SCHEDULED_TASK_CRON=<cron expression>`](#PAPERLESS_WORKFLOW_SCHEDULED_TASK_CRON) {#PAPERLESS_WORKFLOW_SCHEDULED_TASK_CRON}
|
||||
|
||||
: Configures the scheduled workflow check frequency. The value should be a
|
||||
valid crontab(5) expression describing when to run.
|
||||
|
||||
: If set to the string "disable", scheduled workflows will not run.
|
||||
|
||||
Defaults to `5 */1 * * *` or every hour at 5 minutes past the hour.
|
||||
|
||||
#### [`PAPERLESS_ENABLE_COMPRESSION=<bool>`](#PAPERLESS_ENABLE_COMPRESSION) {#PAPERLESS_ENABLE_COMPRESSION}
|
||||
|
||||
: Enables compression of the responses from the webserver.
|
||||
|
||||
: Defaults to 1, enabling compression.
|
||||
: Defaults to true, enabling compression.
|
||||
|
||||
!!! note
|
||||
|
||||
@@ -1257,6 +1331,30 @@ consumers working on the same file. Configure this to prevent that.
|
||||
|
||||
Defaults to 0.5 seconds.
|
||||
|
||||
## Workflow webhooks
|
||||
|
||||
#### [`PAPERLESS_WEBHOOKS_ALLOWED_SCHEMES=<str>`](#PAPERLESS_WEBHOOKS_ALLOWED_SCHEMES) {#PAPERLESS_WEBHOOKS_ALLOWED_SCHEMES}
|
||||
|
||||
: A comma-separated list of allowed schemes for webhooks. This setting
|
||||
controls which URL schemes are permitted for webhook URLs.
|
||||
|
||||
Defaults to `http,https`.
|
||||
|
||||
#### [`PAPERLESS_WEBHOOKS_ALLOWED_PORTS=<str>`](#PAPERLESS_WEBHOOKS_ALLOWED_PORTS) {#PAPERLESS_WEBHOOKS_ALLOWED_PORTS}
|
||||
|
||||
: A comma-separated list of allowed ports for webhooks. This setting
|
||||
controls which ports are permitted for webhook URLs. For example, if you
|
||||
set this to `80,443`, webhooks will only be sent to URLs that use these
|
||||
ports.
|
||||
|
||||
Defaults to empty list, which allows all ports.
|
||||
|
||||
#### [`PAPERLESS_WEBHOOKS_ALLOW_INTERNAL_REQUESTS=<bool>`](#PAPERLESS_WEBHOOKS_ALLOW_INTERNAL_REQUESTS) {#PAPERLESS_WEBHOOKS_ALLOW_INTERNAL_REQUESTS}
|
||||
|
||||
: If set to false, webhooks cannot be sent to internal URLs (e.g., localhost).
|
||||
|
||||
Defaults to true, which allows internal requests.
|
||||
|
||||
## Incoming Mail {#incoming_mail}
|
||||
|
||||
### Email OAuth {#email_oauth}
|
||||
@@ -1667,6 +1765,11 @@ started by the container.
|
||||
|
||||
: Path to an image file in the /media/logo directory, must include 'logo', e.g. `/logo/Atari_logo.svg`
|
||||
|
||||
!!! note
|
||||
|
||||
The logo file will be viewable by anyone with access to the Paperless instance login page,
|
||||
so consider your choice of logo carefully and removing exif data from images before uploading.
|
||||
|
||||
#### [`PAPERLESS_ENABLE_UPDATE_CHECK=<bool>`](#PAPERLESS_ENABLE_UPDATE_CHECK) {#PAPERLESS_ENABLE_UPDATE_CHECK}
|
||||
|
||||
!!! note
|
||||
|
||||
@@ -95,13 +95,13 @@ first-time setup.
|
||||
|
||||
7. You can now either ...
|
||||
|
||||
- install redis or
|
||||
- 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 database server) 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 database server) or
|
||||
|
||||
- spin up a bare redis container
|
||||
- spin up a bare Redis container
|
||||
|
||||
```
|
||||
docker run -d -p 6379:6379 --restart unless-stopped redis:latest
|
||||
@@ -147,7 +147,7 @@ $ ng build --configuration production
|
||||
### Testing
|
||||
|
||||
- Run `pytest` in the `src/` directory to execute all tests. This also
|
||||
generates a HTML coverage report. When runnings test, `paperless.conf`
|
||||
generates a HTML coverage report. When running tests, `paperless.conf`
|
||||
is loaded as well. However, the tests rely on the default
|
||||
configuration. This is not ideal. But for now, make sure no settings
|
||||
except for DEBUG are overridden when testing.
|
||||
@@ -338,13 +338,13 @@ LANGUAGES = [
|
||||
|
||||
## Building the documentation
|
||||
|
||||
The documentation is built using material-mkdocs, see their [documentation](https://squidfunk.github.io/mkdocs-material/reference/).
|
||||
The documentation is built using Zensical, see their [documentation](https://zensical.org/docs/).
|
||||
If you want to build the documentation locally, this is how you do it:
|
||||
|
||||
1. Build the documentation
|
||||
|
||||
```bash
|
||||
$ uv run mkdocs build --config-file mkdocs.yml
|
||||
$ uv run zensical build
|
||||
```
|
||||
|
||||
_alternatively..._
|
||||
@@ -355,10 +355,10 @@ If you want to build the documentation locally, this is how you do it:
|
||||
something.
|
||||
|
||||
```bash
|
||||
$ uv run mkdocs serve
|
||||
$ uv run zensical serve
|
||||
```
|
||||
|
||||
## Building the Docker image
|
||||
## Building the Docker image {#docker_build}
|
||||
|
||||
The docker image is primarily built by the GitHub actions workflow, but
|
||||
it can be faster when developing to build and tag an image locally.
|
||||
@@ -470,9 +470,14 @@ To get started:
|
||||
|
||||
2. VS Code will prompt you with "Reopen in container". Do so and wait for the environment to start.
|
||||
|
||||
3. Initialize the project by running the task **Project Setup: Run all Init Tasks**. This
|
||||
3. In case your host operating system is Windows:
|
||||
|
||||
- The Source Control view in Visual Studio Code might show: "The detected Git repository is potentially unsafe as the folder is owned by someone other than the current user." Use "Manage Unsafe Repositories" to fix this.
|
||||
- Git might have detecteded modifications for all files, because Windows is using CRLF line endings. Run `git checkout .` in the containers terminal to fix this issue.
|
||||
|
||||
4. Initialize the project by running the task **Project Setup: Run all Init Tasks**. This
|
||||
will initialize the database tables and create a superuser. Then you can compile the front end
|
||||
for production or run the frontend in debug mode.
|
||||
|
||||
4. The project is ready for debugging, start either run the fullstack debug or individual debug
|
||||
5. The project is ready for debugging, start either run the fullstack debug or individual debug
|
||||
processes. Yo spin up the project without debugging run the task **Project Start: Run all Services**
|
||||
|
||||
10
docs/faq.md
10
docs/faq.md
@@ -1,3 +1,7 @@
|
||||
---
|
||||
title: FAQs
|
||||
---
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
## _What's the general plan for Paperless-ngx?_
|
||||
@@ -63,8 +67,10 @@ elsewhere. Here are a couple notes about that.
|
||||
Paperless also supports various Office documents (.docx, .doc, odt,
|
||||
.ppt, .pptx, .odp, .xls, .xlsx, .ods).
|
||||
|
||||
Paperless-ngx determines the type of a file by inspecting its content.
|
||||
The file extensions do not matter.
|
||||
Paperless-ngx determines the type of a file by inspecting its content
|
||||
rather than its file extensions. However, files processed via the
|
||||
consumption directory will be rejected if they have a file extension that
|
||||
not supported by any of the available parsers.
|
||||
|
||||
## _Will paperless-ngx run on Raspberry Pi?_
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
title: Home
|
||||
---
|
||||
|
||||
<div class="grid-left" markdown>
|
||||
{.index-logo}
|
||||
{.index-logo}
|
||||
@@ -30,7 +34,7 @@ physical documents into a searchable online archive so you can keep, well, _less
|
||||
- Utilizes the open-source Tesseract engine to recognize more than 100 languages.
|
||||
- Documents are saved as PDF/A format which is designed for long term storage, alongside the unaltered originals.
|
||||
- Uses machine-learning to automatically add tags, correspondents and document types to your documents.
|
||||
- Supports PDF documents, images, plain text files, Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents)[^1] and more.
|
||||
- Supports PDF documents, images, plain text files, Office documents (Word, Excel, PowerPoint, and LibreOffice equivalents)[^1] and more.
|
||||
- Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely with different configurations assigned to different documents.
|
||||
- **Beautiful, modern web application** that features:
|
||||
- Customizable dashboard with statistics.
|
||||
|
||||
571
docs/setup.md
571
docs/setup.md
@@ -1,52 +1,77 @@
|
||||
## Installation
|
||||
---
|
||||
title: Setup
|
||||
---
|
||||
|
||||
You can go multiple routes to setup and run Paperless:
|
||||
# Installation
|
||||
|
||||
- [Use the script to setup a Docker install](#docker_script)
|
||||
- [Use the Docker compose templates](#docker)
|
||||
- [Build the Docker image yourself](#docker_build)
|
||||
- [Install Paperless-ngx directly on your system manually ("bare metal")](#bare_metal)
|
||||
- A user-maintained list of commercial hosting providers can be found [in the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Related-Projects)
|
||||
|
||||
The Docker routes are quick & easy. These are the recommended routes.
|
||||
This configures all the stuff from the above automatically so that it
|
||||
just works and uses sensible defaults for all configuration options.
|
||||
Here you find a cheat-sheet for docker beginners: [CLI
|
||||
Basics](https://www.sehn.tech/refs/devops-with-docker/)
|
||||
|
||||
The bare metal route is complicated to setup but makes it easier should
|
||||
you want to contribute some code back. You need to configure and run the
|
||||
above mentioned components yourself.
|
||||
|
||||
### Use the Installation Script {#docker_script}
|
||||
|
||||
Paperless provides an interactive installation script to setup a Docker Compose
|
||||
installation. The script asks for a couple configuration options, and will then create the
|
||||
necessary configuration files, pull the docker image, start Paperless-ngx and create your superuser
|
||||
account. The script essentially automatically performs the steps described in [Docker setup](#docker).
|
||||
|
||||
1. Make sure that Docker and Docker Compose are [installed](https://docs.docker.com/engine/install/){:target="\_blank"}.
|
||||
|
||||
2. Download and run the installation script:
|
||||
!!! tip "Quick Start"
|
||||
|
||||
If you just want Paperless-ngx running quickly, use our installation script:
|
||||
```shell-session
|
||||
bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"
|
||||
```
|
||||
_If piping into a shell directly from the internet makes you nervous, inspect [the script](https://github.com/paperless-ngx/paperless-ngx/blob/main/install-paperless-ngx.sh) first!_
|
||||
|
||||
!!! note
|
||||
## Overview
|
||||
|
||||
macOS users will need to install [gnu-sed](https://formulae.brew.sh/formula/gnu-sed) with support
|
||||
for running as `sed` as well as [wget](https://formulae.brew.sh/formula/wget).
|
||||
Choose the installation route that best fits your setup:
|
||||
|
||||
### Use Docker Compose {#docker}
|
||||
| Route | Best for | Effort |
|
||||
| ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------ |
|
||||
| [Installation script](#docker_script) | Fastest first-time setup with guided prompts (recommended for most users) | Low |
|
||||
| [Docker Compose templates](#docker) | Manual control over compose files and settings | Medium |
|
||||
| [Bare metal](#bare_metal) | Advanced setups, packaging, and development-adjacent workflows | High |
|
||||
| [Hosted providers (wiki)](https://github.com/paperless-ngx/paperless-ngx/wiki/Related-Projects#hosting-providers) | Managed hosting options maintained by the community — check details carefully | Varies |
|
||||
|
||||
1. Make sure that Docker and Docker Compose are [installed](https://docs.docker.com/engine/install/){:target="\_blank"}.
|
||||
For most users, Docker is the best option. It is faster to set up,
|
||||
easier to maintain, and ships with sensible defaults.
|
||||
|
||||
2. Go to the [/docker/compose directory on the project
|
||||
The bare-metal route gives you more control, but it requires manual
|
||||
installation and operation of all components. It is usually best suited
|
||||
for advanced users and contributors.
|
||||
|
||||
!!! info
|
||||
|
||||
Because [superuser](usage.md#superusers) accounts have full access to all objects and documents, you may want to create a separate user account for daily use,
|
||||
or "downgrade" your superuser account to a normal user account after setup.
|
||||
|
||||
## Installation Script {#docker_script}
|
||||
|
||||
Paperless-ngx provides an interactive script for Docker Compose setups.
|
||||
It asks a few configuration questions, then creates the required files,
|
||||
pulls the image, starts the containers, and creates your [superuser](usage.md#superusers)
|
||||
account. In short, it automates the [Docker Compose setup](#docker) described below.
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
- Docker and Docker Compose must be [installed](https://docs.docker.com/engine/install/){:target="\_blank"}.
|
||||
- macOS users will need [GNU sed](https://formulae.brew.sh/formula/gnu-sed) with support for running as `sed` as well as [wget](https://formulae.brew.sh/formula/wget).
|
||||
|
||||
#### Run the installation script
|
||||
|
||||
```shell-session
|
||||
bash -c "$(curl --location --silent --show-error https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"
|
||||
```
|
||||
|
||||
#### After installation
|
||||
|
||||
Paperless-ngx should be available at `http://127.0.0.1:8000` (or similar,
|
||||
depending on your configuration) and you will be able to login with the
|
||||
credentials you provided during the installation script.
|
||||
|
||||
## Docker Compose Install {#docker}
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
- Docker and Docker Compose must be [installed](https://docs.docker.com/engine/install/){:target="\_blank"}.
|
||||
|
||||
#### Installation
|
||||
|
||||
1. Go to the [/docker/compose directory on the project
|
||||
page](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose){:target="\_blank"}
|
||||
and download one of the `docker-compose.*.yml` files, depending on which database backend
|
||||
you want to use. Place the files in a local directory and rename it `docker-compose.yml`. Download the
|
||||
`docker-compose.env` file and the `.env` file as well in the same directory.
|
||||
and download one `docker-compose.*.yml` file for your preferred
|
||||
database backend. Save it in a local directory as `docker-compose.yml`.
|
||||
Also download `docker-compose.env` and `.env` into that same directory.
|
||||
|
||||
If you want to enable optional support for Office and other documents, download a
|
||||
file with `-tika` in the file name.
|
||||
@@ -56,15 +81,16 @@ account. The script essentially automatically performs the steps described in [D
|
||||
For new installations, it is recommended to use PostgreSQL as the
|
||||
database backend.
|
||||
|
||||
3. Modify `docker-compose.yml` as needed. For example, you may want to change the paths to the
|
||||
consumption, media etc. directories to use 'bind mounts'.
|
||||
2. Modify `docker-compose.yml` as needed. For example, you may want to
|
||||
change the paths for `consume`, `media`, and other directories to
|
||||
use bind mounts.
|
||||
Find the line that specifies where to mount the directory, e.g.:
|
||||
|
||||
```yaml
|
||||
- ./consume:/usr/src/paperless/consume
|
||||
```
|
||||
|
||||
Replace the part _before_ the colon with a local directory of your choice:
|
||||
Replace the part _before_ the colon with your local directory:
|
||||
|
||||
```yaml
|
||||
- /home/jonaswinkler/paperless-inbox:/usr/src/paperless/consume
|
||||
@@ -78,38 +104,15 @@ account. The script essentially automatically performs the steps described in [D
|
||||
- 8010:8000
|
||||
```
|
||||
|
||||
**Rootless**
|
||||
|
||||
!!! warning
|
||||
|
||||
It is currently not possible to run the container rootless if additional languages are specified via `PAPERLESS_OCR_LANGUAGES`.
|
||||
|
||||
If you want to run Paperless as a rootless container, you will need
|
||||
to do the following in your `docker-compose.yml`:
|
||||
|
||||
- set the `user` running the container to map to the `paperless`
|
||||
user in the container. This value (`user_id` below), should be
|
||||
the same id that `USERMAP_UID` and `USERMAP_GID` are set to in
|
||||
the next step. See `USERMAP_UID` and `USERMAP_GID`
|
||||
[here](configuration.md#docker).
|
||||
|
||||
Your entry for Paperless should contain something like:
|
||||
|
||||
> ```
|
||||
> webserver:
|
||||
> image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
> user: <user_id>
|
||||
> ```
|
||||
|
||||
4. Modify `docker-compose.env` with any configuration options you'd like.
|
||||
3. Modify `docker-compose.env` with any configuration options you need.
|
||||
See the [configuration documentation](configuration.md) for all options.
|
||||
|
||||
You may also need to set `USERMAP_UID` and `USERMAP_GID` to
|
||||
the uid and gid of your user on the host system. Use `id -u` and
|
||||
`id -g` to get these. This ensures that both the container and the host
|
||||
user have write access to the consumption directory. If your UID
|
||||
and GID on the host system is 1000 (the default for the first normal
|
||||
user on most systems), it will work out of the box without any
|
||||
the UID and GID of your user on the host system. Use `id -u` and
|
||||
`id -g` to get these values. This ensures both the container and the
|
||||
host user can write to the consumption directory. If your UID and
|
||||
GID are `1000` (the default for the first normal user on many
|
||||
systems), this usually works out of the box without
|
||||
modifications. Run `id "username"` to check.
|
||||
|
||||
!!! note
|
||||
@@ -118,79 +121,62 @@ account. The script essentially automatically performs the steps described in [D
|
||||
appending `_FILE` to configuration values. For example [`PAPERLESS_DBUSER`](configuration.md#PAPERLESS_DBUSER)
|
||||
can be set using `PAPERLESS_DBUSER_FILE=/var/run/secrets/password.txt`.
|
||||
|
||||
!!! warning
|
||||
|
||||
Some file systems such as NFS network shares don't support file
|
||||
system notifications with `inotify`. When storing the consumption
|
||||
directory on such a file system, paperless will not pick up new
|
||||
files with the default configuration. You will need to use
|
||||
[`PAPERLESS_CONSUMER_POLLING`](configuration.md#PAPERLESS_CONSUMER_POLLING), which will disable inotify. See
|
||||
[here](configuration.md#polling).
|
||||
|
||||
5. Run `docker compose pull`. This will pull the image from the GitHub container registry
|
||||
by default but you can change the image to pull from Docker Hub by changing the `image`
|
||||
4. Run `docker compose pull`. This pulls the image from the GitHub container registry
|
||||
by default, but you can pull from Docker Hub by changing the `image`
|
||||
line to `image: paperlessngx/paperless-ngx:latest`.
|
||||
|
||||
6. Run `docker compose up -d`. This will create and start the necessary containers.
|
||||
5. Run `docker compose up -d`. This will create and start the necessary containers.
|
||||
|
||||
7. Congratulations! Your Paperless-ngx instance should now be accessible at `http://127.0.0.1:8000`
|
||||
(or similar, depending on your configuration). When you first access the web interface, you will be
|
||||
prompted to create a superuser account.
|
||||
#### After installation
|
||||
|
||||
### Build the Docker image yourself {#docker_build}
|
||||
Your Paperless-ngx instance should now be accessible at
|
||||
`http://127.0.0.1:8000` (or similar, depending on your configuration).
|
||||
When you first access the web interface, you will be prompted to create
|
||||
a [superuser](usage.md#superusers) account.
|
||||
|
||||
1. Clone the entire repository of paperless:
|
||||
#### Optional Advanced Compose Configurations {#advanced_compose data-toc-label="Advanced Compose Configurations"}
|
||||
|
||||
```shell-session
|
||||
git clone https://github.com/paperless-ngx/paperless-ngx
|
||||
```
|
||||
**Rootless**
|
||||
|
||||
The main branch always reflects the latest stable version.
|
||||
!!! warning
|
||||
|
||||
2. Copy one of the `docker/compose/docker-compose.*.yml` to
|
||||
`docker-compose.yml` in the root folder, depending on which database
|
||||
backend you want to use. Copy `docker-compose.env` into the project
|
||||
root as well.
|
||||
It is currently not possible to run the container rootless if additional languages are specified via `PAPERLESS_OCR_LANGUAGES`.
|
||||
|
||||
3. In the `docker-compose.yml` file, find the line that instructs
|
||||
Docker Compose to pull the paperless image from Docker Hub:
|
||||
If you want to run Paperless as a rootless container, make this
|
||||
change in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
webserver:
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
```
|
||||
- Set the `user` running the container to map to the `paperless`
|
||||
user in the container. This value (`user_id` below) should be
|
||||
the same ID that `USERMAP_UID` and `USERMAP_GID` are set to in
|
||||
`docker-compose.env`. See `USERMAP_UID` and `USERMAP_GID`
|
||||
[here](configuration.md#docker).
|
||||
|
||||
and replace it with a line that instructs Docker Compose to build
|
||||
the image from the current working directory instead:
|
||||
Your entry for Paperless should contain something like:
|
||||
|
||||
```yaml
|
||||
webserver:
|
||||
build:
|
||||
context: .
|
||||
```
|
||||
> ```
|
||||
> webserver:
|
||||
> image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
> user: <user_id>
|
||||
> ```
|
||||
|
||||
4. Follow the [Docker setup](#docker) above except when asked to run
|
||||
`docker compose pull` to pull the image, run
|
||||
**File systems without inotify support (e.g. NFS)**
|
||||
|
||||
```shell-session
|
||||
docker compose build
|
||||
```
|
||||
Some file systems, such as NFS network shares, don't support file system
|
||||
notifications with `inotify`. When the consumption directory is on such a
|
||||
file system, Paperless-ngx will not pick up new files with the default
|
||||
configuration. Use [`PAPERLESS_CONSUMER_POLLING`](configuration.md#PAPERLESS_CONSUMER_POLLING)
|
||||
to enable polling and disable inotify. See [here](configuration.md#polling).
|
||||
|
||||
instead to build the image.
|
||||
## Bare Metal Install {#bare_metal}
|
||||
|
||||
### Bare Metal Route {#bare_metal}
|
||||
#### Prerequisites
|
||||
|
||||
Paperless runs on linux only. The following procedure has been tested on
|
||||
a minimal installation of Debian/Buster, which is the current stable
|
||||
release at the time of writing. Windows is not and will never be
|
||||
supported.
|
||||
- Paperless runs on Linux only, Windows is not supported.
|
||||
- Python 3 is required with versions 3.10 - 3.12 currently supported. Newer versions may work, but some dependencies may not be fully compatible.
|
||||
|
||||
Paperless requires Python 3. At this time, 3.10 - 3.12 are tested versions.
|
||||
Newer versions may work, but some dependencies may not fully support newer versions.
|
||||
Support for older Python versions may be dropped as they reach end of life or as newer versions
|
||||
are released, dependency support is confirmed, etc.
|
||||
#### Installation
|
||||
|
||||
1. Install dependencies. Paperless requires the following packages.
|
||||
1. Install dependencies. Paperless requires the following packages:
|
||||
|
||||
- `python3`
|
||||
- `python3-pip`
|
||||
@@ -254,8 +240,8 @@ are released, dependency support is confirmed, etc.
|
||||
|
||||
2. Install `redis` >= 6.0 and configure it to start automatically.
|
||||
|
||||
3. Optional. Install `postgresql` and configure a database, user and
|
||||
password for paperless. If you do not wish to use PostgreSQL,
|
||||
3. Optional: Install `postgresql` and configure a database, user, and
|
||||
password for Paperless-ngx. If you do not wish to use PostgreSQL,
|
||||
MariaDB and SQLite are available as well.
|
||||
|
||||
!!! note
|
||||
@@ -264,61 +250,60 @@ are released, dependency support is confirmed, etc.
|
||||
extension](https://code.djangoproject.com/wiki/JSON1Extension) is
|
||||
enabled. This is usually the case, but not always.
|
||||
|
||||
4. Create a system user with a new home folder under which you wish
|
||||
to run paperless.
|
||||
4. Create a system user with a new home folder in which you want
|
||||
to run Paperless-ngx.
|
||||
|
||||
```shell-session
|
||||
adduser paperless --system --home /opt/paperless --group
|
||||
```
|
||||
|
||||
5. Get the release archive from
|
||||
<https://github.com/paperless-ngx/paperless-ngx/releases> for example with
|
||||
5. Download a release archive from
|
||||
<https://github.com/paperless-ngx/paperless-ngx/releases>. For example:
|
||||
|
||||
```shell-session
|
||||
curl -O -L https://github.com/paperless-ngx/paperless-ngx/releases/download/v1.10.2/paperless-ngx-v1.10.2.tar.xz
|
||||
curl -O -L https://github.com/paperless-ngx/paperless-ngx/releases/download/vX.Y.Z/paperless-ngx-vX.Y.Z.tar.xz
|
||||
```
|
||||
|
||||
Extract the archive with
|
||||
|
||||
```shell-session
|
||||
tar -xf paperless-ngx-v1.10.2.tar.xz
|
||||
tar -xf paperless-ngx-vX.Y.Z.tar.xz
|
||||
```
|
||||
|
||||
and copy the contents to the
|
||||
home folder of the user you created before (`/opt/paperless`).
|
||||
and copy the contents to the home directory of the user you created
|
||||
earlier (`/opt/paperless`).
|
||||
|
||||
Optional: If you cloned the git repo, you will have to
|
||||
compile the frontend yourself, see [here](development.md#front-end-development)
|
||||
Optional: If you cloned the Git repository, you will need to
|
||||
compile the frontend yourself. See [here](development.md#front-end-development)
|
||||
and use the `build` step, not `serve`.
|
||||
|
||||
6. Configure paperless. See [configuration](configuration.md) for details.
|
||||
6. Configure Paperless-ngx. See [configuration](configuration.md) for details.
|
||||
Edit the included `paperless.conf` and adjust the settings to your
|
||||
needs. Required settings for getting
|
||||
paperless running are:
|
||||
needs. Required settings for getting Paperless-ngx running are:
|
||||
|
||||
- [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS) should point to your redis server, such as
|
||||
<redis://localhost:6379>.
|
||||
- [`PAPERLESS_DBENGINE`](configuration.md#PAPERLESS_DBENGINE) optional, and should be one of `postgres`,
|
||||
- [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS) should point to your Redis server, such as
|
||||
`redis://localhost:6379`.
|
||||
- [`PAPERLESS_DBENGINE`](configuration.md#PAPERLESS_DBENGINE) is optional, and should be one of `postgres`,
|
||||
`mariadb`, or `sqlite`
|
||||
- [`PAPERLESS_DBHOST`](configuration.md#PAPERLESS_DBHOST) should be the hostname on which your
|
||||
PostgreSQL server is running. Do not configure this to use
|
||||
SQLite instead. Also configure port, database name, user and
|
||||
password as necessary.
|
||||
- [`PAPERLESS_CONSUMPTION_DIR`](configuration.md#PAPERLESS_CONSUMPTION_DIR) should point to a folder which
|
||||
paperless should watch for documents. You might want to have
|
||||
this somewhere else. Likewise, [`PAPERLESS_DATA_DIR`](configuration.md#PAPERLESS_DATA_DIR) and
|
||||
[`PAPERLESS_MEDIA_ROOT`](configuration.md#PAPERLESS_MEDIA_ROOT) define where paperless stores its data.
|
||||
If you like, you can point both to the same directory.
|
||||
- [`PAPERLESS_CONSUMPTION_DIR`](configuration.md#PAPERLESS_CONSUMPTION_DIR) should point to the folder
|
||||
that Paperless-ngx should watch for incoming documents.
|
||||
Likewise, [`PAPERLESS_DATA_DIR`](configuration.md#PAPERLESS_DATA_DIR) and
|
||||
[`PAPERLESS_MEDIA_ROOT`](configuration.md#PAPERLESS_MEDIA_ROOT) define where Paperless-ngx stores its data.
|
||||
If needed, these can point to the same directory.
|
||||
- [`PAPERLESS_SECRET_KEY`](configuration.md#PAPERLESS_SECRET_KEY) should be a random sequence of
|
||||
characters. It's used for authentication. Failure to do so
|
||||
allows third parties to forge authentication credentials.
|
||||
- [`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) if you are behind a reverse proxy. This should
|
||||
- Set [`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) if you are behind a reverse proxy. This should
|
||||
point to your domain. Please see
|
||||
[configuration](configuration.md) for more
|
||||
information.
|
||||
|
||||
Many more adjustments can be made to paperless, especially the OCR
|
||||
part. The following options are recommended for everyone:
|
||||
You can make many more adjustments, especially for OCR.
|
||||
The following options are recommended for most users:
|
||||
|
||||
- Set [`PAPERLESS_OCR_LANGUAGE`](configuration.md#PAPERLESS_OCR_LANGUAGE) to the language most of your
|
||||
documents are written in.
|
||||
@@ -326,17 +311,16 @@ are released, dependency support is confirmed, etc.
|
||||
|
||||
!!! warning
|
||||
|
||||
Ensure your Redis instance [is secured](https://redis.io/docs/getting-started/#securing-redis).
|
||||
Ensure your Redis instance [is secured](https://redis.io/docs/latest/operate/oss_and_stack/management/security/).
|
||||
|
||||
7. Create the following directories if they are missing:
|
||||
7. Create the following directories if they do not already exist:
|
||||
|
||||
- `/opt/paperless/media`
|
||||
- `/opt/paperless/data`
|
||||
- `/opt/paperless/consume`
|
||||
|
||||
Adjust as necessary if you configured different folders.
|
||||
Ensure that the paperless user has write permissions for every one
|
||||
of these folders with
|
||||
Adjust these paths if you configured different folders.
|
||||
Then verify that the `paperless` user has write permissions:
|
||||
|
||||
```shell-session
|
||||
ls -l -d /opt/paperless/media
|
||||
@@ -350,45 +334,44 @@ are released, dependency support is confirmed, etc.
|
||||
sudo chown paperless:paperless /opt/paperless/consume
|
||||
```
|
||||
|
||||
8. Install python requirements from the `requirements.txt` file.
|
||||
8. Install Python dependencies from `requirements.txt`.
|
||||
|
||||
```shell-session
|
||||
sudo -Hu paperless pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
This will install all python dependencies in the home directory of
|
||||
This will install all Python dependencies in the home directory of
|
||||
the new paperless user.
|
||||
|
||||
!!! tip
|
||||
|
||||
It is up to you if you wish to use a virtual environment or not for the Python
|
||||
dependencies. This is an alternative to the above and may require adjusting
|
||||
the example scripts to utilize the virtual environment paths
|
||||
You can use a virtual environment if you prefer. If you do,
|
||||
you may need to adjust the example scripts for your virtual
|
||||
environment paths.
|
||||
|
||||
!!! tip
|
||||
|
||||
If you use modern Python tooling, such as `uv`, installation will not include
|
||||
dependencies for Postgres or Mariadb. You can select those extras with `--extra <EXTRA>`
|
||||
or all with `--all-extras`
|
||||
dependencies for PostgreSQL or MariaDB. You can select those
|
||||
extras with `--extra <EXTRA>`, or install all extras with
|
||||
`--all-extras`.
|
||||
|
||||
9. Go to `/opt/paperless/src`, and execute the following command:
|
||||
9. Go to `/opt/paperless/src` and execute the following command:
|
||||
|
||||
```bash
|
||||
# This creates the database schema.
|
||||
sudo -Hu paperless python3 manage.py migrate
|
||||
```
|
||||
|
||||
When you first access the web interface you will be prompted to create a superuser account.
|
||||
|
||||
10. Optional: Test that paperless is working by executing
|
||||
10. Optional: Test that Paperless-ngx is working by running
|
||||
|
||||
```bash
|
||||
# Manually starts the webserver
|
||||
sudo -Hu paperless python3 manage.py runserver
|
||||
```
|
||||
|
||||
and pointing your browser to http://localhost:8000 if
|
||||
accessing from the same devices on which paperless is installed.
|
||||
Then point your browser to `http://localhost:8000` if
|
||||
accessing from the same device on which Paperless-ngx is installed.
|
||||
If accessing from another machine, set up systemd services. You may need
|
||||
to set `PAPERLESS_DEBUG=true` in order for the development server to work
|
||||
normally in your browser.
|
||||
@@ -396,23 +379,24 @@ are released, dependency support is confirmed, etc.
|
||||
!!! warning
|
||||
|
||||
This is a development server which should not be used in production.
|
||||
It is not audited for security and performance is inferior to
|
||||
production ready web servers.
|
||||
It is not audited for security, and performance is inferior to
|
||||
production-ready web servers.
|
||||
|
||||
!!! tip
|
||||
|
||||
This will not start the consumer. Paperless does this in a separate
|
||||
process.
|
||||
|
||||
11. Setup systemd services to run paperless automatically. You may use
|
||||
11. Set up systemd services to run Paperless-ngx automatically. You may use
|
||||
the service definition files included in the `scripts` folder as a
|
||||
starting point.
|
||||
|
||||
Paperless needs the `webserver` script to run the webserver, the
|
||||
`consumer` script to watch the input folder, `taskqueue` for the
|
||||
background workers used to handle things like document consumption
|
||||
and the `scheduler` script to run tasks such as email checking at
|
||||
certain times .
|
||||
Paperless needs:
|
||||
|
||||
- The `webserver` script to run the webserver.
|
||||
- The `consumer` script to watch the input folder.
|
||||
- The `taskqueue` script for background workers (document consumption, etc.).
|
||||
- The `scheduler` script for periodic tasks such as email checking.
|
||||
|
||||
!!! note
|
||||
|
||||
@@ -421,9 +405,9 @@ are released, dependency support is confirmed, etc.
|
||||
`Require=paperless-webserver.socket` in the `webserver` script
|
||||
and configure `granian` to listen on port 80 (set `GRANIAN_PORT`).
|
||||
|
||||
These services rely on redis and optionally the database server, but
|
||||
These services rely on Redis and optionally the database server, but
|
||||
don't need to be started in any particular order. The example files
|
||||
depend on redis being started. If you use a database server, you
|
||||
depend on Redis being started. If you use a database server, you
|
||||
should add additional dependencies.
|
||||
|
||||
!!! note
|
||||
@@ -433,19 +417,16 @@ are released, dependency support is confirmed, etc.
|
||||
|
||||
!!! warning
|
||||
|
||||
If celery won't start (check with
|
||||
If Celery won't start, check
|
||||
`sudo systemctl status paperless-task-queue.service` for
|
||||
paperless-task-queue.service and paperless-scheduler.service
|
||||
) you need to change the path in the files. Example:
|
||||
`paperless-task-queue.service` and `paperless-scheduler.service`.
|
||||
You may need to change the path in the files. Example:
|
||||
`ExecStart=/opt/paperless/.local/bin/celery --app paperless worker --loglevel INFO`
|
||||
|
||||
12. Optional: Install a samba server and make the consumption folder
|
||||
available as a network share.
|
||||
|
||||
13. Configure ImageMagick to allow processing of PDF documents. Most
|
||||
12. Configure ImageMagick to allow processing of PDF documents. Most
|
||||
distributions have this disabled by default, since PDF documents can
|
||||
contain malware. If you don't do this, paperless will fall back to
|
||||
ghostscript for certain steps such as thumbnail generation.
|
||||
contain malware. If you don't do this, Paperless-ngx will fall back to
|
||||
Ghostscript for certain steps such as thumbnail generation.
|
||||
|
||||
Edit `/etc/ImageMagick-6/policy.xml` and adjust
|
||||
|
||||
@@ -459,32 +440,38 @@ are released, dependency support is confirmed, etc.
|
||||
<policy domain="coder" rights="read|write" pattern="PDF" />
|
||||
```
|
||||
|
||||
14. Optional: Install the
|
||||
[jbig2enc](https://ocrmypdf.readthedocs.io/en/latest/jbig2.html)
|
||||
encoder. This will reduce the size of generated PDF documents.
|
||||
You'll most likely need to compile this by yourself, because this
|
||||
software has been patented until around 2017 and binary packages are
|
||||
not available for most distributions.
|
||||
**Optional: Install the [jbig2enc](https://ocrmypdf.readthedocs.io/en/latest/jbig2.html) encoder.**
|
||||
This will reduce the size of generated PDF documents. You'll most likely need to compile this yourself, because this
|
||||
software has been patented until around 2017 and binary packages are not available for most distributions.
|
||||
|
||||
15. Optional: If using the NLTK machine learning processing (see
|
||||
[`PAPERLESS_ENABLE_NLTK`](configuration.md#PAPERLESS_ENABLE_NLTK) for details),
|
||||
download the NLTK data for the Snowball
|
||||
Stemmer, Stopwords and Punkt tokenizer to `/usr/share/nltk_data`. Refer to the [NLTK
|
||||
instructions](https://www.nltk.org/data.html) for details on how to
|
||||
download the data.
|
||||
**Optional: download the NLTK data**
|
||||
If using the NLTK machine-learning processing (see [`PAPERLESS_ENABLE_NLTK`](configuration.md#PAPERLESS_ENABLE_NLTK) for details),
|
||||
download the NLTK data for the Snowball Stemmer, Stopwords and Punkt tokenizer to `/usr/share/nltk_data`. Refer to the [NLTK
|
||||
instructions](https://www.nltk.org/data.html) for details on how to download the data.
|
||||
|
||||
# Migrating to Paperless-ngx
|
||||
#### After installation
|
||||
|
||||
Migration is possible both from Paperless-ng or directly from the
|
||||
'original' Paperless.
|
||||
Your Paperless-ngx instance should now be accessible at `http://localhost:8000` (or similar, depending on your configuration).
|
||||
When you first access the web interface you will be prompted to create a [superuser](usage.md#superusers) account.
|
||||
|
||||
## Migrating from Paperless-ng
|
||||
## Build the Docker image yourself {#docker_build data-toc-label="Building the Docker image"}
|
||||
|
||||
Paperless-ngx is meant to be a drop-in replacement for Paperless-ng and
|
||||
thus upgrading should be trivial for most users, especially when using
|
||||
docker. However, as with any major change, it is recommended to take a
|
||||
Building the Docker image yourself is typically used for development, but it can also be used for production
|
||||
if you want to customize the image. See [Building the Docker image](development.md#docker_build) in the
|
||||
development documentation.
|
||||
|
||||
## Migrating to Paperless-ngx
|
||||
|
||||
You can migrate to Paperless-ngx from Paperless-ng or from the original
|
||||
Paperless project.
|
||||
|
||||
<h3 id="migration_ng">Migrating from Paperless-ng</h3>
|
||||
|
||||
Paperless-ngx is meant to be a drop-in replacement for Paperless-ng, and
|
||||
upgrading should be trivial for most users, especially when using
|
||||
Docker. However, as with any major change, it is recommended to take a
|
||||
full backup first. Once you are ready, simply change the docker image to
|
||||
point to the new source. E.g. if using Docker Compose, edit
|
||||
point to the new source. For example, if using Docker Compose, edit
|
||||
`docker-compose.yml` and change:
|
||||
|
||||
```
|
||||
@@ -497,66 +484,65 @@ to
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
```
|
||||
|
||||
and then run `docker compose up -d` which will pull the new image
|
||||
recreate the container. That's it!
|
||||
and then run `docker compose up -d`, which will pull the new image and
|
||||
recreate the container. That's it.
|
||||
|
||||
Users who installed with the bare-metal route should also update their
|
||||
Git clone to point to `https://github.com/paperless-ngx/paperless-ngx`,
|
||||
e.g. using the command
|
||||
for example using:
|
||||
`git remote set-url origin https://github.com/paperless-ngx/paperless-ngx`
|
||||
and then pull the latest version.
|
||||
|
||||
## Migrating from Paperless
|
||||
<h3 id="migration_paperless">Migrating from Paperless</h3>
|
||||
|
||||
At its core, paperless-ngx is still paperless and fully compatible.
|
||||
At its core, Paperless-ngx is still Paperless and fully compatible.
|
||||
However, some things have changed under the hood, so you need to adapt
|
||||
your setup depending on how you installed paperless.
|
||||
your setup depending on how you installed Paperless.
|
||||
|
||||
This setup describes how to update an existing paperless Docker
|
||||
installation. The important things to keep in mind are as follows:
|
||||
This section describes how to update an existing Paperless Docker
|
||||
installation. Keep these points in mind:
|
||||
|
||||
- Read the [changelog](changelog.md) and
|
||||
take note of breaking changes.
|
||||
- You should decide if you want to stick with SQLite or want to
|
||||
migrate your database to PostgreSQL. See [documentation](#sqlite_to_psql)
|
||||
for details on
|
||||
how to move your data from SQLite to PostgreSQL. Both work fine with
|
||||
paperless. However, if you already have a database server running
|
||||
for other services, you might as well use it for paperless as well.
|
||||
- The task scheduler of paperless, which is used to execute periodic
|
||||
- Decide whether to stay on SQLite or migrate to PostgreSQL.
|
||||
See [documentation](#sqlite_to_psql) for details on moving data
|
||||
from SQLite to PostgreSQL. Both work fine with
|
||||
Paperless. However, if you already have a database server running
|
||||
for other services, you might as well use it for Paperless as well.
|
||||
- The task scheduler of Paperless, which is used to execute periodic
|
||||
tasks such as email checking and maintenance, requires a
|
||||
[redis](https://redis.io/) message broker instance. The
|
||||
[Redis](https://redis.io/) message broker instance. The
|
||||
Docker Compose route takes care of that.
|
||||
- The layout of the folder structure for your documents and data
|
||||
remains the same, so you can just plug your old docker volumes into
|
||||
remains the same, so you can plug your old Docker volumes into
|
||||
paperless-ngx and expect it to find everything where it should be.
|
||||
|
||||
Migration to paperless-ngx is then performed in a few simple steps:
|
||||
Migration to Paperless-ngx is then performed in a few simple steps:
|
||||
|
||||
1. Stop paperless.
|
||||
1. Stop Paperless.
|
||||
|
||||
```bash
|
||||
cd /path/to/current/paperless
|
||||
docker compose down
|
||||
```
|
||||
|
||||
2. Do a backup for two purposes: If something goes wrong, you still
|
||||
have your data. Second, if you don't like paperless-ngx, you can
|
||||
switch back to paperless.
|
||||
2. Create a backup for two reasons: if something goes wrong, you still
|
||||
have your data; and if you don't like paperless-ngx, you can
|
||||
switch back to Paperless.
|
||||
|
||||
3. Download the latest release of paperless-ngx. You can either go with
|
||||
3. Download the latest release of Paperless-ngx. You can either use
|
||||
the Docker Compose files from
|
||||
[here](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose)
|
||||
or clone the repository to build the image yourself (see
|
||||
[above](#docker_build)). You can
|
||||
either replace your current paperless folder or put paperless-ngx in
|
||||
[development docs](development.md#docker_build)). You can either replace your current paperless
|
||||
folder or put Paperless-ngx in
|
||||
a different location.
|
||||
|
||||
!!! warning
|
||||
|
||||
Paperless-ngx includes a `.env` file. This will set the project name
|
||||
for docker compose to `paperless`, which will also define the name
|
||||
of the volumes by paperless-ngx. However, if you experience that
|
||||
for Docker Compose to `paperless`, which will also define the
|
||||
volume names created by Paperless-ngx. However, if you notice that
|
||||
paperless-ngx is not using your old paperless volumes, verify the
|
||||
names of your volumes with
|
||||
|
||||
@@ -572,10 +558,10 @@ Migration to paperless-ngx is then performed in a few simple steps:
|
||||
after you migrated your existing SQLite database.
|
||||
|
||||
5. Adjust `docker-compose.yml` and `docker-compose.env` to your needs.
|
||||
See [Docker setup](#docker) details on
|
||||
which edits are advised.
|
||||
See [Docker setup](#docker) for details on
|
||||
which edits are recommended.
|
||||
|
||||
6. [Update paperless.](administration.md#updating)
|
||||
6. Follow the update procedure in [Update paperless](administration.md#updating).
|
||||
|
||||
7. In order to find your existing documents with the new search
|
||||
feature, you need to invoke a one-time operation that will create
|
||||
@@ -586,136 +572,99 @@ Migration to paperless-ngx is then performed in a few simple steps:
|
||||
```
|
||||
|
||||
This will migrate your database and create the search index. After
|
||||
that, paperless will take care of maintaining the index by itself.
|
||||
that, Paperless-ngx will maintain the index automatically.
|
||||
|
||||
8. Start paperless-ngx.
|
||||
8. Start Paperless-ngx.
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This will run paperless in the background and automatically start it
|
||||
This will run Paperless-ngx in the background and automatically start it
|
||||
on system boot.
|
||||
|
||||
9. Paperless installed a permanent redirect to `admin/` in your
|
||||
9. Paperless may have installed a permanent redirect to `admin/` in your
|
||||
browser. This redirect is still in place and prevents access to the
|
||||
new UI. Clear your browsing cache in order to fix this.
|
||||
new UI. Clear your browser cache to fix this.
|
||||
|
||||
10. Optionally, follow the instructions below to migrate your existing
|
||||
data to PostgreSQL.
|
||||
|
||||
## Migrating from LinuxServer.io Docker Image
|
||||
<h3 id="migration_lsio">Migrating from LinuxServer.io Docker Image</h3>
|
||||
|
||||
As with any upgrades and large changes, it is highly recommended to
|
||||
As with any upgrade or large change, it is highly recommended to
|
||||
create a backup before starting. This assumes the image was running
|
||||
using Docker Compose, but the instructions are translatable to Docker
|
||||
commands as well.
|
||||
|
||||
1. Stop and remove the paperless container
|
||||
2. If using an external database, stop the container
|
||||
3. Update Redis configuration
|
||||
1. Stop and remove the Paperless container.
|
||||
2. If using an external database, stop that container.
|
||||
3. Update Redis configuration.
|
||||
|
||||
1. If `REDIS_URL` is already set, change it to [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS)
|
||||
and continue to step 4.
|
||||
|
||||
1. Otherwise, in the `docker-compose.yml` add a new service for
|
||||
Redis, following [the example compose
|
||||
1. Otherwise, add a new Redis service in `docker-compose.yml`,
|
||||
following [the example compose
|
||||
files](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose)
|
||||
|
||||
1. Set the environment variable [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS) so it points to
|
||||
the new Redis container
|
||||
the new Redis container.
|
||||
|
||||
4. Update user mapping
|
||||
4. Update user mapping.
|
||||
|
||||
1. If set, change the environment variable `PUID` to `USERMAP_UID`
|
||||
1. If set, change the environment variable `PUID` to `USERMAP_UID`.
|
||||
|
||||
1. If set, change the environment variable `PGID` to `USERMAP_GID`
|
||||
1. If set, change the environment variable `PGID` to `USERMAP_GID`.
|
||||
|
||||
5. Update configuration paths
|
||||
5. Update configuration paths.
|
||||
|
||||
1. Set the environment variable [`PAPERLESS_DATA_DIR`](configuration.md#PAPERLESS_DATA_DIR) to `/config`
|
||||
1. Set the environment variable [`PAPERLESS_DATA_DIR`](configuration.md#PAPERLESS_DATA_DIR) to `/config`.
|
||||
|
||||
6. Update media paths
|
||||
6. Update media paths.
|
||||
|
||||
1. Set the environment variable [`PAPERLESS_MEDIA_ROOT`](configuration.md#PAPERLESS_MEDIA_ROOT) to
|
||||
`/data/media`
|
||||
`/data/media`.
|
||||
|
||||
7. Update timezone
|
||||
7. Update timezone.
|
||||
|
||||
1. Set the environment variable [`PAPERLESS_TIME_ZONE`](configuration.md#PAPERLESS_TIME_ZONE) to the same
|
||||
value as `TZ`
|
||||
value as `TZ`.
|
||||
|
||||
8. Modify the `image:` to point to
|
||||
8. Modify `image:` to point to
|
||||
`ghcr.io/paperless-ngx/paperless-ngx:latest` or a specific version
|
||||
if preferred.
|
||||
9. Start the containers as before, using `docker compose`.
|
||||
|
||||
## Moving data from SQLite to PostgreSQL or MySQL/MariaDB {#sqlite_to_psql}
|
||||
## Running Paperless-ngx on less powerful devices {#less-powerful-devices data-toc-label="Less Powerful Devices"}
|
||||
|
||||
The best way to migrate between database types is to perform an [export](administration.md#exporter) and then
|
||||
[import](administration.md#importer) into a clean installation of Paperless-ngx.
|
||||
|
||||
## Moving back to Paperless
|
||||
|
||||
Lets say you migrated to Paperless-ngx and used it for a while, but
|
||||
decided that you don't like it and want to move back (If you do, send
|
||||
me a mail about what part you didn't like!), you can totally do that
|
||||
with a few simple steps.
|
||||
|
||||
Paperless-ngx modified the database schema slightly, however, these
|
||||
changes can be reverted while keeping your current data, so that your
|
||||
current data will be compatible with original Paperless. Thumbnails
|
||||
were also changed from PNG to WEBP format and will need to be
|
||||
re-generated.
|
||||
|
||||
Execute this:
|
||||
|
||||
```shell-session
|
||||
$ cd /path/to/paperless
|
||||
$ docker compose run --rm webserver migrate documents 0023
|
||||
```
|
||||
|
||||
Or without docker:
|
||||
|
||||
```shell-session
|
||||
$ cd /path/to/paperless/src
|
||||
$ python3 manage.py migrate documents 0023
|
||||
```
|
||||
|
||||
After regenerating thumbnails, you'll need to clear your cookies
|
||||
(Paperless-ngx comes with updated dependencies that do cookie-processing
|
||||
differently) and probably your cache as well.
|
||||
|
||||
# Considerations for less powerful devices {#less-powerful-devices}
|
||||
|
||||
Paperless runs on Raspberry Pi. However, some things are rather slow on
|
||||
the Pi and configuring some options in paperless can help improve
|
||||
performance immensely:
|
||||
Paperless runs on Raspberry Pi. Some tasks can be slow on lower-powered
|
||||
hardware, but a few settings can improve performance:
|
||||
|
||||
- Stick with SQLite to save some resources. See [troubleshooting](troubleshooting.md#log-reports-creating-paperlesstask-failed)
|
||||
if you encounter issues with SQLite locking.
|
||||
- If you do not need the filesystem-based consumer, consider disabling it
|
||||
entirely by setting [`PAPERLESS_CONSUMER_DISABLE`](configuration.md#PAPERLESS_CONSUMER_DISABLE) to `true`.
|
||||
- Consider setting [`PAPERLESS_OCR_PAGES`](configuration.md#PAPERLESS_OCR_PAGES) to 1, so that paperless will
|
||||
only OCR the first page of your documents. In most cases, this page
|
||||
- Consider setting [`PAPERLESS_OCR_PAGES`](configuration.md#PAPERLESS_OCR_PAGES) to 1, so that Paperless
|
||||
OCRs only the first page of your documents. In most cases, this page
|
||||
contains enough information to be able to find it.
|
||||
- [`PAPERLESS_TASK_WORKERS`](configuration.md#PAPERLESS_TASK_WORKERS) and [`PAPERLESS_THREADS_PER_WORKER`](configuration.md#PAPERLESS_THREADS_PER_WORKER) are
|
||||
configured to use all cores. The Raspberry Pi models 3 and up have 4
|
||||
cores, meaning that paperless will use 2 workers and 2 threads per
|
||||
cores, meaning that Paperless will use 2 workers and 2 threads per
|
||||
worker. This may result in sluggish response times during
|
||||
consumption, so you might want to lower these settings (example: 2
|
||||
workers and 1 thread to always have some computing power left for
|
||||
other tasks).
|
||||
- Keep [`PAPERLESS_OCR_MODE`](configuration.md#PAPERLESS_OCR_MODE) at its default value `skip` and consider
|
||||
OCR'ing your documents before feeding them into paperless. Some
|
||||
OCRing your documents before feeding them into Paperless. Some
|
||||
scanners are able to do this!
|
||||
- Set [`PAPERLESS_OCR_SKIP_ARCHIVE_FILE`](configuration.md#PAPERLESS_OCR_SKIP_ARCHIVE_FILE) to `with_text` to skip archive
|
||||
file generation for already ocr'ed documents, or `always` to skip it
|
||||
file generation for already OCRed documents, or `always` to skip it
|
||||
for all documents.
|
||||
- If you want to perform OCR on the device, consider using
|
||||
`PAPERLESS_OCR_CLEAN=none`. This will speed up OCR times and use
|
||||
less memory at the expense of slightly worse OCR results.
|
||||
- If using docker, consider setting [`PAPERLESS_WEBSERVER_WORKERS`](configuration.md#PAPERLESS_WEBSERVER_WORKERS) to 1. This will save some memory.
|
||||
- If using Docker, consider setting [`PAPERLESS_WEBSERVER_WORKERS`](configuration.md#PAPERLESS_WEBSERVER_WORKERS) to 1. This will save some memory.
|
||||
- Consider setting [`PAPERLESS_ENABLE_NLTK`](configuration.md#PAPERLESS_ENABLE_NLTK) to false, to disable the
|
||||
more advanced language processing, which can take more memory and
|
||||
processing time.
|
||||
@@ -727,17 +676,19 @@ For details, refer to [configuration](configuration.md).
|
||||
Updating the
|
||||
[automatic matching algorithm](advanced_usage.md#automatic-matching) takes quite a bit of time. However, the update mechanism
|
||||
checks if your data has changed before doing the heavy lifting. If you
|
||||
experience the algorithm taking too much cpu time, consider changing the
|
||||
experience the algorithm taking too much CPU time, consider changing the
|
||||
schedule in the admin interface to daily. You can also manually invoke
|
||||
the task by changing the date and time of the next run to today/now.
|
||||
|
||||
The actual matching of the algorithm is fast and works on Raspberry Pi
|
||||
as well as on any other device.
|
||||
|
||||
# Using nginx as a reverse proxy {#nginx}
|
||||
## Additional considerations
|
||||
|
||||
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx#nginx) for user-maintained documentation of using nginx with Paperless-ngx.
|
||||
**Using a reverse proxy with Paperless-ngx**
|
||||
|
||||
# Enhancing security {#security}
|
||||
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx#nginx) for user-maintained documentation on using nginx with Paperless-ngx.
|
||||
|
||||
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-Security-Tools-with-Paperless-ngx) for user-maintained documentation of how to configure security tools like Fail2ban with Paperless-ngx.
|
||||
**Enhancing security**
|
||||
|
||||
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-Security-Tools-with-Paperless-ngx) for user-maintained documentation on configuring security tools like Fail2ban with Paperless-ngx.
|
||||
|
||||
@@ -33,7 +33,7 @@ warns that
|
||||
`OCR for XX failed, but we're going to stick with what we've got since FORGIVING_OCR is enabled`,
|
||||
then you might need to install the [Tesseract language
|
||||
files](https://packages.ubuntu.com/search?keywords=tesseract-ocr)
|
||||
marching your document's languages.
|
||||
matching your document's languages.
|
||||
|
||||
As an example, if you are running Paperless-ngx from any Ubuntu or
|
||||
Debian box, and your documents are written in Spanish you may need to
|
||||
@@ -335,7 +335,7 @@ You may see errors when deleting documents like:
|
||||
Data too long for column 'transaction_id' at row 1
|
||||
```
|
||||
|
||||
This error can occur in installations which have upgraded from a version of Paperless-ngx that used Django 4 (Paperless-ngx versions prior to v2.13.0) with a MariaDB/MySQL database. Due to the backawards-incompatible change in Django 5, the column "documents_document.transaction_id" will need to be re-created, which can be done with a one-time run of the following management command:
|
||||
This error can occur in installations which have upgraded from a version of Paperless-ngx that used Django 4 (Paperless-ngx versions prior to v2.13.0) with a MariaDB/MySQL database. Due to the backwards-incompatible change in Django 5, the column "documents_document.transaction_id" will need to be re-created, which can be done with a one-time run of the following management command:
|
||||
|
||||
```shell-session
|
||||
$ python3 manage.py convert_mariadb_uuid
|
||||
|
||||
149
docs/usage.md
149
docs/usage.md
@@ -1,4 +1,8 @@
|
||||
# Usage Overview
|
||||
---
|
||||
title: Basic Usage
|
||||
---
|
||||
|
||||
# Usage
|
||||
|
||||
Paperless-ngx is an application that manages your personal documents. With
|
||||
the (optional) help of a document scanner (see [the scanners wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Scanner-&-Software-Recommendations)), Paperless-ngx transforms your unwieldy
|
||||
@@ -30,6 +34,9 @@ Each document has data fields that you can assign to them:
|
||||
- A _document type_ is used to demarcate the type of a document such
|
||||
as letter, bank statement, invoice, contract, etc. It is used to
|
||||
identify what a document is about.
|
||||
- The document _storage path_ is the location where the document files
|
||||
are stored. See [Storage Paths](advanced_usage.md#storage-paths) for
|
||||
more information.
|
||||
- The _date added_ of a document is the date the document was scanned
|
||||
into paperless. You cannot and should not change this date.
|
||||
- The _date created_ of a document is the date the document was
|
||||
@@ -89,6 +96,16 @@ and more. These areas allow you to view, add, edit, delete and manage permission
|
||||
for these objects. You can also manage saved views, mail accounts, mail rules,
|
||||
workflows and more from the management sections.
|
||||
|
||||
### Nested Tags
|
||||
|
||||
Paperless-ngx v2.19 introduces support for nested tags, allowing you to create a
|
||||
hierarchy of tags, which may be useful for organizing your documents. Tags can
|
||||
have a 'parent' tag, creating a tree-like structure, to a maximum depth of 5. When
|
||||
a tag is added to a document, all of its parent tags are also added automatically
|
||||
and similarly, when a tag is removed from a document, all of its child tags are
|
||||
also removed. Additionally, assigning a parent to an existing tag will automatically
|
||||
update all documents that have this tag assigned, adding the parent tag as well.
|
||||
|
||||
## Adding documents to Paperless-ngx
|
||||
|
||||
Once you've got Paperless setup, you need to start feeding documents
|
||||
@@ -248,6 +265,10 @@ different means. These are as follows:
|
||||
Paperless is set up to check your mails every 10 minutes. This can be
|
||||
configured via [`PAPERLESS_EMAIL_TASK_CRON`](configuration.md#PAPERLESS_EMAIL_TASK_CRON)
|
||||
|
||||
#### Processed Mail
|
||||
|
||||
Paperless keeps track of emails it has processed in order to avoid processing the same mail multiple times. This uses the message `UID` provided by the mail server, which should be unique for each message. You can view and manage processed mails from the web UI under Mail > Processed Mails. If you need to re-process a message, you can delete the corresponding processed mail entry, which will allow Paperless-ngx to process the email again the next time the mail fetch task runs.
|
||||
|
||||
#### OAuth Email Setup
|
||||
|
||||
Paperless-ngx supports OAuth2 authentication for Gmail and Outlook email accounts. To set up an email account with OAuth2, you will need to create a 'developer' app with the respective provider and obtain the client ID and client secret and set the appropriate [configuration variables](configuration.md#email_oauth). You will also need to set either [`PAPERLESS_OAUTH_CALLBACK_BASE_URL`](configuration.md#PAPERLESS_OAUTH_CALLBACK_BASE_URL) or [`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) to the correct value for the OAuth2 flow to work correctly.
|
||||
@@ -327,6 +348,11 @@ permissions can be granted to limit access to certain parts of the UI (and corre
|
||||
|
||||
Superusers can access all parts of the front and backend application as well as any and all objects. Superuser status can only be granted by another superuser.
|
||||
|
||||
!!! tip
|
||||
|
||||
Because superuser accounts can see all objects and documents, you may want to use a regular account for day-to-day use. Additional superuser accounts can
|
||||
be created via [cli](administration.md#create-superuser) or granted superuser status from an existing superuser account.
|
||||
|
||||
#### Admin Status
|
||||
|
||||
Admin status (Django 'staff status') grants access to viewing the paperless logs and the system status dialog
|
||||
@@ -397,7 +423,7 @@ fields and permissions, which will be merged.
|
||||
|
||||
#### Types {#workflow-trigger-types}
|
||||
|
||||
Currently, there are three events that correspond to workflow trigger 'types':
|
||||
Currently, there are four events that correspond to workflow trigger 'types':
|
||||
|
||||
1. **Consumption Started**: _before_ a document is consumed, so events can include filters by source (mail, consumption
|
||||
folder or API), file path, file name, mail rule
|
||||
@@ -405,12 +431,12 @@ Currently, there are three events that correspond to workflow trigger 'types':
|
||||
but the document content has been extracted and metadata such as document type, tags, etc. have been set, so these can now
|
||||
be used for filtering.
|
||||
3. **Document Updated**: when a document is updated. Similar to 'added' events, triggers can include filtering by content matching,
|
||||
tags, doc type, or correspondent.
|
||||
tags, doc type, correspondent or storage path.
|
||||
4. **Scheduled**: a scheduled trigger that can be used to run workflows at a specific time. The date used can be either the document
|
||||
added, created, updated date or you can specify a (date) custom field. You can also specify a day offset from the date (positive
|
||||
offsets will trigger before the date, negative offsets will trigger after).
|
||||
offsets will trigger after the date, negative offsets will trigger before).
|
||||
|
||||
The following flow diagram illustrates the three document trigger types:
|
||||
The following flow diagram illustrates the four document trigger types:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
@@ -426,6 +452,10 @@ flowchart TD
|
||||
'Updated'
|
||||
trigger(s)"}
|
||||
|
||||
scheduled{"Documents
|
||||
matching
|
||||
trigger(s)"}
|
||||
|
||||
A[New Document] --> consumption
|
||||
consumption --> |Yes| C[Workflow Actions Run]
|
||||
consumption --> |No| D
|
||||
@@ -438,6 +468,11 @@ flowchart TD
|
||||
updated --> |Yes| J[Workflow Actions Run]
|
||||
updated --> |No| K
|
||||
J --> K[Document Saved]
|
||||
L[Scheduled Task Check<br/>hourly at :05] --> M[Get All Scheduled Triggers]
|
||||
M --> scheduled
|
||||
scheduled --> |Yes| N[Workflow Actions Run]
|
||||
scheduled --> |No| O[Document Saved]
|
||||
N --> O
|
||||
```
|
||||
|
||||
#### Filters {#workflow-trigger-filters}
|
||||
@@ -445,14 +480,24 @@ flowchart TD
|
||||
Workflows allow you to filter by:
|
||||
|
||||
- Source, e.g. documents uploaded via consume folder, API (& the web UI) and mail fetch
|
||||
- File name, including wildcards e.g. \*.pdf will apply to all pdfs
|
||||
- File name, including wildcards e.g. \*.pdf will apply to all pdfs.
|
||||
- File path, including wildcards. Note that enabling `PAPERLESS_CONSUMER_RECURSIVE` would allow, for
|
||||
example, automatically assigning documents to different owners based on the upload directory.
|
||||
- Mail rule. Choosing this option will force 'mail fetch' to be the workflow source.
|
||||
- Content matching (`Added` and `Updated` triggers only). Filter document content using the matching settings.
|
||||
- Tags (`Added` and `Updated` triggers only). Filter for documents with any of the specified tags
|
||||
- Document type (`Added` and `Updated` triggers only). Filter documents with this doc type
|
||||
- Correspondent (`Added` and `Updated` triggers only). Filter documents with this correspondent
|
||||
- Content matching (`Added`, `Updated` and `Scheduled` triggers only). Filter document content using the matching settings.
|
||||
|
||||
There are also 'advanced' filters available for `Added`, `Updated` and `Scheduled` triggers:
|
||||
|
||||
- Any Tags: Filter for documents with any of the specified tags.
|
||||
- All Tags: Filter for documents with all of the specified tags.
|
||||
- No Tags: Filter for documents with none of the specified tags.
|
||||
- Document type: Filter documents with this document type.
|
||||
- Not Document types: Filter documents without any of these document types.
|
||||
- Correspondent: Filter documents with this correspondent.
|
||||
- Not Correspondents: Filter documents without any of these correspondents.
|
||||
- Storage path: Filter documents with this storage path.
|
||||
- Not Storage paths: Filter documents without any of these storage paths.
|
||||
- Custom field query: Filter documents with a custom field query (the same as used for the document list filters).
|
||||
|
||||
### Workflow Actions
|
||||
|
||||
@@ -496,37 +541,59 @@ The following workflow action types are available:
|
||||
- Encoding for the request body, either JSON or form data
|
||||
- The request headers as key-value pairs
|
||||
|
||||
For security reasons, webhooks can be limited to specific ports and disallowed from connecting to local URLs. See the relevant
|
||||
[configuration settings](configuration.md#workflow-webhooks) to change this behavior. If you are allowing non-admins to create workflows,
|
||||
you may want to adjust these settings to prevent abuse.
|
||||
|
||||
#### Workflow placeholders
|
||||
|
||||
Some workflow text can include placeholders but the available options differ depending on the type of
|
||||
workflow trigger. This is because at the time of consumption (when the text is to be set), no automatic tags etc. have been
|
||||
applied. You can use the following placeholders with any trigger type:
|
||||
Titles can be assigned by workflows using [Jinja templates](https://jinja.palletsprojects.com/en/3.1.x/templates/).
|
||||
This allows for complex logic to be used to generate the title, including [logical structures](https://jinja.palletsprojects.com/en/3.1.x/templates/#list-of-control-structures)
|
||||
and [filters](https://jinja.palletsprojects.com/en/3.1.x/templates/#id11).
|
||||
The template is provided as a string.
|
||||
|
||||
- `{correspondent}`: assigned correspondent name
|
||||
- `{document_type}`: assigned document type name
|
||||
- `{owner_username}`: assigned owner username
|
||||
- `{added}`: added datetime
|
||||
- `{added_year}`: added year
|
||||
- `{added_year_short}`: added year
|
||||
- `{added_month}`: added month
|
||||
- `{added_month_name}`: added month name
|
||||
- `{added_month_name_short}`: added month short name
|
||||
- `{added_day}`: added day
|
||||
- `{added_time}`: added time in HH:MM format
|
||||
- `{original_filename}`: original file name without extension
|
||||
- `{filename}`: current file name without extension
|
||||
Using Jinja2 Templates is also useful for [Date localization](advanced_usage.md#Date-Localization) in the title.
|
||||
|
||||
The available inputs differ depending on the type of workflow trigger.
|
||||
This is because at the time of consumption (when the text is to be set), no automatic tags etc. have been
|
||||
applied. You can use the following placeholders in the template with any trigger type:
|
||||
|
||||
- `{{correspondent}}`: assigned correspondent name
|
||||
- `{{document_type}}`: assigned document type name
|
||||
- `{{owner_username}}`: assigned owner username
|
||||
- `{{added}}`: added datetime
|
||||
- `{{added_year}}`: added year
|
||||
- `{{added_year_short}}`: added year
|
||||
- `{{added_month}}`: added month
|
||||
- `{{added_month_name}}`: added month name
|
||||
- `{{added_month_name_short}}`: added month short name
|
||||
- `{{added_day}}`: added day
|
||||
- `{{added_time}}`: added time in HH:MM format
|
||||
- `{{original_filename}}`: original file name without extension
|
||||
- `{{filename}}`: current file name without extension
|
||||
- `{{doc_title}}`: current document title
|
||||
|
||||
The following placeholders are only available for "added" or "updated" triggers
|
||||
|
||||
- `{created}`: created datetime
|
||||
- `{created_year}`: created year
|
||||
- `{created_year_short}`: created year
|
||||
- `{created_month}`: created month
|
||||
- `{created_month_name}`: created month name
|
||||
- `{created_month_name_short}`: created month short name
|
||||
- `{created_day}`: created day
|
||||
- `{created_time}`: created time in HH:MM format
|
||||
- `{doc_url}`: URL to the document in the web UI. Requires the `PAPERLESS_URL` setting to be set.
|
||||
- `{{created}}`: created datetime
|
||||
- `{{created_year}}`: created year
|
||||
- `{{created_year_short}}`: created year
|
||||
- `{{created_month}}`: created month
|
||||
- `{{created_month_name}}`: created month name
|
||||
- `{created_month_name_short}}`: created month short name
|
||||
- `{{created_day}}`: created day
|
||||
- `{{created_time}}`: created time in HH:MM format
|
||||
- `{{doc_url}}`: URL to the document in the web UI. Requires the `PAPERLESS_URL` setting to be set.
|
||||
|
||||
##### Examples
|
||||
|
||||
```jinja2
|
||||
{{ created | localize_date('MMMM', 'en_US') }}
|
||||
<!-- Output: "January" -->
|
||||
|
||||
{{ added | localize_date('MMMM', 'de_DE') }}
|
||||
<!-- Output: "Juni" --> # codespell:ignore
|
||||
```
|
||||
|
||||
### Workflow permissions
|
||||
|
||||
@@ -573,12 +640,14 @@ The following custom field types are supported:
|
||||
|
||||
## PDF Actions
|
||||
|
||||
Paperless-ngx supports four basic editing operations for PDFs (these operations currently cannot be performed on non-PDF files):
|
||||
Paperless-ngx supports basic editing operations for PDFs (these operations currently cannot be performed on non-PDF files). When viewing an individual document you can
|
||||
open the 'PDF Editor' to use a simple UI for re-arranging, rotating, deleting pages and splitting documents.
|
||||
|
||||
- Merging documents: available when selecting multiple documents for 'bulk editing'.
|
||||
- Rotating documents: available when selecting multiple documents for 'bulk editing' and from an individual document's details page.
|
||||
- Splitting documents: available from an individual document's details page.
|
||||
- Deleting pages: available from an individual document's details page.
|
||||
- Rotating documents: available when selecting multiple documents for 'bulk editing' and via the pdf editor on an individual document's details page.
|
||||
- Splitting documents: via the pdf editor on an individual document's details page.
|
||||
- Deleting pages: via the pdf editor on an individual document's details page.
|
||||
- Re-arranging pages: via the pdf editor on an individual document's details page.
|
||||
|
||||
!!! important
|
||||
|
||||
@@ -596,7 +665,7 @@ When you first delete a document it is moved to the 'trash' until either it is e
|
||||
You can set how long documents remain in the trash before being automatically deleted with [`PAPERLESS_EMPTY_TRASH_DELAY`](configuration.md#PAPERLESS_EMPTY_TRASH_DELAY), which defaults
|
||||
to 30 days. Until the file is actually deleted (e.g. the trash is emptied), all files and database content remains intact and can be restored at any point up until that time.
|
||||
|
||||
Additionally you may configure a directory where deleted files are moved to when they the trash is emptied with [`PAPERLESS_EMPTY_TRASH_DIR`](configuration.md#PAPERLESS_EMPTY_TRASH_DIR).
|
||||
Additionally you may configure a directory where deleted files are moved to when the trash is emptied with [`PAPERLESS_EMPTY_TRASH_DIR`](configuration.md#PAPERLESS_EMPTY_TRASH_DIR).
|
||||
Note that the empty trash directory only stores the original file, the archive file and all database information is permanently removed once a document is fully deleted.
|
||||
|
||||
## Best practices {#basic-searching}
|
||||
|
||||
@@ -52,12 +52,12 @@ if ! command -v wget &> /dev/null ; then
|
||||
fi
|
||||
|
||||
if ! command -v docker &> /dev/null ; then
|
||||
echo "docker executable not found. Is docker installed?"
|
||||
echo "docker executable not found. Is Docker installed?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! docker compose &> /dev/null ; then
|
||||
echo "docker compose plugin not found. Is docker compose installed?"
|
||||
echo "docker compose plugin not found. Is Docker Compose installed?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -66,7 +66,7 @@ fi
|
||||
if ! docker stats --no-stream &> /dev/null ; 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 (may require restarting shell)."
|
||||
echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user (may require restarting the shell)."
|
||||
echo ""
|
||||
sleep 3
|
||||
fi
|
||||
@@ -135,7 +135,7 @@ DATABASE_BACKEND=$ask_result
|
||||
|
||||
echo ""
|
||||
echo "Paperless is able to use Apache Tika to support Office documents such as"
|
||||
echo "Word, Excel, Powerpoint, and Libreoffice equivalents. This feature"
|
||||
echo "Word, Excel, PowerPoint, and LibreOffice equivalents. This feature"
|
||||
echo "requires more resources due to the required services."
|
||||
echo ""
|
||||
|
||||
@@ -157,7 +157,7 @@ echo ""
|
||||
echo "Specify the user id and group id you wish to run paperless as."
|
||||
echo "Paperless will also change ownership on the data, media and consume"
|
||||
echo "folder to the specified values, so it's a good idea to supply the user id"
|
||||
echo "and group id of your unix user account."
|
||||
echo "and group id of your Unix user account."
|
||||
echo "If unsure, leave default."
|
||||
echo ""
|
||||
|
||||
@@ -212,7 +212,7 @@ if [[ "$DATABASE_BACKEND" == "sqlite" ]] ; then
|
||||
echo -n "SQLite database, the "
|
||||
fi
|
||||
echo "search index and other data."
|
||||
echo "As with the media folder, leave empty to have this managed by docker."
|
||||
echo "As with the media folder, leave empty to have this managed by Docker."
|
||||
echo ""
|
||||
echo "CAUTION: If specified, you must specify an absolute path starting with /"
|
||||
echo "or a relative path starting with ./ here."
|
||||
@@ -224,7 +224,7 @@ DATA_FOLDER=$ask_result
|
||||
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
|
||||
echo ""
|
||||
echo "The database folder, where your database stores its data."
|
||||
echo "Leave empty to have this managed by docker."
|
||||
echo "Leave empty to have this managed by Docker."
|
||||
echo ""
|
||||
echo "CAUTION: If specified, you must specify an absolute path starting with /"
|
||||
echo "or a relative path starting with ./ here."
|
||||
@@ -276,18 +276,18 @@ echo ""
|
||||
echo "Target folder: $TARGET_FOLDER"
|
||||
echo "Consume folder: $CONSUME_FOLDER"
|
||||
if [[ -z $MEDIA_FOLDER ]] ; then
|
||||
echo "Media folder: Managed by docker"
|
||||
echo "Media folder: Managed by Docker"
|
||||
else
|
||||
echo "Media folder: $MEDIA_FOLDER"
|
||||
fi
|
||||
if [[ -z $DATA_FOLDER ]] ; then
|
||||
echo "Data folder: Managed by docker"
|
||||
echo "Data folder: Managed by Docker"
|
||||
else
|
||||
echo "Data folder: $DATA_FOLDER"
|
||||
fi
|
||||
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
|
||||
if [[ -z $DATABASE_FOLDER ]] ; then
|
||||
echo "Database folder: Managed by docker"
|
||||
echo "Database folder: Managed by Docker"
|
||||
else
|
||||
echo "Database folder: $DATABASE_FOLDER"
|
||||
fi
|
||||
@@ -374,7 +374,7 @@ fi
|
||||
# of the provided folder
|
||||
if [[ -n $DATABASE_FOLDER ]] ; then
|
||||
if [[ "$DATABASE_BACKEND" == "postgres" ]] ; then
|
||||
sed -i "s#- pgdata:/var/lib/postgresql/data#- $DATABASE_FOLDER:/var/lib/postgresql/data#g" docker-compose.yml
|
||||
sed -i "s#- pgdata:/var/lib/postgresql#- $DATABASE_FOLDER:/var/lib/postgresql#g" docker-compose.yml
|
||||
sed -i "/^\s*pgdata:/d" docker-compose.yml
|
||||
elif [[ "$DATABASE_BACKEND" == "mariadb" ]]; then
|
||||
sed -i "s#- dbdata:/var/lib/mysql#- $DATABASE_FOLDER:/var/lib/mysql#g" docker-compose.yml
|
||||
|
||||
85
mkdocs.yml
85
mkdocs.yml
@@ -1,85 +0,0 @@
|
||||
site_name: Paperless-ngx
|
||||
theme:
|
||||
name: material
|
||||
logo: assets/logo.svg
|
||||
font:
|
||||
text: Roboto
|
||||
code: Roboto Mono
|
||||
palette:
|
||||
# Palette toggle for automatic mode
|
||||
- media: "(prefers-color-scheme)"
|
||||
toggle:
|
||||
icon: material/brightness-auto
|
||||
name: Switch to light mode
|
||||
# Palette toggle for light mode
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
scheme: default
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
# Palette toggle for dark mode
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
scheme: slate
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to system preference
|
||||
features:
|
||||
- navigation.tabs
|
||||
- navigation.top
|
||||
- toc.integrate
|
||||
- content.code.annotate
|
||||
icon:
|
||||
repo: fontawesome/brands/github
|
||||
favicon: assets/favicon.png
|
||||
repo_url: https://github.com/paperless-ngx/paperless-ngx
|
||||
repo_name: paperless-ngx/paperless-ngx
|
||||
edit_uri: blob/main/docs/
|
||||
extra_css:
|
||||
- assets/extra.css
|
||||
markdown_extensions:
|
||||
- attr_list
|
||||
- md_in_html
|
||||
- def_list
|
||||
- admonition
|
||||
- tables
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
- pymdownx.superfences
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- footnotes
|
||||
- pymdownx.superfences:
|
||||
custom_fences:
|
||||
- name: mermaid
|
||||
class: mermaid
|
||||
format: !!python/name:pymdownx.superfences.fence_code_format
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
||||
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
||||
strict: true
|
||||
nav:
|
||||
- index.md
|
||||
- setup.md
|
||||
- 'Basic Usage': usage.md
|
||||
- configuration.md
|
||||
- administration.md
|
||||
- advanced_usage.md
|
||||
- 'REST API': api.md
|
||||
- development.md
|
||||
- 'FAQs': faq.md
|
||||
- troubleshooting.md
|
||||
- changelog.md
|
||||
copyright: Copyright © 2016 - 2023 Daniel Quinn, Jonas Winkler, and the Paperless-ngx team
|
||||
extra:
|
||||
social:
|
||||
- icon: fontawesome/brands/github
|
||||
link: https://github.com/paperless-ngx/paperless-ngx
|
||||
- icon: fontawesome/brands/docker
|
||||
link: https://hub.docker.com/r/paperlessngx/paperless-ngx
|
||||
- icon: material/chat
|
||||
link: https://matrix.to/#/#paperless:matrix.org
|
||||
plugins:
|
||||
- search
|
||||
- glightbox:
|
||||
skip_classes:
|
||||
- no-lightbox
|
||||
@@ -1,10 +1,6 @@
|
||||
# Have a look at the docs for documentation.
|
||||
# https://docs.paperless-ngx.com/configuration/
|
||||
|
||||
# Debug. Only enable this for development.
|
||||
|
||||
#PAPERLESS_DEBUG=false
|
||||
|
||||
# Required services
|
||||
|
||||
#PAPERLESS_REDIS=redis://localhost:6379
|
||||
|
||||
151
pyproject.toml
151
pyproject.toml
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "paperless-ngx"
|
||||
version = "2.16.3"
|
||||
description = "A community-supported supercharged version of paperless: scan, index and archive all your physical documents"
|
||||
version = "2.20.9"
|
||||
description = "A community-supported supercharged document management system: scan, index and archive all your physical documents"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
classifiers = [
|
||||
@@ -10,12 +10,14 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
]
|
||||
# TODO: Move certain things to groups and then utilize that further
|
||||
# This will allow testing to not install a webserver, mysql, etc
|
||||
|
||||
dependencies = [
|
||||
"bleach~=6.2.0",
|
||||
"babel>=2.17",
|
||||
"bleach~=6.3.0",
|
||||
"celery[redis]~=5.5.1",
|
||||
"channels~=4.2",
|
||||
"channels-redis~=4.2",
|
||||
@@ -23,45 +25,48 @@ dependencies = [
|
||||
"dateparser~=1.2",
|
||||
# WARNING: django does not use semver.
|
||||
# Only patch versions are guaranteed to not introduce breaking changes.
|
||||
"django~=5.1.7",
|
||||
"django-allauth[socialaccount,mfa]~=65.4.0",
|
||||
"django-auditlog~=3.1.2",
|
||||
"django~=5.2.5",
|
||||
"django-allauth[mfa,socialaccount]~=65.12.1",
|
||||
"django-auditlog~=3.3.0",
|
||||
"django-cachalot~=2.8.0",
|
||||
"django-celery-results~=2.6.0",
|
||||
"django-compression-middleware~=0.5.0",
|
||||
"django-cors-headers~=4.7.0",
|
||||
"django-cors-headers~=4.9.0",
|
||||
"django-extensions~=4.1",
|
||||
"django-filter~=25.1",
|
||||
"django-guardian~=2.4.0",
|
||||
"django-multiselectfield~=0.1.13",
|
||||
"django-guardian~=3.2.0",
|
||||
"django-multiselectfield~=1.0.1",
|
||||
"django-soft-delete~=1.0.18",
|
||||
"djangorestframework~=3.15",
|
||||
"djangorestframework-guardian~=0.3.0",
|
||||
"django-treenode>=0.23.2",
|
||||
"djangorestframework~=3.16",
|
||||
"djangorestframework-guardian~=0.4.0",
|
||||
"drf-spectacular~=0.28",
|
||||
"drf-spectacular-sidecar~=2025.4.1",
|
||||
"drf-spectacular-sidecar~=2025.10.1",
|
||||
"drf-writable-nested~=0.7.1",
|
||||
"filelock~=3.18.0",
|
||||
"filelock~=3.20.0",
|
||||
"flower~=2.0.1",
|
||||
"gotenberg-client~=0.10.0",
|
||||
"gotenberg-client~=0.12.0",
|
||||
"httpx-oauth~=0.16",
|
||||
"imap-tools~=1.10.0",
|
||||
"imap-tools~=1.11.0",
|
||||
"inotifyrecursive~=0.3",
|
||||
"jinja2~=3.1.5",
|
||||
"langdetect~=1.0.9",
|
||||
"nltk~=3.9.1",
|
||||
"ocrmypdf~=16.10.0",
|
||||
"pathvalidate~=3.2.3",
|
||||
"ocrmypdf~=16.12.0",
|
||||
"pathvalidate~=3.3.1",
|
||||
"pdf2image~=1.17.0",
|
||||
"python-dateutil~=2.9.0",
|
||||
"python-dotenv~=1.1.0",
|
||||
"python-dotenv~=1.2.1",
|
||||
"python-gnupg~=0.5.4",
|
||||
"python-ipware~=3.0.0",
|
||||
"python-magic~=0.4.27",
|
||||
"pyzbar~=0.1.9",
|
||||
"rapidfuzz~=3.13.0",
|
||||
"rapidfuzz~=3.14.0",
|
||||
"redis[hiredis]~=5.2.1",
|
||||
"scikit-learn~=1.6.1",
|
||||
"regex>=2025.9.18",
|
||||
"scikit-learn~=1.7.0",
|
||||
"setproctitle~=1.3.4",
|
||||
"tika-client~=0.9.0",
|
||||
"tika-client~=0.10.0",
|
||||
"tqdm~=4.67.1",
|
||||
"watchdog~=6.0",
|
||||
"whitenoise~=6.9",
|
||||
@@ -73,12 +78,13 @@ optional-dependencies.mariadb = [
|
||||
"mysqlclient~=2.2.7",
|
||||
]
|
||||
optional-dependencies.postgres = [
|
||||
"psycopg[c]==3.2.5",
|
||||
"psycopg[c,pool]==3.2.12",
|
||||
# Direct dependency for proper resolution of the pre-built wheels
|
||||
"psycopg-c==3.2.5",
|
||||
"psycopg-c==3.2.12",
|
||||
"psycopg-pool==3.2.7",
|
||||
]
|
||||
optional-dependencies.webserver = [
|
||||
"granian[uvloop]~=2.3.2",
|
||||
"granian[uvloop]~=2.5.1",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
@@ -90,17 +96,16 @@ dev = [
|
||||
]
|
||||
|
||||
docs = [
|
||||
"mkdocs-glightbox~=0.4.0",
|
||||
"mkdocs-material~=9.6.4",
|
||||
"zensical>=0.0.21",
|
||||
]
|
||||
|
||||
testing = [
|
||||
"daphne",
|
||||
"factory-boy~=3.3.1",
|
||||
"imagehash",
|
||||
"pytest~=8.3.3",
|
||||
"pytest-cov~=6.0.0",
|
||||
"pytest-django~=4.10.0",
|
||||
"pytest~=8.4.1",
|
||||
"pytest-cov~=7.0.0",
|
||||
"pytest-django~=4.11.1",
|
||||
"pytest-env",
|
||||
"pytest-httpx",
|
||||
"pytest-mock",
|
||||
@@ -110,9 +115,9 @@ testing = [
|
||||
]
|
||||
|
||||
lint = [
|
||||
"pre-commit~=4.1.0",
|
||||
"pre-commit-uv~=4.1.3",
|
||||
"ruff~=0.9.9",
|
||||
"pre-commit~=4.4.0",
|
||||
"pre-commit-uv~=4.2.0",
|
||||
"ruff~=0.14.0",
|
||||
]
|
||||
|
||||
typing = [
|
||||
@@ -120,6 +125,7 @@ typing = [
|
||||
"django-filter-stubs",
|
||||
"django-stubs[compatible-mypy]",
|
||||
"djangorestframework-stubs[compatible-mypy]",
|
||||
"lxml-stubs",
|
||||
"mypy",
|
||||
"types-bleach",
|
||||
"types-colorama",
|
||||
@@ -127,11 +133,31 @@ typing = [
|
||||
"types-markdown",
|
||||
"types-pygments",
|
||||
"types-python-dateutil",
|
||||
"types-pytz",
|
||||
"types-redis",
|
||||
"types-setuptools",
|
||||
"types-tqdm",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
required-version = ">=0.5.14"
|
||||
package = false
|
||||
environments = [
|
||||
"sys_platform == 'darwin'",
|
||||
"sys_platform == 'linux'",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
# Markers are chosen to select these almost exclusively when building the Docker image
|
||||
psycopg-c = [
|
||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-bookworm-3.2.12/psycopg_c-3.2.12-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-bookworm-3.2.12/psycopg_c-3.2.12-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
||||
]
|
||||
zxing-cpp = [
|
||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py310"
|
||||
line-length = 88
|
||||
@@ -172,6 +198,7 @@ lint.extend-select = [
|
||||
]
|
||||
lint.ignore = [
|
||||
"DJ001",
|
||||
"PLC0415",
|
||||
"RUF012",
|
||||
"SIM105",
|
||||
]
|
||||
@@ -200,45 +227,19 @@ lint.per-file-ignores."docker/wait-for-redis.py" = [
|
||||
"INP001",
|
||||
"T201",
|
||||
]
|
||||
lint.per-file-ignores."src/documents/file_handling.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/documents/management/commands/document_consumer.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/documents/management/commands/document_exporter.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/documents/migrations/1012_fix_archive_files.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/documents/models.py" = [
|
||||
"SIM115",
|
||||
]
|
||||
lint.per-file-ignores."src/documents/parsers.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/documents/signals/handlers.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/documents/views.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/paperless/checks.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/paperless/settings.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/paperless_mail/mail.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [
|
||||
"PTH",
|
||||
"RUF001",
|
||||
] # TODO PTH Enable & remove
|
||||
]
|
||||
lint.isort.force-single-line = true
|
||||
|
||||
[tool.codespell]
|
||||
write-changes = true
|
||||
ignore-words-list = "criterias,afterall,valeu,ureue,equest,ure,assertIn,Oktober,commitish"
|
||||
skip = "src-ui/src/locale/*,src-ui/pnpm-lock.yaml,src-ui/e2e/*,src/paperless_mail/tests/samples/*,src/documents/tests/samples/*,*.po,*.json"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
minversion = "8.0"
|
||||
pythonpath = [
|
||||
@@ -250,6 +251,7 @@ testpaths = [
|
||||
"src/paperless_mail/tests/",
|
||||
"src/paperless_tesseract/tests/",
|
||||
"src/paperless_tika/tests",
|
||||
"src/paperless_text/tests/",
|
||||
]
|
||||
addopts = [
|
||||
"--pythonwarnings=all",
|
||||
@@ -290,10 +292,10 @@ exclude_also = [
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
mypy_path = "src"
|
||||
plugins = [
|
||||
"mypy_django_plugin.main",
|
||||
"mypy_drf_plugin.main",
|
||||
"numpy.typing.mypy_plugin",
|
||||
]
|
||||
check_untyped_defs = true
|
||||
disallow_any_generics = true
|
||||
@@ -302,24 +304,5 @@ disallow_untyped_defs = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
|
||||
[tool.uv]
|
||||
required-version = ">=0.5.14"
|
||||
package = false
|
||||
environments = [
|
||||
"sys_platform == 'darwin'",
|
||||
"sys_platform == 'linux'",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
# Markers are chosen to select these almost exclusively when building the Docker image
|
||||
psycopg-c = [
|
||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
||||
]
|
||||
zxing-cpp = [
|
||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
||||
]
|
||||
|
||||
[tool.django-stubs]
|
||||
django_settings_module = "paperless.settings"
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"sv-SE": "src/locale/messages.sv_SE.xlf",
|
||||
"tr-TR": "src/locale/messages.tr_TR.xlf",
|
||||
"uk-UA": "src/locale/messages.uk_UA.xlf",
|
||||
"vi-VN": "src/locale/messages.vi_VN.xlf",
|
||||
"zh-CN": "src/locale/messages.zh_CN.xlf",
|
||||
"zh-TW": "src/locale/messages.zh_TW.xlf"
|
||||
}
|
||||
@@ -60,10 +61,12 @@
|
||||
"path": "./extra-webpack.config.ts"
|
||||
},
|
||||
"outputPath": "dist/paperless-ui",
|
||||
"main": "src/main.ts",
|
||||
"outputHashing": "none",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"polyfills": [
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"localize": true,
|
||||
"assets": [
|
||||
@@ -86,12 +89,15 @@
|
||||
"file-saver",
|
||||
"utif"
|
||||
],
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"buildOptimizer": false,
|
||||
"sourceMap": true,
|
||||
"optimization": false,
|
||||
"namedChunks": true
|
||||
"namedChunks": true,
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": [
|
||||
"."
|
||||
]
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
@@ -107,8 +113,6 @@
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
@@ -188,6 +192,30 @@
|
||||
},
|
||||
"@angular-eslint/schematics:library": {
|
||||
"setParserOptionsProject": true
|
||||
},
|
||||
"@schematics/angular:component": {
|
||||
"type": "component"
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"type": "directive"
|
||||
},
|
||||
"@schematics/angular:service": {
|
||||
"type": "service"
|
||||
},
|
||||
"@schematics/angular:guard": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:interceptor": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:module": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:pipe": {
|
||||
"typeSeparator": "."
|
||||
},
|
||||
"@schematics/angular:resolver": {
|
||||
"typeSeparator": "."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ test('bulk edit', async ({ page }) => {
|
||||
await expect(page.locator('pngx-document-list')).toHaveText(
|
||||
/Selected 61 of 61 documents/i
|
||||
)
|
||||
await page.getByRole('button', { name: 'Cancel' }).click()
|
||||
await page.getByRole('button', { name: 'None' }).click()
|
||||
|
||||
await page.locator('pngx-document-card-small').nth(1).click()
|
||||
await page.locator('pngx-document-card-small').nth(2).click()
|
||||
|
||||
@@ -8,7 +8,7 @@ module.exports = {
|
||||
'abstract-paperless-service',
|
||||
],
|
||||
transformIgnorePatterns: [
|
||||
`<rootDir>/node_modules/.pnpm/(?!.*\\.mjs$|lodash-es)`,
|
||||
`<rootDir>/node_modules/.pnpm/(?!.*\\.mjs$|lodash-es|@angular\\+common.*locales)`,
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'^src/(.*)': '<rootDir>/src/$1',
|
||||
|
||||
2564
src-ui/messages.xlf
2564
src-ui/messages.xlf
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "paperless-ngx-ui",
|
||||
"version": "2.16.3",
|
||||
"version": "2.20.9",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"ng": "ng",
|
||||
@@ -11,64 +11,66 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/cdk": "^19.2.14",
|
||||
"@angular/common": "~19.2.13",
|
||||
"@angular/compiler": "~19.2.14",
|
||||
"@angular/core": "~19.2.14",
|
||||
"@angular/forms": "~19.2.14",
|
||||
"@angular/localize": "~19.2.14",
|
||||
"@angular/platform-browser": "~19.2.14",
|
||||
"@angular/platform-browser-dynamic": "~19.2.14",
|
||||
"@angular/router": "~19.2.14",
|
||||
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
|
||||
"@ng-select/ng-select": "^14.9.0",
|
||||
"@angular/cdk": "^20.2.13",
|
||||
"@angular/common": "~20.3.15",
|
||||
"@angular/compiler": "~20.3.15",
|
||||
"@angular/core": "~20.3.15",
|
||||
"@angular/forms": "~20.3.15",
|
||||
"@angular/localize": "~20.3.15",
|
||||
"@angular/platform-browser": "~20.3.15",
|
||||
"@angular/platform-browser-dynamic": "~20.3.15",
|
||||
"@angular/router": "~20.3.15",
|
||||
"@ng-bootstrap/ng-bootstrap": "^19.0.1",
|
||||
"@ng-select/ng-select": "^20.7.0",
|
||||
"@ngneat/dirty-check-forms": "^3.0.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"bootstrap": "^5.3.6",
|
||||
"bootstrap": "^5.3.8",
|
||||
"file-saver": "^2.0.5",
|
||||
"mime-names": "^1.0.0",
|
||||
"ng2-pdf-viewer": "^10.4.0",
|
||||
"ngx-bootstrap-icons": "^1.9.3",
|
||||
"ngx-color": "^10.0.0",
|
||||
"ngx-cookie-service": "^19.1.2",
|
||||
"ngx-device-detector": "^9.0.0",
|
||||
"ngx-ui-tour-ng-bootstrap": "^16.0.0",
|
||||
"ngx-color": "^10.1.0",
|
||||
"ngx-cookie-service": "^20.1.1",
|
||||
"ngx-device-detector": "^10.1.0",
|
||||
"ngx-ui-tour-ng-bootstrap": "^17.0.1",
|
||||
"rxjs": "^7.8.2",
|
||||
"tslib": "^2.8.1",
|
||||
"utif": "^3.1.0",
|
||||
"uuid": "^11.1.0",
|
||||
"uuid": "^13.0.0",
|
||||
"zone.js": "^0.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "^19.0.1",
|
||||
"@angular-builders/jest": "^19.0.1",
|
||||
"@angular-devkit/build-angular": "^19.2.14",
|
||||
"@angular-devkit/core": "^19.2.14",
|
||||
"@angular-devkit/schematics": "^19.2.14",
|
||||
"@angular-eslint/builder": "19.7.0",
|
||||
"@angular-eslint/eslint-plugin": "19.7.0",
|
||||
"@angular-eslint/eslint-plugin-template": "19.7.0",
|
||||
"@angular-eslint/schematics": "19.7.0",
|
||||
"@angular-eslint/template-parser": "19.7.0",
|
||||
"@angular/cli": "~19.2.14",
|
||||
"@angular/compiler-cli": "~19.2.14",
|
||||
"@angular-builders/custom-webpack": "^20.0.0",
|
||||
"@angular-builders/jest": "^20.0.0",
|
||||
"@angular-devkit/core": "^20.3.13",
|
||||
"@angular-devkit/schematics": "^20.3.13",
|
||||
"@angular-eslint/builder": "20.6.0",
|
||||
"@angular-eslint/eslint-plugin": "20.6.0",
|
||||
"@angular-eslint/eslint-plugin-template": "20.6.0",
|
||||
"@angular-eslint/schematics": "20.6.0",
|
||||
"@angular-eslint/template-parser": "20.6.0",
|
||||
"@angular/build": "^20.3.13",
|
||||
"@angular/cli": "~20.3.13",
|
||||
"@angular/compiler-cli": "~20.3.15",
|
||||
"@codecov/webpack-plugin": "^1.9.1",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.15.29",
|
||||
"@typescript-eslint/eslint-plugin": "^8.33.0",
|
||||
"@typescript-eslint/parser": "^8.33.0",
|
||||
"@typescript-eslint/utils": "^8.33.0",
|
||||
"eslint": "^9.28.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"@playwright/test": "^1.57.0",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^24.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
||||
"@typescript-eslint/parser": "^8.48.1",
|
||||
"@typescript-eslint/utils": "^8.48.1",
|
||||
"eslint": "^9.39.1",
|
||||
"jest": "30.2.0",
|
||||
"jest-environment-jsdom": "^30.2.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"jest-preset-angular": "^14.5.5",
|
||||
"jest-preset-angular": "^15.0.3",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-organize-imports": "^4.3.0",
|
||||
"ts-node": "~10.9.1",
|
||||
"typescript": "^5.5.4"
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.103.0"
|
||||
},
|
||||
"packageManager": "pnpm@10.17.1",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@parcel/watcher",
|
||||
@@ -77,6 +79,5 @@
|
||||
"lmdb",
|
||||
"msgpackr-extract"
|
||||
]
|
||||
},
|
||||
"typings": "./src/typings.d.ts"
|
||||
}
|
||||
}
|
||||
|
||||
8587
src-ui/pnpm-lock.yaml
generated
8587
src-ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,16 @@
|
||||
import '@angular/localize/init'
|
||||
import { jest } from '@jest/globals'
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'
|
||||
import { TextDecoder, TextEncoder } from 'util'
|
||||
import { TextDecoder, TextEncoder } from 'node:util'
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
setupZoneTestEnv()
|
||||
}
|
||||
global.TextEncoder = TextEncoder
|
||||
global.TextDecoder = TextDecoder
|
||||
;(globalThis as any).TextEncoder = TextEncoder as unknown as {
|
||||
new (): TextEncoder
|
||||
}
|
||||
;(globalThis as any).TextDecoder = TextDecoder as unknown as {
|
||||
new (): TextDecoder
|
||||
}
|
||||
|
||||
import { registerLocaleData } from '@angular/common'
|
||||
import localeAf from '@angular/common/locales/af'
|
||||
@@ -40,6 +44,7 @@ import localeSr from '@angular/common/locales/sr'
|
||||
import localeSv from '@angular/common/locales/sv'
|
||||
import localeTr from '@angular/common/locales/tr'
|
||||
import localeUk from '@angular/common/locales/uk'
|
||||
import localeVi from '@angular/common/locales/vi'
|
||||
import localeZh from '@angular/common/locales/zh'
|
||||
import localeZhHant from '@angular/common/locales/zh-Hant'
|
||||
|
||||
@@ -75,6 +80,7 @@ registerLocaleData(localeSr)
|
||||
registerLocaleData(localeSv)
|
||||
registerLocaleData(localeTr)
|
||||
registerLocaleData(localeUk)
|
||||
registerLocaleData(localeVi)
|
||||
registerLocaleData(localeZh)
|
||||
registerLocaleData(localeZhHant)
|
||||
|
||||
@@ -114,13 +120,43 @@ if (!URL.revokeObjectURL) {
|
||||
Object.defineProperty(window.URL, 'revokeObjectURL', { value: jest.fn() })
|
||||
}
|
||||
Object.defineProperty(window, 'ResizeObserver', { value: mock() })
|
||||
Object.defineProperty(window, 'location', {
|
||||
configurable: true,
|
||||
value: { reload: jest.fn() },
|
||||
})
|
||||
|
||||
if (typeof IntersectionObserver === 'undefined') {
|
||||
class MockIntersectionObserver {
|
||||
constructor(
|
||||
public callback: IntersectionObserverCallback,
|
||||
public options?: IntersectionObserverInit
|
||||
) {}
|
||||
|
||||
observe = jest.fn()
|
||||
unobserve = jest.fn()
|
||||
disconnect = jest.fn()
|
||||
takeRecords = jest.fn()
|
||||
}
|
||||
|
||||
Object.defineProperty(window, 'IntersectionObserver', {
|
||||
writable: true,
|
||||
configurable: true,
|
||||
value: MockIntersectionObserver,
|
||||
})
|
||||
}
|
||||
|
||||
HTMLCanvasElement.prototype.getContext = <
|
||||
typeof HTMLCanvasElement.prototype.getContext
|
||||
>jest.fn()
|
||||
|
||||
if (!HTMLElement.prototype.scrollTo) {
|
||||
HTMLElement.prototype.scrollTo = jest.fn()
|
||||
}
|
||||
|
||||
jest.mock('uuid', () => ({
|
||||
v4: jest.fn(() =>
|
||||
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char: string) => {
|
||||
const random = Math.floor(Math.random() * 16)
|
||||
const value = char === 'x' ? random : (random & 0x3) | 0x8
|
||||
return value.toString(16)
|
||||
})
|
||||
),
|
||||
}))
|
||||
|
||||
jest.mock('pdfjs-dist')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnDestroy, OnInit, Renderer2 } from '@angular/core'
|
||||
import { Component, inject, OnDestroy, OnInit, Renderer2 } from '@angular/core'
|
||||
import { Router, RouterOutlet } from '@angular/router'
|
||||
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||
import { first, Subscription } from 'rxjs'
|
||||
@@ -29,22 +29,22 @@ import { WebsocketStatusService } from './services/websocket-status.service'
|
||||
],
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
private settings = inject(SettingsService)
|
||||
private websocketStatusService = inject(WebsocketStatusService)
|
||||
private toastService = inject(ToastService)
|
||||
private router = inject(Router)
|
||||
private tasksService = inject(TasksService)
|
||||
tourService = inject(TourService)
|
||||
private renderer = inject(Renderer2)
|
||||
private permissionsService = inject(PermissionsService)
|
||||
private hotKeyService = inject(HotKeyService)
|
||||
private componentRouterService = inject(ComponentRouterService)
|
||||
|
||||
newDocumentSubscription: Subscription
|
||||
successSubscription: Subscription
|
||||
failedSubscription: Subscription
|
||||
|
||||
constructor(
|
||||
private settings: SettingsService,
|
||||
private websocketStatusService: WebsocketStatusService,
|
||||
private toastService: ToastService,
|
||||
private router: Router,
|
||||
private tasksService: TasksService,
|
||||
public tourService: TourService,
|
||||
private renderer: Renderer2,
|
||||
private permissionsService: PermissionsService,
|
||||
private hotKeyService: HotKeyService,
|
||||
private componentRouterService: ComponentRouterService
|
||||
) {
|
||||
constructor() {
|
||||
let anyWindow = window as any
|
||||
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.mjs'
|
||||
this.settings.updateAppearanceSettings()
|
||||
|
||||
@@ -19,13 +19,18 @@
|
||||
<div class="col">
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<div class="card-title">
|
||||
<h6>
|
||||
{{option.title}}
|
||||
<a class="btn btn-sm btn-link" title="Read the documentation about this setting" i18n-title [href]="getDocsUrl(option.config_key)" target="_blank" referrerpolicy="no-referrer">
|
||||
<i-bs name="info-circle"></i-bs>
|
||||
</a>
|
||||
<div class="card-title d-flex align-items-center">
|
||||
<h6 class="mb-0">
|
||||
{{option.title}}
|
||||
</h6>
|
||||
<a class="btn btn-sm btn-link" title="Read the documentation about this setting" i18n-title [href]="getDocsUrl(option.config_key)" target="_blank" referrerpolicy="no-referrer">
|
||||
<i-bs name="info-circle"></i-bs>
|
||||
</a>
|
||||
@if (isSet(option.key)) {
|
||||
<button type="button" class="btn btn-sm btn-link text-danger ms-auto pe-0" title="Reset" i18n-title (click)="resetOption(option.key)">
|
||||
<i-bs class="me-1" name="x"></i-bs><ng-container i18n>Reset</ng-container>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<div class="mb-n3">
|
||||
@switch (option.type) {
|
||||
@@ -50,7 +55,7 @@
|
||||
<div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div>
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group me-2">
|
||||
<button type="button" (click)="discardChanges()" class="btn btn-secondary" [disabled]="loading || (isDirty$ | async) === false" i18n>Discard</button>
|
||||
<button type="button" (click)="discardChanges()" class="btn btn-outline-secondary" [disabled]="loading || (isDirty$ | async) === false" i18n>Discard</button>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button type="submit" class="btn btn-primary" [disabled]="loading || !configForm.valid || (isDirty$ | async) === false" i18n>Save</button>
|
||||
|
||||
@@ -144,4 +144,18 @@ describe('ConfigComponent', () => {
|
||||
component.uploadFile(new File([], 'test.png'), 'app_logo')
|
||||
expect(initSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should reset option to null', () => {
|
||||
component.configForm.patchValue({ output_type: OutputTypeConfig.PDF_A })
|
||||
expect(component.isSet('output_type')).toBeTruthy()
|
||||
component.resetOption('output_type')
|
||||
expect(component.configForm.get('output_type').value).toBeNull()
|
||||
expect(component.isSet('output_type')).toBeFalsy()
|
||||
component.configForm.patchValue({ app_title: 'Test Title' })
|
||||
component.resetOption('app_title')
|
||||
expect(component.configForm.get('app_title').value).toBeNull()
|
||||
component.configForm.patchValue({ barcodes_enabled: true })
|
||||
component.resetOption('barcodes_enabled')
|
||||
expect(component.configForm.get('barcodes_enabled').value).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AsyncPipe } from '@angular/common'
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
|
||||
import {
|
||||
AbstractControl,
|
||||
FormControl,
|
||||
@@ -57,6 +57,10 @@ export class ConfigComponent
|
||||
extends LoadingComponentWithPermissions
|
||||
implements OnInit, OnDestroy, DirtyComponent
|
||||
{
|
||||
private configService = inject(ConfigService)
|
||||
private toastService = inject(ToastService)
|
||||
private settingsService = inject(SettingsService)
|
||||
|
||||
public readonly ConfigOptionType = ConfigOptionType
|
||||
|
||||
// generated dynamically
|
||||
@@ -77,11 +81,7 @@ export class ConfigComponent
|
||||
storeSub: Subscription
|
||||
isDirty$: Observable<boolean>
|
||||
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
private toastService: ToastService,
|
||||
private settingsService: SettingsService
|
||||
) {
|
||||
constructor() {
|
||||
super()
|
||||
this.configForm.addControl('id', new FormControl())
|
||||
PaperlessConfigOptions.forEach((option) => {
|
||||
@@ -208,4 +208,12 @@ export class ConfigComponent
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
public isSet(key: string): boolean {
|
||||
return this.configForm.get(key).value != null
|
||||
}
|
||||
|
||||
public resetOption(key: string) {
|
||||
this.configForm.get(key).setValue(null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,23 @@
|
||||
i18n-title
|
||||
info="Review the log files for the application and for email checking."
|
||||
i18n-info>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" [(ngModel)]="autoRefreshEnabled">
|
||||
<label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label>
|
||||
<div class="input-group input-group-sm align-items-center">
|
||||
<div class="input-group input-group-sm me-3">
|
||||
<span class="input-group-text text-muted" i18n>Show</span>
|
||||
<input
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="100"
|
||||
step="100"
|
||||
[(ngModel)]="limit"
|
||||
(ngModelChange)="onLimitChange($event)"
|
||||
style="width: 100px;">
|
||||
<span class="input-group-text text-muted" i18n>lines</span>
|
||||
</div>
|
||||
<div class="form-check form-switch mt-1">
|
||||
<input class="form-check-input" type="checkbox" role="switch" [(ngModel)]="autoRefreshEnabled">
|
||||
<label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label>
|
||||
</div>
|
||||
</div>
|
||||
</pngx-page-header>
|
||||
|
||||
@@ -27,16 +41,23 @@
|
||||
}
|
||||
</ul>
|
||||
|
||||
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||
|
||||
<div class="bg-dark p-3 text-light font-monospace log-container" #logContainer>
|
||||
@if (loading && logFiles.length) {
|
||||
<div #logContainer class="bg-dark text-light font-monospace log-container p-3" (scroll)="onScroll()">
|
||||
@if (loading && !logFiles.length) {
|
||||
<div>
|
||||
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
|
||||
<ng-container i18n>Loading...</ng-container>
|
||||
</div>
|
||||
}
|
||||
@for (log of logs; track $index) {
|
||||
<p class="m-0 p-0 log-entry-{{getLogLevel(log)}}">{{log}}</p>
|
||||
} @else {
|
||||
@for (log of logs; track log) {
|
||||
<p class="m-0 p-0" [ngClass]="'log-entry-' + log.level">{{log.message}}</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-secondary jump-to-bottom position-fixed bottom-0 end-0 m-5"
|
||||
[class.visible]="showJumpToBottom"
|
||||
(click)="scrollToBottom()"
|
||||
>
|
||||
↓ <span i18n>Jump to bottom</span>
|
||||
</button>
|
||||
|
||||
@@ -16,11 +16,21 @@
|
||||
}
|
||||
|
||||
.log-container {
|
||||
overflow-y: scroll;
|
||||
height: calc(100vh - 200px);
|
||||
top: 70px;
|
||||
height: calc(100vh - 190px);
|
||||
overflow-y: auto;
|
||||
|
||||
p {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.jump-to-bottom {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 120ms ease-in-out;
|
||||
}
|
||||
|
||||
.jump-to-bottom.visible {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import {
|
||||
CdkVirtualScrollViewport,
|
||||
ScrollingModule,
|
||||
} from '@angular/cdk/scrolling'
|
||||
import { CommonModule } from '@angular/common'
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
@@ -38,6 +43,9 @@ describe('LogsComponent', () => {
|
||||
NgxBootstrapIconsModule.pick(allIcons),
|
||||
LogsComponent,
|
||||
PageHeaderComponent,
|
||||
CommonModule,
|
||||
CdkVirtualScrollViewport,
|
||||
ScrollingModule,
|
||||
],
|
||||
providers: [
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
@@ -54,13 +62,12 @@ describe('LogsComponent', () => {
|
||||
fixture = TestBed.createComponent(LogsComponent)
|
||||
component = fixture.componentInstance
|
||||
reloadSpy = jest.spyOn(component, 'reloadLogs')
|
||||
window.HTMLElement.prototype.scroll = function () {} // mock scroll
|
||||
jest.useFakeTimers()
|
||||
fixture.detectChanges()
|
||||
})
|
||||
|
||||
it('should display logs with first log initially', () => {
|
||||
expect(logSpy).toHaveBeenCalledWith('paperless')
|
||||
expect(logSpy).toHaveBeenCalledWith('paperless', 5000)
|
||||
fixture.detectChanges()
|
||||
expect(fixture.debugElement.nativeElement.textContent).toContain(
|
||||
paperless_logs[0]
|
||||
@@ -71,7 +78,7 @@ describe('LogsComponent', () => {
|
||||
fixture.debugElement
|
||||
.queryAll(By.directive(NgbNavLink))[1]
|
||||
.nativeElement.dispatchEvent(new MouseEvent('click'))
|
||||
expect(logSpy).toHaveBeenCalledWith('mail')
|
||||
expect(logSpy).toHaveBeenCalledWith('mail', 5000)
|
||||
})
|
||||
|
||||
it('should handle error with no logs', () => {
|
||||
@@ -83,6 +90,10 @@ describe('LogsComponent', () => {
|
||||
})
|
||||
|
||||
it('should auto refresh, allow toggle', () => {
|
||||
jest
|
||||
.spyOn(CdkVirtualScrollViewport.prototype, 'scrollToIndex')
|
||||
.mockImplementation(() => undefined)
|
||||
|
||||
jest.advanceTimersByTime(6000)
|
||||
expect(reloadSpy).toHaveBeenCalledTimes(2)
|
||||
|
||||
@@ -90,4 +101,20 @@ describe('LogsComponent', () => {
|
||||
jest.advanceTimersByTime(6000)
|
||||
expect(reloadSpy).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('should debounce limit changes before reloading logs', () => {
|
||||
const initialCalls = reloadSpy.mock.calls.length
|
||||
component.onLimitChange(6000)
|
||||
jest.advanceTimersByTime(299)
|
||||
expect(reloadSpy).toHaveBeenCalledTimes(initialCalls)
|
||||
jest.advanceTimersByTime(1)
|
||||
expect(reloadSpy).toHaveBeenCalledTimes(initialCalls + 1)
|
||||
})
|
||||
|
||||
it('should update jump to bottom visibility on scroll', () => {
|
||||
component.showJumpToBottom = false
|
||||
jest.spyOn(component as any, 'isNearBottom').mockReturnValue(false)
|
||||
component.onScroll()
|
||||
expect(component.showJumpToBottom).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { CommonModule } from '@angular/common'
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
@@ -5,10 +6,11 @@ import {
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
inject,
|
||||
} from '@angular/core'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { filter, takeUntil, timer } from 'rxjs'
|
||||
import { Subject, debounceTime, filter, takeUntil, timer } from 'rxjs'
|
||||
import { LogService } from 'src/app/services/rest/log.service'
|
||||
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
|
||||
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
|
||||
@@ -20,6 +22,7 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
|
||||
imports: [
|
||||
PageHeaderComponent,
|
||||
NgbNavModule,
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
@@ -28,14 +31,10 @@ export class LogsComponent
|
||||
extends LoadingComponentWithPermissions
|
||||
implements OnInit, OnDestroy
|
||||
{
|
||||
constructor(
|
||||
private logService: LogService,
|
||||
private changedetectorRef: ChangeDetectorRef
|
||||
) {
|
||||
super()
|
||||
}
|
||||
private logService = inject(LogService)
|
||||
private changedetectorRef = inject(ChangeDetectorRef)
|
||||
|
||||
public logs: string[] = []
|
||||
public logs: Array<{ message: string; level: number }> = []
|
||||
|
||||
public logFiles: string[] = []
|
||||
|
||||
@@ -43,9 +42,19 @@ export class LogsComponent
|
||||
|
||||
public autoRefreshEnabled: boolean = true
|
||||
|
||||
@ViewChild('logContainer') logContainer: ElementRef
|
||||
public limit: number = 5000
|
||||
|
||||
public showJumpToBottom = false
|
||||
|
||||
private readonly limitChange$ = new Subject<number>()
|
||||
|
||||
@ViewChild('logContainer') logContainer: ElementRef<HTMLElement>
|
||||
|
||||
ngOnInit(): void {
|
||||
this.limitChange$
|
||||
.pipe(debounceTime(300), takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe(() => this.reloadLogs())
|
||||
|
||||
this.logService
|
||||
.list()
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
@@ -71,16 +80,37 @@ export class LogsComponent
|
||||
super.ngOnDestroy()
|
||||
}
|
||||
|
||||
onLimitChange(limit: number): void {
|
||||
this.limitChange$.next(limit)
|
||||
}
|
||||
|
||||
reloadLogs() {
|
||||
this.loading = true
|
||||
const shouldStickToBottom = this.isNearBottom()
|
||||
this.logService
|
||||
.get(this.activeLog)
|
||||
.get(this.activeLog, this.limit)
|
||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
this.logs = result
|
||||
this.loading = false
|
||||
this.scrollToBottom()
|
||||
const parsed = this.parseLogsWithLevel(result)
|
||||
const hasChanges =
|
||||
parsed.length !== this.logs.length ||
|
||||
parsed.some((log, idx) => {
|
||||
const current = this.logs[idx]
|
||||
return (
|
||||
!current ||
|
||||
current.message !== log.message ||
|
||||
current.level !== log.level
|
||||
)
|
||||
})
|
||||
if (hasChanges) {
|
||||
this.logs = parsed
|
||||
if (shouldStickToBottom) {
|
||||
this.scrollToBottom()
|
||||
}
|
||||
this.showJumpToBottom = !shouldStickToBottom
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
this.logs = []
|
||||
@@ -103,12 +133,35 @@ export class LogsComponent
|
||||
}
|
||||
}
|
||||
|
||||
private parseLogsWithLevel(
|
||||
logs: string[]
|
||||
): Array<{ message: string; level: number }> {
|
||||
return logs.map((log) => ({
|
||||
message: log,
|
||||
level: this.getLogLevel(log),
|
||||
}))
|
||||
}
|
||||
|
||||
scrollToBottom(): void {
|
||||
const viewport = this.logContainer?.nativeElement
|
||||
if (!viewport) {
|
||||
return
|
||||
}
|
||||
this.changedetectorRef.detectChanges()
|
||||
this.logContainer?.nativeElement.scroll({
|
||||
top: this.logContainer.nativeElement.scrollHeight,
|
||||
left: 0,
|
||||
behavior: 'auto',
|
||||
})
|
||||
viewport.scrollTop = viewport.scrollHeight
|
||||
this.showJumpToBottom = false
|
||||
}
|
||||
|
||||
private isNearBottom(): boolean {
|
||||
if (!this.logContainer?.nativeElement) return true
|
||||
const distanceFromBottom =
|
||||
this.logContainer.nativeElement.scrollHeight -
|
||||
this.logContainer.nativeElement.scrollTop -
|
||||
this.logContainer.nativeElement.clientHeight
|
||||
return distanceFromBottom <= 40
|
||||
}
|
||||
|
||||
onScroll(): void {
|
||||
this.showJumpToBottom = !this.isNearBottom()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,7 @@
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
||||
<pngx-input-check i18n-title title="Show document counts in sidebar saved views" formControlName="sidebarViewsShowCount"></pngx-input-check>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -357,6 +358,6 @@
|
||||
|
||||
<div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div>
|
||||
|
||||
<button type="submit" class="btn btn-primary mb-2" [disabled]="(isDirty$ | async) === false" i18n>Save</button>
|
||||
<button type="button" (click)="reset()" class="btn btn-secondary ms-2 mb-2" [disabled]="(isDirty$ | async) === false" i18n>Cancel</button>
|
||||
<button type="button" (click)="reset()" class="btn btn-outline-secondary mb-2" [disabled]="(isDirty$ | async) === false" i18n>Cancel</button>
|
||||
<button type="submit" class="btn btn-primary ms-2 mb-2" [disabled]="(isDirty$ | async) === false" i18n>Save</button>
|
||||
</form>
|
||||
|
||||
@@ -28,13 +28,14 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { GroupService } from 'src/app/services/rest/group.service'
|
||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||
import { Toast, ToastService } from 'src/app/services/toast.service'
|
||||
import * as navUtils from 'src/app/utils/navigation'
|
||||
import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
import { CheckComponent } from '../../common/input/check/check.component'
|
||||
@@ -59,6 +60,40 @@ const groups = [
|
||||
{ id: 2, name: 'group2' },
|
||||
]
|
||||
|
||||
const status: SystemStatus = {
|
||||
pngx_version: '2.4.3',
|
||||
server_os: 'macOS-14.1.1-arm64-arm-64bit',
|
||||
install_type: InstallType.BareMetal,
|
||||
storage: { total: 494384795648, available: 13573525504 },
|
||||
database: {
|
||||
type: 'sqlite',
|
||||
url: '/paperless-ngx/data/db.sqlite3',
|
||||
status: SystemStatusItemStatus.ERROR,
|
||||
error: null,
|
||||
migration_status: {
|
||||
latest_migration: 'socialaccount.0006_alter_socialaccount_extra_data',
|
||||
unapplied_migrations: [],
|
||||
},
|
||||
},
|
||||
tasks: {
|
||||
redis_url: 'redis://localhost:6379',
|
||||
redis_status: SystemStatusItemStatus.ERROR,
|
||||
redis_error: 'Error 61 connecting to localhost:6379. Connection refused.',
|
||||
celery_status: SystemStatusItemStatus.ERROR,
|
||||
celery_url: 'celery@localhost',
|
||||
celery_error: 'Error connecting to celery@localhost',
|
||||
index_status: SystemStatusItemStatus.OK,
|
||||
index_last_modified: new Date().toISOString(),
|
||||
index_error: null,
|
||||
classifier_status: SystemStatusItemStatus.OK,
|
||||
classifier_last_trained: new Date().toISOString(),
|
||||
classifier_error: null,
|
||||
sanity_check_status: SystemStatusItemStatus.ERROR,
|
||||
sanity_check_last_run: new Date().toISOString(),
|
||||
sanity_check_error: 'Error running sanity check.',
|
||||
},
|
||||
}
|
||||
|
||||
describe('SettingsComponent', () => {
|
||||
let component: SettingsComponent
|
||||
let fixture: ComponentFixture<SettingsComponent>
|
||||
@@ -72,6 +107,7 @@ describe('SettingsComponent', () => {
|
||||
let groupService: GroupService
|
||||
let modalService: NgbModal
|
||||
let systemStatusService: SystemStatusService
|
||||
let savedViewsService: SavedViewService
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -92,7 +128,6 @@ describe('SettingsComponent', () => {
|
||||
ConfirmDialogComponent,
|
||||
CheckComponent,
|
||||
ColorComponent,
|
||||
SafeHtmlPipe,
|
||||
SelectComponent,
|
||||
TextComponent,
|
||||
NumberComponent,
|
||||
@@ -122,6 +157,7 @@ describe('SettingsComponent', () => {
|
||||
permissionsService = TestBed.inject(PermissionsService)
|
||||
modalService = TestBed.inject(NgbModal)
|
||||
systemStatusService = TestBed.inject(SystemStatusService)
|
||||
savedViewsService = TestBed.inject(SavedViewService)
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
jest
|
||||
.spyOn(permissionsService, 'currentUserHasObjectPermissions')
|
||||
@@ -212,7 +248,7 @@ describe('SettingsComponent', () => {
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
expect(storeSpy).toHaveBeenCalled()
|
||||
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
|
||||
expect(setSpy).toHaveBeenCalledTimes(29)
|
||||
expect(setSpy).toHaveBeenCalledTimes(30)
|
||||
|
||||
// succeed
|
||||
storeSpy.mockReturnValueOnce(of(true))
|
||||
@@ -222,6 +258,9 @@ describe('SettingsComponent', () => {
|
||||
})
|
||||
|
||||
it('should offer reload if settings changes require', () => {
|
||||
const reloadSpy = jest
|
||||
.spyOn(navUtils, 'locationReload')
|
||||
.mockImplementation(() => {})
|
||||
completeSetup()
|
||||
let toast: Toast
|
||||
toastService.getToasts().subscribe((t) => (toast = t[0]))
|
||||
@@ -238,6 +277,7 @@ describe('SettingsComponent', () => {
|
||||
|
||||
expect(toast.actionName).toEqual('Reload now')
|
||||
toast.action()
|
||||
expect(reloadSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should allow setting theme color, visually apply change immediately but not save', () => {
|
||||
@@ -266,7 +306,7 @@ describe('SettingsComponent', () => {
|
||||
)
|
||||
completeSetup(userService)
|
||||
fixture.detectChanges()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should show errors on load if load groups failure', () => {
|
||||
@@ -278,44 +318,10 @@ describe('SettingsComponent', () => {
|
||||
)
|
||||
completeSetup(groupService)
|
||||
fixture.detectChanges()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should load system status on initialize, show errors if needed', () => {
|
||||
const status: SystemStatus = {
|
||||
pngx_version: '2.4.3',
|
||||
server_os: 'macOS-14.1.1-arm64-arm-64bit',
|
||||
install_type: InstallType.BareMetal,
|
||||
storage: { total: 494384795648, available: 13573525504 },
|
||||
database: {
|
||||
type: 'sqlite',
|
||||
url: '/paperless-ngx/data/db.sqlite3',
|
||||
status: SystemStatusItemStatus.ERROR,
|
||||
error: null,
|
||||
migration_status: {
|
||||
latest_migration: 'socialaccount.0006_alter_socialaccount_extra_data',
|
||||
unapplied_migrations: [],
|
||||
},
|
||||
},
|
||||
tasks: {
|
||||
redis_url: 'redis://localhost:6379',
|
||||
redis_status: SystemStatusItemStatus.ERROR,
|
||||
redis_error:
|
||||
'Error 61 connecting to localhost:6379. Connection refused.',
|
||||
celery_status: SystemStatusItemStatus.ERROR,
|
||||
celery_url: 'celery@localhost',
|
||||
celery_error: 'Error connecting to celery@localhost',
|
||||
index_status: SystemStatusItemStatus.OK,
|
||||
index_last_modified: new Date().toISOString(),
|
||||
index_error: null,
|
||||
classifier_status: SystemStatusItemStatus.OK,
|
||||
classifier_last_trained: new Date().toISOString(),
|
||||
classifier_error: null,
|
||||
sanity_check_status: SystemStatusItemStatus.ERROR,
|
||||
sanity_check_last_run: new Date().toISOString(),
|
||||
sanity_check_error: 'Error running sanity check.',
|
||||
},
|
||||
}
|
||||
jest.spyOn(systemStatusService, 'get').mockReturnValue(of(status))
|
||||
jest.spyOn(permissionsService, 'isAdmin').mockReturnValue(true)
|
||||
completeSetup()
|
||||
@@ -332,6 +338,8 @@ describe('SettingsComponent', () => {
|
||||
|
||||
it('should open system status dialog', () => {
|
||||
const modalOpenSpy = jest.spyOn(modalService, 'open')
|
||||
jest.spyOn(systemStatusService, 'get').mockReturnValue(of(status))
|
||||
jest.spyOn(permissionsService, 'isAdmin').mockReturnValue(true)
|
||||
completeSetup()
|
||||
component.showSystemStatus()
|
||||
expect(modalOpenSpy).toHaveBeenCalledWith(SystemStatusDialogComponent, {
|
||||
@@ -345,4 +353,14 @@ describe('SettingsComponent', () => {
|
||||
component.reset()
|
||||
expect(component.settingsForm.get('themeColor').value).toEqual('')
|
||||
})
|
||||
|
||||
it('should trigger maybeRefreshDocumentCounts on settings save', () => {
|
||||
completeSetup()
|
||||
const maybeRefreshSpy = jest.spyOn(
|
||||
savedViewsService,
|
||||
'maybeRefreshDocumentCounts'
|
||||
)
|
||||
settingsService.settingsSaved.emit(true)
|
||||
expect(maybeRefreshSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,10 +2,10 @@ import { AsyncPipe, ViewportScroller } from '@angular/common'
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
Inject,
|
||||
LOCALE_ID,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
inject,
|
||||
} from '@angular/core'
|
||||
import {
|
||||
FormControl,
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
PermissionsService,
|
||||
} from 'src/app/services/permissions.service'
|
||||
import { GroupService } from 'src/app/services/rest/group.service'
|
||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import {
|
||||
LanguageOption,
|
||||
@@ -56,6 +57,7 @@ import {
|
||||
} from 'src/app/services/settings.service'
|
||||
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||
import { Toast, ToastService } from 'src/app/services/toast.service'
|
||||
import { locationReload } from 'src/app/utils/navigation'
|
||||
import { CheckComponent } from '../../common/input/check/check.component'
|
||||
import { ColorComponent } from '../../common/input/color/color.component'
|
||||
import { PermissionsGroupComponent } from '../../common/input/permissions/permissions-group/permissions-group.component'
|
||||
@@ -104,6 +106,21 @@ export class SettingsComponent
|
||||
extends ComponentWithPermissions
|
||||
implements OnInit, AfterViewInit, OnDestroy, DirtyComponent
|
||||
{
|
||||
private documentListViewService = inject(DocumentListViewService)
|
||||
private toastService = inject(ToastService)
|
||||
private settings = inject(SettingsService)
|
||||
currentLocale = inject(LOCALE_ID)
|
||||
private viewportScroller = inject(ViewportScroller)
|
||||
private activatedRoute = inject(ActivatedRoute)
|
||||
readonly tourService = inject(TourService)
|
||||
private usersService = inject(UserService)
|
||||
private groupsService = inject(GroupService)
|
||||
private router = inject(Router)
|
||||
permissionsService = inject(PermissionsService)
|
||||
private modalService = inject(NgbModal)
|
||||
private systemStatusService = inject(SystemStatusService)
|
||||
private savedViewsService = inject(SavedViewService)
|
||||
|
||||
activeNavID: number
|
||||
|
||||
settingsForm = new FormGroup({
|
||||
@@ -138,6 +155,7 @@ export class SettingsComponent
|
||||
notificationsConsumerSuppressOnDashboard: new FormControl(null),
|
||||
|
||||
savedViewsWarnOnUnsavedChange: new FormControl(null),
|
||||
sidebarViewsShowCount: new FormControl(null),
|
||||
})
|
||||
|
||||
SettingsNavIDs = SettingsNavIDs
|
||||
@@ -167,7 +185,8 @@ export class SettingsComponent
|
||||
this.systemStatus.tasks.classifier_status ===
|
||||
SystemStatusItemStatus.ERROR ||
|
||||
this.systemStatus.tasks.sanity_check_status ===
|
||||
SystemStatusItemStatus.ERROR
|
||||
SystemStatusItemStatus.ERROR ||
|
||||
this.systemStatus.websocket_connected === SystemStatusItemStatus.ERROR
|
||||
)
|
||||
}
|
||||
|
||||
@@ -179,24 +198,11 @@ export class SettingsComponent
|
||||
)
|
||||
}
|
||||
|
||||
constructor(
|
||||
private documentListViewService: DocumentListViewService,
|
||||
private toastService: ToastService,
|
||||
private settings: SettingsService,
|
||||
@Inject(LOCALE_ID) public currentLocale: string,
|
||||
private viewportScroller: ViewportScroller,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
public readonly tourService: TourService,
|
||||
private usersService: UserService,
|
||||
private groupsService: GroupService,
|
||||
private router: Router,
|
||||
public permissionsService: PermissionsService,
|
||||
private modalService: NgbModal,
|
||||
private systemStatusService: SystemStatusService
|
||||
) {
|
||||
constructor() {
|
||||
super()
|
||||
this.settings.settingsSaved.subscribe(() => {
|
||||
if (!this.savePending) this.initialize()
|
||||
this.savedViewsService.maybeRefreshDocumentCounts()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -308,6 +314,9 @@ export class SettingsComponent
|
||||
savedViewsWarnOnUnsavedChange: this.settings.get(
|
||||
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE
|
||||
),
|
||||
sidebarViewsShowCount: this.settings.get(
|
||||
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT
|
||||
),
|
||||
defaultPermsOwner: this.settings.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER),
|
||||
defaultPermsViewUsers: this.settings.get(
|
||||
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS
|
||||
@@ -485,6 +494,10 @@ export class SettingsComponent
|
||||
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE,
|
||||
this.settingsForm.value.savedViewsWarnOnUnsavedChange
|
||||
)
|
||||
this.settings.set(
|
||||
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT,
|
||||
this.settingsForm.value.sidebarViewsShowCount
|
||||
)
|
||||
this.settings.set(
|
||||
SETTINGS_KEYS.DEFAULT_PERMS_OWNER,
|
||||
this.settingsForm.value.defaultPermsOwner
|
||||
@@ -539,7 +552,7 @@ export class SettingsComponent
|
||||
savedToast.content = $localize`Settings were saved successfully. Reload is required to apply some changes.`
|
||||
savedToast.actionName = $localize`Reload now`
|
||||
savedToast.action = () => {
|
||||
location.reload()
|
||||
locationReload()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
NgbNavItem,
|
||||
} from '@ng-bootstrap/ng-bootstrap'
|
||||
import { allIcons, NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { throwError } from 'rxjs'
|
||||
import { routes } from 'src/app/app-routing.module'
|
||||
import {
|
||||
PaperlessTask,
|
||||
@@ -28,6 +29,7 @@ import { PermissionsGuard } from 'src/app/guards/permissions.guard'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { TasksService } from 'src/app/services/tasks.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { environment } from 'src/environments/environment'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
|
||||
@@ -123,6 +125,7 @@ describe('TasksComponent', () => {
|
||||
let router: Router
|
||||
let httpTestingController: HttpTestingController
|
||||
let reloadSpy
|
||||
let toastService: ToastService
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -157,6 +160,7 @@ describe('TasksComponent', () => {
|
||||
httpTestingController = TestBed.inject(HttpTestingController)
|
||||
modalService = TestBed.inject(NgbModal)
|
||||
router = TestBed.inject(Router)
|
||||
toastService = TestBed.inject(ToastService)
|
||||
fixture = TestBed.createComponent(TasksComponent)
|
||||
component = fixture.componentInstance
|
||||
jest.useFakeTimers()
|
||||
@@ -249,6 +253,42 @@ describe('TasksComponent', () => {
|
||||
expect(dismissSpy).toHaveBeenCalledWith(selected)
|
||||
})
|
||||
|
||||
it('should show an error and re-enable modal buttons when dismissing multiple tasks fails', () => {
|
||||
component.selectedTasks = new Set([tasks[0].id, tasks[1].id])
|
||||
const error = new Error('dismiss failed')
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
const dismissSpy = jest
|
||||
.spyOn(tasksService, 'dismissTasks')
|
||||
.mockReturnValue(throwError(() => error))
|
||||
|
||||
let modal: NgbModalRef
|
||||
modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1]))
|
||||
|
||||
component.dismissTasks()
|
||||
expect(modal).not.toBeUndefined()
|
||||
|
||||
modal.componentInstance.confirmClicked.emit()
|
||||
|
||||
expect(dismissSpy).toHaveBeenCalledWith(new Set([tasks[0].id, tasks[1].id]))
|
||||
expect(toastSpy).toHaveBeenCalledWith('Error dismissing tasks', error)
|
||||
expect(modal.componentInstance.buttonsEnabled).toBe(true)
|
||||
expect(component.selectedTasks.size).toBe(0)
|
||||
})
|
||||
|
||||
it('should show an error when dismissing a single task fails', () => {
|
||||
const error = new Error('dismiss failed')
|
||||
const toastSpy = jest.spyOn(toastService, 'showError')
|
||||
const dismissSpy = jest
|
||||
.spyOn(tasksService, 'dismissTasks')
|
||||
.mockReturnValue(throwError(() => error))
|
||||
|
||||
component.dismissTask(tasks[0])
|
||||
|
||||
expect(dismissSpy).toHaveBeenCalledWith(new Set([tasks[0].id]))
|
||||
expect(toastSpy).toHaveBeenCalledWith('Error dismissing task', error)
|
||||
expect(component.selectedTasks.size).toBe(0)
|
||||
})
|
||||
|
||||
it('should support dismiss all tasks', () => {
|
||||
let modal: NgbModalRef
|
||||
modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1]))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NgTemplateOutlet, SlicePipe } from '@angular/common'
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { Component, inject, OnDestroy, OnInit } from '@angular/core'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { Router } from '@angular/router'
|
||||
import {
|
||||
@@ -24,6 +24,7 @@ import { PaperlessTask } from 'src/app/data/paperless-task'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { TasksService } from 'src/app/services/tasks.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
|
||||
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
|
||||
@@ -69,6 +70,11 @@ export class TasksComponent
|
||||
extends LoadingComponentWithPermissions
|
||||
implements OnInit, OnDestroy
|
||||
{
|
||||
tasksService = inject(TasksService)
|
||||
private modalService = inject(NgbModal)
|
||||
private readonly router = inject(Router)
|
||||
private readonly toastService = inject(ToastService)
|
||||
|
||||
public activeTab: TaskTab
|
||||
public selectedTasks: Set<number> = new Set()
|
||||
public togggleAll: boolean = false
|
||||
@@ -105,14 +111,6 @@ export class TasksComponent
|
||||
: $localize`Dismiss all`
|
||||
}
|
||||
|
||||
constructor(
|
||||
public tasksService: TasksService,
|
||||
private modalService: NgbModal,
|
||||
private readonly router: Router
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.tasksService.reload()
|
||||
timer(5000, 5000)
|
||||
@@ -158,11 +156,19 @@ export class TasksComponent
|
||||
modal.componentInstance.confirmClicked.pipe(first()).subscribe(() => {
|
||||
modal.componentInstance.buttonsEnabled = false
|
||||
modal.close()
|
||||
this.tasksService.dismissTasks(tasks)
|
||||
this.tasksService.dismissTasks(tasks).subscribe({
|
||||
error: (e) => {
|
||||
this.toastService.showError($localize`Error dismissing tasks`, e)
|
||||
modal.componentInstance.buttonsEnabled = true
|
||||
},
|
||||
})
|
||||
this.clearSelection()
|
||||
})
|
||||
} else {
|
||||
this.tasksService.dismissTasks(tasks)
|
||||
this.tasksService.dismissTasks(tasks).subscribe({
|
||||
error: (e) =>
|
||||
this.toastService.showError($localize`Error dismissing task`, e),
|
||||
})
|
||||
this.clearSelection()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
} from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { of, throwError } from 'rxjs'
|
||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { TrashService } from 'src/app/services/trash.service'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
@@ -53,7 +52,6 @@ describe('TrashComponent', () => {
|
||||
TrashComponent,
|
||||
PageHeaderComponent,
|
||||
ConfirmDialogComponent,
|
||||
SafeHtmlPipe,
|
||||
],
|
||||
}).compileComponents()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnDestroy } from '@angular/core'
|
||||
import { Component, OnDestroy, inject } from '@angular/core'
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||
import { Router } from '@angular/router'
|
||||
import {
|
||||
@@ -36,19 +36,19 @@ export class TrashComponent
|
||||
extends LoadingComponentWithPermissions
|
||||
implements OnDestroy
|
||||
{
|
||||
private trashService = inject(TrashService)
|
||||
private toastService = inject(ToastService)
|
||||
private modalService = inject(NgbModal)
|
||||
private settingsService = inject(SettingsService)
|
||||
private router = inject(Router)
|
||||
|
||||
public documentsInTrash: Document[] = []
|
||||
public selectedDocuments: Set<number> = new Set()
|
||||
public allToggled: boolean = false
|
||||
public page: number = 1
|
||||
public totalDocuments: number
|
||||
|
||||
constructor(
|
||||
private trashService: TrashService,
|
||||
private toastService: ToastService,
|
||||
private modalService: NgbModal,
|
||||
private settingsService: SettingsService,
|
||||
private router: Router
|
||||
) {
|
||||
constructor() {
|
||||
super()
|
||||
this.reload()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
>
|
||||
</pngx-page-header>
|
||||
|
||||
@if (users) {
|
||||
@if (canViewUsers && users) {
|
||||
<h4 class="d-flex">
|
||||
<ng-container i18n>Users</ng-container>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editUser()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.User }">
|
||||
@@ -26,7 +26,7 @@
|
||||
@for (user of users; track user) {
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editUser(user)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.User)">{{user.username}}</button></div>
|
||||
<div class="col d-flex align-items-center" [class.opacity-50]="!user.is_active"><button class="btn btn-link p-0 text-start" type="button" (click)="editUser(user)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.User)">{{user.username}}</button></div>
|
||||
<div class="col d-flex align-items-center">{{user.first_name}} {{user.last_name}}</div>
|
||||
<div class="col d-flex align-items-center">{{user.groups?.map(getGroupName, this).join(', ')}}</div>
|
||||
<div class="col">
|
||||
@@ -45,7 +45,7 @@
|
||||
</ul>
|
||||
}
|
||||
|
||||
@if (groups) {
|
||||
@if (canViewGroups && groups) {
|
||||
<h4 class="mt-4 d-flex">
|
||||
<ng-container i18n>Groups</ng-container>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editGroup()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.Group }">
|
||||
@@ -86,7 +86,7 @@
|
||||
</ul>
|
||||
}
|
||||
|
||||
@if (!users || !groups) {
|
||||
@if ((canViewUsers && !users) || (canViewGroups && !groups)) {
|
||||
<div>
|
||||
<div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div>
|
||||
<div class="visually-hidden" i18n>Loading...</div>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { GroupService } from 'src/app/services/rest/group.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import * as navUtils from 'src/app/utils/navigation'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
|
||||
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
|
||||
@@ -107,7 +108,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||
editDialog.failed.emit()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
settingsService.currentUser = users[1] // simulate logged in as different user
|
||||
editDialog.succeeded.emit(users[0])
|
||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||
@@ -130,7 +131,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
throwError(() => new Error('error deleting user'))
|
||||
)
|
||||
deleteDialog.confirm()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
deleteSpy.mockReturnValueOnce(of(true))
|
||||
deleteDialog.confirm()
|
||||
expect(listAllSpy).toHaveBeenCalled()
|
||||
@@ -142,19 +143,18 @@ describe('UsersAndGroupsComponent', () => {
|
||||
let modal: NgbModalRef
|
||||
modalService.activeInstances.subscribe((refs) => (modal = refs[0]))
|
||||
component.editUser(users[0])
|
||||
const navSpy = jest
|
||||
.spyOn(navUtils, 'setLocationHref')
|
||||
.mockImplementation(() => {})
|
||||
const editDialog = modal.componentInstance as UserEditDialogComponent
|
||||
editDialog.passwordIsSet = true
|
||||
settingsService.currentUser = users[0] // simulate logged in as same user
|
||||
editDialog.succeeded.emit(users[0])
|
||||
fixture.detectChanges()
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
href: 'http://localhost/',
|
||||
},
|
||||
writable: true, // possibility to override
|
||||
})
|
||||
tick(2600)
|
||||
expect(window.location.href).toContain('logout')
|
||||
expect(navSpy).toHaveBeenCalledWith(
|
||||
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
)
|
||||
}))
|
||||
|
||||
it('should support edit / create group, show error if needed', () => {
|
||||
@@ -166,7 +166,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||
editDialog.failed.emit()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
editDialog.succeeded.emit(groups[0])
|
||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||
`Saved group "${groups[0].name}".`
|
||||
@@ -188,7 +188,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
throwError(() => new Error('error deleting group'))
|
||||
)
|
||||
deleteDialog.confirm()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
deleteSpy.mockReturnValueOnce(of(true))
|
||||
deleteDialog.confirm()
|
||||
expect(listAllSpy).toHaveBeenCalled()
|
||||
@@ -210,7 +210,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
)
|
||||
completeSetup(userService)
|
||||
fixture.detectChanges()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should show errors on load if load groups failure', () => {
|
||||
@@ -222,6 +222,6 @@ describe('UsersAndGroupsComponent', () => {
|
||||
)
|
||||
completeSetup(groupService)
|
||||
fixture.detectChanges()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { Subject, first, takeUntil } from 'rxjs'
|
||||
import { Group } from 'src/app/data/group'
|
||||
import { User } from 'src/app/data/user'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import {
|
||||
PermissionAction,
|
||||
PermissionType,
|
||||
PermissionsService,
|
||||
} from 'src/app/services/permissions.service'
|
||||
import { GroupService } from 'src/app/services/rest/group.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { setLocationHref } from 'src/app/utils/navigation'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
|
||||
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
|
||||
@@ -31,46 +36,60 @@ export class UsersAndGroupsComponent
|
||||
extends ComponentWithPermissions
|
||||
implements OnInit, OnDestroy
|
||||
{
|
||||
private usersService = inject(UserService)
|
||||
private groupsService = inject(GroupService)
|
||||
private toastService = inject(ToastService)
|
||||
private modalService = inject(NgbModal)
|
||||
permissionsService = inject(PermissionsService)
|
||||
private settings = inject(SettingsService)
|
||||
|
||||
users: User[]
|
||||
groups: Group[]
|
||||
|
||||
unsubscribeNotifier: Subject<any> = new Subject()
|
||||
|
||||
constructor(
|
||||
private usersService: UserService,
|
||||
private groupsService: GroupService,
|
||||
private toastService: ToastService,
|
||||
private modalService: NgbModal,
|
||||
public permissionsService: PermissionsService,
|
||||
private settings: SettingsService
|
||||
) {
|
||||
super()
|
||||
public get canViewUsers(): boolean {
|
||||
return this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.User
|
||||
)
|
||||
}
|
||||
|
||||
public get canViewGroups(): boolean {
|
||||
return this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.Group
|
||||
)
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.usersService
|
||||
.listAll(null, null, { full_perms: true })
|
||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe({
|
||||
next: (r) => {
|
||||
this.users = r.results
|
||||
},
|
||||
error: (e) => {
|
||||
this.toastService.showError($localize`Error retrieving users`, e)
|
||||
},
|
||||
})
|
||||
if (this.canViewUsers) {
|
||||
this.usersService
|
||||
.listAll(null, null, { full_perms: true })
|
||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe({
|
||||
next: (r) => {
|
||||
this.users = r.results
|
||||
},
|
||||
error: (e) => {
|
||||
this.toastService.showError($localize`Error retrieving users`, e)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
this.groupsService
|
||||
.listAll(null, null, { full_perms: true })
|
||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe({
|
||||
next: (r) => {
|
||||
this.groups = r.results
|
||||
},
|
||||
error: (e) => {
|
||||
this.toastService.showError($localize`Error retrieving groups`, e)
|
||||
},
|
||||
})
|
||||
if (this.canViewGroups) {
|
||||
this.groupsService
|
||||
.listAll(null, null, { full_perms: true })
|
||||
.pipe(first(), takeUntil(this.unsubscribeNotifier))
|
||||
.subscribe({
|
||||
next: (r) => {
|
||||
this.groups = r.results
|
||||
},
|
||||
error: (e) => {
|
||||
this.toastService.showError($localize`Error retrieving groups`, e)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
@@ -97,7 +116,9 @@ export class UsersAndGroupsComponent
|
||||
$localize`Password has been changed, you will be logged out momentarily.`
|
||||
)
|
||||
setTimeout(() => {
|
||||
window.location.href = `${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
setLocationHref(
|
||||
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
)
|
||||
}, 2500)
|
||||
} else {
|
||||
this.toastService.showInfo(
|
||||
|
||||
@@ -68,13 +68,15 @@
|
||||
<nav id="sidebarMenu" class="d-md-block bg-light sidebar collapse"
|
||||
[ngClass]="slimSidebarEnabled ? 'slim' : 'col-md-3 col-lg-2 col-xxxl-1'" [class.animating]="slimSidebarAnimating"
|
||||
[ngbCollapse]="isMenuCollapsed">
|
||||
<button class="btn btn-sm btn-dark sidebar-slim-toggler" (click)="toggleSlimSidebar()">
|
||||
@if (slimSidebarEnabled) {
|
||||
<i-bs width="0.9em" height="0.9em" name="chevron-double-right"></i-bs>
|
||||
} @else {
|
||||
<i-bs width="0.9em" height="0.9em" name="chevron-double-left"></i-bs>
|
||||
}
|
||||
</button>
|
||||
@if (canSaveSettings) {
|
||||
<button class="btn btn-sm btn-dark sidebar-slim-toggler" (click)="toggleSlimSidebar()">
|
||||
@if (slimSidebarEnabled) {
|
||||
<i-bs width="0.9em" height="0.9em" name="chevron-double-right"></i-bs>
|
||||
} @else {
|
||||
<i-bs width="0.9em" height="0.9em" name="chevron-double-left"></i-bs>
|
||||
}
|
||||
</button>
|
||||
}
|
||||
<div class="sidebar-sticky pt-3 d-flex flex-column justify-space-around">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item app-link">
|
||||
@@ -108,11 +110,19 @@
|
||||
<li class="nav-item w-100 app-link" cdkDrag [cdkDragDisabled]="!settingsService.organizingSidebarSavedViews"
|
||||
cdkDragPreviewContainer="parent" cdkDragPreviewClass="navItemDrag" (cdkDragStarted)="onDragStart($event)"
|
||||
(cdkDragEnded)="onDragEnd($event)">
|
||||
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="view/{{view.id}}"
|
||||
<a class="nav-link" routerLink="view/{{view.id}}"
|
||||
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name"
|
||||
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
|
||||
popoverClass="popover-slim">
|
||||
<i-bs class="me-1" name="funnel"></i-bs><span> {{view.name}}</span>
|
||||
<i-bs class="me-1" name="funnel"></i-bs>
|
||||
<span> <div class="d-inline-flex view-name"><span class="overflow-hidden" [class.text-wrap]="!slimSidebarEnabled">{{view.name}}</span></div>
|
||||
@if (showSidebarCounts && !slimSidebarEnabled) {
|
||||
<span class="badge bg-info text-dark ms-2 d-inline">{{ savedViewService.getDocumentCount(view) }}</span>
|
||||
}
|
||||
</span>
|
||||
@if (showSidebarCounts && slimSidebarEnabled) {
|
||||
<span class="badge bg-info text-dark position-absolute top-0 end-0 d-none d-md-block">{{ savedViewService.getDocumentCount(view) }}</span>
|
||||
}
|
||||
</a>
|
||||
@if (settingsService.organizingSidebarSavedViews) {
|
||||
<div class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
|
||||
@@ -139,7 +149,7 @@
|
||||
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
|
||||
popoverClass="popover-slim">
|
||||
<i-bs class="me-1" name="file-text"></i-bs><span> {{d.title | documentTitle}}</span>
|
||||
<span class="close" (click)="closeDocument(d); $event.preventDefault()">
|
||||
<span class="close flex-column justify-content-center" (click)="closeDocument(d); $event.preventDefault()">
|
||||
<i-bs name="x"></i-bs>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
height: 0.8em;
|
||||
}
|
||||
|
||||
.view-name {
|
||||
max-width: calc(100% - 50px)
|
||||
}
|
||||
|
||||
.nav-group:not(:has(.app-link)) .sidebar-heading {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -187,7 +191,7 @@ main {
|
||||
list-style-type: none;
|
||||
|
||||
&:hover .close {
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.close {
|
||||
@@ -277,7 +281,7 @@ main {
|
||||
.navbar .dropdown-menu {
|
||||
font-size: 0.875rem; // body size
|
||||
|
||||
a i-bs {
|
||||
a i-bs, button i-bs {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ describe('AppFrameComponent', () => {
|
||||
let router: Router
|
||||
let savedViewSpy
|
||||
let modalService: NgbModal
|
||||
let maybeRefreshSpy
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -113,7 +114,11 @@ describe('AppFrameComponent', () => {
|
||||
{
|
||||
provide: SavedViewService,
|
||||
useValue: {
|
||||
reload: () => {},
|
||||
reload: (fn: any) => {
|
||||
if (fn) {
|
||||
fn()
|
||||
}
|
||||
},
|
||||
listAll: () =>
|
||||
of({
|
||||
all: [saved_views.map((v) => v.id)],
|
||||
@@ -121,6 +126,8 @@ describe('AppFrameComponent', () => {
|
||||
results: saved_views,
|
||||
}),
|
||||
sidebarViews: saved_views.filter((v) => v.show_in_sidebar),
|
||||
getDocumentCount: (view: SavedView) => 5,
|
||||
maybeRefreshDocumentCounts: () => {},
|
||||
},
|
||||
},
|
||||
PermissionsService,
|
||||
@@ -169,6 +176,7 @@ describe('AppFrameComponent', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
|
||||
savedViewSpy = jest.spyOn(savedViewService, 'reload')
|
||||
maybeRefreshSpy = jest.spyOn(savedViewService, 'maybeRefreshDocumentCounts')
|
||||
|
||||
fixture = TestBed.createComponent(AppFrameComponent)
|
||||
component = fixture.componentInstance
|
||||
@@ -359,4 +367,8 @@ describe('AppFrameComponent', () => {
|
||||
expect(toastErrorSpy).toHaveBeenCalledTimes(2)
|
||||
expect(toastInfoSpy).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
it('should call maybeRefreshDocumentCounts after saved views reload', () => {
|
||||
expect(maybeRefreshSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user