Compare commits

..

617 Commits

Author SHA1 Message Date
jonaswinkler
6c961dfad9 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-03-06 12:59:29 +01:00
Jonas Winkler
b0b5f9e0a1 Merge pull request #702 from auanasgheps/patch-1
Update Italian Translation
2021-03-06 12:58:49 +01:00
Oliver Cervera
69df7f8ef6 Update Italian Translation
Minor change to Italian translation.
Manual PR since Transifex doesn't do it.
2021-03-06 12:55:24 +01:00
jonaswinkler
0bb0fc5a19 changelog 2021-03-06 12:42:07 +01:00
jonaswinkler
be15e86458 added a test case for title_content filter 2021-03-06 12:41:59 +01:00
jonaswinkler
24eff6b02b documentation 2021-03-06 12:28:41 +01:00
jonaswinkler
ddb96829ca fix typo 2021-03-06 12:08:52 +01:00
jonaswinkler
4290f38e81 fix closing all documents from sidebar 2021-03-06 12:00:58 +01:00
jonaswinkler
ee4a5d53ad instructions on building the docker image 2021-03-06 11:47:11 +01:00
jonaswinkler
1f4d2e76d7 dark mode for color picker 2021-03-05 23:36:09 +01:00
jonaswinkler
9816efb816 Revert "Merge pull request #628 from shamoon/feature/popover-previews"
This reverts commit 8da1521508, reversing
changes made to a7846925b1.
2021-03-05 23:19:44 +01:00
Jonas Winkler
8da1521508 Merge pull request #628 from shamoon/feature/popover-previews
Feature: popover document previews
2021-03-05 21:34:19 +01:00
Jonas Winkler
a7846925b1 Merge pull request #699 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_en_GB
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'en_GB'
2021-03-05 09:36:52 +01:00
Jonas Winkler
d678bd71e8 Merge pull request #700 from jonaswinkler/translations_src-ui-messages-xlf--dev_en_GB
Translate '/src-ui/messages.xlf' in 'en_GB'
2021-03-05 09:36:39 +01:00
transifex-integration[bot]
67754efd36 Translate /src-ui/messages.xlf in en_GB
translation completed for the source file '/src-ui/messages.xlf'
on the 'en_GB' language.
2021-03-05 08:36:32 +00:00
transifex-integration[bot]
80e0fc21cc Apply translations in en_GB
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'en_GB' language.
2021-03-05 08:36:30 +00:00
Jonas Winkler
1025f6ccb5 Merge pull request #698 from siancu/dev
Postgres won't run without a password.
2021-03-04 16:44:49 +01:00
Stelian Iancu
ede06f4fd4 Postgres won't run without a password. 2021-03-04 16:26:03 +01:00
Jonas Winkler
6dd0de0201 Merge pull request #694 from jonaswinkler/translations_src-ui-messages-xlf--dev_nl_NL
Translate '/src-ui/messages.xlf' in 'nl_NL'
2021-03-04 11:32:56 +01:00
transifex-integration[bot]
7f01ead374 Translate /src-ui/messages.xlf in nl_NL
translation completed for the source file '/src-ui/messages.xlf'
on the 'nl_NL' language.
2021-03-04 10:25:24 +00:00
Michael Shamoon
cba507258d Merge remote-tracking branch 'upstream/dev' into feature/popover-previews 2021-03-03 21:19:51 -08:00
Michael Shamoon
ae93999f6a Simplify preview tooltip since its delayed 2021-03-03 21:14:24 -08:00
Michael Shamoon
1ff7a89a35 Larger popover preview size 2021-03-03 20:49:14 -08:00
Michael Shamoon
31cd32406c Refactor popover css to single file 2021-03-03 20:49:02 -08:00
Michael Shamoon
e5f750dcbe Fix double scrollbar, I think 2021-03-03 20:42:50 -08:00
jonaswinkler
59ecff8701 test case for the migration 2021-03-04 00:16:24 +01:00
jonaswinkler
80e91cde70 changelog 2021-03-04 00:10:15 +01:00
jonaswinkler
b1c2c79d80 update docs 2021-03-03 23:58:53 +01:00
jonaswinkler
5b2576ace4 remove test cases #677 2021-03-03 23:55:25 +01:00
Jonas Winkler
da9f370924 Merge pull request #677 from skuzzle/dev
Add the possibility to customize the remote user header name
2021-03-03 23:54:33 +01:00
jonaswinkler
e56daf07d4 Merge branch 'master' into dev 2021-03-03 23:46:37 +01:00
jonaswinkler
9e3caa57ec fix quoting 2021-03-03 23:44:43 +01:00
jonaswinkler
4fd7b9ef1f Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-03-03 23:44:15 +01:00
jonaswinkler
12235cc853 fixes #689 2021-03-03 23:35:26 +01:00
Jonas Winkler
56e4264394 Merge pull request #680 from jonaswinkler/translations_src-ui-messages-xlf--dev_it
Translate '/src-ui/messages.xlf' in 'it'
2021-03-03 09:39:50 +01:00
Jonas Winkler
baf8203b80 Merge pull request #681 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_it
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'it'
2021-03-03 09:39:39 +01:00
transifex-integration[bot]
41d966895f Apply translations in it
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'it' language.
2021-03-03 07:30:59 +00:00
transifex-integration[bot]
551792274f Translate /src-ui/messages.xlf in it
translation completed for the source file '/src-ui/messages.xlf'
on the 'it' language.
2021-03-03 07:30:31 +00:00
jonaswinkler
c7abdb61e8 added remote user auth test 2021-03-02 23:19:06 +01:00
Simon Taddiken
72ebe7df58 Improve documentation 2021-03-02 10:21:50 +01:00
Simon Taddiken
97f3c214e8 Add the possibility to customize the remote user header name
Inspired by the discussion here https://github.com/jonaswinkler/paperless-ng/discussions/639#discussion-3242017 it is worthwhile to be able to customize the header name that is used for authentication as its name is not really standardized.
2021-03-02 09:07:42 +01:00
Jonas Winkler
47842bac1c Merge pull request #665 from holzhannes/patch-1
Update to status of affiliateded projects
2021-03-02 00:39:13 +01:00
Jonas Winkler
ede498fa52 Merge pull request #671 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_nl_NL
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'nl_NL'
2021-03-01 16:37:53 +01:00
transifex-integration[bot]
c8a06416e7 Apply translations in nl_NL
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'nl_NL' language.
2021-03-01 15:30:27 +00:00
Jonas Winkler
5fea7f2bc4 Merge pull request #666 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_de
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'de'
2021-02-28 21:48:20 +01:00
Jonas Winkler
15420137fa Merge pull request #667 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-02-28 21:48:10 +01:00
transifex-integration[bot]
73791e0e50 Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-02-28 20:47:42 +00:00
transifex-integration[bot]
7af0f12e6b Apply translations in de
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'de' language.
2021-02-28 20:47:14 +00:00
HolzHannes
895f9c911b Update to status of affiliateded projects 2021-02-28 20:23:38 +01:00
Jonas Winkler
204f47265b Merge pull request #663 from jonaswinkler/translations_src-ui-messages-xlf--dev_ro
Translate '/src-ui/messages.xlf' in 'ro'
2021-02-28 19:54:45 +01:00
transifex-integration[bot]
b240b4821a Translate /src-ui/messages.xlf in ro
translation completed for the source file '/src-ui/messages.xlf'
on the 'ro' language.
2021-02-28 18:04:39 +00:00
Jonas Winkler
249f6e4f27 Merge pull request #661 from jonaswinkler/translations_src-ui-messages-xlf--dev_pt_BR
Translate '/src-ui/messages.xlf' in 'pt_BR'
2021-02-28 18:02:10 +01:00
Jonas Winkler
32facd2877 Merge pull request #662 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_pt_BR
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'pt_BR'
2021-02-28 18:01:23 +01:00
transifex-integration[bot]
fd3cb6e2ca Apply translations in pt_BR
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'pt_BR' language.
2021-02-28 16:59:47 +00:00
transifex-integration[bot]
1df6d857e5 Translate /src-ui/messages.xlf in pt_BR
translation completed for the source file '/src-ui/messages.xlf'
on the 'pt_BR' language.
2021-02-28 16:59:35 +00:00
Jonas Winkler
3b9c2fd4d2 Merge pull request #660 from jonaswinkler/translations_src-ui-messages-xlf--dev_fr
Translate '/src-ui/messages.xlf' in 'fr'
2021-02-28 17:38:14 +01:00
Jonas Winkler
baa130e646 Merge pull request #659 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_fr
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'fr'
2021-02-28 17:38:04 +01:00
transifex-integration[bot]
c3ec40189f Translate /src-ui/messages.xlf in fr
translation completed for the source file '/src-ui/messages.xlf'
on the 'fr' language.
2021-02-28 16:36:24 +00:00
transifex-integration[bot]
686ae5b213 Apply translations in fr
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'fr' language.
2021-02-28 16:35:20 +00:00
jonaswinkler
5c19a8bb70 improved color generation logic 2021-02-28 16:35:02 +01:00
jonaswinkler
efa7b7b0b5 filter by title or title+content fixes #636 2021-02-28 16:19:41 +01:00
jonaswinkler
e466c31bb3 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-28 13:39:17 +01:00
Jonas Winkler
942acf5940 Merge pull request #658 from jonaswinkler/translations_src-ui-messages-xlf--dev_ro
Translate '/src-ui/messages.xlf' in 'ro'
2021-02-28 13:39:01 +01:00
Jonas Winkler
b38d8d8640 Merge pull request #657 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_ro
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'ro'
2021-02-28 13:38:35 +01:00
transifex-integration[bot]
c4c8a96f25 Translate /src-ui/messages.xlf in ro
translation completed for the source file '/src-ui/messages.xlf'
on the 'ro' language.
2021-02-28 12:36:01 +00:00
transifex-integration[bot]
65377d881c Apply translations in ro
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'ro' language.
2021-02-28 12:35:21 +00:00
jonaswinkler
b847e0c65e changelog, documentation, version bump 2021-02-28 13:20:31 +01:00
jonaswinkler
6ab884a95c update dependencies 2021-02-28 13:01:26 +01:00
jonaswinkler
4b17a485ae update messages and typo fix 2021-02-28 12:42:46 +01:00
jonaswinkler
e5a4715161 add Romanian locale configuration 2021-02-28 12:40:25 +01:00
Jonas Winkler
53b7b14467 Merge pull request #656 from jonaswinkler/translations_src-ui-messages-xlf--dev_ro
Translate '/src-ui/messages.xlf' in 'ro'
2021-02-28 12:02:42 +01:00
transifex-integration[bot]
5eeb012557 Translate /src-ui/messages.xlf in ro
translation completed for the source file '/src-ui/messages.xlf'
on the 'ro' language.
2021-02-28 11:02:26 +00:00
Jonas Winkler
cf9884aaba Merge pull request #655 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_ro
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'ro'
2021-02-28 11:47:35 +01:00
transifex-integration[bot]
d811cab57e Apply translations in ro
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'ro' language.
2021-02-28 10:33:26 +00:00
jonaswinkler
d5a3443076 Merge branch 'fix/issue-603' into dev 2021-02-27 11:25:21 +01:00
jonaswinkler
bd11d3a34f small changes 2021-02-27 11:24:58 +01:00
jonaswinkler
5f6a202d65 Merge remote-tracking branch 'shamoon/fix/issue-603' into fix/issue-603 2021-02-27 11:13:21 +01:00
jonaswinkler
6db3f7023f remove unused files 2021-02-26 23:24:13 +01:00
Michael Shamoon
8a64898798 TypeScript fixes 2021-02-26 14:16:31 -08:00
Michael Shamoon
6ad14d63d6 Consistency please 2021-02-26 14:11:25 -08:00
Michael Shamoon
a26150ca40 Hover state for close x button 2021-02-26 14:08:13 -08:00
Michael Shamoon
57c9066f81 Closing should only navigate if closing current document 2021-02-26 14:08:04 -08:00
jonaswinkler
fe3c3b8946 Merge branch 'dev' into fix/issue-603 2021-02-26 22:25:33 +01:00
Jonas Winkler
76686c22fc Merge pull request #648 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_nl_NL
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'nl_NL'
2021-02-26 22:24:23 +01:00
Jonas Winkler
bba4057616 Merge pull request #647 from jonaswinkler/translations_src-ui-messages-xlf--dev_nl_NL
Translate '/src-ui/messages.xlf' in 'nl_NL'
2021-02-26 22:24:11 +01:00
transifex-integration[bot]
10a1f34164 Apply translations in nl_NL
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'nl_NL' language.
2021-02-26 21:07:13 +00:00
transifex-integration[bot]
4516a5def5 Translate /src-ui/messages.xlf in nl_NL
translation completed for the source file '/src-ui/messages.xlf'
on the 'nl_NL' language.
2021-02-26 21:06:40 +00:00
Jonas Winkler
4ef4af452a Merge pull request #646 from C0nsultant/ansible-CI-stability
Ansible - Fix GitHub actions
2021-02-26 21:28:43 +01:00
Fabian Koller
8f1e95a0ce Fix GitHub actions
Follow-up to #630.
The check for the target git sha can does not work in the molecule step.
Instead expose the sha through an env variable in GitHub actions.
2021-02-26 19:39:44 +01:00
Jonas Winkler
824aea0c7b Merge pull request #645 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_en_GB
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'en_GB'
2021-02-26 15:42:54 +01:00
Jonas Winkler
7d54f0e336 Merge pull request #644 from jonaswinkler/translations_src-ui-messages-xlf--dev_en_GB
Translate '/src-ui/messages.xlf' in 'en_GB'
2021-02-26 15:42:42 +01:00
transifex-integration[bot]
961b47239b Apply translations in en_GB
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'en_GB' language.
2021-02-26 14:36:09 +00:00
transifex-integration[bot]
248f19a550 Translate /src-ui/messages.xlf in en_GB
translation completed for the source file '/src-ui/messages.xlf'
on the 'en_GB' language.
2021-02-26 14:36:04 +00:00
jonaswinkler
29e6529884 Merge branch 'master' into dev 2021-02-26 15:29:58 +01:00
jonaswinkler
cb4f822a19 documentation changes 2021-02-26 15:29:33 +01:00
jonaswinkler
2159fefc87 Merge branch 'master' into dev 2021-02-26 14:54:47 +01:00
jonaswinkler
d341bf14ff Merge branch 'feature-invert-thumb-option' into dev 2021-02-26 14:31:18 +01:00
Jonas Winkler
c793ac9c6c Merge pull request #638 from smaktacular/fix-dev-setup-docs
Added First-Time Step-by-Step procedure for Dev Env and CopyPaste lists of depen…
2021-02-26 14:08:43 +01:00
jonaswinkler
289589654b tests for API versioning 2021-02-26 13:21:33 +01:00
jonaswinkler
8cb9c9faf9 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-26 13:09:14 +01:00
jonaswinkler
1c0069e95e fix pycodestyle 2021-02-26 13:09:02 +01:00
Jonas Winkler
8f23642e7d Merge pull request #643 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_de
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'de'
2021-02-26 12:59:56 +01:00
Jonas Winkler
b59758379e Merge pull request #642 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-02-26 12:59:47 +01:00
transifex-integration[bot]
d91267ee91 Apply translations in de
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'de' language.
2021-02-26 11:59:26 +00:00
transifex-integration[bot]
6196bc7b2e Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-02-26 11:59:16 +00:00
jonaswinkler
adfe5c123c update messages 2021-02-26 12:57:36 +01:00
jonaswinkler
573726d0b2 add Italian locale 2021-02-26 12:55:39 +01:00
Sebastian König
5cb95b6e55 Minor changes 2021-02-26 12:50:12 +01:00
Jonas Winkler
4ce79955f8 Merge pull request #640 from jonaswinkler/translations_src-ui-messages-xlf--dev_it
Translate '/src-ui/messages.xlf' in 'it'
2021-02-26 12:35:17 +01:00
Jonas Winkler
3d320b4e61 Merge pull request #641 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_it
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'it'
2021-02-26 12:34:58 +01:00
Bolko Schreiber
420feb3a52 lowered thumb brightness in non-inverted mode 2021-02-26 12:26:23 +01:00
transifex-integration[bot]
4d425cf7b8 Apply translations in it
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'it' language.
2021-02-26 11:24:55 +00:00
transifex-integration[bot]
f716300152 Translate /src-ui/messages.xlf in it
translation completed for the source file '/src-ui/messages.xlf'
on the 'it' language.
2021-02-26 11:02:24 +00:00
jonaswinkler
fea625e443 Merge branch 'dev' into fix/issue-603 2021-02-26 11:27:53 +01:00
jonaswinkler
068fdbd3f9 added versioning headers to the API 2021-02-26 11:13:28 +01:00
Sebastian König
fd2cbc13ff Added First-Time Step-by-Step procedure and CopyPaste lists of depeendencies to the docs 2021-02-26 09:49:36 +01:00
jonaswinkler
6a70b9c4b4 Merge branch 'feature-autocolor' into dev 2021-02-25 23:46:27 +01:00
jonaswinkler
4ee7d16d3e fix #600 2021-02-25 23:23:26 +01:00
jonaswinkler
6fd25da80d API documentation 2021-02-25 23:04:37 +01:00
jonaswinkler
2fb286cd43 shadows 2021-02-25 22:49:47 +01:00
jonaswinkler
3a75b2571a increase luminance threshold for better readability 2021-02-25 22:49:38 +01:00
jonaswinkler
66fe821b04 color picker fixes, random default color 2021-02-25 22:43:45 +01:00
jonaswinkler
ba478c6cbb tests 2021-02-25 22:17:18 +01:00
jonaswinkler
7b1145c75e bugfixes 2021-02-25 22:16:31 +01:00
jonaswinkler
b45630080b Merge branch 'dev' into feature-autocolor 2021-02-25 18:54:40 +01:00
jonaswinkler
170ddbc76f Merge branch 'dev' 2021-02-25 17:53:17 +01:00
Jonas Winkler
91b364f531 Merge pull request #630 from C0nsultant/ansible-CI-stability
Ansible - Improve CI stability
2021-02-25 17:50:40 +01:00
Michael Shamoon
aadcc85e73 fix forgot to remove class when nested nav-link-additional scss block 2021-02-25 07:48:48 -08:00
Michael Shamoon
548b1ea7ec Allow closing individual documents from sidebar 2021-02-25 07:43:02 -08:00
jonaswinkler
463696e6a8 changelog 2021-02-25 16:42:06 +01:00
Michael Shamoon
66f7ae3773 Remove redundant openDocuments and unused subscription 2021-02-25 07:29:51 -08:00
jonaswinkler
cc2b836646 fix date input error display 2021-02-25 16:29:49 +01:00
jonaswinkler
816a4c1887 fix an issue with the pt-br translation 2021-02-25 16:24:24 +01:00
jonaswinkler
604da64f52 fixed a regression in the date pipe 2021-02-25 16:22:50 +01:00
jonaswinkler
247edc89f0 fix default api version 2021-02-25 16:04:45 +01:00
Bolko Schreiber
6a1f5fb4cd cleanup css 2021-02-25 15:35:15 +01:00
Jonas Winkler
01c11f9fa0 Merge pull request #634 from chriscn/patch-1
Fixed typo of scripts
2021-02-25 15:32:59 +01:00
Christopher Nethercott
9434851c54 Fixed typo of scripts 2021-02-25 14:16:22 +00:00
Bolko Schreiber
a331e4b49c Set default value of DARK_MODE_THUMB_INVERTED to true 2021-02-25 14:52:55 +01:00
Bolko Schreiber
772f5cb1a8 Added option to invert thumbnails in dark mode 2021-02-25 13:57:45 +01:00
jonaswinkler
0fa9d71da8 fix duplicate IDs 2021-02-25 11:42:38 +01:00
jonaswinkler
3872c3c49b fixes #603 2021-02-25 11:42:21 +01:00
jonaswinkler
018b53db1b fix pt-br calendar 2021-02-25 11:37:39 +01:00
jonaswinkler
33ed2c8851 adjust frontend to use the new tag color api 2021-02-25 11:32:22 +01:00
jonaswinkler
c75e30fd28 validate hex color strings 2021-02-25 11:30:49 +01:00
jonaswinkler
7cc940a16c add text color generation based on luminance 2021-02-25 11:30:36 +01:00
Fabian Koller
ba853be00d Use local HEAD hash as installation target
GitHub actions' GITHUB_SHA provides unexpected hashes for manually
re-run pipelines. Instead rely on the commit as seen from the current
git history.
2021-02-25 06:59:27 +01:00
Fabian Koller
a720ed6a77 Do not require molecule teardown for success
CI failures should not be caused by intermittently failing docker
communication furing test teardown.
2021-02-25 06:22:59 +01:00
jonaswinkler
d84ca511d4 versioning for the tags API 2021-02-24 23:54:19 +01:00
jonaswinkler
95fe803bf6 remove colorhash (this will be done client side) 2021-02-24 23:52:52 +01:00
jonaswinkler
87c364a104 adjust migration 2021-02-24 23:52:25 +01:00
jonaswinkler
7233695824 Merge branch 'dev' into feature-autocolor 2021-02-24 23:10:59 +01:00
jonaswinkler
3d3300ac32 add versioning support to the API 2021-02-24 22:27:43 +01:00
Michael Shamoon
ef7775b658 Remove object tag contents 2021-02-24 13:08:39 -08:00
Michael Shamoon
a8776603c3 Fix removed setting 2021-02-24 13:08:31 -08:00
Michael Shamoon
ee92ab1136 Support large cards 2021-02-24 13:02:26 -08:00
Michael Shamoon
83c88ca472 Unused settings 2021-02-24 12:48:58 -08:00
Michael Shamoon
6f22297158 Merge remote-tracking branch 'upstream/dev' into feature/popover-previews 2021-02-24 12:41:44 -08:00
Jonas Winkler
df87599f1b Merge pull request #625 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_fr
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'fr'
2021-02-24 20:53:25 +01:00
Jonas Winkler
367b5e73cb Merge pull request #626 from jonaswinkler/translations_src-ui-messages-xlf--dev_fr
Translate '/src-ui/messages.xlf' in 'fr'
2021-02-24 20:53:13 +01:00
transifex-integration[bot]
d6cbea97f9 Translate /src-ui/messages.xlf in fr
translation completed for the source file '/src-ui/messages.xlf'
on the 'fr' language.
2021-02-24 19:30:01 +00:00
transifex-integration[bot]
e43ab23a45 Apply translations in fr
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'fr' language.
2021-02-24 19:29:33 +00:00
Michael Shamoon
706b1f4622 Remove ng2-pdf-viewer and use only native viewer 2021-02-24 11:26:39 -08:00
jonaswinkler
5b75321a31 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-24 20:02:10 +01:00
Michael Shamoon
2f2b5b90ea Dark mode compatibility 2021-02-24 19:58:51 +01:00
Jonas Winkler
cf3dedf4bb Merge pull request #622 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_en_GB
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'en_GB'
2021-02-24 19:43:59 +01:00
Jonas Winkler
d874145f52 Merge pull request #623 from jonaswinkler/translations_src-ui-messages-xlf--dev_en_GB
Translate '/src-ui/messages.xlf' in 'en_GB'
2021-02-24 19:43:51 +01:00
transifex-integration[bot]
800f4deb90 Translate /src-ui/messages.xlf in en_GB
translation completed for the source file '/src-ui/messages.xlf'
on the 'en_GB' language.
2021-02-24 18:43:40 +00:00
transifex-integration[bot]
c22af0a782 Apply translations in en_GB
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'en_GB' language.
2021-02-24 18:43:39 +00:00
Jonas Winkler
8465b8cfca Update README.md 2021-02-24 19:27:16 +01:00
jonaswinkler
6ae65e0ab4 changelog 2021-02-24 19:21:15 +01:00
jonaswinkler
6a64bf6fc1 changelog 2021-02-24 19:05:06 +01:00
jonaswinkler
f038bb90bb fixes for #600 2021-02-24 19:03:21 +01:00
jonaswinkler
a5ba14e446 version bump 2021-02-24 19:03:03 +01:00
Jonas Winkler
70f657beff Merge pull request #602 from C0nsultant/ansible-newocrvars
Ansible - Update exposed configuration variables
2021-02-24 18:56:20 +01:00
jonaswinkler
b1085c36ff Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-24 18:45:16 +01:00
jonaswinkler
1896aa7da1 use new date inputs for the date filter as well 2021-02-24 18:45:05 +01:00
Jonas Winkler
087856c535 Merge pull request #621 from jonaswinkler/translations_src-ui-messages-xlf--dev_pt_BR
Translate '/src-ui/messages.xlf' in 'pt_BR'
2021-02-24 18:26:55 +01:00
Jonas Winkler
27f1364caa Merge pull request #620 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_pt_BR
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'pt_BR'
2021-02-24 18:26:33 +01:00
transifex-integration[bot]
bec5365ac9 Translate /src-ui/messages.xlf in pt_BR
translation completed for the source file '/src-ui/messages.xlf'
on the 'pt_BR' language.
2021-02-24 17:18:26 +00:00
transifex-integration[bot]
0db14d6f74 Apply translations in pt_BR
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'pt_BR' language.
2021-02-24 17:18:07 +00:00
Jonas Winkler
ccb44e1bd1 Merge pull request #618 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_de
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'de'
2021-02-24 18:11:48 +01:00
Jonas Winkler
8c3db627e4 Merge pull request #619 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-02-24 18:11:36 +01:00
transifex-integration[bot]
56204f8497 Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-02-24 17:10:24 +00:00
transifex-integration[bot]
fc75e88cb7 Apply translations in de
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'de' language.
2021-02-24 17:10:20 +00:00
jonaswinkler
d2719b5309 added a missing string 2021-02-24 18:08:55 +01:00
jonaswinkler
e5254abfcd Merge remote-tracking branch 'origin/master' into dev 2021-02-24 18:07:57 +01:00
jonaswinkler
96b8d35a00 update dependencies 2021-02-24 18:00:35 +01:00
jonaswinkler
035b0a449b use ng-bootstrap date selector, with proper formatting/parsing according to the current locale #177 2021-02-24 18:00:26 +01:00
jonaswinkler
6b20177b09 update README.md 2021-02-24 17:41:32 +01:00
jonaswinkler
15861ea41a add translation strings for portuguese 2021-02-24 16:50:05 +01:00
jonaswinkler
d836756e19 fix up quoting (hope this gets pushed into transifex 2021-02-24 16:43:55 +01:00
jonaswinkler
191d9a7b2b add configuration for pt-br 2021-02-24 16:39:41 +01:00
Jonas Winkler
0394cd7631 Merge pull request #617 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_pt_BR
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'pt_BR'
2021-02-24 16:19:00 +01:00
transifex-integration[bot]
c1ddff4ee5 Apply translations in pt_BR
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'pt_BR' language.
2021-02-24 15:00:14 +00:00
Jonas Winkler
55cc428cd3 Merge pull request #616 from jonaswinkler/translations_src-ui-messages-xlf--dev_pt_BR
Translate '/src-ui/messages.xlf' in 'pt_BR'
2021-02-24 15:03:51 +01:00
transifex-integration[bot]
39505c9764 Translate /src-ui/messages.xlf in pt_BR
translation completed for the source file '/src-ui/messages.xlf'
on the 'pt_BR' language.
2021-02-24 13:33:41 +00:00
Jonas Winkler
887a2f9be1 Merge pull request #615 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_de
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'de'
2021-02-24 12:53:22 +01:00
transifex-integration[bot]
1328fa9e5e Apply translations in de
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'de' language.
2021-02-24 11:52:24 +00:00
Jonas Winkler
513830f319 Merge pull request #612 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_fr
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'fr'
2021-02-24 11:13:36 +01:00
transifex-integration[bot]
440abcda7c Apply translations in fr
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'fr' language.
2021-02-24 09:09:47 +00:00
Jonas Winkler
6f3fdbecea Merge pull request #611 from bish0polis/patch-1
Update README.md
2021-02-24 09:40:50 +01:00
Bishop Clark
3a46cc33ee Update README.md
Like 'stuff' and 'traffic', 'mail' gets no 's'.
2021-02-23 22:09:12 -08:00
Jonas Winkler
ae3eb84e01 Merge pull request #600 from joelnordell/apple-touch-icon
Add apple-touch-icon for iOS devices "Add to Home Screen"
2021-02-23 16:18:56 +01:00
jonaswinkler
7a73e18596 fix pycodestyle and messages 2021-02-23 13:21:11 +01:00
jonaswinkler
c89779e8fc Merge branch 'master' into dev 2021-02-23 13:17:23 +01:00
jonaswinkler
5ca8d10d04 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-23 13:17:20 +01:00
jonaswinkler
1ce6aef57c Merge branch 'master' of github.com:jonaswinkler/paperless-ng 2021-02-23 13:17:08 +01:00
jonaswinkler
49b23aafa3 documentation typos 2021-02-23 13:16:56 +01:00
jonaswinkler
f888647b12 front end support for displaying error messages about regular expressions. 2021-02-23 13:13:38 +01:00
jonaswinkler
065ff6eaf5 rename some steps 2021-02-23 13:13:12 +01:00
Jonas Winkler
bf976fe188 Merge pull request #608 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_de
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'de'
2021-02-23 13:12:29 +01:00
transifex-integration[bot]
5ce73574c8 Apply translations in de
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'de' language.
2021-02-23 12:12:00 +00:00
jonaswinkler
5d94a983d2 move codestyle checks into separate job 2021-02-23 13:09:41 +01:00
Jonas Winkler
25366af4bb Update CONTRIBUTING.md 2021-02-23 13:03:20 +01:00
jonaswinkler
f397c5472c validation for regular expressions on matching models #605 2021-02-23 12:35:24 +01:00
Fabian Koller
6273eb0061 Handle OCR languages with hyphens
See https://github.com/jonaswinkler/paperless-ng/issues/584#issuecomment-782830878
2021-02-23 08:48:46 +01:00
Fabian Koller
734fd7c0fc Update exposed configuration variables
Include the newly added OCR clean and deskew parameters
2021-02-23 08:35:10 +01:00
Joel Nordell
8797a58efc Add apple-touch-icon for iOS devices "Add to Home Screen" 2021-02-22 22:25:10 -06:00
Jonas Winkler
8da85d3609 Update ci.yml 2021-02-22 13:25:21 +01:00
jonaswinkler
127d30918d lets hope this works! 2021-02-22 12:03:07 +01:00
jonaswinkler
3b553f6455 changelog 2021-02-22 11:53:13 +01:00
jonaswinkler
6d934da5dd Revert "associate error messages with documents"
This reverts commit aa3d91a3
2021-02-22 11:52:54 +01:00
jonaswinkler
aa3d91a338 associate error messages with documents 2021-02-22 11:38:16 +01:00
jonaswinkler
d64818b46c fixes #591 2021-02-22 11:11:04 +01:00
jonaswinkler
99a18516b2 tests 2021-02-22 00:17:16 +01:00
jonaswinkler
30b0a30146 dropdown menu shadows 2021-02-22 00:04:44 +01:00
jonaswinkler
cb10617979 enable deskewing and rotation by default 2021-02-21 23:40:26 +01:00
jonaswinkler
265432f2a5 fix up the ocrmypdf parameter construction for clean-final and redo 2021-02-21 23:39:19 +01:00
jonaswinkler
a13e9f23b1 use archived file for thumbnail, if available 2021-02-21 23:30:14 +01:00
jonaswinkler
65b37f61ca update thumbnail in archiver, since page rotation might have changed 2021-02-21 23:29:52 +01:00
jonaswinkler
7751755399 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-21 22:27:58 +01:00
jonaswinkler
14e2ad7bc4 more parameter checking 2021-02-21 22:19:24 +01:00
jonaswinkler
dfc23a2b38 bugfix for tika parser 2021-02-21 21:36:43 +01:00
Jonas Winkler
d2fc840293 Merge pull request #587 from jonaswinkler/translations_src-ui-messages-xlf--dev_fr
Translate '/src-ui/messages.xlf' in 'fr'
2021-02-21 16:01:53 +01:00
Jonas Winkler
37fe6fb9c3 Merge pull request #586 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_fr
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'fr'
2021-02-21 16:01:41 +01:00
transifex-integration[bot]
a21ec76997 Translate /src-ui/messages.xlf in fr
translation completed for the source file '/src-ui/messages.xlf'
on the 'fr' language.
2021-02-21 14:16:49 +00:00
transifex-integration[bot]
501d8d9683 Apply translations in fr
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'fr' language.
2021-02-21 14:15:43 +00:00
jonaswinkler
8562ca9a77 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-21 13:44:15 +01:00
jonaswinkler
29641e5d66 Merge branch 'master' into dev 2021-02-21 13:44:07 +01:00
jonaswinkler
ee7308be2d documentation on how to build the documentation 2021-02-21 13:43:54 +01:00
jonaswinkler
ef4009e94f documentation 2021-02-21 13:35:47 +01:00
Jonas Winkler
27d2ae6976 Merge pull request #585 from jonaswinkler/translations_src-ui-messages-xlf--dev_en_GB
Translate '/src-ui/messages.xlf' in 'en_GB'
2021-02-21 13:12:31 +01:00
transifex-integration[bot]
0f9675f9d6 Translate /src-ui/messages.xlf in en_GB
translation completed for the source file '/src-ui/messages.xlf'
on the 'en_GB' language.
2021-02-21 12:12:19 +00:00
jonaswinkler
bac4a63cc8 run the polling file change checks on individual threads to speed up queueing of new files 2021-02-21 12:43:55 +01:00
jonaswinkler
0453787d38 increased default delay when waiting for file changes with polling 2021-02-21 12:14:54 +01:00
jonaswinkler
afc3e41f13 changelog 2021-02-21 01:48:14 +01:00
jonaswinkler
86d6316cc9 version bump 2021-02-21 01:30:03 +01:00
jonaswinkler
7b2c1f82f5 documentation 2021-02-21 01:29:55 +01:00
jonaswinkler
e2a932d744 update dependencies 2021-02-21 00:24:33 +01:00
jonaswinkler
b978994525 documentation for the new configuration options 2021-02-21 00:23:01 +01:00
jonaswinkler
6da237dd9e pycodestyle 2021-02-21 00:21:43 +01:00
jonaswinkler
50c1978d36 tests 2021-02-21 00:18:34 +01:00
jonaswinkler
fdb310c497 changelog 2021-02-21 00:17:12 +01:00
jonaswinkler
ce121a261d completely reworked the OCRmyPDF parser. 2021-02-21 00:16:57 +01:00
jonaswinkler
ebdfd4241a Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-21 00:13:17 +01:00
jonaswinkler
9cbb1c5726 add some test files 2021-02-21 00:13:08 +01:00
Jonas Winkler
85dabccbe7 Merge pull request #579 from jonaswinkler/translations_src-ui-messages-xlf--dev_nl_NL
Translate '/src-ui/messages.xlf' in 'nl_NL'
2021-02-20 19:01:08 +01:00
transifex-integration[bot]
a9a8189d4b Translate /src-ui/messages.xlf in nl_NL
translation completed for the source file '/src-ui/messages.xlf'
on the 'nl_NL' language.
2021-02-20 17:45:27 +00:00
Jonas Winkler
30579112d2 Merge pull request #578 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-02-20 16:45:20 +01:00
transifex-integration[bot]
ccfd009c1a Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-02-20 15:44:19 +00:00
jonaswinkler
044a939623 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-20 16:11:25 +01:00
jonaswinkler
203bc162cd front end support for downloading multiple documents 2021-02-20 16:10:50 +01:00
jonaswinkler
31f03ef1d3 API support for downloading compressed archives of multiple documents 2021-02-20 16:09:29 +01:00
Jonas Winkler
4d3552dc64 Merge pull request #570 from jonaswinkler/translations_src-ui-messages-xlf--dev_en_GB
Translate '/src-ui/messages.xlf' in 'en_GB'
2021-02-19 12:18:46 +01:00
Jonas Winkler
ea8a52404f Merge pull request #569 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-02-19 12:18:35 +01:00
Jonas Winkler
0ae9aecdef Update README.md 2021-02-19 11:51:59 +01:00
jonaswinkler
4de4789605 this took way too much time 2021-02-19 11:34:51 +01:00
jonaswinkler
950bb46827 version bump 2021-02-19 11:31:14 +01:00
Jonas Winkler
44936dc5f0 Update README.md 2021-02-19 11:24:21 +01:00
Jonas Winkler
1140a878b4 Update README.md 2021-02-19 11:22:43 +01:00
transifex-integration[bot]
efb49af7ac Translate /src-ui/messages.xlf in en_GB
translation completed for the source file '/src-ui/messages.xlf'
on the 'en_GB' language.
2021-02-18 16:44:55 +00:00
transifex-integration[bot]
b5a8106a6a Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-02-18 16:43:41 +00:00
jonaswinkler
0f80eee54e refactored most of the list view; fixes #147, much snappier UX when switching between views 2021-02-18 17:29:21 +01:00
jonaswinkler
0e237fa459 messages 2021-02-18 17:11:47 +01:00
Jonas Winkler
702b985ceb Merge pull request #558 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_de
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'de'
2021-02-17 14:46:25 +01:00
Jonas Winkler
7d87bcbb98 Merge pull request #560 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_en_GB
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'en_GB'
2021-02-17 14:46:14 +01:00
Jonas Winkler
340521aa0d Merge pull request #559 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_nl_NL
Translate '/src/locale/en_US/LC_MESSAGES/django.po' in 'nl_NL'
2021-02-17 14:46:03 +01:00
transifex-integration[bot]
7bc557a999 Apply translations in en_GB
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'en_GB' language.
2021-02-17 13:36:15 +00:00
jonaswinkler
dfa7cdf47e Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-17 14:26:26 +01:00
jonaswinkler
0d78e58d77 fixed paperless not properly selecting en-gb 2021-02-17 14:26:06 +01:00
transifex-integration[bot]
58df3d5767 Apply translations in nl_NL
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'nl_NL' language.
2021-02-17 12:57:48 +00:00
Jonas Winkler
4e4d6e806c Update Crowdin configuration file 2021-02-17 13:22:45 +01:00
Jonas Winkler
6ff99945f3 Update Crowdin configuration file 2021-02-17 13:18:19 +01:00
transifex-integration[bot]
b7f1b9f8ad Apply translations in de
translation completed for the source file '/src/locale/en_US/LC_MESSAGES/django.po'
on the 'de' language.
2021-02-17 11:50:01 +00:00
jonaswinkler
08a44cf468 changelog and version 2021-02-17 12:31:19 +01:00
jonaswinkler
a1162d6d5a update requirements 2021-02-17 12:25:34 +01:00
jonaswinkler
1c81d88013 add support for iso 8601 date display 2021-02-17 12:15:22 +01:00
jonaswinkler
1e4ec7e29e added en-GB language 2021-02-16 14:54:18 +01:00
jonaswinkler
2c4e34dd0c changelog 2021-02-15 23:44:48 +01:00
jonaswinkler
cb308fae7b only show inbox statistics if inbox tags are defined 2021-02-15 23:14:54 +01:00
jonaswinkler
3f03d51b24 version bump 2021-02-15 16:52:45 +01:00
jonaswinkler
831db6ab87 note regarding Python 3.6 2021-02-15 16:46:06 +01:00
jonaswinkler
43fdf634f2 added a note regarding python 3.6 2021-02-15 16:37:44 +01:00
jonaswinkler
f07a6b4586 PAPERLESS_WEBSERVER_WORKERS option 2021-02-15 16:27:35 +01:00
jonaswinkler
2fcf484229 bugfix dismissing wrong status messages 2021-02-15 14:52:47 +01:00
jonaswinkler
8bf4241b16 some search index optimizations 2021-02-15 13:26:36 +01:00
jonaswinkler
56bd966c02 local import of ocrmypdf so that the webserver does not load that 2021-02-15 12:18:10 +01:00
jonaswinkler
416101d557 only import dateparser when required 2021-02-15 11:52:46 +01:00
jonaswinkler
c330cca2c9 remove unused imports 2021-02-15 11:26:13 +01:00
jonaswinkler
7e88085377 load sklearn modules only when training data has changed 2021-02-15 11:25:25 +01:00
jonaswinkler
5e669534f2 reorganized test case 2021-02-14 17:24:31 +01:00
jonaswinkler
98b147b622 better sanity checker that logs messages in the log files and does not fail on warnings. 2021-02-14 17:08:29 +01:00
jonaswinkler
df6c59bc4f update dependencies 2021-02-14 15:38:47 +01:00
jonaswinkler
6e48da41e5 changelog 2021-02-14 14:05:42 +01:00
Jonas Winkler
5c8a01a6e8 Merge pull request #538 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_cs
Translate '/src/locale/en-us/LC_MESSAGES/django.po' in 'cs'
2021-02-14 13:41:33 +01:00
jonaswinkler
3d0a52c25f only load channels app if DEBUG is enabled; its only purpose is to monkey-patch the runserver command. 2021-02-14 12:50:30 +01:00
jonaswinkler
43c729568b release worker memory after tasks are done. 2021-02-14 12:29:55 +01:00
transifex-integration[bot]
62caeed283 Apply translations in cs
translation completed for the source file '/src/locale/en-us/LC_MESSAGES/django.po'
on the 'cs' language.
2021-02-14 07:05:05 +00:00
jonaswinkler
12836d4c68 revert django-q configuration 2021-02-13 20:25:52 +01:00
jonaswinkler
b48e67d714 revert a faulty change that caused memory usage to explode #537 2021-02-13 19:51:04 +01:00
jonaswinkler
f91f4d71bb Merge branch 'master' into dev 2021-02-13 18:09:14 +01:00
jonaswinkler
0a1f264c71 Gotenberg troubleshooting 2021-02-13 18:09:00 +01:00
jonaswinkler
64d61ae2fa version bump 2021-02-13 18:01:19 +01:00
jonaswinkler
5f0e800f6e metadata tab not showing anything if files are missing #534 2021-02-13 16:41:03 +01:00
jonaswinkler
8b2965d55b added sanity checker management command for manual execution #534 2021-02-13 16:39:29 +01:00
jonaswinkler
ed478a1d73 change thumbnail display for extra wide images #433 2021-02-12 18:20:17 +01:00
jonaswinkler
13e91d8c95 changelog 2021-02-12 18:04:15 +01:00
jonaswinkler
6ac90181cb documentation and changelog 2021-02-12 16:54:00 +01:00
jonaswinkler
d6c3471909 reprganized docker file, less layers, new shortcuts for management commands 2021-02-12 16:53:51 +01:00
jonaswinkler
5b56fad9c7 fix test case 2021-02-12 01:31:50 +01:00
jonaswinkler
ed0b1fe115 better exception logging 2021-02-11 22:16:41 +01:00
jonaswinkler
4211153527 update file renaming logic 2021-02-11 13:47:17 +01:00
jonaswinkler
2f85461109 added some test cases that I still need to address 2021-02-10 23:53:48 +01:00
jonaswinkler
3fa7dcb0cb changes to the admin document list 2021-02-10 21:34:58 +01:00
jonaswinkler
857fe3a55c fix one incorrect use of archive_version 2021-02-10 21:34:39 +01:00
jonaswinkler
dd19ea46fe backup documentation 2021-02-10 20:14:55 +01:00
jonaswinkler
21740a9d87 changelog 2021-02-10 20:05:02 +01:00
jonaswinkler
658fb2f208 remove invalid test cases 2021-02-10 20:01:35 +01:00
jonaswinkler
252d4cb513 update document admin 2021-02-10 18:55:39 +01:00
jonaswinkler
5aed41223b Merge branch 'master' into dev 2021-02-10 18:44:02 +01:00
jonaswinkler
45dfbf3747 downgrades 2021-02-10 18:27:41 +01:00
jonaswinkler
e4fe5bebab requirements 2021-02-10 17:09:22 +01:00
jonaswinkler
04519ee623 more testing of the migration 2021-02-10 16:58:55 +01:00
jonaswinkler
6c8f010f7a retries for archive generation 2021-02-10 14:50:20 +01:00
jonaswinkler
ed84cf26e7 update dependencies 2021-02-10 14:31:17 +01:00
jonaswinkler
1bc961f0c0 update dependencies 2021-02-10 11:50:57 +01:00
jonaswinkler
77d745381f more testing 2021-02-10 01:31:15 +01:00
jonaswinkler
7082cb9c36 document renamer testing 2021-02-10 01:12:45 +01:00
jonaswinkler
34e84cc757 sanity checker testing 2021-02-10 00:52:18 +01:00
jonaswinkler
9246411610 better logging for the migration 2021-02-10 00:52:01 +01:00
jonaswinkler
8330b3598c changelog 2021-02-09 23:23:11 +01:00
jonaswinkler
1d002149dc added ASN to filename format #519 2021-02-09 23:03:07 +01:00
jonaswinkler
8d6071e977 fix a bug with thumbnail generation when TIKA was enabled 2021-02-09 22:12:43 +01:00
jonaswinkler
7d67766508 todo note #520 2021-02-09 21:53:10 +01:00
jonaswinkler
887dd122fe more info in the admin 2021-02-09 21:00:04 +01:00
jonaswinkler
a1293c77b9 fix migration and more tests 2021-02-09 20:54:02 +01:00
jonaswinkler
ee9a73aa95 codestyle 2021-02-09 20:46:41 +01:00
jonaswinkler
9df332b614 test resources 2021-02-09 19:51:25 +01:00
jonaswinkler
d13e86a892 update all test cases to address the archive filename changes 2021-02-09 19:51:16 +01:00
jonaswinkler
69d7f8c180 testing the updated migration 2021-02-09 19:49:29 +01:00
jonaswinkler
1ba89ddd09 refactor migration tests to allow testing for exceptions while migrating 2021-02-09 19:47:50 +01:00
jonaswinkler
0c40a28ad3 more sanity checks regarding archive versions 2021-02-09 19:46:59 +01:00
jonaswinkler
2b7424c42a imports 2021-02-09 19:46:42 +01:00
jonaswinkler
a9f1766d1c todo note 2021-02-09 19:46:32 +01:00
jonaswinkler
fca8576d80 archive filenames are now stored in the database and checked for collisions just as original filenames as well, unified method for archive version checking 2021-02-09 19:46:19 +01:00
jonaswinkler
05f59e7d5e another way to make the test case fail 2021-02-09 02:13:25 +01:00
jonaswinkler
c9511680b3 version push 2021-02-09 01:36:39 +01:00
jonaswinkler
0ed001c56e validate move before migration 2021-02-09 00:13:13 +01:00
jonaswinkler
1e5a418191 more testing #511 2021-02-09 00:01:11 +01:00
jonaswinkler
e05735bc0f fix some test cases 2021-02-09 00:00:46 +01:00
jonaswinkler
7621e10840 only move unaffected files, regenerate affected files 2021-02-08 23:54:07 +01:00
jonaswinkler
d90080f325 only move files if necessary 2021-02-08 22:49:01 +01:00
jonaswinkler
0c676b90f2 migration for #511 2021-02-08 20:59:14 +01:00
jonaswinkler
c2d8bda83c fix for #511 2021-02-08 19:59:14 +01:00
jonaswinkler
302ebf737e refactor migration test case 2021-02-08 13:18:39 +01:00
jonaswinkler
816c95a4ae code style 2021-02-08 13:18:08 +01:00
jonaswinkler
40106f6fcc updated documentation regarding execution of management commands with docker fixes #509 2021-02-08 00:10:52 +01:00
jonaswinkler
61143b3ad1 make the test case fail 2021-02-07 19:53:08 +01:00
jonaswinkler
9b64eebd10 revert commit 2021-02-07 18:26:03 +01:00
jonaswinkler
731418349f added a test case that replicates #511 2021-02-07 18:23:54 +01:00
Michael Shamoon
5306d58991 Dark mode + sizing tweaks 2021-02-07 08:19:00 -08:00
jonaswinkler
7728920670 Merge branch 'dev' 2021-02-07 01:22:04 +01:00
jonaswinkler
f555bb95ae possible fix for the ansible roles 2021-02-07 00:49:53 +01:00
jonaswinkler
bcee17c3ac github actions: build release tags 2021-02-07 00:14:24 +01:00
Michael Shamoon
dcb17f2f21 hide on card mouseleave 2021-02-06 14:54:36 -08:00
Michael Shamoon
37b3a30619 Better minimum size for native viewer 2021-02-06 14:30:44 -08:00
jonaswinkler
7bc16edfe2 Merge branch 'master' into dev 2021-02-06 23:28:39 +01:00
Michael Shamoon
b0fed61a01 Pre-load pdf contents 2021-02-06 14:28:26 -08:00
Jonas Winkler
40f3e8e45a Update README.md 2021-02-06 23:26:44 +01:00
Michael Shamoon
4a471138b2 Dark mode popover arrow fixes 2021-02-06 14:25:53 -08:00
jonaswinkler
656b4708ac update requirements.txt 2021-02-06 21:16:06 +01:00
jonaswinkler
7702f5012b classifier cache timeout 2021-02-06 21:03:32 +01:00
jonaswinkler
ffe96c8fff classifier caching 2021-02-06 20:54:58 +01:00
jonaswinkler
0024c2aae4 caching 2021-02-06 20:51:04 +01:00
jonaswinkler
ed39e9e15f update copyright #506 2021-02-06 20:48:23 +01:00
jonaswinkler
fd8c40fee7 increase worker timeout 2021-02-06 18:51:53 +01:00
jonaswinkler
0e76441684 fix log viewer for empty logs 2021-02-06 18:51:31 +01:00
jonaswinkler
d2c34fb9c7 update requirements.txt 2021-02-06 17:37:47 +01:00
jonaswinkler
d09ea8408c keep numpy below 1.20, since that breaks python 3.6 2021-02-06 17:36:31 +01:00
jonaswinkler
e8a1fde1c7 documentation 2021-02-06 17:22:05 +01:00
jonaswinkler
5ef6f2697e default settings 2021-02-06 17:21:40 +01:00
jonaswinkler
80b3fa75a5 pycodestyle 2021-02-06 17:21:32 +01:00
jonaswinkler
9993381cda rework of the log viewer 2021-02-06 17:07:25 +01:00
jonaswinkler
9fa3f91e8a update logging config 2021-02-06 17:05:07 +01:00
jonaswinkler
fe435f6653 update API access for logs 2021-02-06 17:02:00 +01:00
jonaswinkler
5696c02ac3 remove log admin 2021-02-06 17:00:06 +01:00
jonaswinkler
019dfeb3ee separate mail logging 2021-02-06 16:59:04 +01:00
jonaswinkler
e35723e7af fix some logging messages 2021-02-06 15:30:47 +01:00
jonaswinkler
1d50d017ac update bare metal instructions 2021-02-06 14:54:00 +01:00
jonaswinkler
9e5d1ad952 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-06 13:44:08 +01:00
Jonas Winkler
9faaf4b1e4 Merge pull request #504 from jonaswinkler/translations_src-ui-messages-xlf--dev_en_GB
Translate '/src-ui/messages.xlf' in 'en_GB'
2021-02-06 13:33:28 +01:00
Jonas Winkler
ac229ce004 Merge pull request #503 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_en_GB
Translate '/src/locale/en-us/LC_MESSAGES/django.po' in 'en_GB'
2021-02-06 13:32:59 +01:00
Michael Shamoon
41540a3a5f Document popover previews 2021-02-06 00:40:18 -08:00
transifex-integration[bot]
968614a289 Translate /src-ui/messages.xlf in en_GB
translation completed for the source file '/src-ui/messages.xlf'
on the 'en_GB' language.
2021-02-06 00:12:00 +00:00
transifex-integration[bot]
2f68f236bb Apply translations in en_GB
translation completed for the source file '/src/locale/en-us/LC_MESSAGES/django.po'
on the 'en_GB' language.
2021-02-06 00:09:49 +00:00
jonaswinkler
00903bd90c update statistics when new documents are added 2021-02-06 00:13:49 +01:00
jonaswinkler
96d7114fa7 lazy load fuzzy only when required 2021-02-05 01:11:03 +01:00
jonaswinkler
431d4fd8e4 rework most of the logging 2021-02-05 01:10:29 +01:00
jonaswinkler
0d3ab3aaf7 remove lots of unused code 2021-02-04 23:40:53 +01:00
jonaswinkler
138f38d1f3 fix lost messages when queuing many files 2021-02-04 23:32:11 +01:00
jonaswinkler
222fb48378 dismiss completed dismisses successful items only 2021-02-04 16:55:48 +01:00
jonaswinkler
5718f50c4f increase worker timeout for Raspberry Pi 2021-02-04 15:46:51 +01:00
jonaswinkler
d8e0ef257e don't load sklearn libraries unless needed 2021-02-04 15:15:11 +01:00
jonaswinkler
866c8fc848 websocket testing 2021-02-04 14:17:18 +01:00
Jonas Winkler
758631c53f Update README.md 2021-02-04 13:31:20 +01:00
jonaswinkler
8711a206a0 fix websocket test cases 2021-02-04 13:26:58 +01:00
jonaswinkler
44ec3a3d9c lazy loading for parsers 2021-02-04 13:17:24 +01:00
jonaswinkler
883a6b26a4 update gunicorn.conf 2021-02-04 00:17:23 +01:00
jonaswinkler
b9e1ed7d7e added test for web socket authentication 2021-02-03 23:18:28 +01:00
jonaswinkler
c51fa60172 close connection when not authenticated anymore. 2021-02-03 21:53:39 +01:00
jonaswinkler
e300ee0c92 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-03 20:44:37 +01:00
jonaswinkler
ba3c8308b3 websocket authentication 2021-02-03 20:44:10 +01:00
Jonas Winkler
63b11514c9 Merge pull request #501 from jonaswinkler/translations_src-ui-messages-xlf--dev_fr
Translate '/src-ui/messages.xlf' in 'fr'
2021-02-03 18:45:25 +01:00
transifex-integration[bot]
49d2084d8a Translate /src-ui/messages.xlf in fr
translation completed for the source file '/src-ui/messages.xlf'
on the 'fr' language.
2021-02-03 17:10:45 +00:00
jonaswinkler
c36fe577de Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-03 17:08:07 +01:00
jonaswinkler
28c79f1abb documentation 2021-02-03 17:07:37 +01:00
Jonas Winkler
a1f07417da Merge pull request #500 from jonaswinkler/translations_src-ui-messages-xlf--dev_nl_NL
Translate '/src-ui/messages.xlf' in 'nl_NL'
2021-02-03 16:32:52 +01:00
transifex-integration[bot]
611b4fe31b Translate /src-ui/messages.xlf in nl_NL
translation completed for the source file '/src-ui/messages.xlf'
on the 'nl_NL' language.
2021-02-03 15:25:14 +00:00
Jonas Winkler
697670ef97 Merge pull request #499 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-02-03 16:13:43 +01:00
transifex-integration[bot]
ee487529fe Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-02-03 15:11:49 +00:00
jonaswinkler
2129cda38d display http errors 2021-02-03 16:01:32 +01:00
jonaswinkler
afb63f763a Merge branch 'master' into dev 2021-02-03 15:24:38 +01:00
jonaswinkler
a186d6fae2 bugfix 2021-02-03 15:22:20 +01:00
jonaswinkler
d17de45791 fix typo 2021-02-03 14:51:04 +01:00
Jonas Winkler
18980bbbe3 Merge pull request #498 from C0nsultant/ansible-varnames
Ansible - apply consistent name structure to vars
2021-02-03 11:49:53 +01:00
Fabian Koller
32c9369ba1 Apply consistent name structure to vars 2021-02-03 08:06:34 +01:00
jonaswinkler
bdc247ce49 code style 2021-02-02 23:58:25 +01:00
jonaswinkler
85f46424a8 fixes layout issues #482 2021-02-02 23:58:11 +01:00
jonaswinkler
d198a89f22 update dependencies 2021-02-02 20:43:07 +01:00
jonaswinkler
82730ab491 update dependencies 2021-02-02 20:31:25 +01:00
jonaswinkler
c78d54e71e update the easy install script and documentation 2021-02-02 19:54:56 +01:00
jonaswinkler
08d96a1b76 configure websocket endpoint for production 2021-02-02 16:22:36 +01:00
jonaswinkler
b2a744e880 version bump 2021-02-02 15:11:29 +01:00
jonaswinkler
3f421a4f64 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-02 14:52:07 +01:00
jonaswinkler
2624aec758 changelog 2021-02-02 14:51:54 +01:00
Jonas Winkler
f3a18c26f4 Merge pull request #494 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-02-02 14:08:11 +01:00
jonaswinkler
47ea60372f disable CI for translation branches 2021-02-02 14:07:43 +01:00
transifex-integration[bot]
8efba4c57f Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-02-02 13:07:05 +00:00
jonaswinkler
060dcd71cd messages, reorganized settings 2021-02-02 14:06:16 +01:00
Jonas Winkler
fd6e727299 Merge pull request #492 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-02-02 13:35:59 +01:00
Jonas Winkler
c4adb20eff Merge pull request #493 from jonaswinkler/translations_src-ui-messages-xlf--dev_fr
Translate '/src-ui/messages.xlf' in 'fr'
2021-02-02 13:35:47 +01:00
transifex-integration[bot]
e70af00321 Translate /src-ui/messages.xlf in fr
translation completed for the source file '/src-ui/messages.xlf'
on the 'fr' language.
2021-02-02 12:34:03 +00:00
transifex-integration[bot]
9ebe16d823 Translate /src-ui/messages.xlf in de
translated for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-02-02 12:30:13 +00:00
Jonas Winkler
fd05da96d5 Merge pull request #488 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-02-02 13:27:55 +01:00
jonaswinkler
5f44ab5abc CI: ignore translation branches 2021-02-02 13:27:08 +01:00
transifex-integration[bot]
ffc82c17ba Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-02-02 12:17:25 +00:00
jonaswinkler
b33e25b1f7 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-02-02 13:14:11 +01:00
jonaswinkler
55c50e3ac2 more message changes 2021-02-02 13:10:51 +01:00
jonaswinkler
211a88129e removed dead files 2021-02-02 12:58:23 +01:00
jonaswinkler
2a629b81ce updates messages 2021-02-02 12:58:11 +01:00
Jonas Winkler
2efb99192a add pull to docker setup 2021-02-01 21:45:45 +01:00
Jonas Winkler
0a28ccf62f adjustments to setup 2021-02-01 21:43:50 +01:00
Jonas Winkler
48b042bfb0 troubleshooting for #216 2021-02-01 20:14:29 +01:00
Jonas Winkler
da08eec91f Merge pull request #482 from shamoon/feature/sidebar-info-2
Updates to sidebar "Misc" section
2021-02-01 12:42:26 +01:00
Jonas Winkler
684f91b2e3 Merge pull request #486 from jonaswinkler/translations_src-ui-messages-xlf--dev_nl_NL
Translate '/src-ui/messages.xlf' in 'nl_NL'
2021-02-01 12:42:05 +01:00
transifex-integration[bot]
689579c7f2 Translate /src-ui/messages.xlf in nl_NL
translation completed for the source file '/src-ui/messages.xlf'
on the 'nl_NL' language.
2021-02-01 09:31:34 +00:00
Michael Shamoon
4a549ccd33 additional link spacing 2021-01-31 19:24:37 -08:00
Michael Shamoon
32f0a2fdbc Yes, 2px margin on the svg icon 2021-01-31 19:20:00 -08:00
Michael Shamoon
abee54ebe3 Fix Safari overflow issue 2021-01-31 19:16:38 -08:00
Michael Shamoon
58ae14c364 space for info heading 2021-01-31 19:11:27 -08:00
Michael Shamoon
34104f0d42 These arent needed on h6s 2021-01-31 19:11:21 -08:00
Michael Shamoon
dabcdbad00 Prevent safari wrap 2021-01-31 19:09:31 -08:00
Michael Shamoon
4c314130f7 Use div containers 2021-01-31 17:16:39 -08:00
Michael Shamoon
53a71384b0 Merge remote-tracking branch 'upstream/dev' into feature/sidebar-info-2 2021-01-31 17:04:35 -08:00
Jonas Winkler
294a5ebec6 Merge pull request #480 from shamoon/feature/github-discussion-ideas
Encourage Discussion Ideas instead of Issues for [Feature Requests]
2021-02-01 01:52:30 +01:00
Michael Shamoon
6a4b31b354 Correct discussions category link 2021-01-31 16:33:39 -08:00
Michael Shamoon
76c9715748 Change "Misc" to "Info"
Also move to bottom of screen, add suggestions link, change GH icon
2021-01-31 16:31:32 -08:00
Michael Shamoon
e657ca7496 Revert "App 'submit an idea icon'"
This reverts commit 406ec17050.
2021-01-31 16:23:08 -08:00
Michael Shamoon
ddf01eef14 Update to use new feature-requests category of discussions 2021-01-31 16:22:41 -08:00
Jonas Winkler
3d093f2c08 Merge pull request #479 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_fr
Translate '/src/locale/en-us/LC_MESSAGES/django.po' in 'fr'
2021-02-01 00:09:01 +01:00
transifex-integration[bot]
004e3134fc Apply translations in fr
translation completed for the source file '/src/locale/en-us/LC_MESSAGES/django.po'
on the 'fr' language.
2021-01-31 22:33:04 +00:00
Michael Shamoon
85ee74f2d8 Remove feature request template, update others to point to Discussions 2021-01-31 14:32:09 -08:00
Michael Shamoon
406ec17050 App 'submit an idea icon' 2021-01-31 14:19:25 -08:00
jonaswinkler
7f85180264 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-01-31 22:28:41 +01:00
jonaswinkler
ad29fc23a6 added an easy install script 2021-01-31 22:28:26 +01:00
Michael Shamoon
b9690de9ab Add GH Ideas info to readme 2021-01-31 13:19:55 -08:00
Jonas Winkler
d4deefc81c Merge pull request #478 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_nl_NL
Translate '/src/locale/en-us/LC_MESSAGES/django.po' in 'nl_NL'
2021-01-31 17:00:39 +01:00
transifex-integration[bot]
32780472db Apply translations in nl_NL
translation completed for the source file '/src/locale/en-us/LC_MESSAGES/django.po'
on the 'nl_NL' language.
2021-01-31 15:45:35 +00:00
Jonas Winkler
b1f906461e Merge pull request #476 from jonaswinkler/translations_src-locale-en-us-lc-messages-django-po--dev_de
Translate '/src/locale/en-us/LC_MESSAGES/django.po' in 'de'
2021-01-31 15:50:57 +01:00
Jonas Winkler
7e449d661b Merge pull request #477 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-01-31 15:50:48 +01:00
transifex-integration[bot]
24b3a03000 Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-01-31 14:45:58 +00:00
transifex-integration[bot]
cd28fbf563 Apply translations in de
translation completed for the source file '/src/locale/en-us/LC_MESSAGES/django.po'
on the 'de' language.
2021-01-31 14:45:43 +00:00
jonaswinkler
d7b2906977 fix typo 2021-01-31 15:37:09 +01:00
jonaswinkler
3180b38797 adjust ansible scripts to use gunicorn.conf.py 2021-01-31 15:36:52 +01:00
jonaswinkler
fd59def1bd Merge branch 'feature-websockets-status' into dev 2021-01-31 14:37:15 +01:00
jonaswinkler
8cd1dd71e8 Merge branch 'master' into dev 2021-01-31 14:03:44 +01:00
Jonas Winkler
48cfb64416 Merge pull request #474 from C0nsultant/ansible-GITHUB_SHA
Ansible - Switch CI target to sha instead of ref
2021-01-31 14:03:04 +01:00
jonaswinkler
adb76eafaf moved gunicorn.conf.py to a more appropriate place and reference that in the configuration scripts. 2021-01-31 13:52:21 +01:00
jonaswinkler
4e2efebaa7 addresses race condition between API and document consumer #472 2021-01-31 13:20:21 +01:00
Fabian Koller
eb6153c5aa Change CI ref from merge to head
At this time in the CI, merge is not accessible from tree
https://github.community/t/github-sha-but-how-to-get-the-latest-commit-reference-on-a-branch/17160
2021-01-31 12:00:41 +01:00
Fabian Koller
9c3b16c879 Switch CI target to sha instead of ref 2021-01-31 11:28:54 +01:00
jonaswinkler
ba637c8582 replace preview icon #471 2021-01-30 21:56:17 +01:00
jonaswinkler
46ea86a6d2 Merge branch 'dev' into feature-websockets-status 2021-01-30 16:08:50 +01:00
jonaswinkler
9d148c08ce Merge branch 'master' into dev 2021-01-30 16:06:42 +01:00
Jonas Winkler
ac459f7b05 Merge pull request #411 from C0nsultant/ansible-fromsource
Ansible - build release archive from source
2021-01-30 16:05:51 +01:00
jonaswinkler
35a4779cc0 fix typo 2021-01-30 15:44:23 +01:00
Jonas Winkler
ffa44f51cd Update README.md 2021-01-30 15:30:39 +01:00
jonaswinkler
682a9d2a09 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-01-30 15:23:03 +01:00
jonaswinkler
4c6a02aee7 pycodestyle 2021-01-30 15:22:51 +01:00
jonaswinkler
87a18eae2d centralized classifier loading, better error handling, no error messages when auto matching is not used 2021-01-30 14:22:23 +01:00
Fabian Koller
269673ce05 DEBUG - use very long txt file 2021-01-30 14:13:03 +01:00
Fabian Koller
d9e06958dc DEBUG - force flush 2nd try 2021-01-30 12:50:49 +01:00
Fabian Koller
063bfc245c DEBUG - force newline in .txt 2021-01-30 12:20:55 +01:00
Fabian Koller
21c501de28 force flush on temp file during consumption
file.write() does not guarantee that a file handle contains anything
without calling an accompanying file.flush()
For typical files that are larger than the OS-file buffer, this is no
problem
For small files (e.g. 64 characters in a .TXT), this race condition
leads to `inode/x-empty` because the file inode is created, but no
content is written to it
2021-01-30 11:51:43 +01:00
Jonas Winkler
d50d1bd2ea Merge pull request #470 from jonaswinkler/translations_src-ui-messages-xlf--dev_fr
Translate '/src-ui/messages.xlf' in 'fr'
2021-01-29 22:34:17 +01:00
transifex-integration[bot]
e4aa31a62d Translate /src-ui/messages.xlf in fr
translation completed for the source file '/src-ui/messages.xlf'
on the 'fr' language.
2021-01-29 19:57:40 +00:00
Jonas Winkler
fdf58bed6a Merge pull request #469 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-01-29 19:59:09 +01:00
transifex-integration[bot]
a54784da11 Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-01-29 18:59:00 +00:00
jonaswinkler
a40e4fe3bc update messages 2021-01-29 16:57:00 +01:00
jonaswinkler
ea30fbf14f Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-01-29 16:49:07 +01:00
jonaswinkler
d22d9a023c frontend support for suggestions #264 2021-01-29 16:48:51 +01:00
jonaswinkler
ddcc0883eb add support for suggestions 2021-01-29 16:45:23 +01:00
Jonas Winkler
047a21eb48 Merge pull request #468 from jonaswinkler/translations_src-ui-messages-xlf--dev_fr
Translate '/src-ui/messages.xlf' in 'fr'
2021-01-29 14:01:46 +01:00
transifex-integration[bot]
439f06ccda Translate /src-ui/messages.xlf in fr
translation completed for the source file '/src-ui/messages.xlf'
on the 'fr' language.
2021-01-29 12:42:54 +00:00
jonaswinkler
05866da04b remove dead code 2021-01-29 12:54:54 +01:00
jonaswinkler
dcd350a30c not sure what happened with reportlab 3.5.60, maybe a bad release. 2021-01-29 00:26:17 +01:00
jonaswinkler
107cc2ca20 add missing quotes 2021-01-28 22:38:12 +01:00
jonaswinkler
f872221c49 filesystem permission checks now issue warnings instead of errors, improves NFS compatibility 2021-01-28 22:22:25 +01:00
jonaswinkler
2faa425caf localization for websockets 2021-01-28 22:06:02 +01:00
jonaswinkler
94be8781e9 test cases 2021-01-28 19:28:48 +01:00
jonaswinkler
47189342e4 update dependencies 2021-01-28 12:43:27 +01:00
jonaswinkler
fafe259e53 added a status summary line 2021-01-28 10:54:56 +01:00
jonaswinkler
41fe607157 documentation 2021-01-27 20:45:14 +01:00
jonaswinkler
062008269b prevent updates to scipy, since 1.6 is incompatible with python 3.6 2021-01-27 20:27:40 +01:00
jonaswinkler
29e1b51164 add python 3.9 for tests 2021-01-27 20:27:06 +01:00
jonaswinkler
d2a8bc5c4c fix redis configuration 2021-01-27 20:08:09 +01:00
jonaswinkler
7414b3d23a update configuration 2021-01-27 19:57:04 +01:00
jonaswinkler
a0a781a3d5 i18n 2021-01-27 19:11:50 +01:00
jonaswinkler
d9e6f6e07c add gunicorn again 2021-01-27 19:11:18 +01:00
jonaswinkler
9167357908 update list view when documents are added 2021-01-27 18:47:02 +01:00
jonaswinkler
3b541b22e8 improved the logic of the "dismiss completed" button 2021-01-27 18:34:59 +01:00
jonaswinkler
71d5af233d update messages 2021-01-27 17:57:16 +01:00
jonaswinkler
b0ed06003b better error messages 2021-01-27 17:56:06 +01:00
jonaswinkler
91f7c4e685 settings and localization 2021-01-27 17:28:11 +01:00
jonaswinkler
5a4cb5fe4a proper messages when uploading fails 2021-01-27 16:04:06 +01:00
jonaswinkler
cc74be9ccc better error messages 2021-01-27 15:50:37 +01:00
jonaswinkler
68a9dd7e05 fix an issue with upload status identification 2021-01-27 15:23:29 +01:00
jonaswinkler
682cf33c78 clarification for some steps in the migration guide 2021-01-27 12:05:30 +01:00
Jonas Winkler
763b780aa4 Update issue templates 2021-01-27 11:32:30 +01:00
Jonas Winkler
24313fe001 Merge pull request #452 from shamoon/feature/consumer-alerts
Updates to consumer status alerts
2021-01-27 00:36:33 +01:00
Michael Shamoon
51dc40ae73 Maximum limit of alerts 2021-01-26 14:48:39 -08:00
jonaswinkler
ab04817bea alter defaults for workers and threads to allow more parallel tasks #446 2021-01-26 22:10:43 +01:00
jonaswinkler
8dde7fa043 Merge branch 'master' of github.com:jonaswinkler/paperless-ng 2021-01-26 20:48:43 +01:00
jonaswinkler
edfebe18a2 removed all occurences of pipenv from the documentation 2021-01-26 20:48:32 +01:00
jonaswinkler
0b07c3bd8f more documentation 2021-01-26 20:37:36 +01:00
Jonas Winkler
1483f450ad Update issue templates 2021-01-26 20:32:35 +01:00
jonaswinkler
600c13204e added FAQ for #449 2021-01-26 20:13:29 +01:00
jonaswinkler
2073279473 documentation for #441 2021-01-26 19:53:56 +01:00
jonaswinkler
d126ecfa4d troubleshooting for #346 2021-01-26 19:42:06 +01:00
jonaswinkler
944aaf5438 documentation #444 2021-01-26 19:32:22 +01:00
Michael Shamoon
52f17a9ba6 Fix dark mode primary progress color 2021-01-26 10:17:58 -08:00
Michael Shamoon
44600961d8 Fix initial upload progress > 100% 2021-01-26 10:17:45 -08:00
Michael Shamoon
d60b532b8f Dark mode support 2021-01-26 10:09:13 -08:00
Michael Shamoon
55584a6a87 Fix alert resizing 2021-01-26 09:50:48 -08:00
jonaswinkler
7c02a8b414 requirements 2021-01-26 18:01:24 +01:00
jonaswinkler
18d105b4f8 pinned scikit learn version 2021-01-26 18:00:59 +01:00
jonaswinkler
f0d49a1382 enable docker image builds on feature branches 2021-01-26 15:55:47 +01:00
Jonas Winkler
277bf0eb83 Merge pull request #414 from tido-/documentation_changes
Documentation changes
2021-01-26 15:50:28 +01:00
jonaswinkler
3b5d55c428 pycodestyle 2021-01-26 15:43:07 +01:00
jonaswinkler
313f2886d7 fix test cases 2021-01-26 15:26:25 +01:00
jonaswinkler
868fd4155a bug fixes, test case fixes 2021-01-26 15:19:56 +01:00
jonaswinkler
4eeb5642f5 i18n, better error handling 2021-01-26 15:02:46 +01:00
Michael Shamoon
532a3d955c Dismiss all 2021-01-26 02:52:16 -08:00
Michael Shamoon
53309f017f Lovely file upload alerts 2021-01-26 02:32:45 -08:00
Michael Shamoon
5651a03b14 Drop over background color 2021-01-26 00:17:27 -08:00
jonaswinkler
7dedb99dae fix status reports for consumption directory status messages 2021-01-26 01:15:27 +01:00
jonaswinkler
b581e42216 functions for combined upload progress bars 2021-01-26 01:10:39 +01:00
jonaswinkler
ede3bd1391 rework of the front end components 2021-01-26 00:51:45 +01:00
jonaswinkler
339e96b63c task ids 2021-01-26 00:51:20 +01:00
jonaswinkler
f31073a341 update dependencies 2021-01-26 00:50:29 +01:00
jonaswinkler
2fa78f7820 update Pipfile.lock 2021-01-26 00:35:07 +01:00
jonaswinkler
77b85b9e93 remove status widget 2021-01-26 00:34:36 +01:00
Fabian Koller
29ce2515ee Build source package in paperlessng_directory
Avoids permission problems in /tmp
2021-01-25 21:57:20 +01:00
Reto
d57c237980 fixed merge conflict 2021-01-24 20:12:38 +01:00
Reto
37ade0c6b2 fix my ignorance of idempotent 2021-01-24 20:00:09 +01:00
Reto
28a2479f24 fix my ignorance of idempotent 2021-01-24 19:37:46 +01:00
Fabian Koller
be2013975c Simplify building from source
Do not package app, instead copy directly into expected directory
2021-01-24 09:59:54 +01:00
Jonas Winkler
6e688a5b82 Update README.md 2021-01-24 00:00:46 +01:00
Fabian Koller
bfbdfe857f Simplify molecule tests
"Upgrade" path includes multiple paths anyway:
- installing the latest official release package
- builing the current PR from source
- upgrading between the two versions
2021-01-23 22:55:46 +01:00
Fabian Koller
0d19957d4b Fully prepare release package 2021-01-23 22:45:32 +01:00
jonaswinkler
05d69c0882 Merge branch 'dev' into feature-websockets-status 2021-01-23 22:22:17 +01:00
Reto
2104e65462 minor changes, like numbering 2021-01-23 13:14:31 +01:00
Reto
c0882e74e2 tried to fill the gaps and change wording where it felt necessary for better understanding 2021-01-23 12:55:50 +01:00
Jonas Winkler
ced6a61869 Update README.md 2021-01-22 17:22:02 +01:00
Fabian Koller
6c3b1db4dd Determine installed version by git commit hash 2021-01-22 16:28:13 +01:00
Jonas Winkler
25a0845efd Update README.md 2021-01-22 15:07:52 +01:00
Fabian Koller
8d62493774 Adapt github action to build PR version 2021-01-22 11:10:56 +01:00
Fabian Koller
eca1289ce2 Build release archive when version is not avail
Default ansible installation version to "latest" (pulls the latest
published release archive).
2021-01-22 10:49:59 +01:00
jonaswinkler
d153672f0d some changes 2021-01-04 23:05:16 +01:00
jonaswinkler
9f9581e1f8 Merge branch 'dev' into feature-websockets-status 2021-01-04 22:45:56 +01:00
jonaswinkler
e2456f4b3f changes 2020-12-07 12:44:23 +01:00
jonaswinkler
fcaaf7ce03 pipfile fix 2020-12-06 22:54:11 +01:00
jonaswinkler
522ada88ea Merge branch 'dev' into feature-websockets-status 2020-12-06 22:53:54 +01:00
jayme-github
26784a5325 Add automatic coloring of tags
Please see this as proposal on how to implement automatic/random colors
for tags while keeping the current set of hard coded colors in place (at
least in the frontend).

Bear with me as I have even less Angular knowledge than Django and just
tried to get away with as few changes as possible. :-) AIUI you want to
change to a color picking system anyways in the future, which could also
play well with this.

fixes #51
2020-12-04 10:05:47 +01:00
Jonas Winkler
6cf0b851b7 post-merge changes 2020-11-22 23:12:24 +01:00
Jonas Winkler
d252a1dcda Merge branch 'dev' into celery-tasks 2020-11-22 22:49:37 +01:00
Jonas Winkler
391020a2b0 small changes 2020-11-20 10:58:17 +01:00
Jonas Winkler
cb2340539d updated pipenv 2020-11-19 22:14:11 +01:00
Jonas Winkler
17430210a1 Merge branch 'dev' into celery-tasks 2020-11-19 22:10:57 +01:00
Jonas Winkler
9e81c82452 Pipfile update 2020-11-10 01:26:27 +01:00
Jonas Winkler
b3126934b3 Merge branch 'dev' into celery-tasks 2020-11-10 00:16:59 +01:00
Jonas Winkler
37bd4a7d0e added broker to compose file 2020-11-07 12:56:26 +01:00
Jonas Winkler
ae8a048ea6 fixed up the docker 2020-11-07 12:47:17 +01:00
Jonas Winkler
b0465e65c3 nicer status 2020-11-07 12:10:53 +01:00
Jonas Winkler
036f11acaa better toasts, better dashboard, first implementation of consumer status 2020-11-07 12:05:15 +01:00
Jonas Winkler
572e40ca27 backend that supports asgi and status update sockets with channels 2020-11-07 11:31:04 +01:00
225 changed files with 22685 additions and 3816 deletions

48
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,48 @@
---
name: Bug report
about: Something is not working
title: "[BUG] Concise description of the issue"
labels: ''
assignees: ''
---
<!---
=> Before opening an issue, please check the documentation and see if it helps you resolve your issue: https://paperless-ng.readthedocs.io/en/latest/troubleshooting.html
=> Please also make sure that you followed the installation instructions.
=> Please search the issues and look for similar issues before opening a bug report.
=> If you would like to submit a feature request please submit one under https://github.com/jonaswinkler/paperless-ng/discussions/categories/feature-requests
=> If you encounter issues while installing of configuring Paperless-ng, please post that in the "Support" section of the discussions. Remember that Paperless successfully runs on a variety of different systems. If paperless does not start, it's probably an issue with your system, and not an issue of paperless.
=> Don't remove the [BUG] prefix from the title.
-->
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Webserver logs**
```
If available, post any logs from the web server related to your issue.
```
**Relevant information**
- Host OS of the machine running paperless: [e.g. Archlinux / Ubuntu 20.04]
- Browser [e.g. chrome, safari]
- Version [e.g. 1.0.0]
- Installation method: [docker / bare metal]
- Any configuration changes you made in `docker-compose.yml`, `docker-compose.env` or `paperless.conf`.

20
.github/ISSUE_TEMPLATE/other.md vendored Normal file
View File

@@ -0,0 +1,20 @@
---
name: Other
about: Anything that is not a feature request or bug.
title: "[Other] Title of your issue"
labels: ''
assignees: ''
---
<!--
=> Discussions, Feedback and other suggestions belong in the "Discussion" section and not on the issue tracker.
=> If you would like to submit a feature request please submit one under https://github.com/jonaswinkler/paperless-ng/discussions/categories/feature-requests
=> If you encounter issues while installing of configuring Paperless-ng, please post that in the "Support" section of the discussions. Remember that Paperless successfully runs on a variety of different systems. If paperless does not start, it's probably is an issue with your system, and not an issue of paperless.
=> Don't remove the [Other] prefix from the title.
-->

View File

@@ -1,19 +1,32 @@
---
name: Ansible Role
on: [push, pull_request]
on:
push:
branches-ignore:
- 'translations**'
pull_request:
branches-ignore:
- 'translations**'
jobs:
# https://molecule.readthedocs.io/en/latest/ci.html#github-actions
test:
runs-on: ubuntu-latest
# https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions#github-context
if: github.event_name == 'pull_request' || (github.event_name == 'push' && contains(github.ref, 'refs/heads/'))
steps:
- name: Check out the codebase
uses: actions/checkout@v2
if: github.event_name != 'pull_request'
with:
path: "${{ github.repository }}"
- name: Check out the codebase
uses: actions/checkout@v2
if: github.event_name == 'pull_request'
with:
# merge commit is not available from tree at this point in time
# https://github.com/actions/checkout#checkout-pull-request-head-commit-instead-of-merge-commit
ref: "${{ github.event.pull_request.head.sha }}"
path: "${{ github.repository }}"
- name: Set up Python
uses: actions/setup-python@v2
- name: Set up Docker
@@ -21,21 +34,22 @@ jobs:
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install molecule[ansible,docker]
python3 -m pip install molecule[ansible,docker] jmespath
ansible --version
docker --version
molecule --version
python --version
- name: Test fresh installation with molecule
- name: Test installation/build/upgrade with molecule
run: |
cd ansible
molecule test -s fresh
working-directory: "${{ github.repository }}"
- name: Test release update with molecule
run: |
cd ansible
molecule test -s update
molecule create
molecule verify
molecule converge
molecule idempotence
molecule verify
working-directory: "${{ github.repository }}"
env:
TARGET_GITHUB_SHA: "${{ github.event.pull_request.head.sha }}"
# # https://galaxy.ansible.com/docs/contributing/importing.html
# release:
# runs-on: ubuntu-latest

View File

@@ -1,6 +1,13 @@
name: ci
on: [push, pull_request]
on:
push:
tags: ng-*
branches-ignore:
- 'translations**'
pull_request:
branches-ignore:
- 'translations**'
jobs:
documentation:
@@ -13,7 +20,7 @@ jobs:
name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.8
python-version: 3.7
-
name: Get pip cache dir
id: pip-cache
@@ -28,8 +35,6 @@ jobs:
-
name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev
pip install --upgrade pipenv
pipenv install --system --dev --ignore-pipfile
-
@@ -44,11 +49,44 @@ jobs:
name: documentation
path: docs/_build/html/
codestyle:
runs-on: ubuntu-20.04
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.7
-
name: Get pip cache dir
id: pip-cache
run: |
echo "::set-output name=dir::$(pip cache dir)"
-
name: Persistent Github pip cache
uses: actions/cache@v2
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip${{ matrix.python-version }}
-
name: Install dependencies
run: |
pip install --upgrade pipenv
pipenv install --system --dev --ignore-pipfile
-
name: Codestyle
run: |
cd src/
pycodestyle
tests:
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: ['3.6', '3.7', '3.8']
python-version: ['3.6', '3.7', '3.8', '3.9']
fail-fast: false
steps:
-
@@ -71,10 +109,10 @@ jobs:
path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip${{ matrix.python-version }}
-
name: Prepare tests
name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev unpaper tesseract-ocr imagemagick ghostscript optipng
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript optipng
pip install --upgrade pipenv
pipenv install --system --dev --ignore-pipfile
-
@@ -82,11 +120,6 @@ jobs:
run: |
cd src/
pytest
-
name: Codestyle
run: |
cd src/
pycodestyle
-
name: Publish coverage results
if: matrix.python-version == '3.8'
@@ -107,6 +140,13 @@ jobs:
uses: actions/setup-node@v2
with:
node-version: '15'
-
name: Configure version on dev branches
if: startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev'
run: |
git_hash=$(git rev-parse --short "$GITHUB_SHA")
git_branch=${GITHUB_REF#refs/heads/}
sed -i -E "s/version: \"(.*)\"/version: \"${git_branch} ${git_hash}\"/g" src-ui/src/environments/environment.prod.ts
-
name: Build frontend
run: ./compile-frontend.sh
@@ -118,7 +158,7 @@ jobs:
path: src/documents/static/frontend/
build-release:
needs: [frontend, documentation, tests]
needs: [frontend, documentation, tests, codestyle]
runs-on: ubuntu-20.04
steps:
-
@@ -133,7 +173,7 @@ jobs:
name: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev gettext liblept5
sudo apt-get install -qq --no-install-recommends gettext liblept5
pip3 install -r requirements.txt
-
name: Download frontend artifact
@@ -155,6 +195,7 @@ jobs:
mkdir dist/paperless-ng/scripts
cp .dockerignore .env Dockerfile Pipfile Pipfile.lock LICENSE README.md requirements.txt dist/paperless-ng/
cp paperless.conf.example dist/paperless-ng/paperless.conf
cp gunicorn.conf.py dist/paperless-ng/gunicorn.conf.py
cp docker/ dist/paperless-ng/docker -r
cp scripts/*.service scripts/*.sh dist/paperless-ng/scripts/
cp src/ dist/paperless-ng/src -r
@@ -225,9 +266,9 @@ jobs:
# build and push image to docker hub.
build-docker-image:
if: github.event_name == 'push' && (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/ng-'))
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/ng-'))
runs-on: ubuntu-latest
needs: [frontend, tests]
needs: [frontend, tests, codestyle]
steps:
-
name: Prepare

View File

@@ -9,11 +9,11 @@ If you want to implement something big: Please start a discussion about that in
## Python
Use python 3.6 for development. Paperless supports python 3.6, 3.7 and 3.8.
Paperless supports python 3.6, 3.7, 3.8 and 3.9.
## Branches
master always reflects the latest release.
master always reflects the latest release. Apart from changes to the documentation or readme, absolutely no functional changes on this branch in between releases.
dev contains all changes that will be part of the next release. Use this branch to start making your changes.

View File

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

32
Pipfile
View File

@@ -9,38 +9,52 @@ verify_ssl = true
name = "piwheels"
[packages]
dateparser = "~=0.7.6"
dateparser = "~=1.0.0"
django = "~=3.1.3"
django-cors-headers = "*"
django-extensions = "*"
django-filter = "~=2.4.0"
django-q = "~=1.3.4"
django-q = "==1.3.4"
djangorestframework = "~=3.12.2"
filelock = "*"
fuzzywuzzy = "*"
fuzzywuzzy = {extras = ["speedup"], version = "*"}
gunicorn = "*"
imap-tools = "*"
langdetect = "*"
pdftotext = "*"
# numpy 1.20.0 drops python 3.6 support
numpy = "~=1.19.5"
pathvalidate = "*"
# pinned to 8.1.0, since aarch64 wheels might not be available beyond that https://github.com/python-pillow/Pillow/issues/5202
pillow = "==8.1.0"
pikepdf = "~=2.2.5"
pikepdf = "~=2.5.0"
python-gnupg = "*"
python-dotenv = "*"
python-dateutil = "*"
python-Levenshtein = "*"
python-magic = "*"
psycopg2-binary = "*"
redis = "*"
scikit-learn="~=0.24.0"
# Pinned because aarch64 wheels and updates cause warnings when loading the classifier model.
scikit-learn="==0.24.0"
# Prevent scipy updates because 1.6 is incompatible with python 3.6
scipy="~=1.5.4"
whitenoise = "~=5.2.0"
watchdog = "*"
watchdog = "~=1.0.0"
whoosh="~=2.7.4"
inotifyrecursive = "~=0.3.4"
ocrmypdf = "~=11.4.5"
ocrmypdf = "~=11.6"
tqdm = "*"
tika = "*"
# TODO: This will sadly also install daphne+dependencies,
# which an ASGI server we don't need. Adds about 15MB image size.
channels = "~=3.0"
channels-redis = "*"
uvicorn = {extras = ["standard"], version = "*"}
concurrent-log-handler = "*"
# uvloop 0.15+ incompatible with python 3.6
uvloop = "~=0.14.0"
# TODO: keep an eye on piwheel builds and update this once available (https://www.piwheels.org/project/cryptography/)
cryptography = "~=3.4"
"pdfminer.six" = "*"
[dev-packages]
coveralls = "*"

957
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
![ci](https://github.com/jonaswinkler/paperless-ng/workflows/ci/badge.svg)
[![ci](https://github.com/jonaswinkler/paperless-ng/workflows/ci/badge.svg)](https://github.com/jonaswinkler/paperless-ng/actions)
![Ansible Role](https://github.com/jonaswinkler/paperless-ng/workflows/Ansible%20Role/badge.svg)
[![Documentation Status](https://readthedocs.org/projects/paperless-ng/badge/?version=latest)](https://paperless-ng.readthedocs.io/en/latest/?badge=latest)
[![Gitter](https://badges.gitter.im/paperless-ng/community.svg)](https://gitter.im/paperless-ng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Docker Hub Pulls](https://img.shields.io/docker/pulls/jonaswinkler/paperless-ng.svg)](https://hub.docker.com/r/jonaswinkler/paperless-ng)
@@ -6,13 +7,16 @@
# Paperless-ng
[Paperless](https://github.com/the-paperless-project/paperless) is an application by Daniel Quinn and contributors that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents.
[Paperless (click me)](https://github.com/the-paperless-project/paperless) is an application by Daniel Quinn and contributors that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents.
Paperless-ng is a fork of the original project, adding a new interface and many other changes under the hood. For a detailed list of changes, have a look at the changelog in the documentation.
Paperless-ng is a fork of the original project, adding a new interface and many other changes under the hood. These key points should help you decide whether Paperless-ng is something you would prefer over Paperless:
# Survey
* Interface: The new front end is the main interface for paperless-ng, the old interface still exists but most customizations (such as thumbnails for the document list) have been removed.
* Encryption: Paperless-ng does not support GnuPG anymore, since storing your data on encrypted file systems (that you optionally mount on demand) achieves about the same result.
* Resource usage: Paperless-ng does use a bit more resources than Paperless. Running the web server requires about 300MB of RAM or more, depending on the configuration. While adding documents, it requires about 300MB additional RAM, depending on the document. It still runs on Pi (many users do that), but it has been generally geared to better use the resources of more powerful systems.
* API changes: If you rely on the REST API of paperless, some of its functionality has been changed.
If you already used Paperless-ng for a bit, would like to give some anonymous feedback, and help me decide on what to focus on next: I've created a survey, [see here](https://github.com/jonaswinkler/paperless-ng/issues/402). Thank you!
For a detailed list of changes, have a look at the [change log](https://paperless-ng.readthedocs.io/en/latest/changelog.html) in the documentation.
# How it Works
@@ -33,8 +37,10 @@ Here's what you get:
# Features
* Performs OCR on your documents, adds selectable text to image only documents and adds tags, correspondents and document types to your documents.
* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and can be configured freely.
* Single page application front end. Should be pretty snappy. Will be mobile friendly in the future.
* Supports PDF documents, images, plain text files, and Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents).
* Office document support is optional and provided by Apache Tika (see [configuration](https://paperless-ng.readthedocs.io/en/latest/configuration.html#tika-settings))
* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely.
* Single page application front end.
* Includes a dashboard that shows basic statistics and has document upload.
* Filtering by tags, correspondents, types, and more.
* Customizable views can be saved and displayed on the dashboard.
@@ -45,33 +51,13 @@ Here's what you get:
* Searching for similar documents ("More like this")
* Email processing: Paperless adds documents from your email accounts.
* Configure multiple accounts and filters for each account.
* When adding documents from mails, paperless can move these mails to a new folder, mark them as read, flag them or delete them.
* When adding documents from mail, paperless can move these mail to a new folder, mark them as read, flag them as important or delete them.
* Machine learning powered document matching.
* Paperless learns from your documents and will be able to automatically assign tags, correspondents and types to documents once you've stored a few documents in paperless.
* A task processor that processes documents in parallel and also tells you when something goes wrong. On modern multi core systems, consumption is blazing fast.
* Optimized for multi core systems: Paperless-ng consumes multiple documents in parallel.
* The integrated sanity checker makes sure that your document archive is in good health.
If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html). However, some parts of the UI have changed since I took these.
For a complete list of changes from paperless, check out the [changelog](https://paperless-ng.readthedocs.io/en/latest/changelog.html)
# Roadmap for 1.0
- Make the front end nice (except mobile).
- Fix whatever bugs I and you find.
- Make the documentation nice.
## On the chopping block.
- **GnuPG encrypion.** [Here's a note about encryption in paperless](https://paperless-ng.readthedocs.io/en/latest/administration.html#managing-encryption). The gist of it is that I don't see which attacks this implementation protects against. It gives a false sense of security to users who don't care about how it works.
## Wont-do list.
These features will probably never make it into paperless, since paperless is meant to be an easy to use set-and-forget solution.
- **Document versions.** I might consider adding the ability to update a document with a newer version, but that's about it. The kind of documents that get added to paperless usually don't change at all.
- **Workflows.** I don't see a use case for these, yet.
- **Folders.** Tags are superior in just about every way.
- **Apps / extension support.** Again, paperless is meant to be simple.
If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html).
# Getting started
@@ -91,17 +77,21 @@ The documentation for Paperless-ng is available on [ReadTheDocs](https://paperle
# Translation
Paperless is currently available in English, German, Dutch and French. Translation is coordinated at transifex: https://www.transifex.com/paperless/paperless-ng
Paperless is currently available in English, German, Dutch, French, and Portuguese.
If you want to see paperless in your own language, request that language at transifex and you can start translating after I approve the language.
There's an active translation project at transifex! If you want to help out by translating paperless into your language, please head over to https://github.com/jonaswinkler/paperless-ng/issues/212 for details.
# Suggestions? Questions? Something not working?
# Feature Requests
Please open an issue and start a discussion about it!
Feature requests can be submitted via [GitHub Discussions](https://github.com/jonaswinkler/paperless-ng/discussions/categories/feature-requests), you can search for existing ideas, add your own and vote for the ones you care about! Note that some older feature requests can also be found under [issues](https://github.com/jonaswinkler/paperless-ng/issues).
# Questions? Something not working?
For bugs please [open an issue](https://github.com/jonaswinkler/paperless-ng/issues) or [start a discussion](https://github.com/jonaswinkler/paperless-ng/discussions) if you have questions.
## Feel like helping out?
There's still lots of things to be done, just have a look at that issue log. If you feel like contributing to the project, please do! Bug fixes and improvements to the front end (I just can't seem to get some of these CSS things right) are always welcome. The documentation has some basic information on how to get started.
There's still lots of things to be done, just have a look at open issues & discussions. If you feel like contributing to the project, please do! Bug fixes and improvements to the front end (I just can't seem to get some of these CSS things right) are always welcome. The documentation has some basic information on how to get started.
If you want to implement something big: Please start a discussion about that in the issues! Maybe I've already had something similar in mind and we can make it happen together. However, keep in mind that the general roadmap is to make the existing features stable and get them tested. See the roadmap above.
@@ -114,9 +104,13 @@ Paperless has been around a while now, and people are starting to build stuff on
These projects also exist, but their status and compatibility with paperless-ng is unknown.
* [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): A desktop UI for your Paperless installation. Runs on Mac, Linux, and Windows.
* [paperless-cli](https://github.com/stgarf/paperless-cli): A golang command line binary to interact with a Paperless instance.
This project also exists, but needs updates to be compatile with paperless-ng.
* [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): A desktop UI for your Paperless installation. Runs on Mac, Linux, and Windows.
Known issues on Mac: (Could not load reminders and documents)
# Important Note
Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. Everything is stored in the clear without encryption by default (it needs to be searchable, so if someone has ideas on how to do that on encrypted data, I'm all ears). This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home.
Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. Everything is stored in the clear without encryption. This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home.

View File

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

View File

@@ -1,5 +1,5 @@
---
paperlessng_version: 0.9.14
paperlessng_version: latest # 'latest', release number, or github branch/tag/commit/ref
# Required services
paperlessng_redis_host: localhost
@@ -18,30 +18,34 @@ paperlessng_directory: /opt/paperless-ng
paperlessng_consumption_dir: "{{ paperlessng_directory }}/consumption"
paperlessng_data_dir: "{{ paperlessng_directory }}/data"
paperlessng_media_root: "{{ paperlessng_directory }}/media"
paperlessng_static_dir: "{{ paperlessng_directory }}/static"
paperlessng_staticdir: "{{ paperlessng_directory }}/static"
paperlessng_filename_format:
paperlessng_logging_dir: "{{ paperlessng_data_dir }}/log"
paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv"
# Hosting & Security
paperless_secret_key: PLEASECHANGETHISFORTHELOVEOFGOD
paperless_allowed_hosts: "*"
paperless_cors_allowed_hosts: http://localhost:8000
paperless_force_script_name:
paperless_static_url: /static/
paperless_auto_login_username:
paperless_cookie_prefix: ""
paperless_enable_http_remote_user: False
paperlessng_secret_key: PLEASECHANGETHISFORTHELOVEOFGOD
paperlessng_allowed_hosts: "*"
paperlessng_cors_allowed_hosts: http://localhost:8000
paperlessng_force_script_name:
paperlessng_static_url: /static/
paperlessng_auto_login_username:
paperlessng_cookie_prefix: ""
paperlessng_enable_http_remote_user: False
# OCR settings
paperlessng_ocr_languages:
- eng
paperlessng_ocr_mode: skip
paperlessng_ocr_clean: clean
paperlessng_ocr_deskew: True
paperlessng_ocr_rotate_pages: True
paperlessng_ocr_rotate_pages_threshold: 12
paperlessng_ocr_output_type: pdfa
paperlessng_ocr_pages: 0
paperlessng_ocr_image_dpi:
# see https://ocrmypdf.readthedocs.io/en/latest/api.html#ocrmypdf.ocr
paperlessng_ocr_user_args:
#- "deskew": True # https://github.com/jonaswinkler/paperless-ng/issues/231
- "optimize": 1
paperlessng_use_jbig2enc: True
paperlessng_big2enc_lossy: False
@@ -57,10 +61,11 @@ paperlessng_consumer_polling: 0
paperlessng_consumer_delete_duplicates: False
paperlessng_consumer_recursive: False
paperlessng_consumer_subdirs_as_tags: False
paperlessng_convert_memory_limit: 0
paperlessng_convert_tmpdir:
paperlessng_optimize_thumbnails: True
paperlessng_post_consume_script:
paperlessng_filename_date_order:
paperlessng_filename_parse_transforms:
paperlessng_thumbnail_font_name: /usr/share/fonts/liberation/LiberationSerif-Regular.ttf
paperlessng_ignore_dates: ""

View File

@@ -2,10 +2,9 @@
- name: update previous release to newest release
hosts: all
tasks:
- name: set current version as installation target
- name: set current github commit as version when available
set_fact:
paperlessng_version: 0.9.14
paperlessng_version: "{{ lookup('env', 'TARGET_GITHUB_SHA') | default('master', True) }}"
- name: update to newest paperless-ng release
include_role:
name: ansible

View File

@@ -3,7 +3,7 @@
tasks:
- name: set previous version as installation target
set_fact:
paperlessng_version: 0.9.13
paperlessng_version: latest
- name: install previous paperless-ng release
include_role:

View File

@@ -0,0 +1,94 @@
---
- name: Verify
hosts: all
gather_facts: false
vars_files:
- ../../defaults/main.yml
tasks:
- name: check if webserver is up
uri:
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}"
status_code: [200, 302]
return_content: yes
register: landingpage
failed_when: "'Sign in</button>' not in landingpage.content"
- name: generate random name and content
set_fact:
content: "{{ lookup('password', '/dev/null length=65536 chars=ascii_letters') }}"
filename: "{{ lookup('password', '/dev/null length=8 chars=ascii_letters') }}"
- name: check if document posting works
uri:
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/documents/post_document/"
method: POST
body_format: form-multipart
body:
document:
content: "{{ content }}"
filename: "{{ filename }}.txt"
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
Content-Type: text/plain
return_content: yes
register: post_document
failed_when: "'OK' not in post_document.content"
- name: verify uploaded document has been accepted
uri:
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/paperless/"
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: logs
failed_when: "('Consuming ' + filename + '.txt') not in logs.content"
- name: sleep till consumption finished
pause:
seconds: 10
- name: verify uploaded document has been consumed
uri:
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/logs/paperless/"
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: logs
failed_when: "filename + ' consumption finished' not in logs.content"
- name: get documents
uri:
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/documents/"
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: documents
- name: set document index
set_fact:
index: "{{ documents.json['results'][0]['id'] }}"
- name: verify uploaded document is avaiable
uri:
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/documents/{{ index }}/"
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: document
failed_when: "'Not found.' in document.content or content not in document.json['content']"
- name: check if deleting uploaded document works
uri:
url: "http://{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}/api/documents/bulk_edit/"
method: POST
body_format: json
body:
documents: ["{{ index }}"]
method: delete
parameters: {}
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
register: delete_document
failed_when: "'OK' not in delete_document.json['result']"

View File

@@ -1,7 +0,0 @@
---
- name: fresh installation
hosts: all
tasks:
- name: install paperless-ng with default parameters
include_role:
name: ansible

View File

@@ -1,60 +0,0 @@
---
- name: Verify
hosts: all
gather_facts: false
vars_files:
- ../../defaults/main.yml
tasks:
- name: check if webserver is up
uri:
url: http://localhost:8000
status_code: [200, 302]
return_content: yes
register: landingpage
failed_when: "'Sign in</button>' not in landingpage.content"
- name: check if document posting works
uri:
url: http://localhost:8000/api/documents/post_document/
method: POST
body_format: form-multipart
body:
document:
content: FOO
filename: document.txt
mime_type: text/plain
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: post_document
failed_when: "'OK' not in post_document.content"
- name: verify uploaded document has been accepted
uri:
url: http://localhost:8000/api/logs/
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: logs
failed_when: "'Consuming document.txt' not in logs.content"
# assumes txt consumption finished by now, might have to sleep a bit
- name: verify uploaded document has been consumed
uri:
url: http://localhost:8000/api/logs/
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: logs
failed_when: "'document consumption finished' not in logs.content"
- name: verify uploaded document is avaiable
uri:
url: http://localhost:8000/api/documents/1/
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: document
failed_when: "'Not found.' in document.content or 'FOO' not in document.content"

View File

@@ -1,35 +0,0 @@
---
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: ubuntu_focal
image: jrei/systemd-ubuntu:20.04
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
tmpfs:
- /tmp
- /run
- /run/lock
override_command: False
# ubuntu 18.04 bionic works except that
# the default redis configuration expects IPv6 which is not enabled in docker by default
# the default Python environment is configured for ASCII instead of UTF-8
# ubuntu 16.04 xenial only has Python 3.5 which is EOL and breaks multiple dependencies
- name: debian_buster
image: jrei/systemd-debian:10
privileged: true
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:ro
tmpfs:
- /tmp
- /run
- /run/lock
override_command: False
# debian 9 stretch only has Python 3.5 which is EOL and breaks multiple dependencies
provisioner:
name: ansible
verifier:
name: ansible

View File

@@ -1,60 +0,0 @@
---
- name: Verify
hosts: all
gather_facts: false
vars_files:
- ../../defaults/main.yml
tasks:
- name: check if webserver is up
uri:
url: http://localhost:8000
status_code: [200, 302]
return_content: yes
register: landingpage
failed_when: "'Sign in</button>' not in landingpage.content"
- name: check if document posting works
uri:
url: http://localhost:8000/api/documents/post_document/
method: POST
body_format: form-multipart
body:
document:
content: FOO
filename: document.txt
mime_type: text/plain
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: post_document
failed_when: "'OK' not in post_document.content"
- name: verify uploaded document has been accepted
uri:
url: http://localhost:8000/api/logs/
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: logs
failed_when: "'Consuming document.txt' not in logs.content"
# assumes txt consumption finished by now, might have to sleep a bit
- name: verify uploaded document has been consumed
uri:
url: http://localhost:8000/api/logs/
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: logs
failed_when: "'document consumption finished' not in logs.content"
- name: verify uploaded document is avaiable
uri:
url: http://localhost:8000/api/documents/1/
headers:
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
return_content: yes
register: document
failed_when: "'Not found.' in document.content or 'FOO' not in document.content"

View File

@@ -0,0 +1,6 @@
---
- name: extract paperless-ng
unarchive:
src: "https://github.com/jonaswinkler/paperless-ng/releases/download/ng-{{ paperlessng_version }}/paperless-ng-{{ paperlessng_version }}.tar.xz"
remote_src: yes
dest: "{{ tempdir.path }}"

View File

@@ -0,0 +1,112 @@
---
- name: install dev dependencies
apt:
pkg:
- git
- npm
- gettext
- name: create output directories
file:
path: "{{ item }}"
state: directory
owner: "{{ paperlessng_system_user }}"
group: "{{ paperlessng_system_group }}"
mode: "750"
with_items:
- "{{ tempdir.path }}/paperless-ng"
- "{{ tempdir.path }}/paperless-ng/scripts"
- block:
- name: create temporary git directory
tempfile:
state: directory
path: "{{ paperlessng_directory }}"
register: gitdir
- name: pull paperless-ng
git:
repo: https://github.com/jonaswinkler/paperless-ng.git
dest: "{{ gitdir.path }}"
version: "{{ paperlessng_version }}"
refspec: "+refs/pull/*:refs/pull/*"
- name: compile frontend
command:
cmd: "{{ item }}"
args:
chdir: "{{ gitdir.path }}/src-ui"
failed_when: false
with_items:
- npm install -g @angular/cli
- npm install
- ./node_modules/.bin/ng build --prod
- name: copy application into place
copy:
src: "{{ gitdir.path }}/{{ item.src }}"
remote_src: yes
dest: "{{ tempdir.path }}/paperless-ng/{{ item.dest | default('') }}"
with_items:
- src: CONTRIBUTING.md
- src: LICENSE
- src: Pipfile
- src: Pipfile.lock
- src: README.md
- src: requirements.txt
- src: gunicorn.conf.py
- src: paperless.conf.example
dest: "paperless.conf"
- name: glob all scripts
find:
paths: ["{{ gitdir.path }}/scripts/"]
patterns:
- "*.service"
- "*.sh"
register: glob
- name: copy scripts
copy:
src: "{{ item.path }}"
remote_src: yes
dest: "{{ tempdir.path }}/paperless-ng/scripts/"
with_items:
- "{{ glob.files }}"
- name: copy sources
command:
cmd: "cp -r src/ {{ tempdir.path }}/paperless-ng/src"
args:
chdir: "{{ gitdir.path }}"
- name: create paperlessng venv
command:
cmd: "python3 -m virtualenv {{ gitdir.path }}/.venv/ -p /usr/bin/python3"
- name: install paperlessng requirements
command:
cmd: "{{ gitdir.path }}/.venv/bin/python3 -m pip install -r {{ gitdir.path }}/requirements.txt"
- name: compile messages
command: "{{ gitdir.path }}/.venv/bin/python3 manage.py compilemessages"
args:
chdir: "{{ tempdir.path }}/paperless-ng/src/"
- name: collect static files
command: "{{ gitdir.path }}/.venv/bin/python3 manage.py collectstatic --no-input"
args:
chdir: "{{ tempdir.path }}/paperless-ng/src/"
- name: remove pycache directories
shell: find . -name __pycache__ | xargs rm -r
args:
chdir: "{{ tempdir.path }}"
- name: remove temporary git directory
file:
path: "{{ gitdir.path }}"
state: absent
become: yes
become_user: "{{ paperlessng_system_user }}"

View File

@@ -15,7 +15,6 @@
- imagemagick
- optipng
- gnupg
- libpoppler-cpp-dev
- libpq-dev
- libmagic-dev
- mime-support
@@ -34,11 +33,17 @@
- build-essential
- python3-setuptools
- python3-wheel
- python3-virtualenv
# upstream virtualenv in Ubuntu 20.04 is broken
# https://github.com/pypa/virtualenv/issues/1873
- name: install python virtualenv
pip:
name: virtualenv
extra_args: --upgrade
- name: install ocr languages
apt:
pkg: "{{ paperlessng_ocr_languages | map('regex_replace', '^(.*)$', 'tesseract-ocr-\\1') | list }}"
pkg: "{{ paperlessng_ocr_languages | map('regex_replace', '^(.*)$', 'tesseract-ocr-\\1') | map('replace', '_', '-') | list }}"
- name: set up notesalexp repository key (for jbig2enc)
apt_key:
@@ -97,71 +102,147 @@
# GNUPG_HOME required due to paperless db.py
create_home: yes
- block:
- name: get latest release version
uri:
url: https://api.github.com/repos/jonaswinkler/paperless-ng/releases/latest
method: GET
register: latest_release
- name: parse latest release version
set_fact:
paperlessng_version: "{{ latest_release.json['tag_name'] }}"
when: paperlessng_version == "latest"
- name: check if version is ref
fail:
msg: "Specifying `paperlessng_version` as git ref may not work as expected!"
ignore_errors: True # Output failure (as warning), but don't consider play failed
when: paperlessng_version.startswith('refs/')
- block:
- name: sanitize version string
set_fact:
paperlessng_version: "{{ paperlessng_version | regex_replace('^ng-(\\d+\\.\\d+\\.\\d+)$', '\\1') }}"
- name: get tag data
uri:
url: https://api.github.com/repos/jonaswinkler/paperless-ng/tags
method: GET
register: tags
- name: get commit for target tag
set_fact:
paperlessng_commit: "{{ tags.json | json_query('[?name==`ng-' + paperlessng_version +'`] | [0].commit.sha') }}"
when: paperlessng_version | regex_search("^(ng-)?(\d+\.\d+\.\d+)$")
- block:
- name: check if version is branch
uri:
url: "https://api.github.com/repos/jonaswinkler/paperless-ng/branches/{{ paperlessng_version }}"
method: GET
status_code: [200, 404]
register: branch
- name: get commit for target branch
set_fact:
paperlessng_commit: "{{ branch.json | json_query('commit.sha') }}"
when: branch.status == 200
- block:
- name: check if version is commit-or-ref
uri:
url: "https://api.github.com/repos/jonaswinkler/paperless-ng/commits/{{ paperlessng_version }}"
method: GET
status_code: [200, 404, 422]
register: commit
- name: get commit for target commit-or-ref
set_fact:
paperlessng_commit: "{{ commit.json | json_query('sha') }}"
when: commit.status == 200
- name: fail
fail:
msg: "Can not determine commit from `paperlessng_version=={{ paperlessng_version }}`!"
when: commit.status != 200
when: branch.status == 404
when: not(paperlessng_version | regex_search("^(ng-)?(\d+\.\d+\.\d+)$"))
- name: check for paperless-ng installation
command:
cmd: 'grep -Po "(?<=Paperless-ng )\d+\.\d+\.\d+" {{ paperlessng_directory }}/docs/changelog.html'
changed_when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
cmd: "cat {{ paperlessng_directory }}/.installed_version"
changed_when: '"No such file or directory" in paperlessng_current_commit.stderr or paperlessng_current_commit.stdout != paperlessng_commit | string'
failed_when: false
ignore_errors: yes
register: paperlessng_current_version
register: paperlessng_current_commit
- name: register current state
set_fact:
fresh_installation: '{{ "No such file or directory" in paperlessng_current_version.stderr }}'
update_installation: '{{ "No such file or directory" not in paperlessng_current_version.stderr and paperlessng_current_version.stdout != paperlessng_version | string }}'
reconfigure_only: '{{ paperlessng_current_version.stdout == paperlessng_version | string }}'
fresh_installation: '{{ "No such file or directory" in paperlessng_current_commit.stderr }}'
update_installation: '{{ "No such file or directory" not in paperlessng_current_commit.stderr and paperlessng_current_commit.stdout != paperlessng_commit | string }}'
reconfigure_only: "{{ paperlessng_current_commit.stdout == paperlessng_commit | string }}"
- name: backup current paperless-ng installation
copy:
src: "{{ paperlessng_directory }}"
remote_src: yes
dest: "{{ paperlessng_directory }}-{{ ansible_date_time.iso8601 }}/"
- block:
- name: backup current paperless-ng installation
copy:
src: "{{ paperlessng_directory }}"
remote_src: yes
dest: "{{ paperlessng_directory }}-{{ ansible_date_time.iso8601 }}/"
- name: remove current paperless sources
file:
path: "{{ paperlessng_directory }}/{{ item }}"
state: absent
with_items:
- docker
- docs
- scripts
- src
- static
when: update_installation
- name: remove current paperless sources
file:
path: "{{ paperlessng_directory }}/{{ item }}"
state: absent
with_items:
- docker
- docs
- scripts
- src
- static
when: update_installation
- name: create temporary directory
tempfile:
state: directory
register: tempdir
when: not reconfigure_only
- name: extract paperless-ng
unarchive:
src: "https://github.com/jonaswinkler/paperless-ng/releases/download/ng-{{ paperlessng_version }}/paperless-ng-{{ paperlessng_version }}.tar.xz"
remote_src: yes
dest: "{{ tempdir.path }}"
when: not reconfigure_only
- name: change owner and permissions of paperless-ng
command:
cmd: "{{ item }}"
warn: false
with_items:
- "chown -R {{ paperlessng_system_user }}:{{ paperlessng_system_group }} {{ tempdir.path }}"
- "find {{ tempdir.path }} -type d -exec chmod 0750 {} ;"
- "find {{ tempdir.path }} -type f -exec chmod 0640 {} ;"
when: not reconfigure_only
- name: move paperless-ng
command:
cmd: "cp -a {{ tempdir.path }}/paperless-ng/. {{ paperlessng_directory }}"
when: not reconfigure_only
- name: remove temporary directory
file:
path: "{{ tempdir.path }}"
state: absent
- block:
- name: create paperless-ng directory and set permissions
file:
path: "{{ paperlessng_directory }}"
state: directory
owner: "{{ paperlessng_system_user }}"
group: "{{ paperlessng_system_group }}"
mode: "750"
- name: create temporary directory
become: yes
become_user: "{{ paperlessng_system_user }}"
tempfile:
state: directory
path: "{{ paperlessng_directory }}"
register: tempdir
- name: check if version is available as release archive
uri:
url: "https://github.com/jonaswinkler/paperless-ng/releases/download/ng-{{ paperlessng_version }}/paperless-ng-{{ paperlessng_version }}.tar.xz"
method: GET
status_code: [200, 404]
register: release_archive
- name: install paperless-ng from source
include_tasks: install-source.yml
when: release_archive.status == 404
- name: install paperless-ng from release archive
include_tasks: install-release.yml
when: release_archive.status == 200
- name: change owner and permissions of paperless-ng
command:
cmd: "{{ item }}"
warn: false
with_items:
- "chown -R {{ paperlessng_system_user }}:{{ paperlessng_system_group }} {{ tempdir.path }}"
- "find {{ tempdir.path }} -type d -exec chmod 0750 {} ;"
- "find {{ tempdir.path }} -type f -exec chmod 0640 {} ;"
- name: move paperless-ng
command:
cmd: "cp -a {{ tempdir.path }}/paperless-ng/. {{ paperlessng_directory }}"
- name: store commit hash of installed version
copy:
content: "{{ paperlessng_commit }}"
dest: "{{ paperlessng_directory }}/.installed_version"
owner: "{{ paperlessng_system_user }}"
group: "{{ paperlessng_system_group }}"
mode: "0440"
- name: remove temporary directory
file:
path: "{{ tempdir.path }}"
state: absent
when: not reconfigure_only
- name: create paperless-ng directories and set permissions
@@ -172,15 +253,14 @@
group: "{{ paperlessng_system_group }}"
mode: "750"
with_items:
- "{{ paperlessng_directory }}"
- "{{ paperlessng_consumption_dir }}"
- "{{ paperlessng_data_dir }}"
- "{{ paperlessng_media_root }}"
- "{{ paperlessng_static_dir }}"
- "{{ paperlessng_staticdir }}"
- name: rename initial config
command:
cmd: "mv {{ paperlessng_directory }}/paperless.conf {{ paperlessng_directory }}/paperless.conf.template"
cmd: "mv -f {{ paperlessng_directory }}/paperless.conf {{ paperlessng_directory }}/paperless.conf.template"
removes: "{{ paperlessng_directory }}/paperless.conf"
- name: configure paperless-ng
@@ -200,31 +280,41 @@
- regexp: PAPERLESS_MEDIA_ROOT
line: "PAPERLESS_MEDIA_ROOT={{ paperlessng_media_root }}"
- regexp: PAPERLESS_STATICDIR
line: "PAPERLESS_STATICDIR={{ paperlessng_static_dir }}"
line: "PAPERLESS_STATICDIR={{ paperlessng_staticdir }}"
- regexp: PAPERLESS_FILENAME_FORMAT
line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}"
- regexp: PAPERLESS_LOGGING_DIR
line: "PAPERLESS_LOGGING_DIR={{ paperlessng_logging_dir }}"
# Hosting & Security
- regexp: PAPERLESS_SECRET_KEY
line: "PAPERLESS_SECRET_KEY={{ paperless_secret_key }}"
line: "PAPERLESS_SECRET_KEY={{ paperlessng_secret_key }}"
- regexp: PAPERLESS_ALLOWED_HOSTS
line: "PAPERLESS_ALLOWED_HOSTS={{ paperless_allowed_hosts }}"
line: "PAPERLESS_ALLOWED_HOSTS={{ paperlessng_allowed_hosts }}"
- regexp: PAPERLESS_CORS_ALLOWED_HOSTS
line: "PAPERLESS_CORS_ALLOWED_HOSTS={{ paperless_cors_allowed_hosts }}"
line: "PAPERLESS_CORS_ALLOWED_HOSTS={{ paperlessng_cors_allowed_hosts }}"
- regexp: PAPERLESS_FORCE_SCRIPT_NAME
line: "PAPERLESS_FORCE_SCRIPT_NAME={{ paperless_force_script_name }}"
line: "PAPERLESS_FORCE_SCRIPT_NAME={{ paperlessng_force_script_name }}"
- regexp: PAPERLESS_STATIC_URL
line: "PAPERLESS_STATIC_URL={{ paperless_static_url }}"
line: "PAPERLESS_STATIC_URL={{ paperlessng_static_url }}"
- regexp: PAPERLESS_AUTO_LOGIN_USERNAME
line: "PAPERLESS_AUTO_LOGIN_USERNAME={{ paperless_auto_login_username }}"
line: "PAPERLESS_AUTO_LOGIN_USERNAME={{ paperlessng_auto_login_username }}"
- regexp: PAPERLESS_COOKIE_PREFIX
line: "PAPERLESS_COOKIE_PREFIX={{ paperless_cookie_prefix }}"
line: "PAPERLESS_COOKIE_PREFIX={{ paperlessng_cookie_prefix }}"
- regexp: PAPERLESS_ENABLE_HTTP_REMOTE_USER
line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperless_enable_http_remote_user }}"
line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperlessng_enable_http_remote_user }}"
# OCR settings
- regexp: PAPERLESS_OCR_LANGUAGE
line: "PAPERLESS_OCR_LANGUAGE={{ paperlessng_ocr_languages | join('+') }}"
line: "PAPERLESS_OCR_LANGUAGE={{ paperlessng_ocr_languages | join('+') | replace('-','_') }}"
- regexp: PAPERLESS_OCR_MODE
line: "PAPERLESS_OCR_MODE={{ paperlessng_ocr_mode }}"
- regexp: PAPERLESS_OCR_CLEAN
line: "PAPERLESS_OCR_CLEAN={{ paperlessng_ocr_clean }}"
- regexp: PAPERLESS_OCR_DESKEW
line: "PAPERLESS_OCR_DESKEW={{ paperlessng_ocr_deskew }}"
- regexp: PAPERLESS_OCR_ROTATE_PAGES
line: "PAPERLESS_OCR_ROTATE_PAGES={{ paperlessng_ocr_rotate_pages }}"
- regexp: PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD
line: "PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD={{ paperlessng_ocr_rotate_pages_threshold }}"
- regexp: PAPERLESS_OCR_OUTPUT_TYPE
line: "PAPERLESS_OCR_OUTPUT_TYPE={{ paperlessng_ocr_output_type }}"
- regexp: PAPERLESS_OCR_PAGES
@@ -251,6 +341,10 @@
line: "PAPERLESS_CONSUMER_RECURSIVE={{ paperlessng_consumer_recursive }}"
- regexp: PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS
line: "PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS={{ paperlessng_consumer_subdirs_as_tags }}"
- regexp: PAPERLESS_CONVERT_MEMORY_LIMIT
line: "PAPERLESS_CONVERT_MEMORY_LIMIT={{ paperlessng_convert_memory_limit }}"
- regexp: PAPERLESS_CONVERT_TMPDIR
line: "PAPERLESS_CONVERT_TMPDIR={{ paperlessng_convert_tmpdir }}"
- regexp: PAPERLESS_OPTIMIZE_THUMBNAILS
line: "PAPERLESS_OPTIMIZE_THUMBNAILS={{ paperlessng_optimize_thumbnails }}"
- regexp: PAPERLESS_POST_CONSUME_SCRIPT
@@ -310,21 +404,20 @@
creates: "{{ paperlessng_virtualenv }}"
register: venv
- name: install paperlessng requirements
become: yes
become_user: "{{ paperlessng_system_user }}"
pip:
requirements: "{{ paperlessng_directory }}/requirements.txt"
executable: "{{ paperlessng_virtualenv }}/bin/pip3"
extra_args: --upgrade
when: not reconfigure_only
- name: migrate database schema
become: yes
become_user: "{{ paperlessng_system_user }}"
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py migrate"
register: database_schema
changed_when: '"No migrations to apply." not in database_schema.stdout'
- block:
- name: install paperlessng requirements
become: yes
become_user: "{{ paperlessng_system_user }}"
pip:
requirements: "{{ paperlessng_directory }}/requirements.txt"
executable: "{{ paperlessng_virtualenv }}/bin/pip3"
extra_args: --upgrade
- name: migrate database schema
become: yes
become_user: "{{ paperlessng_system_user }}"
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py migrate"
register: database_schema
changed_when: '"No migrations to apply." not in database_schema.stdout'
when: not reconfigure_only
- name: configure paperless superuser
@@ -376,6 +469,12 @@
line: '\1<policy domain="coder" rights="read|write" pattern="PDF" />'
backrefs: yes
- name: configure gunicorn web server
lineinfile:
path: "{{ paperlessng_directory }}/gunicorn.conf.py"
regexp: '^bind = '
line: "bind = '{{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}'"
- name: configure systemd services
ini_file:
path: "{{ paperlessng_directory }}/scripts/{{ item[0] }}"
@@ -392,7 +491,7 @@
# https://www.freedesktop.org/software/systemd/man/systemd.exec.html
{ option: "User", value: "{{ paperlessng_system_user }}" },
{ option: "Group", value: "{{ paperlessng_system_group }}" },
{ option: "WorkingDirectory", value: "{{ paperlessng_directory }}/src", },
{ option: "WorkingDirectory", value: "{{ paperlessng_directory }}/src" },
{ option: "ProtectSystem", value: "full" },
{ option: "NoNewPrivileges", value: "true" },
{ option: "PrivateUsers", value: "true" },
@@ -418,7 +517,7 @@
path: "{{ paperlessng_directory }}/scripts/paperless-webserver.service"
section: "Service"
option: "ExecStart"
value: "{{ paperlessng_virtualenv }}/bin/gunicorn paperless.wsgi -w 2 -b {{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}"
value: "{{ paperlessng_virtualenv }}/bin/gunicorn -c {{ paperlessng_directory }}/gunicorn.conf.py paperless.asgi:application"
- name: copy systemd services
copy:

View File

@@ -1,5 +1,5 @@
files:
- source: /src/locale/en-us/LC_MESSAGES/django.po
translation: /src/locale/%two_letters_code%/LC_MESSAGES/django.po
- source: /src/locale/en_US/LC_MESSAGES/django.po
translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po
- source: /src-ui/messages.xlf
translation: /src-ui/src/locale/messages.%two_letters_code%.xlf
translation: /src-ui/src/locale/messages.%locale_with_underscore%.xlf

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ loglevel=info ; log level; default info; others: debug,warn,trace
user=root
[program:gunicorn]
command=gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.wsgi
command=gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.asgi:application
user=paperless
stdout_logfile=/dev/stdout

View File

@@ -23,6 +23,12 @@ Options available to any installation of paperless:
* The document exporter is also able to update an already existing export.
Therefore, incremental backups with ``rsync`` are entirely possible.
.. caution::
You cannot import the export generated with one version of paperless in a
different version of paperless. The export contains an exact image of the
database, and migrations may change the database layout.
Options available to docker installations:
* Backup the docker volumes. These usually reside within
@@ -101,17 +107,17 @@ Then you can start paperless-ng with ``-d`` to have it run in the background.
update to newer versions. In order to enable updates as described above, either
get the new ``docker-compose.yml`` file from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
or edit the ``docker-compose.yml`` file, find the line that says
.. code::
image: jonaswinkler/paperless-ng:0.9.x
and replace the version with ``latest``:
.. code::
image: jonaswinkler/paperless-ng:latest
Bare Metal Route
================
@@ -121,27 +127,19 @@ After grabbing the new release and unpacking the contents, do the following:
dependencies. The dependencies required are listed in the section about
:ref:`bare metal installations <setup-bare_metal>`.
2. Update python requirements. If you use Pipenv, this is done with the following steps.
2. Update python requirements. Keep in mind to activate your virtual environment
before that, if you use one.
.. code:: shell-session
$ pip install --upgrade pipenv
$ cd /path/to/paperless
$ pipenv clean
$ pipenv install
This creates a new virtual environment (or uses your existing environment)
and installs all dependencies into it.
You can also use the included ``requirements.txt`` file instead and create the virtual
environment yourself. This file includes exactly the same dependencies.
$ pip install -r requirements.txt
3. Migrate the database.
.. code:: shell-session
$ cd src
$ pipenv run python3 manage.py migrate
$ python3 manage.py migrate
This might not actually do anything. Not every new paperless version comes with new
database migrations.
@@ -179,25 +177,62 @@ Most of the update process is automated when using the ansible role.
$ ansible-playbook playbook.yml
Downgrading Paperless
#####################
Downgrades are possible. However, some updates also contain database migrations (these change the layout of the database and may move data).
In order to move back from a version that applied database migrations, you'll have to revert the database migration *before* downgrading,
and then downgrade paperless.
This table lists the compatible versions for each database migration number.
+------------------+-----------------+
| Migration number | Version range |
+------------------+-----------------+
| 1011 | 1.0.0 |
+------------------+-----------------+
| 1012 | 1.1.0 - 1.2.1 |
+------------------+-----------------+
| 1014 | 1.3.0 - current |
+------------------+-----------------+
Execute the following management command to migrate your database:
.. code:: shell-session
$ python3 manage.py migrate documents <migration number>
.. note::
Some migrations cannot be undone. The command will issue errors if that happens.
.. _utilities-management-commands:
Management utilities
####################
Paperless comes with some management commands that perform various maintenance
tasks on your paperless instance. You can invoke these commands either by
tasks on your paperless instance. You can invoke these commands in the following way:
With docker-compose, while paperless is running:
.. code:: shell-session
$ cd /path/to/paperless
$ docker-compose run --rm webserver <command> <arguments>
$ docker-compose exec webserver <command> <arguments>
or
With docker, while paperless is running:
.. code:: shell-session
$ docker exec -it <container-name> <command> <arguments>
Bare metal:
.. code:: shell-session
$ cd /path/to/paperless/src
$ pipenv run python manage.py <command> <arguments>
depending on whether you use docker or not.
$ python3 manage.py <command> <arguments>
All commands have built-in help, which can be accessed by executing them with
the argument ``--help``.
@@ -218,7 +253,7 @@ backup or migration to another DMS.
-c, --compare-checksums
-f, --use-filename-format
-d, --delete
``target`` is a folder to which the data gets written. This includes documents,
thumbnails and a ``manifest.json`` file. The manifest contains all metadata from
the database (correspondents, tags, etc).
@@ -375,6 +410,34 @@ the naming scheme.
The command takes no arguments and processes all your documents at once.
.. _utilities-sanity-checker:
Sanity checker
==============
Paperless has a built-in sanity checker that inspects your document collection for issues.
The issues detected by the sanity checker are as follows:
* Missing original files.
* Missing archive files.
* Inaccessible original files due to improper permissions.
* Inaccessible archive files due to improper permissions.
* Corrupted original documents by comparing their checksum against what is stored in the database.
* Corrupted archive documents by comparing their checksum against what is stored in the database.
* Missing thumbnails.
* Inaccessible thumbnails due to improper permissions.
* Documents without any content (warning).
* Orphaned files in the media directory (warning). These are files that are not referenced by any document im paperless.
.. code::
document_sanity_checker
The command takes no arguments. Depending on the size of your document archive, this may take some time.
Fetching e-mail
===============
@@ -462,6 +525,3 @@ Basic usage to disable encryption of your document store:
.. code::
decrypt_documents [--passphrase SECR3TP4SSPHRA$E]
.. _Pipenv: https://pipenv.pypa.io/en/latest/

View File

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

View File

@@ -284,3 +284,53 @@ The endpoint supports the following optional form fields:
The endpoint will immediately return "OK" if the document consumption process
was started successfully. No additional status information about the consumption
process itself is available, since that happens in a different process.
.. _api-versioning:
API Versioning
##############
The REST API is versioned since Paperless-ng 1.3.0.
* Versioning ensures that changes to the API don't break older clients.
* Clients specify the specific version of the API they wish to use with every request and Paperless will handle the request using the specified API version.
* Even if the underlying data model changes, older API versions will always serve compatible data.
* If no version is specified, Paperless will serve version 1 to ensure compatibility with older clients that do not request a specific API version.
API versions are specified by submitting an additional HTTP ``Accept`` header with every request:
.. code::
Accept: application/json; version=6
If an invalid version is specified, Paperless 1.3.0 will respond with "406 Not Acceptable" and an error message in the body.
Earlier versions of Paperless will serve API version 1 regardless of whether a version is specified via the ``Accept`` header.
If a client wishes to verify whether it is compatible with any given server, the following procedure should be performed:
1. Perform an *authenticated* request against any API endpoint. If the server is on version 1.3.0 or newer, the server will
add two custom headers to the response:
.. code::
X-Api-Version: 2
X-Version: 1.3.0
2. Determine whether the client is compatible with this server based on the presence/absence of these headers and their values if present.
API Changelog
=============
Version 1
---------
Initial API version.
Version 2
---------
* Added field ``Tag.color``. This read/write string field contains a hex color such as ``#a6cee3``.
* Added read-only field ``Tag.text_color``. This field contains the text color to use for a specific tag, which is either black or white depending on the brightness of ``Tag.color``.
* Removed field ``Tag.colour``.

View File

@@ -5,6 +5,194 @@
Changelog
*********
paperless-ng 1.3.0
##################
This release contains new database migrations.
* Changes
* The REST API is versioned from this point onwards. This will allow me to make changes without breaking existing clients. See the documentation about :ref:`api-versioning` for details.
* Added a color picker for tag colors.
* Added the ability to use the filter for searching the document content as well.
* Added translations into Italian and Romanian. Thank you!
* Close individual documents from the sidebar. Thanks to `Michael Shamoon`_.
* `BolkoSchreiber <https://github.com/BolkoSchreiber>`_ added an option to disable/enable thumbnail inversion in dark mode.
* `Simon Taddiken <https://github.com/skuzzle>`_ added the ability to customize the header used for remote user authentication with SSO applications.
* Bug fixes
* Fixed an issue with the auto matching algorithm when more than 256 tags were used.
paperless-ng 1.2.1
##################
* `Rodrigo Avelino <https://github.com/rodavelino>`_ translated Paperless into Portuguese (Brazil)!
* The date input fields now respect the currently selected date format.
* Added a fancy icon when adding paperless to the home screen on iOS devices. Thanks to `Joel Nordell <https://github.com/joelnordell>`_.
* When using regular expression matching, the regular expression is now validated before saving the tag/correspondent/type.
* Regression fix: Dates on the front end did not respect date locale settings in some cases.
paperless-ng 1.2.0
##################
* Changes to the OCRmyPDF integration
* Added support for deskewing and automatic rotation of incorrectly rotated pages. This is enabled by default, see :ref:`configuration-ocr`.
* Better support for encrypted files.
* Better support for various other PDF files: Paperless will now attempt to force OCR with safe options when OCR fails with the configured options.
* Added an explicit option to skip cleaning with ``unpaper``.
* Download multiple selected documents as a zip archive.
* The document list now remembers the current page.
* Improved responsiveness when switching between saved views and the document list.
* Increased the default wait time when observing files in the consumption folder
with polling from 1 to 5 seconds. This will decrease the likelihood of paperless
consuming partially written files.
* Fixed a crash of the document archiver management command when trying to process documents with unknown mime types.
* Paperless no longer depends on ``libpoppler-cpp-dev``.
.. note::
Some packages that paperless depends on are slowly dropping Python 3.6
support one after another, including the web server. Supporting Python
3.6 means that I cannot update these packages anymore.
At some point, paperless will drop Python 3.6 support. If using a bare
metal installation and you're still on Python 3.6, upgrade to 3.7 or newer.
If using docker, this does not affect you.
paperless-ng 1.1.4
##################
* Added English (GB) locale.
* Added ISO-8601 date display option.
paperless-ng 1.1.3
##################
* Added a docker-specific configuration option to adjust the number of
worker processes of the web server. See :ref:`configuration-docker`.
* Some more memory usage optimizations.
* Don't show inbox statistics if no inbox tag is defined.
paperless-ng 1.1.2
##################
* Always show top left corner of thumbnails, even for extra wide documents.
* Added a management command for executing the sanity checker directly.
See :ref:`utilities-sanity-checker`.
* The weekly sanity check now reports messages in the log files.
* Fixed an issue with the metadata tab not reporting anything in case of missing files.
* Reverted a change from 1.1.0 that caused huge memory usage due to redis caching.
* Some memory usage optimizations.
paperless-ng 1.1.1
##################
This release contains new database migrations.
* Fixed a bug in the sanity checker that would cause it to display "x not in list" errors instead of actual issues.
* Fixed a bug with filename generation for archive filenames that would cause the archive files of two documents to overlap.
* This happened when ``PAPERLESS_FILENAME_FORMAT`` is used and the filenames of two or more documents are the same, except for the file extension.
* Paperless will now store the archive filename in the database as well instead of deriving it from the original filename, and use the
same logic for detecting and avoiding filename clashes that's also used for original filenames.
* The migrations will repair any missing archive files. If you're using tika, ensure that tika is running while performing the migration. Docker-compose will take care of that.
* Fixed a bug with thumbnail regeneration when TIKA integration was used.
* Added ASN as a placeholder field to the filename format.
* The docker image now comes with built-in shortcuts for most management commands. These are now the recommended way to execute management commands, since these
also ensure that they're always executed as the paperless user and you're less likely to run into permission issues. See :ref:`utilities-management-commands`.
paperless-ng 1.1.0
##################
* Document processing status
* Paperless now shows the status of processing documents on the dashboard in real time.
* Status notifications when
* New documents are detected in the consumption folder, in mails, uploaded on the front end,
or added with one of the mobile apps.
* Documents are successfully added to paperless.
* Document consumption failed (with error messages)
* Configuration options to enable/disable individual notifications.
* Live updates to document lists and saved views when new documents are added.
.. hint::
For status notifications and live updates to work, paperless now requires an `ASGI <https://asgi.readthedocs.io/en/latest/>`_-enabled
web server. The docker images uses ``gunicorn`` and an ASGI-enabled worker called `uvicorn <http://www.uvicorn.org/>`_,
and there is no need to configure anything.
For bare metal installations, changes are required for the notifications to work. Adapt the service ``paperless-webserver.service``
to use the supplied ``gunicorn.conf.py`` configuration file and adapt the reference to the ASGI application as follows:
.. code::
ExecStart=/opt/paperless/.local/bin/gunicorn -c /opt/paperless/gunicorn.conf.py paperless.asgi:application
Paperless will continue to work with WSGI, but you will not get any status notifications.
Apache ``mod_wsgi`` users, see :ref:`this note <faq-mod_wsgi>`.
* Paperless now offers suggestions for tags, correspondents and types on the document detail page.
* Added an interactive easy install script that automatically downloads, configures and starts paperless with docker.
* Official support for Python 3.9.
* Other changes and fixes
* Adjusted the default parallelization settings to run more than one task in parallel on systems with 4 or less cores.
This addresses issues with paperless not consuming any new files when other tasks are running.
* Fixed a rare race condition that would cause paperless to process incompletely written files when using the upload on the dashboard.
* The document classifier no longer issues warnings and errors when auto matching is not used at all.
* Better icon for document previews.
* Better info section in the side bar.
* Paperless no longer logs to the database. Instead, logs are written to rotating log files. This solves many "database is locked"
issues on Raspberry Pi, especially when SQLite is used.
* By default, log files are written to ``PAPERLESS_DATA_DIR/log/``. Logging settings can be adjusted with
``PAPERLESS_LOGGING_DIR``, ``PAPERLESS_LOGROTATE_MAX_SIZE`` and
``PAPERLESS_LOGROTATE_MAX_BACKUPS``.
paperless-ng 1.0.0
##################

View File

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

View File

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

View File

@@ -5,29 +5,82 @@ Paperless development
This section describes the steps you need to take to start development on paperless-ng.
1. Check out the source from github. The repository is organized in the following way:
Check out the source from github. The repository is organized in the following way:
* ``master`` always represents the latest release and will only see changes
when a new release is made.
* ``dev`` contains the code that will be in the next release.
* ``feature-X`` contain bigger changes that will be in some release, but not
necessarily the next one.
Apart from that, the folder structure is as follows:
* ``master`` always represents the latest release and will only see changes
when a new release is made.
* ``dev`` contains the code that will be in the next release.
* ``feature-X`` contain bigger changes that will be in some release, but not
necessarily the next one.
* ``docs/`` - Documentation.
* ``src-ui/`` - Code of the front end.
* ``src/`` - Code of the back end.
* ``scripts/`` - Various scripts that help with different parts of development.
* ``docker/`` - Files required to build the docker image.
When making functional changes to paperless, *always* make your changes on the ``dev`` branch.
2. Install some dependencies.
Apart from that, the folder structure is as follows:
* Python 3.6.
* All dependencies listed in the :ref:`Bare metal route <setup-bare_metal>`
* redis. You can either install redis or use the included scritps/start-services.sh
to use docker to fire up a redis instance (and some other services such as tika,
gotenberg and a postgresql server).
* ``docs/`` - Documentation.
* ``src-ui/`` - Code of the front end.
* ``src/`` - Code of the back end.
* ``scripts/`` - Various scripts that help with different parts of development.
* ``docker/`` - Files required to build the docker image.
Initial setup and first start
=============================
After you forked and cloned the code from github you need to perform a first-time setup.
To do the setup you need to perform the steps from the following chapters in a certain order:
1. Install prerequisites + pipenv as mentioned in :ref:`Bare metal route <setup-bare_metal>`
2. Copy ``paperless.conf.example`` to ``paperless.conf`` and enable debug mode.
3. Install the Angular CLI interface:
.. code:: shell-session
$ npm install -g @angular/cli
4. Create ``consume`` and ``media`` folders in the cloned root folder.
.. code:: shell-session
mkdir -p consume media
5. You can now either ...
* install redis or
* use the included scripts/start-services.sh to use docker to fire up a redis instance (and some other services such as tika, gotenberg and a postgresql server) or
* spin up a bare redis container
.. code:: shell-session
docker run -d -p 6379:6379 -restart unless-stopped redis:latest
6. Install the python dependencies by performing in the src/ directory.
.. code:: shell-session
pipenv install --dev
7. Generate the static UI so you can perform a login to get session that is required for frontend development (this needs to be done one time only). From root folder:
.. code:: shell-session
compile-frontend.sh
8. Apply migrations and create a superuser for your dev instance:
.. code:: shell-session
python3 manage.py migrate
python3 manage.py createsuperuser
9. Now spin up the dev backend. Depending on which part of paperless you're developing for, you need to have some or all of them running.
.. code:: shell-session
python3 manage.py runserver & python3 manage.py document_consumer & python3 manage.py qcluster
10. Login with the superuser credentials provided in step 8 at ``http://localhost:8000`` to create a session that enables you to use the backend.
Backend development environment is now ready, to start Frontend development go to ``/src-ui`` and run ``ng serve``. From there you can use ``http://localhost:4200`` for a preview.
Back end development
====================
@@ -35,21 +88,18 @@ Back end development
The backend is a django application. I use PyCharm for development, but you can use whatever
you want.
Install the python dependencies by performing ``pipenv install --dev`` in the src/ directory.
This will also create a virtual environment, which you can enter with ``pipenv shell`` or
execute one-shot commands in with ``pipenv run``.
Copy ``paperless.conf.example`` to ``paperless.conf`` and enable debug mode.
Configure the IDE to use the src/ folder as the base source folder. Configure the following
launch configurations in your IDE:
* python3 manage.py runserver
* python3 manage.py qcluster
* python3 manage.py consumer
* python3 manage.py document_consumer
Depending on which part of paperless you're developing for, you need to have some or all of
them running.
To start them all:
.. code:: shell-session
python3 manage.py runserver & python3 manage.py document_consumer & python3 manage.py qcluster
Testing and code style:
@@ -62,7 +112,7 @@ Testing and code style:
The line length rule E501 is generally useful for getting multiple source files
next to each other on the screen. However, in some cases, its just not possible
to make some lines fit, especially complicated IF cases. Append `` # NOQA: E501``
to make some lines fit, especially complicated IF cases. Append ``# NOQA: E501``
to disable this check for certain lines.
Front end development
@@ -109,6 +159,133 @@ This will build the front end and put it in a location from which the Django ser
it as static content. This way, you can verify that authentication is working.
Localization
============
Paperless is available in many different languages. Since paperless consists both of a django
application and an Angular front end, both these parts have to be translated separately.
Front end localization
----------------------
* The Angular front end does localization according to the `Angular documentation <https://angular.io/guide/i18n>`_.
* The source language of the project is "en_US".
* The source strings end up in the file "src-ui/messages.xlf".
* The translated strings need to be placed in the "src-ui/src/locale/" folder.
* In order to extract added or changed strings from the source files, call ``ng xi18n --ivy``.
Adding new languages requires adding the translated files in the "src-ui/src/locale/" folder and adjusting a couple files.
1. Adjust "src-ui/angular.json":
.. code:: json
"i18n": {
"sourceLocale": "en-US",
"locales": {
"de": "src/locale/messages.de.xlf",
"nl-NL": "src/locale/messages.nl_NL.xlf",
"fr": "src/locale/messages.fr.xlf",
"en-GB": "src/locale/messages.en_GB.xlf",
"pt-BR": "src/locale/messages.pt_BR.xlf",
"language-code": "language-file"
}
}
2. Add the language to the available options in "src-ui/src/app/services/settings.service.ts":
.. code:: typescript
getLanguageOptions(): LanguageOption[] {
return [
{code: "en-us", name: $localize`English (US)`, englishName: "English (US)", dateInputFormat: "mm/dd/yyyy"},
{code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)", dateInputFormat: "dd/mm/yyyy"},
{code: "de", name: $localize`German`, englishName: "German", dateInputFormat: "dd.mm.yyyy"},
{code: "nl", name: $localize`Dutch`, englishName: "Dutch", dateInputFormat: "dd-mm-yyyy"},
{code: "fr", name: $localize`French`, englishName: "French", dateInputFormat: "dd/mm/yyyy"},
{code: "pt-br", name: $localize`Portuguese (Brazil)`, englishName: "Portuguese (Brazil)", dateInputFormat: "dd/mm/yyyy"}
// Add your new language here
]
}
``dateInputFormat`` is a special string that defines the behavior of the date input fields and absolutely needs to contain "dd", "mm" and "yyyy".
3. Import and register the Angular data for this locale in "src-ui/src/app/app.module.ts":
.. code:: typescript
import localeDe from '@angular/common/locales/de';
registerLocaleData(localeDe)
Back end localization
---------------------
A majority of the strings that appear in the back end appear only when the admin is used. However,
some of these are still shown on the front end (such as error messages).
* The django application does localization according to the `django documentation <https://docs.djangoproject.com/en/3.1/topics/i18n/translation/>`_.
* The source language of the project is "en_US".
* Localization files end up in the folder "src/locale/".
* In order to extract strings from the application, call ``python3 manage.py makemessages -l en_US``. This is important after making changes to translatable strings.
* The message files need to be compiled for them to show up in the application. Call ``python3 manage.py compilemessages`` to do this. The generated files don't get
committed into git, since these are derived artifacts. The build pipeline takes care of executing this command.
Adding new languages requires adding the translated files in the "src/locale/" folder and adjusting the file "src/paperless/settings.py" to include the new language:
.. code:: python
LANGUAGES = [
("en-us", _("English (US)")),
("en-gb", _("English (GB)")),
("de", _("German")),
("nl-nl", _("Dutch")),
("fr", _("French")),
("pt-br", _("Portuguese (Brazil)")),
# Add language here.
]
Building the documentation
==========================
The documentation is built using sphinx. I've configured ReadTheDocs to automatically build
the documentation when changes are pushed. If you want to build the documentation locally,
this is how you do it:
1. Install python dependencies.
.. code:: shell-session
$ cd /path/to/paperless
$ pipenv install --dev
2. Build the documentation
.. code:: shell-session
$ cd /path/to/paperless/docs
$ pipenv run make clean html
This will build the HTML documentation, and put the resulting files in the ``_build/html``
directory.
Building the Docker image
=========================
Building the docker image from source requires the following two steps:
1. Build the front end.
.. code:: shell-session
./compile-frontend.sh
2. Build the docker image.
.. code:: shell-session
docker build . -t <your-tag>
Extending Paperless
===================

View File

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

View File

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

View File

@@ -20,45 +20,45 @@ Paperless consists of the following components:
.. code:: shell-session
$ cd /path/to/paperless/src/
$ pipenv run gunicorn -c /usr/src/paperless/gunicorn.conf.py -b 0.0.0.0:8000 paperless.wsgi
$ gunicorn -c ../gunicorn.conf.py paperless.wsgi
or by any other means such as Apache ``mod_wsgi``.
* **The consumer:** This is what watches your consumption folder for documents.
However, the consumer itself does not consume really consume your documents anymore.
It rather notifies a task processor that a new file is ready for consumption.
However, the consumer itself does not really consume your documents.
Now it notifies a task processor that a new file is ready for consumption.
I suppose it should be named differently.
This also used to check your emails, but that's now gone elsewhere as well.
This was also used to check your emails, but that's now done elsewhere as well.
Start the consumer with the management command ``document_consumer``:
.. code:: shell-session
$ cd /path/to/paperless/src/
$ pipenv run python3 manage.py document_consumer
$ python3 manage.py document_consumer
.. _setup-task_processor:
* **The task processor:** Paperless relies on `Django Q <https://django-q.readthedocs.io/en/latest/>`_
for doing much of the heavy lifting. This is a task queue that accepts tasks from
multiple sources and processes tasks in parallel. It also comes with a scheduler that executes
for doing most of the heavy lifting. This is a task queue that accepts tasks from
multiple sources and processes these in parallel. It also comes with a scheduler that executes
certain commands periodically.
This task processor is responsible for:
* Consuming documents. When the consumer finds new documents, it notifies the task processor to
start a consumption task.
* Consuming emails. It periodically checks your configured accounts for new mails and
produces consumption tasks for any documents it finds.
* The task processor also performs the consumption of any documents you upload through
the web interface.
* Maintain the search index and the automatic matching algorithm. These are things that paperless
* Consuming emails. It periodically checks your configured accounts for new emails and
notifies the task processor to consume the attachment of an email.
* Maintaining the search index and the automatic matching algorithm. These are things that paperless
needs to do from time to time in order to operate properly.
This allows paperless to process multiple documents from your consumption folder in parallel! On
a modern multi core system, consumption with full ocr is blazing fast.
a modern multi core system, this makes the consumption process with full OCR blazingly fast.
The task processor comes with a built-in admin interface that you can use to see whenever any of the
The task processor comes with a built-in admin interface that you can use to check whenever any of the
tasks fail and inspect the errors (i.e., wrong email credentials, errors during consuming a specific
file, etc).
@@ -67,11 +67,11 @@ Paperless consists of the following components:
.. code:: shell-session
$ cd /path/to/paperless/src/
$ pipenv run python3 manage.py qcluster
$ python3 manage.py qcluster
* A `redis <https://redis.io/>`_ message broker: This is a really lightweight service that is responsible
for getting the tasks from the webserver and consumer to the task scheduler. These run in different
processes (maybe even on different machines!), and therefore, this is necessary.
for getting the tasks from the webserver and the consumer to the task scheduler. These run in a different
process (maybe even on different machines!), and therefore, this is necessary.
* Optional: A database server. Paperless supports both PostgreSQL and SQLite for storing its data.
@@ -79,34 +79,58 @@ Paperless consists of the following components:
Installation
############
You can go multiple routes with setting up and running Paperless:
You can go multiple routes to setup and run Paperless:
* :ref:`Use the easy install docker script <setup-docker_script>`
* :ref:`Pull the image from Docker Hub <setup-docker_hub>`
* :ref:`Build the Docker image yourself <setup-docker_build>`
* :ref:`Install Paperless directly on your system manually (bare metal) <setup-bare_metal>`
* :ref:`Use ansible to install Paperless on your system automatically (bare metal) <setup-ansible>`
The Docker routes are quick & easy. These are the recommended routes. This configures all the stuff
from above automatically so that it just works and uses sensible defaults for all configuration options.
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://sehn.tech/post/devops-with-docker/>`_
The bare metal route is more complicated to setup but makes it easier
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.
The ansible route cobines benefits from both options:
the setup process is fully automated, reproducible and idempotent,
it includes the same sensible defaults,
and it simultaneously provides the flexibility of a bare metal installation.
The ansible route combines benefits of both options:
the setup process is fully automated, reproducible and `idempotent <https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#Idempotency>`_,
it includes the same sensible defaults, and it simultaneously provides the flexibility of a bare metal installation.
.. _CLI Basics: https://sehn.tech/post/devops-with-docker/
.. _idempotent: https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#Idempotency
.. _setup-docker_script:
Install Paperless from Docker Hub using the installation script
===============================================================
Paperless provides an interactive installation script. This script will ask you
for a couple configuration options, download and create the necessary configuration files, pull the docker image, start paperless and create your user account. This script essentially
performs all the steps described in :ref:`setup-docker_hub` automatically.
1. Make sure that docker and docker-compose are installed.
2. Download and run the installation script:
.. code:: shell-session
$ wget https://raw.githubusercontent.com/jonaswinkler/paperless-ng/master/install-paperless-ng.sh
$ chmod +x install-paperless-ng.sh
$ ./install-paperless-ng.sh
.. _setup-docker_hub:
Install Paperless from Docker Hub
=================================
1. Go to the `/docker/compose directory on the project page <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
and download one of the ``docker-compose.*.yml`` files, depending on which database backend you
1. Login with your user and create a folder in your home-directory `mkdir -v ~/paperless-ng` to have a place for your configuration files and consumption directory.
2. Go to the `/docker/compose directory on the project page <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
and download one of the `docker-compose.*.yml` files, depending on which database backend you
want to use. Rename this file to `docker-compose.yml`.
If you want to enable optional support for Office documents, download a file with ``-tika`` in its name.
If you want to enable optional support for Office documents, download a file with `-tika` in the file name.
Download the ``docker-compose.env`` file and the ``.env`` file as well and store them
in the same directory.
@@ -115,25 +139,26 @@ Install Paperless from Docker Hub
For new installations, it is recommended to use PostgreSQL as the database
backend.
2. Install `Docker`_ and `docker-compose`_.
3. Install `Docker`_ and `docker-compose`_.
.. caution::
If you want to use the included ``docker-compose.*.yml`` file, you
need to have at least Docker version **17.09.0** and docker-compose
version **1.17.0**.
To check do: `docker-compose -v` or `docker -v`
See the `Docker installation guide`_ on how to install the current
version of Docker for your operating system or Linux distribution of
choice. To get an up-to-date version of docker-compose, follow the
choice. To get the latest version of docker-compose, follow the
`docker-compose installation guide`_ if your package repository doesn't
include it.
.. _Docker installation guide: https://docs.docker.com/engine/installation/
.. _docker-compose installation guide: https://docs.docker.com/compose/install/
3. Modify ``docker-compose.yml`` to your preferences. You may want to change the path
to the consumption directory in this file. Find the line that specifies where
4. Modify ``docker-compose.yml`` to your preferences. You may want to change the path
to the consumption directory. Find the line that specifies where
to mount the consumption directory:
.. code::
@@ -149,31 +174,34 @@ Install Paperless from Docker Hub
Don't change the part after the colon or paperless wont find your documents.
4. Modify ``docker-compose.env``, following the comments in the file. The
5. Modify ``docker-compose.env``, following the comments in the file. The
most important change is to set ``USERMAP_UID`` and ``USERMAP_GID``
to the uid and gid of your user on the host system. This ensures that
to the uid and gid of your user on the host system. Use ``id -u`` and
``id -g`` to get these.
This ensures that
both the docker container and you on the host machine have write access
to the consumption directory. If your UID and GID on the host system is
1000 (the default for the first normal user on most systems), it will
work out of the box without any modifications.
work out of the box without any modifications. `id "username"` to check.
.. note::
You can use any settings from the file ``paperless.conf.example`` in this file.
Have a look at :ref:`configuration` to see whats available.
You can copy any setting from the file ``paperless.conf.example`` and paste it here.
Have a look at :ref:`configuration` to see what's available.
.. caution::
Certain file systems such as NFS network shares don't support file system
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 be unable to pick up new files
on such a file system, paperless will not pick up new files
with the default configuration. You will need to use ``PAPERLESS_CONSUMER_POLLING``,
which will disable inotify. See :ref:`here <configuration-polling>`.
5. Run ``docker-compose up -d``. This will create and start the necessary
containers.
6. Run ``docker-compose pull``, followed by ``docker-compose up -d``.
This will pull the image, create and start the necessary containers.
6. To be able to login, you will need a super user. To create it, execute the
7. To be able to login, you will need a super user. To create it, execute the
following command:
.. code-block:: shell-session
@@ -181,19 +209,19 @@ Install Paperless from Docker Hub
$ docker-compose run --rm webserver createsuperuser
This will prompt you to set a username, an optional e-mail address and
finally a password.
finally a password (at least 8 characters).
7. The default ``docker-compose.yml`` exports the webserver on your local port
8000. If you haven't adapted this, you should now be able to visit your
Paperless instance at ``http://127.0.0.1:8000``. You can login with the
user and password you just created.
8. The default ``docker-compose.yml`` exports the webserver on your local port
8000. If you did not change this, you should now be able to visit your
Paperless instance at ``http://127.0.0.1:8000`` or your servers IP-Address:8000.
Use the login credentials you have created with the previous step.
.. _Docker: https://www.docker.com/
.. _docker-compose: https://docs.docker.com/compose/install/
.. _setup-docker_build:
Build the docker image yourself
Build the Docker image yourself
===============================
1. Clone the entire repository of paperless:
@@ -214,7 +242,7 @@ Build the docker image yourself
webserver:
image: jonaswinkler/paperless-ng:latest
and replace it with a line that instructs docker-compose to build the image from the current working directory instead:
.. code:: yaml
@@ -224,14 +252,14 @@ Build the docker image yourself
4. Run the ``compile-frontend.sh`` script. This requires ``node`` and ``npm >= v15``.
5. Follow steps 2 to 7 of :ref:`setup-docker_hub`. When asked to run
``docker-compose up -d`` to start the containers, do
5. Follow steps 3 to 8 of :ref:`setup-docker_hub`. When asked to run
``docker-compose pull`` to pull the image, do
.. code:: shell-session
$ docker-compose build
before that to build the image.
instead to build the image.
.. _setup-bare_metal:
@@ -244,19 +272,24 @@ writing. Windows is not and will never be supported.
1. Install dependencies. Paperless requires the following packages.
* ``python3`` 3.6, 3.7, 3.8 (3.9 is untested).
* ``python3-pip``, optionally ``pipenv`` for package installation
* ``python3`` 3.6, 3.7, 3.8, 3.9
* ``python3-pip``
* ``python3-dev``
* ``fonts-liberation`` for generating thumbnails for plain text files
* ``imagemagick`` >= 6 for PDF conversion
* ``optipng`` for optimizing thumbnails
* ``gnupg`` for handling encrypted documents
* ``libpoppler-cpp-dev`` for PDF to text conversion
* ``libpq-dev`` for PostgreSQL
* ``libmagic-dev`` for mime type detection
* ``mime-support`` for mime type detection
Use this list for your preferred package management:
.. code::
python3 python3-pip python3-dev imagemagick fonts-liberation optipng gnupg libpq-dev libmagic-dev mime-support
These dependencies are required for OCRmyPDF, which is used for text recognition.
* ``unpaper``
@@ -270,6 +303,12 @@ writing. Windows is not and will never be supported.
* ``tesseract-ocr`` >= 4.0.0 for OCR
* ``tesseract-ocr`` language packs (``tesseract-ocr-eng``, ``tesseract-ocr-deu``, etc)
Use this list for your preferred package management:
.. code::
unpaper ghostscript icc-profiles-free qpdf liblept5 libxml2 pngquant zlib1g tesseract-ocr
On Raspberry Pi, these libraries are required as well:
* ``libatlas-base-dev``
@@ -281,7 +320,7 @@ writing. Windows is not and will never be supported.
2. Install ``redis`` >= 5.0 and configure it to start automatically.
3. Optional. Install ``postgresql`` and configure a database, user and password for paperless. If you do not wish
to use PostgreSQL, SQLite is avialable as well.
to use PostgreSQL, SQLite is available as well.
4. Get the release archive from `<https://github.com/jonaswinkler/paperless-ng/releases>`_.
If you clone the git repo as it is, you also have to compile the front end by yourself.
@@ -305,8 +344,14 @@ writing. Windows is not and will never be supported.
* Set ``PAPERLESS_OCR_LANGUAGE`` to the language most of your documents are written in.
* Set ``PAPERLESS_TIME_ZONE`` to your local time zone.
6. Setup permissions. Create a system users under which you wish to run paperless. Ensure that these directories exist
and that the user has write permissions to the following directories
6. Create a system user under which you wish to run paperless.
.. code:: shell-session
adduser paperless --system --home /opt/paperless --group
7. Ensure that these directories exist
and that the paperless user has write permissions to the following directories:
* ``/opt/paperless/media``
* ``/opt/paperless/data``
@@ -314,39 +359,47 @@ writing. Windows is not and will never be supported.
Adjust as necessary if you configured different folders.
7. Install python requirements. Paperless comes with both Pipfiles for ``pipenv`` as well as with a ``requirements.txt``.
Both will install exactly the same requirements. It is up to you if you wish to use a virtual environment or not.
8. Install python requirements from the ``requirements.txt`` file.
It is up to you if you wish to use a virtual environment or not.
8. Go to ``/opt/paperless/src``, and execute the following commands:
.. code:: shell-session
sudo -Hu paperless pip3 install -r requirements.txt
This will install all python dependencies in the home directory of
the new paperless user.
9. Go to ``/opt/paperless/src``, and execute the following commands:
.. code:: bash
# This creates the database schema.
python3 manage.py migrate
sudo -Hu paperless python3 manage.py migrate
# This creates your first paperless user
python3 manage.py createsuperuser
sudo -Hu paperless python3 manage.py createsuperuser
9. Optional: Test that paperless is working by executing
10. Optional: Test that paperless is working by executing
.. code:: bash
# This collects static files from paperless and django.
python3 manage.py runserver
sudo -Hu paperless python3 manage.py runserver
and pointing your browser to http://localhost:8000/.
.. warning::
This is a development server which should not be used in
production.
production. It is not audited for security and performance
is inferior to production ready web servers.
.. hint::
This will not start the consumer. Paperless does this in a
separate process.
10. Setup systemd services to run paperless automatically. You may
11. Setup systemd services to run paperless automatically. You may
use the service definition files included in the ``scripts`` folder
as a starting point.
@@ -354,17 +407,15 @@ writing. Windows is not and will never be supported.
``consumer`` script to watch the input folder, and the ``scheduler``
script to run tasks such as email checking and document consumption.
You may need to adjust the path to the ``gunicorn`` executable. This
will be installed as part of the python dependencies, and is either located
in the ``bin`` folder of your virtual environment, or in ``~/.local/bin/`` if
no virtual environment is used.
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 should
add additinal dependencies.
.. hint::
You may optionally set up your preferred web server to serve
paperless as a wsgi application directly instead of running the
``webserver`` service. The module containing the wsgi application
is named ``paperless.wsgi``.
add additional dependencies.
.. caution::
@@ -373,10 +424,13 @@ writing. Windows is not and will never be supported.
however, the documentation of GUnicorn states that you should
use a proxy server in front of gunicorn instead.
11. Optional: Install a samba server and make the consumption folder
For instructions on how to use nginx for that,
:ref:`see the instructions below <setup-nginx>`.
12. Optional: Install a samba server and make the consumption folder
available as a network share.
12. Configure ImageMagick to allow processing of PDF documents. Most distributions have
13. Configure ImageMagick to allow processing of PDF documents. Most distributions have
this disabled by default, since PDF documents can contain malware. If
you don't do this, paperless will fall back to ghostscript for certain steps
such as thumbnail generation.
@@ -393,7 +447,7 @@ writing. Windows is not and will never be supported.
<policy domain="coder" rights="read|write" pattern="PDF" />
13. Optional: Install the `jbig2enc <https://ocrmypdf.readthedocs.io/en/latest/jbig2.html>`_
14. Optional: Install the `jbig2enc <https://ocrmypdf.readthedocs.io/en/latest/jbig2.html>`_
encoder. This will reduce the size of generated PDF documents. You'll most likely need
to compile this by yourself, because this software has been patented until around 2017 and
binary packages are not available for most distributions.
@@ -402,11 +456,12 @@ writing. Windows is not and will never be supported.
Install Paperless using ansible
===============================
.. note::
This role currently only supports Debian 10 Buster and Ubuntu 20.04 Focal or later as target hosts.
.. note::
1. Install ansible 2.7+ on the management node.
This role currently only supports Debian 10 Buster and Ubuntu 20.04 Focal or later as target hosts.
1. Install ansible 2.7+ on the management node.
This may be the target host paperless-ng is being installed on or any remote host which can access the target host.
For further details, check the ansible `inventory <https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html>`_ documentation.
@@ -441,7 +496,7 @@ Install Paperless using ansible
.. code:: sh
cd paperless-ng
git checkout ng-0.9.14
git checkout ng-1.0.0
3. Create an ansible ``playbook.yml`` in the paperless-ng root directory:
@@ -491,7 +546,7 @@ Install Paperless using ansible
.. code:: yaml
paperless_secret_key: PleaseGenerateAStrongKeyForThis
paperlessng_secret_key: PleaseGenerateAStrongKeyForThis
paperlessng_superuser_name: YourUserName
paperlessng_superuser_email: name@domain.tld
@@ -518,7 +573,10 @@ Migration to paperless-ng
At its core, paperless-ng 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. The important things to keep in mind are as follows.
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:
* Read the :ref:`changelog <paperless_changelog>` and take note of breaking changes.
* You should decide if you want to stick with SQLite or want to migrate your database
@@ -546,18 +604,25 @@ Migration to paperless-ng is then performed in a few simple steps:
paperless.
3. Download the latest release of paperless-ng. You can either go with the
docker-compose files from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
docker-compose files from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`__
or clone the repository to build the image yourself (see :ref:`above <setup-docker_build>`).
You can either replace your current paperless folder or put paperless-ng
in a different location.
.. caution::
Paperless includes a ``.env`` file. This will set the
project name for docker compose to ``paperless`` so that paperless-ng will
automatically reuse your existing paperless volumes. When you start it, it
will migrate your existing data. After that, your old paperless installation
will be incompatible with the migrated volumes.
Paperless-ng 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-ng. However, if you experience that paperless-ng
is not using your old paperless volumes, verify the names of your volumes with
.. code:: shell-session
$ docker volume ls | grep _data
and adjust the project name in the ``.env`` file so that it matches the name
of the volumes before the ``_data`` part.
4. Download the ``docker-compose.sqlite.yml`` file to ``docker-compose.yml``.
If you want to switch to PostgreSQL, do that after you migrated your existing
@@ -638,14 +703,12 @@ management commands as below.
This will launch the container and initialize the PostgreSQL database.
b) Without docker, open a shell in your virtual environment, switch to
b) Without docker, remember to activate any virtual environment, switch to
the ``src`` directory and create the database schema:
.. code:: shell-session
$ cd /path/to/paperless
$ pipenv shell
$ cd src
$ cd /path/to/paperless/src
$ python3 manage.py migrate
This will not copy any data yet.
@@ -662,7 +725,7 @@ management commands as below.
$ python3 manage.py loaddata data.json
6. Exit the shell.
6. If operating inside Docker, you may exit the shell now.
.. code:: shell-session
@@ -711,7 +774,8 @@ configuring some options in paperless can help improve performance immensely:
* Stick with SQLite to save some resources.
* Consider setting ``PAPERLESS_OCR_PAGES`` to 1, so that paperless will only OCR
the first page of your documents.
the first page of your documents. In most cases, this page contains enough
information to be able to find it.
* ``PAPERLESS_TASK_WORKERS`` and ``PAPERLESS_THREADS_PER_WORKER`` are configured
to use all cores. The Raspberry Pi models 3 and up have 4 cores, meaning that
paperless will use 2 workers and 2 threads per worker. This may result in
@@ -722,8 +786,13 @@ configuring some options in paperless can help improve performance immensely:
your documents before feeding them into paperless. Some scanners are able to
do this! You might want to even specify ``skip_noarchive`` to skip archive
file generation for already ocr'ed documents entirely.
* If you want to perform OCR on the the device, consider using ``PAPERLESS_OCR_CLEAN=none``.
This will speed up OCR times and use less memory at the expense of slightly worse
OCR results.
* Set ``PAPERLESS_OPTIMIZE_THUMBNAILS`` to 'false' if you want faster consumption
times. Thumbnails will be about 20% larger.
* If using docker, consider setting ``PAPERLESS_WEBSERVER_WORKERS`` to
1. This will save some memory.
For details, refer to :ref:`configuration`.
@@ -740,3 +809,46 @@ For details, refer to :ref:`configuration`.
well as on any other device.
.. _redis: https://redis.io/
.. _setup-nginx:
Using nginx as a reverse proxy
##############################
If you want to expose paperless to the internet, you should hide it behind a
reverse proxy with SSL enabled.
In addition to the usual configuration for SSL,
the following configuration is required for paperless to operate:
.. code:: nginx
http {
# Adjust as required. This is the maximum size for file uploads.
# The default value 1M might be a little too small.
client_max_body_size 10M;
server {
location / {
# Adjust host and port as required.
proxy_pass http://localhost:8000/;
# These configuration options are required for WebSockets to work.
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
}
}
Also read `this <https://channels.readthedocs.io/en/stable/deploying.html#nginx-supervisor-ubuntu>`__, towards the end of the section.

View File

@@ -30,13 +30,22 @@ Consumer fails to pickup any new files
######################################
If you notice that the consumer will only pickup files in the consumption
directory at startup, but won't find any other files added later, check out
the configuration file and enable filesystem polling with the setting
``PAPERLESS_CONSUMER_POLLING``.
directory at startup, but won't find any other files added later, you will need to
enable filesystem polling with the configuration option
``PAPERLESS_CONSUMER_POLLING``, see :ref:`here <configuration-polling>`.
This will disable listening to filesystem changes with inotify and paperless will
manually check the consumption directory for changes instead.
Paperless always redirects to /admin
####################################
You probably had the old paperless installed at some point. Paperless installed
a permanent redirect to /admin in your browser, and you need to clear your
browsing data / cache to fix that.
Operation not permitted
#######################
@@ -51,7 +60,8 @@ required so that the user running paperless inside docker has write permissions
to these folders. This happens when pointing these directories to NFS shares,
for example.
Ensure that `chown` is possible on these directories.
Ensure that ``chown`` is possible on these directories.
Classifier error: No training data available
############################################
@@ -64,6 +74,50 @@ This may have two reasons:
with Inbox tags. Verify that there are documents in your archive without inbox tags.
The algorithm will only learn from documents not in your inbox.
UserWarning in sklearn on every single document
###############################################
You may encounter warnings like this:
.. code::
/usr/local/lib/python3.7/site-packages/sklearn/base.py:315:
UserWarning: Trying to unpickle estimator CountVectorizer from version 0.23.2 when using version 0.24.0.
This might lead to breaking code or invalid results. Use at your own risk.
This happens when certain dependencies of paperless that are responsible for the auto matching algorithm are
updated. After updating these, your current training data *might* not be compatible anymore. This can be ignored
in most cases. This warning will disappear automatically when paperless updates the training data.
If you want to get rid of the warning or actually experience issues with automatic matching, delete
the file ``classification_model.pickle`` in the data directory and let paperless recreate it.
504 Server Error: Gateway Timeout when adding Office documents
##############################################################
You may experience these errors when using the optional TIKA integration:
.. code::
requests.exceptions.HTTPError: 504 Server Error: Gateway Timeout for url: http://gotenberg:3000/convert/office
Gotenberg is a server that converts Office documents into PDF documents and has a default timeout of 10 seconds.
When conversion takes longer, Gotenberg raises this error.
You can increase the timeout by configuring an environment variable for gotenberg (see also `here <https://thecodingmachine.github.io/gotenberg/#environment_variables.default_wait_timeout>`__).
If using docker-compose, this is achieved by the following configuration change in the ``docker-compose.yml`` file:
.. code:: yaml
gotenberg:
image: thecodingmachine/gotenberg
restart: unless-stopped
environment:
DISABLE_GOOGLE_CHROME: 1
DEFAULT_WAIT_TIMEOUT: 30
Permission denied errors in the consumption directory
#####################################################
@@ -78,3 +132,79 @@ Ensure that ``USERMAP_UID`` and ``USERMAP_GID`` are set to the user id and group
different from ``1000``. See :ref:`setup-docker_hub`.
Also ensure that you are able to read and write to the consumption directory on the host.
OSError: [Errno 19] No such device when consuming files
#######################################################
If you experience errors such as:
.. code:: shell-session
File "/usr/local/lib/python3.7/site-packages/whoosh/codec/base.py", line 570, in open_compound_file
return CompoundStorage(dbfile, use_mmap=storage.supports_mmap)
File "/usr/local/lib/python3.7/site-packages/whoosh/filedb/compound.py", line 75, in __init__
self._source = mmap.mmap(fileno, 0, access=mmap.ACCESS_READ)
OSError: [Errno 19] No such device
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/django_q/cluster.py", line 436, in worker
res = f(*task["args"], **task["kwargs"])
File "/usr/src/paperless/src/documents/tasks.py", line 73, in consume_file
override_tag_ids=override_tag_ids)
File "/usr/src/paperless/src/documents/consumer.py", line 271, in try_consume_file
raise ConsumerError(e)
Paperless uses a search index to provide better and faster full text searching. This search index is stored inside
the ``data`` folder. The search index uses memory-mapped files (mmap). The above error indicates that paperless
was unable to create and open these files.
This happens when you're trying to store the data directory on certain file systems (mostly network shares)
that don't support memory-mapped files.
Web-UI stuck at "Loading..."
############################
This might have multiple reasons.
1. If you built the docker image yourself or deployed using the bare metal route,
make sure that there are files in ``<paperless-root>/static/frontend/<lang-code>/``.
If there are no files, make sure that you executed ``collectstatic`` successfully, either
manually or as part of the docker image build.
If the front end is still missing, make sure that the front end is compiled (files present in
``src/documents/static/frontend``). If it is not, you need to compile the front end yourself
or download the release archive instead of cloning the repository.
2. Check the output of the web server. You might see errors like this:
.. code::
[2021-01-25 10:08:04 +0000] [40] [ERROR] Socket error processing request.
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 134, in handle
self.handle_request(listener, req, client, addr)
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 190, in handle_request
util.reraise(*sys.exc_info())
File "/usr/local/lib/python3.7/site-packages/gunicorn/util.py", line 625, in reraise
raise value
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 178, in handle_request
resp.write_file(respiter)
File "/usr/local/lib/python3.7/site-packages/gunicorn/http/wsgi.py", line 396, in write_file
if not self.sendfile(respiter):
File "/usr/local/lib/python3.7/site-packages/gunicorn/http/wsgi.py", line 386, in sendfile
sent += os.sendfile(sockno, fileno, offset + sent, count)
OSError: [Errno 22] Invalid argument
To fix this issue, add
.. code::
SENDFILE=0
to your `docker-compose.env` file.

View File

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

295
install-paperless-ng.sh Executable file
View File

@@ -0,0 +1,295 @@
#!/bin/bash
set -e
ask() {
while true ; do
if [[ -z $3 ]] ; then
read -p "$1 [$2]: " result
else
read -p "$1 ($3) [$2]: " result
fi
if [[ -z $result ]]; then
ask_result=$2
return
fi
array=$3
if [[ -z $3 || " ${array[@]} " =~ " ${result} " ]]; then
ask_result=$result
return
else
echo "Invalid option: $result"
fi
done
}
ask_docker_folder() {
while true ; do
read -p "$1 [$2]: " result
if [[ -z $result ]]; then
ask_result=$2
return
fi
if [[ $result == /* || $result == ./* ]]; then
ask_result=$result
return
else
echo "Invalid folder: $result"
fi
done
}
if [[ $(id -u) == "0" ]] ; then
echo "Do not run this script as root."
exit 1
fi
if [[ -z $(which wget) ]] ; then
echo "wget executable not found. Is wget installed?"
exit 1
fi
if [[ -z $(which docker) ]] ; then
echo "docker executable not found. Is docker installed?"
exit 1
fi
if [[ -z $(which docker-compose) ]] ; then
echo "docker-compose executable not found. Is docker-compose installed?"
exit 1
fi
echo ""
echo "############################################"
echo "### Paperless-ng docker installation ###"
echo "############################################"
echo ""
echo "This script will download, configure and start paperless-ng."
echo ""
echo "1. Folder configuration"
echo "======================="
echo ""
echo "The target folder is used to store the configuration files of "
echo "paperless. You can move this folder around after installing paperless."
echo "You will need this folder whenever you want to start, stop, update or "
echo "maintain your paperless instance."
echo ""
ask "Target folder" "$(pwd)/paperless-ng"
TARGET_FOLDER=$ask_result
echo ""
echo "The consume folder is where paperles will search for new documents."
echo "Point this to a folder where your scanner is able to put your scanned"
echo "documents."
echo ""
echo "CAUTION: You must specify an absolute path starting with / or a relative "
echo "path starting with ./ here. Examples:"
echo " /mnt/consume"
echo " ./consume"
echo ""
ask_docker_folder "Consume folder" "$TARGET_FOLDER/consume"
CONSUME_FOLDER=$ask_result
echo ""
echo "The media folder is where paperless stores your documents."
echo "Leave empty and docker will manage this folder for you."
echo "Docker usually stores managed folders in /var/lib/docker/volumes."
echo ""
echo "CAUTION: If specified, you must specify an absolute path starting with /"
echo "or a relative path starting with ./ here."
echo ""
ask_docker_folder "Media folder" ""
MEDIA_FOLDER=$ask_result
echo ""
echo "The data folder is where paperless stores other data, such as your"
echo "SQLite database (if used), the search index and other data."
echo "As with the media folder, leave empty to have this managed by docker."
echo ""
echo "CAUTION: If specified, you must specify an absolute path starting with /"
echo "or a relative path starting with ./ here."
echo ""
ask_docker_folder "Data folder" ""
DATA_FOLDER=$ask_result
echo ""
echo "2. Application configuration"
echo "============================"
echo ""
echo "The port on which the paperless webserver will listen for incoming"
echo "connections."
echo ""
ask "Port" "8000"
PORT=$ask_result
echo ""
echo "Database backend: PostgreSQL and SQLite are available. Use PostgreSQL"
echo "if unsure. If you're running on a low-power device such as Raspberry"
echo "Pi, use SQLite to save resources."
echo ""
ask "Database backend" "postgres" "postgres sqlite"
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 "requires more resources due to the required services."
echo ""
ask "Enable Apache Tika?" "no" "yes no"
TIKA_ENABLED=$ask_result
echo ""
echo "Specify the default language that most of your documents are written in."
echo "Use ISO 639-2, (T) variant language codes: "
echo "https://www.loc.gov/standards/iso639-2/php/code_list.php"
echo "Common values: eng (English) deu (German) nld (Dutch) fra (French)"
echo ""
ask "OCR language" "eng"
OCR_LANGUAGE=$ask_result
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 "If unsure, leave default."
echo ""
ask "User ID" "$(id -u)"
USERMAP_UID=$ask_result
ask "Group ID" "$(id -g)"
USERMAP_GID=$ask_result
echo ""
echo "3. Login credentials"
echo "===================="
echo ""
echo "Specify initial login credentials. You can change these later."
echo "A mail address is required, however it is not used in paperless. You don't"
echo "need to provide an actual mail address."
echo ""
ask "Paperless username" "$(whoami)"
USERNAME=$ask_result
while true; do
read -sp "Paperless password: " PASSWORD
echo ""
if [[ -z $PASSWORD ]] ; then
echo "Password cannot be empty."
continue
fi
read -sp "Paperless password (again): " PASSWORD_REPEAT
echo ""
if [[ ! "$PASSWORD" == "$PASSWORD_REPEAT" ]] ; then
echo "Passwords did not match"
else
break
fi
done
ask "Email" "$USERNAME@localhost"
EMAIL=$ask_result
echo ""
echo "Summary"
echo "======="
echo ""
echo "Target folder: $TARGET_FOLDER"
echo "Consume folder: $CONSUME_FOLDER"
if [[ -z $MEDIA_FOLDER ]] ; then
echo "Media folder: Managed by docker"
else
echo "Media folder: $MEDIA_FOLDER"
fi
if [[ -z $DATA_FOLDER ]] ; then
echo "Data folder: Managed by docker"
else
echo "Data folder: $DATA_FOLDER"
fi
echo ""
echo "Port: $PORT"
echo "Database: $DATABASE_BACKEND"
echo "Tika enabled: $TIKA_ENABLED"
echo "OCR language: $OCR_LANGUAGE"
echo "User id: $USERMAP_UID"
echo "Group id: $USERMAP_GID"
echo ""
echo "Paperless username: $USERNAME"
echo "Paperless email: $EMAIL"
echo ""
read -p "Press any key to install."
echo ""
echo "Installing paperless..."
echo ""
mkdir -p "$TARGET_FOLDER"
cd "$TARGET_FOLDER"
DOCKER_COMPOSE_VERSION=$DATABASE_BACKEND
if [[ $TIKA_ENABLED == "yes" ]] ; then
DOCKER_COMPOSE_VERSION="$DOCKER_COMPOSE_VERSION-tika"
fi
wget "https://raw.githubusercontent.com/jonaswinkler/paperless-ng/master/docker/compose/docker-compose.$DOCKER_COMPOSE_VERSION.yml" -O docker-compose.yml
wget "https://raw.githubusercontent.com/jonaswinkler/paperless-ng/master/docker/compose/.env" -O .env
SECRET_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1)
DEFAULT_LANGUAGES="deu eng fra ita spa"
{
if [[ ! $USERMAP_UID == "1000" ]] ; then
echo "USERMAP_UID=$USERMAP_UID"
fi
if [[ ! $USERMAP_GID == "1000" ]] ; then
echo "USERMAP_GID=$USERMAP_GID"
fi
echo "PAPERLESS_OCR_LANGUAGE=$OCR_LANGUAGE"
echo "PAPERLESS_SECRET_KEY=$SECRET_KEY"
if [[ ! " ${DEFAULT_LANGUAGES[@]} " =~ " ${OCR_LANGUAGE} " ]] ; then
echo "PAPERLESS_OCR_LANGUAGES=$OCR_LANGUAGE"
fi
} > docker-compose.env
sed -i "s/- 8000:8000/- $PORT:8000/g" docker-compose.yml
sed -i "s#- \./consume:/usr/src/paperless/consume#- $CONSUME_FOLDER:/usr/src/paperless/consume#g" docker-compose.yml
if [[ -n $MEDIA_FOLDER ]] ; then
sed -i "s#- media:/usr/src/paperless/media#- $MEDIA_FOLDER:/usr/src/paperless/media#g" docker-compose.yml
fi
if [[ -n $DATA_FOLDER ]] ; then
sed -i "s#- data:/usr/src/paperless/data#- $DATA_FOLDER:/usr/src/paperless/data#g" docker-compose.yml
fi
docker-compose pull
docker-compose run --rm -e DJANGO_SUPERUSER_PASSWORD="$PASSWORD" webserver createsuperuser --noinput --username "$USERNAME" --email "$EMAIL"
docker-compose up -d

View File

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

View File

@@ -7,68 +7,94 @@
-i https://pypi.python.org/simple
--extra-index-url https://www.piwheels.org/simple
arrow==0.17.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
aioredis==1.3.1
arrow==1.0.1; python_version >= '3.6'
asgiref==3.3.1; python_version >= '3.5'
blessed==1.17.12
async-timeout==3.0.1; python_full_version >= '3.5.3'
attrs==20.3.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
autobahn==21.2.2; python_version >= '3.7'
automat==20.2.0
blessed==1.18.0
certifi==2020.12.5
cffi==1.14.4
cffi==1.14.5
channels-redis==3.2.0
channels==3.0.3
chardet==4.0.0; python_version >= '3.1'
click==7.1.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
coloredlogs==15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
cryptography==3.3.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
dateparser==0.7.6
django-cors-headers==3.6.0
django-extensions==3.1.0
concurrent-log-handler==0.9.19
constantly==15.1.0
cryptography==3.4.6
daphne==3.0.1; python_version >= '3.6'
dateparser==1.0.0
django-cors-headers==3.7.0
django-extensions==3.1.1
django-filter==2.4.0
django-picklefield==3.0.1; python_version >= '3'
django-q==1.3.4
django==3.1.5
django==3.1.7
djangorestframework==3.12.2
filelock==3.0.12
fuzzywuzzy==0.18.0
fuzzywuzzy[speedup]==0.18.0
gunicorn==20.0.4
h11==0.12.0; python_version >= '3.6'
hiredis==1.1.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
httptools==0.1.1
humanfriendly==9.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
hyperlink==21.0.0
idna==2.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
imap-tools==0.34.0
imap-tools==0.38.1
img2pdf==0.4.0
importlib-metadata==3.4.0; python_version < '3.8'
incremental==17.5.0
inotify-simple==1.3.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
inotifyrecursive==0.3.5
joblib==1.0.0; python_version >= '3.6'
joblib==1.0.1; python_version >= '3.6'
langdetect==1.0.8
lxml==4.6.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
numpy==1.19.5; python_version >= '3.6'
ocrmypdf==11.4.5
msgpack==1.0.2
numpy==1.19.5
ocrmypdf==11.7.0
pathvalidate==2.3.2
pdfminer.six==20201018; python_version >= '3.4'
pdftotext==2.1.5
pikepdf==2.2.5
pdfminer.six==20201018
pikepdf==2.5.2
pillow==8.1.0
pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
portalocker==2.2.1; python_version >= '3'
psycopg2-binary==2.8.6
pyasn1-modules==0.2.8
pyasn1==0.4.8
pycparser==2.20; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pyopenssl==20.0.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
python-dateutil==2.8.1
python-dotenv==0.15.0
python-gnupg==0.4.6
python-levenshtein==0.12.0
python-magic==0.4.18
pytz==2020.5
python-levenshtein==0.12.2
python-magic==0.4.22
pytz==2021.1
pyyaml==5.4.1
redis==3.5.3
regex==2020.11.13
reportlab==3.5.59
requests==2.25.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
scikit-learn==0.24.0
scipy==1.5.4; python_version >= '3.6'
scipy==1.5.4
service-identity==18.1.0
six==1.15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
sortedcontainers==2.3.0
sqlparse==0.4.1; python_version >= '3.5'
threadpoolctl==2.1.0; python_version >= '3.5'
tika==1.24
tqdm==4.56.0
typing-extensions==3.7.4.3; python_version < '3.8'
tqdm==4.58.0
twisted[tls]==21.2.0; python_full_version >= '3.5.4'
txaio==21.2.1; python_version >= '3.6'
tzlocal==2.1
urllib3==1.26.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
urllib3==1.26.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
uvicorn[standard]==0.13.4
uvloop==0.14.0
watchdog==1.0.2
watchgod==0.7
wcwidth==0.2.5
websockets==8.1
whitenoise==5.2.0
whoosh==2.7.4
zipp==3.4.0; python_version >= '3.6'
zope.interface==5.2.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'

16
resources/logo.txt Normal file
View File

@@ -0,0 +1,16 @@
9w
{@@N
Q@@@@H
G@@@@@@@\
SilN@@@@@@@
*Q *@@@@@@@@S /= = = = = = = = = = = = = = = = = =\
*@ B@@@@@@@@N || ||
N R$ A@@@@@@@@@@ || PAPERLESS-NG ||
x@@ $U B@@@@@@@@@R || ||
N@@N^ @ N@@@@@@@@@* \= = = = = = = = = = = = = = = = = =/
|@@@u @ E@@@@@@@@l
Q@@@ \ Px@@@@@@P
1@@S` @@@o'
z$ ;
v
/

View File

@@ -8,7 +8,7 @@ Requires=redis.service
User=paperless
Group=paperless
WorkingDirectory=/opt/paperless/src
ExecStart=/opt/paperless/.local/bin/gunicorn paperless.wsgi -w 2 -b 0.0.0.0:8000
ExecStart=/opt/paperless/.local/bin/gunicorn -c /opt/paperless/gunicorn.conf.py paperless.asgi:application
[Install]
WantedBy=multi-user.target

View File

@@ -1,4 +1,4 @@
docker run -p 5432:5432 -v paperless_pgdata:/var/lib/postgresql/data -d postgres:13
docker run -p 5432:5432 -e POSTGRES_PASSWORD=password -v paperless_pgdata:/var/lib/postgresql/data -d postgres:13
docker run -d -p 6379:6379 redis:latest
docker run -p 3000:3000 -d thecodingmachine/gotenberg
docker run -p 9998:9998 -d apache/tika

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -2035,6 +2035,11 @@
"to-fast-properties": "^2.0.0"
}
},
"@ctrl/tinycolor": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
"integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ=="
},
"@istanbuljs/schema": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
@@ -2055,9 +2060,9 @@
}
},
"@ng-bootstrap/ng-bootstrap": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.0.tgz",
"integrity": "sha512-v77Gfd8xHH+exq0WqIqVRlxbUEHdA/2+RUJenUP2IDTQN9E1rWl7O461/kosr+0XPuxPArHQJxhh/WsCYckcNg==",
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.4.tgz",
"integrity": "sha512-EdxTwOPOtlvfnwrglPniulmzdnXdXH3lTGaGAY1HrYRvdtGg6wicRvl+BvwVE/3Qik5NPkOWMVghUHpv3evIYg==",
"requires": {
"tslib": "^2.0.0"
}
@@ -5545,6 +5550,11 @@
"schema-utils": "^2.6.5"
}
},
"file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
@@ -7890,6 +7900,11 @@
"object-visit": "^1.0.0"
}
},
"material-colors": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
},
"md5.js": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
@@ -8328,6 +8343,16 @@
}
}
},
"ngx-color": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-6.2.0.tgz",
"integrity": "sha512-n04tcMnCpOgmI24egST94YwHmnSoAxK8O1T2t3nGrTwWbvw5XBRJvImNFnoNrriBXzc4Gx4hFehH5MU8CZxp1w==",
"requires": {
"@ctrl/tinycolor": "^3.1.6",
"material-colors": "^1.2.6",
"tslib": "^2.0.0"
}
},
"ngx-cookie-service": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-10.1.1.tgz",

View File

@@ -20,11 +20,13 @@
"@angular/platform-browser": "~10.1.5",
"@angular/platform-browser-dynamic": "~10.1.5",
"@angular/router": "~10.1.5",
"@ng-bootstrap/ng-bootstrap": "^8.0.0",
"@ng-bootstrap/ng-bootstrap": "^8.0.4",
"@ng-select/ng-select": "^5.0.9",
"bootstrap": "^4.5.0",
"file-saver": "^2.0.5",
"ng-bootstrap": "^1.6.3",
"ng2-pdf-viewer": "^6.3.2",
"ngx-color": "^6.2.0",
"ngx-cookie-service": "^10.1.1",
"ngx-file-drop": "^10.0.0",
"ngx-infinite-scroll": "^9.1.0",

View File

@@ -1,17 +1,70 @@
import { Component } from '@angular/core';
import { SettingsService } from './services/settings.service';
import { SettingsService, SETTINGS_KEYS } from './services/settings.service';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { ConsumerStatusService } from './services/consumer-status.service';
import { ToastService } from './services/toast.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
export class AppComponent implements OnInit, OnDestroy {
constructor (private settings: SettingsService) {
newDocumentSubscription: Subscription;
successSubscription: Subscription;
failedSubscription: Subscription;
constructor (private settings: SettingsService, private consumerStatusService: ConsumerStatusService, private toastService: ToastService, private router: Router) {
let anyWindow = (window as any)
anyWindow.pdfWorkerSrc = '/assets/js/pdf.worker.min.js';
this.settings.updateDarkModeSettings()
}
ngOnDestroy(): void {
this.consumerStatusService.disconnect()
if (this.successSubscription) {
this.successSubscription.unsubscribe()
}
if (this.failedSubscription) {
this.failedSubscription.unsubscribe()
}
if (this.newDocumentSubscription) {
this.newDocumentSubscription.unsubscribe()
}
}
private showNotification(key) {
if (this.router.url == '/dashboard' && this.settings.get(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD)) {
return false
}
return this.settings.get(key)
}
ngOnInit(): void {
this.consumerStatusService.connect()
this.successSubscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
if (this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS)) {
this.toastService.show({title: $localize`Document added`, delay: 10000, content: $localize`Document ${status.filename} was added to paperless.`, actionName: $localize`Open document`, action: () => {
this.router.navigate(['documents', status.documentId])
}})
}
})
this.failedSubscription = this.consumerStatusService.onDocumentConsumptionFailed().subscribe(status => {
if (this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED)) {
this.toastService.showError($localize`Could not add ${status.filename}\: ${status.message}`)
}
})
this.newDocumentSubscription = this.consumerStatusService.onDocumentDetected().subscribe(status => {
if (this.showNotification(SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT)) {
this.toastService.show({title: $localize`New document detected`, delay: 5000, content: $localize`Document ${status.filename} is being processed by paperless.`})
}
})
}
}

View File

@@ -3,7 +3,7 @@ import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NgbDateAdapter, NgbDateParserFormatter, NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { DocumentListComponent } from './components/document-list/document-list.component';
import { DocumentDetailComponent } from './components/document-detail/document-detail.component';
@@ -39,7 +39,6 @@ import { SelectComponent } from './components/common/input/select/select.compone
import { CheckComponent } from './components/common/input/check/check.component';
import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { DateTimeComponent } from './components/common/input/date-time/date-time.component';
import { TagsComponent } from './components/common/input/tags/tags.component';
import { SortableDirective } from './directives/sortable.directive';
import { CookieService } from 'ngx-cookie-service';
@@ -60,14 +59,29 @@ import { NgSelectModule } from '@ng-select/ng-select';
import { NumberComponent } from './components/common/input/number/number.component';
import { SafePipe } from './pipes/safe.pipe';
import { CustomDatePipe } from './pipes/custom-date.pipe';
import { DateComponent } from './components/common/input/date/date.component';
import { ISODateTimeAdapter } from './utils/ngb-iso-date-time-adapter';
import { LocalizedDateParserFormatter } from './utils/ngb-date-parser-formatter';
import { ApiVersionInterceptor } from './interceptors/api-version.interceptor';
import { ColorSliderModule } from 'ngx-color/slider';
import { ColorComponent } from './components/common/input/color/color.component';
import localeFr from '@angular/common/locales/fr';
import localeNl from '@angular/common/locales/nl';
import localeDe from '@angular/common/locales/de';
import localePt from '@angular/common/locales/pt';
import localeIt from '@angular/common/locales/it';
import localeEnGb from '@angular/common/locales/en-GB';
import localeRo from '@angular/common/locales/ro';
registerLocaleData(localeFr)
registerLocaleData(localeNl)
registerLocaleData(localeDe)
registerLocaleData(localePt, "pt-BR")
registerLocaleData(localeIt)
registerLocaleData(localeEnGb)
registerLocaleData(localeRo)
@NgModule({
declarations: [
@@ -102,7 +116,6 @@ registerLocaleData(localeDe)
SelectComponent,
CheckComponent,
SaveViewConfigDialogComponent,
DateTimeComponent,
TagsComponent,
SortableDirective,
SavedViewWidgetComponent,
@@ -118,7 +131,9 @@ registerLocaleData(localeDe)
SelectDialogComponent,
NumberComponent,
SafePipe,
CustomDatePipe
CustomDatePipe,
DateComponent,
ColorComponent
],
imports: [
BrowserModule,
@@ -130,7 +145,8 @@ registerLocaleData(localeDe)
NgxFileDropModule,
InfiniteScrollModule,
PdfViewerModule,
NgSelectModule
NgSelectModule,
ColorSliderModule
],
providers: [
DatePipe,
@@ -138,9 +154,15 @@ registerLocaleData(localeDe)
provide: HTTP_INTERCEPTORS,
useClass: CsrfInterceptor,
multi: true
},{
provide: HTTP_INTERCEPTORS,
useClass: ApiVersionInterceptor,
multi: true
},
FilterPipe,
DocumentTitlePipe
DocumentTitlePipe,
{provide: NgbDateAdapter, useClass: ISODateTimeAdapter},
{provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter}
],
bootstrap: [AppComponent]
})

View File

@@ -52,12 +52,7 @@
<div class="container-fluid">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse" [ngbCollapse]="isMenuCollapsed">
<div style="position: absolute; bottom: 0; left: 0;" class="text-muted p-1">
{{versionString}}
</div>
<div class="sidebar-sticky pt-3">
<div class="sidebar-sticky pt-3 d-flex flex-column justify-space-around">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link" routerLink="dashboard" routerLinkActive="active" (click)="closeMenu()">
@@ -75,7 +70,7 @@
</li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted" *ngIf='savedViewService.sidebarViews.length > 0'>
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted" *ngIf='savedViewService.sidebarViews.length > 0'>
<ng-container i18n>Saved views</ng-container>
</h6>
<ul class="nav flex-column mb-2">
@@ -88,7 +83,7 @@
</li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted" *ngIf='openDocuments.length > 0'>
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted" *ngIf='openDocuments.length > 0'>
<ng-container i18n>Open documents</ng-container>
</h6>
<ul class="nav flex-column mb-2">
@@ -97,9 +92,14 @@
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#file-text"/>
</svg>&nbsp;{{d.title | documentTitle}}
<span class="close bg-light" (click)="closeDocument(d); $event.preventDefault()">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-x" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>
</span>
</a>
</li>
<li class="nav-item w-100" *ngIf="openDocuments.length > 1">
<li class="nav-item w-100" *ngIf="openDocuments.length >= 1">
<a class="nav-link text-truncate" [routerLink]="" (click)="closeAll()">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
@@ -108,7 +108,7 @@
</li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted">
<ng-container i18n>Manage</ng-container>
</h6>
<ul class="nav flex-column mb-2">
@@ -156,8 +156,8 @@
</li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<ng-container i18n>Misc</ng-container>
<h6 class="sidebar-heading px-3 mt-auto pt-4 mb-1 text-muted">
<ng-container i18n>Info</ng-container>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
@@ -168,11 +168,24 @@
</a>
</li>
<li class="nav-item">
<a class="nav-link" target="_blank" rel="noopener noreferrer" href="https://github.com/jonaswinkler/paperless-ng">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#link"/>
</svg>&nbsp;<ng-container i18n>GitHub</ng-container>
</a>
<div class="d-flex w-100 flex-wrap">
<a class="nav-link pr-0 pb-0" target="_blank" rel="noopener noreferrer" href="https://github.com/jonaswinkler/paperless-ng">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
</svg>&nbsp;<ng-container i18n>GitHub</ng-container>
</a>
<a class="nav-link-additional small text-muted ml-3" target="_blank" rel="noopener noreferrer" href="https://github.com/jonaswinkler/paperless-ng/discussions/categories/feature-requests" title="Suggest an idea">
<svg xmlns="http://www.w3.org/2000/svg" width=".9rem" height=".9rem" fill="currentColor" class="bi bi-lightbulb pr-1" viewBox="0 0 16 16">
<path d="M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13a.5.5 0 0 1 0 1 .5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1 0-1 .5.5 0 0 1 0-1 .5.5 0 0 1-.46-.302l-.761-1.77a1.964 1.964 0 0 0-.453-.618A5.984 5.984 0 0 1 2 6zm6-5a5 5 0 0 0-3.479 8.592c.263.254.514.564.676.941L5.83 12h4.342l.632-1.467c.162-.377.413-.687.676-.941A5 5 0 0 0 8 1z"/>
</svg>
<ng-container i18n>Suggest an idea</ng-container>
</a>
</div>
</li>
<li class="nav-item mt-2">
<div class="px-3 py-2 text-muted small">
{{versionString}}
</div>
</li>
</ul>
</div>

View File

@@ -24,6 +24,7 @@
padding-top: 0.5rem;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
min-height: min-content;
}
@supports ((position: -webkit-sticky) or (position: sticky)) {
.sidebar-sticky {
@@ -57,6 +58,49 @@
text-transform: uppercase;
}
.nav {
flex-wrap: nowrap;
}
.nav-item {
position: relative;
&:hover .close {
display: block;
}
.close {
display: none;
position: absolute;
cursor: pointer;
opacity: 1;
top: 0;
padding: .25rem .3rem 0;
right: .4rem;
width: 1.8rem;
height: 100%;
svg {
opacity: 0.5;
}
&:hover svg {
opacity: 1;
}
}
.nav-link-additional {
margin-top: 0.2rem;
margin-left: 0.25rem;
padding-top: 0.5rem;
svg {
margin-bottom: 2px;
}
}
}
/*
* Navbar
*/

View File

@@ -1,7 +1,7 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { from, Observable, Subscription } from 'rxjs';
import { ActivatedRoute, Router, Params } from '@angular/router';
import { from, Observable, Subscription, BehaviorSubject } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { OpenDocumentsService } from 'src/app/services/open-documents.service';
@@ -16,7 +16,7 @@ import { Meta } from '@angular/platform-browser';
templateUrl: './app-frame.component.html',
styleUrls: ['./app-frame.component.scss']
})
export class AppFrameComponent implements OnInit, OnDestroy {
export class AppFrameComponent implements OnInit {
constructor (
public router: Router,
@@ -26,7 +26,7 @@ export class AppFrameComponent implements OnInit, OnDestroy {
public savedViewService: SavedViewService,
private meta: Meta
) {
}
versionString = `${environment.appTitle} ${environment.version}`
@@ -39,9 +39,9 @@ export class AppFrameComponent implements OnInit, OnDestroy {
searchField = new FormControl('')
openDocuments: PaperlessDocument[] = []
openDocumentsSubscription: Subscription
get openDocuments(): PaperlessDocument[] {
return this.openDocumentsService.getOpenDocuments()
}
searchAutoComplete = (text$: Observable<string>) =>
text$.pipe(
@@ -77,12 +77,24 @@ export class AppFrameComponent implements OnInit, OnDestroy {
this.router.navigate(['search'], {queryParams: {query: this.searchField.value}})
}
closeDocument(d: PaperlessDocument) {
this.closeMenu()
this.openDocumentsService.closeDocument(d)
let route = this.activatedRoute.snapshot
while (route.firstChild) {
route = route.firstChild
}
if (route.component == DocumentDetailComponent && route.params['id'] == d.id) {
this.router.navigate([""])
}
}
closeAll() {
this.closeMenu()
this.openDocumentsService.closeAll()
// TODO: is there a better way to do this?
let route = this.activatedRoute
let route = this.activatedRoute.snapshot
while (route.firstChild) {
route = route.firstChild
}
@@ -92,13 +104,6 @@ export class AppFrameComponent implements OnInit, OnDestroy {
}
ngOnInit() {
this.openDocuments = this.openDocumentsService.getOpenDocuments()
}
ngOnDestroy() {
if (this.openDocumentsSubscription) {
this.openDocumentsSubscription.unsubscribe()
}
}
get displayName() {

View File

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

View File

@@ -1,7 +1,10 @@
import { formatDate } from '@angular/common';
import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core';
import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';
import { Subject, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { debounceTime } from 'rxjs/operators';
import { SettingsService } from 'src/app/services/settings.service';
import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter';
export interface DateSelection {
before?: string
@@ -16,10 +19,17 @@ const LAST_YEAR = 3
@Component({
selector: 'app-date-dropdown',
templateUrl: './date-dropdown.component.html',
styleUrls: ['./date-dropdown.component.scss']
styleUrls: ['./date-dropdown.component.scss'],
providers: [
{provide: NgbDateAdapter, useClass: ISODateAdapter},
]
})
export class DateDropdownComponent implements OnInit, OnDestroy {
constructor(settings: SettingsService) {
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
}
quickFilters = [
{id: LAST_7_DAYS, name: $localize`Last 7 days`},
{id: LAST_MONTH, name: $localize`Last month`},
@@ -27,6 +37,8 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
{id: LAST_YEAR, name: $localize`Last year`}
]
datePlaceHolder: string
@Input()
dateBefore: string

View File

@@ -0,0 +1,33 @@
<div class="form-group">
<label [for]="inputId">{{title}}</label>
<div class="input-group" [class.is-invalid]="error">
<div class="input-group-prepend">
<span class="input-group-text" [style.background-color]="value">&nbsp;&nbsp;&nbsp;</span>
</div>
<ng-template #popContent>
<div style="min-width: 200px;" class="pb-3">
<color-slider [color]="value" (onChangeComplete)="colorChanged($event.color.hex)"></color-slider>
</div>
</ng-template>
<input class="form-control" [class.is-invalid]="error" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" [autoClose]="'outside'" [ngbPopover]="popContent" placement="bottom" popoverClass="shadow">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" (click)="randomize()">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dice-5" viewBox="0 0 16 16">
<path d="M13 1a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h10zM3 0a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V3a3 3 0 0 0-3-3H3z"/>
<path d="M5.5 4a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm8 0a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0 8a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm-8 0a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm4-4a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/>
</svg>
</button>
</div>
</div>
<small *ngIf="hint" class="form-text text-muted">{{hint}}</small>
<div class="invalid-feedback">
{{error}}
</div>
</div>

View File

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

View File

@@ -0,0 +1,30 @@
import { Component, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { randomColor } from 'src/app/utils/color';
import { AbstractInputComponent } from '../abstract-input';
@Component({
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ColorComponent),
multi: true
}],
selector: 'app-input-color',
templateUrl: './color.component.html',
styleUrls: ['./color.component.scss']
})
export class ColorComponent extends AbstractInputComponent<string> {
constructor() {
super()
}
randomize() {
this.colorChanged(randomColor())
}
colorChanged(value) {
this.value = value
this.onChange(value)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,4 +22,12 @@
</div>
</div>
<small *ngIf="hint" class="form-text text-muted">{{hint}}</small>
<small *ngIf="getSuggestions().length > 0">
<span i18n>Suggestions:</span>&nbsp;
<ng-container *ngFor="let s of getSuggestions()">
<a (click)="value = s.id; onChange(value)" [routerLink]="">{{s.name}}</a>&nbsp;
</ng-container>
</small>
</div>

View File

@@ -30,11 +30,22 @@ export class SelectComponent extends AbstractInputComponent<number> {
@Input()
allowNull: boolean = false
@Input()
suggestions: number[]
@Output()
createNew = new EventEmitter()
showPlusButton(): boolean {
return this.createNew.observers.length > 0
}
getSuggestions() {
if (this.suggestions && this.items) {
return this.suggestions.filter(id => id != this.value).map(id => this.items.find(item => item.id == id))
} else {
return []
}
}
}

View File

@@ -2,30 +2,25 @@
<label for="tags" i18n>Tags</label>
<div class="input-group flex-nowrap">
<ng-select name="tags" [items]="tags" bindLabel="name" bindValue="id" [(ngModel)]="displayValue"
<ng-select name="tags" [items]="tags" bindLabel="name" bindValue="id" [(ngModel)]="value"
[multiple]="true"
[closeOnSelect]="false"
[clearSearchOnAdd]="true"
[disabled]="disabled"
[hideSelected]="true"
(change)="ngSelectChange()">
(change)="onChange(value)"
(blur)="onTouched()">
<ng-template ng-label-tmp let-item="item">
<span class="tag-wrap tag-wrap-delete" (click)="removeTag(item.id)">
<svg width="1.2em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>
<app-tag style="background-color: none;" [tag]="getTag(item.id)"></app-tag>
<app-tag *ngIf="item.id && tags" style="background-color: none;" [tag]="getTag(item.id)"></app-tag>
</span>
</ng-template>
<ng-template ng-option-tmp let-item="item" let-index="index" let-search="searchTerm">
<div class="tag-wrap">
<div class="selected-icon d-inline-block mr-1">
<svg *ngIf="displayValue.includes(item.id)" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<use xlink:href="assets/bootstrap-icons.svg#check"/>
</svg>
</div>
<app-tag class="mr-2" [tag]="getTag(item.id)"></app-tag>
<app-tag *ngIf="item.id && tags" class="mr-2" [tag]="getTag(item.id)"></app-tag>
</div>
</ng-template>
</ng-select>
@@ -39,5 +34,13 @@
</div>
</div>
<small class="form-text text-muted" *ngIf="hint">{{hint}}</small>
<small *ngIf="getSuggestions().length > 0">
<span i18n>Suggestions:</span>&nbsp;
<ng-container *ngFor="let tag of getSuggestions()">
<a (click)="addTag(tag.id)" [routerLink]="">{{tag.name}}</a>&nbsp;
</ng-container>
</small>
</div>

View File

@@ -26,9 +26,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
writeValue(newValue: number[]): void {
this.value = newValue
if (this.tags) {
this.displayValue = newValue
}
}
registerOnChange(fn: any): void {
this.onChange = fn;
@@ -43,7 +40,6 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
ngOnInit(): void {
this.tagService.listAll().subscribe(result => {
this.tags = result.results
this.displayValue = this.value
})
}
@@ -53,23 +49,28 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
@Input()
hint
value: number[]
@Input()
suggestions: number[]
displayValue: number[] = []
value: number[]
tags: PaperlessTag[]
getTag(id) {
return this.tags.find(tag => tag.id == id)
if (this.tags) {
return this.tags.find(tag => tag.id == id)
} else {
return null
}
}
removeTag(id) {
let index = this.displayValue.indexOf(id)
let index = this.value.indexOf(id)
if (index > -1) {
let oldValue = this.displayValue
let oldValue = this.value
oldValue.splice(index, 1)
this.displayValue = [...oldValue]
this.onChange(this.displayValue)
this.value = [...oldValue]
this.onChange(this.value)
}
}
@@ -79,15 +80,23 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
modal.componentInstance.success.subscribe(newTag => {
this.tagService.listAll().subscribe(tags => {
this.tags = tags.results
this.displayValue = [...this.displayValue, newTag.id]
this.onChange(this.displayValue)
this.value = [...this.value, newTag.id]
this.onChange(this.value)
})
})
}
ngSelectChange() {
this.value = this.displayValue
this.onChange(this.displayValue)
getSuggestions() {
if (this.suggestions && this.tags) {
return this.suggestions.filter(id => !this.value.includes(id)).map(id => this.tags.find(tag => tag.id == id))
} else {
return []
}
}
addTag(id) {
this.value = [...this.value, id]
this.onChange(this.value)
}
}

View File

@@ -1,2 +1,2 @@
<span *ngIf="!clickable" class="badge" [style.background]="getColour().value" [style.color]="getColour().textColor">{{tag.name}}</span>
<a [routerLink]="" [title]="linkTitle" *ngIf="clickable" class="badge" [style.background]="getColour().value" [style.color]="getColour().textColor">{{tag.name}}</a>
<span *ngIf="!clickable" class="badge" [style.background]="tag.color" [style.color]="tag.text_color">{{tag.name}}</span>
<a [routerLink]="" [title]="linkTitle" *ngIf="clickable" class="badge" [style.background]="tag.color" [style.color]="tag.text_color">{{tag.name}}</a>

View File

@@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core';
import { TAG_COLOURS, PaperlessTag } from 'src/app/data/paperless-tag';
import { PaperlessTag } from 'src/app/data/paperless-tag';
@Component({
selector: 'app-tag',
@@ -22,8 +22,4 @@ export class TagComponent implements OnInit {
ngOnInit(): void {
}
getColour() {
return TAG_COLOURS.find(c => c.id == this.tag.colour)
}
}

View File

@@ -3,5 +3,6 @@
[header]="toast.title" [autohide]="true" [delay]="toast.delay"
[class]="toast.classname"
(hide)="toastService.closeToast(toast)">
{{toast.content}}
</ngb-toast>
<p>{{toast.content}}</p>
<p *ngIf="toast.action"><button class="btn btn-sm btn-outline-secondary" (click)="toastService.closeToast(toast); toast.action()">{{toast.actionName}}</button></p>
</ngb-toast>

View File

@@ -19,6 +19,6 @@
<app-statistics-widget></app-statistics-widget>
<app-upload-file-widget></app-upload-file-widget>
</div>
</div>

View File

@@ -18,4 +18,4 @@
</tbody>
</table>
</app-widget-frame>
</app-widget-frame>

View File

@@ -1,8 +1,10 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { ConsumerStatusService } from 'src/app/services/consumer-status.service';
import { DocumentService } from 'src/app/services/rest/document.service';
@Component({
@@ -10,19 +12,33 @@ import { DocumentService } from 'src/app/services/rest/document.service';
templateUrl: './saved-view-widget.component.html',
styleUrls: ['./saved-view-widget.component.scss']
})
export class SavedViewWidgetComponent implements OnInit {
export class SavedViewWidgetComponent implements OnInit, OnDestroy {
constructor(
private documentService: DocumentService,
private router: Router,
private list: DocumentListViewService) { }
private list: DocumentListViewService,
private consumerStatusService: ConsumerStatusService) { }
@Input()
savedView: PaperlessSavedView
documents: PaperlessDocument[] = []
subscription: Subscription
ngOnInit(): void {
this.reload()
this.subscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(status => {
this.reload()
})
}
ngOnDestroy(): void {
this.subscription.unsubscribe()
}
reload() {
this.documentService.listFiltered(1,10,this.savedView.sort_field, this.savedView.sort_reverse, this.savedView.filter_rules).subscribe(result => {
this.documents = result.results
})
@@ -32,7 +48,7 @@ export class SavedViewWidgetComponent implements OnInit {
if (this.savedView.show_in_sidebar) {
this.router.navigate(['view', this.savedView.id])
} else {
this.list.load(this.savedView)
this.list.loadSavedView(this.savedView, true)
this.router.navigate(["documents"])
}
}

View File

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

View File

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

View File

@@ -1,18 +1,52 @@
<app-widget-frame title="Upload new documents" i18n-title>
<div header-buttons>
<a *ngIf="getStatusSuccess().length > 0" (click)="dismissCompleted()" [routerLink]="" >
<span i18n="This button dismisses all status messages about processed documents on the dashboard (failed and successful)">Dismiss completed</span>&nbsp;
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" fill="currentColor" class="bi bi-check2-all" viewBox="0 0 16 16">
<path d="M12.354 4.354a.5.5 0 0 0-.708-.708L5 10.293 1.854 7.146a.5.5 0 1 0-.708.708l3.5 3.5a.5.5 0 0 0 .708 0l7-7zm-4.208 7l-.896-.897.707-.707.543.543 6.646-6.647a.5.5 0 0 1 .708.708l-7 7a.5.5 0 0 1-.708 0z"/>
<path d="M5.354 7.146l.896.897-.707.707-.897-.896a.5.5 0 1 1 .708-.708z"/>
</svg>
</a>
</div>
<div content>
<form>
<ngx-file-drop dropZoneLabel="Drop documents here or" browseBtnLabel="Browse files" (onFileDrop)="dropped($event)"
(onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)" dropZoneClassName="bg-light card"
multiple="true" contentClassName="justify-content-center d-flex align-items-center p-5" [showBrowseBtn]=true
multiple="true" contentClassName="justify-content-center d-flex align-items-center py-5 px-2" [showBrowseBtn]=true
browseBtnClassName="btn btn-sm btn-outline-primary ml-2" i18n-dropZoneLabel i18n-browseBtnLabel>
</ngx-file-drop>
</form>
<div *ngIf="uploadVisible" class="mt-3">
<p i18n>{uploadStatus.length, plural, =1 {Uploading file...} =other {Uploading {{uploadStatus.length}} files...}}</p>
<ngb-progressbar [value]="loadedSum" [max]="totalSum" [striped]="true" [animated]="uploadStatus.length > 0">
</ngb-progressbar>
<p class="mt-3" *ngIf="getStatus().length > 0">{{getStatusSummary()}}</p>
<div *ngFor="let status of getStatus()">
<ng-container [ngTemplateOutlet]="consumerAlert" [ngTemplateOutletContext]="{ $implicit: status }"></ng-container>
</div>
<div *ngIf="getStatusHidden().length" class="alerts-hidden">
<p *ngIf="!alertsExpanded" class="mt-3 mb-0 text-center">
<span i18n="This is shown as a summary line when there are more than 5 document in the processing pipeline.">{getStatusHidden().length, plural, =1 {One more document} other {{{getStatusHidden().length}} more documents}}</span>
&nbsp;&bull;&nbsp;
<a [routerLink]="" (click)="alertsExpanded = !alertsExpanded" aria-controls="hiddenAlerts" [attr.aria-expanded]="alertsExpanded" i18n>Show all</a>
</p>
<div #hiddenAlerts="ngbCollapse" [(ngbCollapse)]="!alertsExpanded">
<div *ngFor="let status of getStatusHidden()">
<ng-container [ngTemplateOutlet]="consumerAlert" [ngTemplateOutletContext]="{ $implicit: status }"></ng-container>
</div>
</div>
</div>
</div>
</app-widget-frame>
</app-widget-frame>
<ng-template #consumerAlert let-status>
<ngb-alert type="secondary" class="mt-2 mb-0" [dismissible]="isFinished(status)" (closed)="dismiss(status)">
<h6 class="alert-heading">{{status.filename}}</h6>
<p class="mb-0 pb-1" *ngIf="!isFinished(status) || (isFinished(status) && !status.documentId)">{{status.message}}</p>
<ngb-progressbar [value]="status.getProgress()" [max]="1" [type]="getStatusColor(status)"></ngb-progressbar>
<div *ngIf="isFinished(status)">
<button *ngIf="status.documentId" class="btn btn-sm btn-outline-primary btn-open" routerLink="/documents/{{status.documentId}}" (click)="dismiss(status)">
<small i18n>Open document</small>
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" fill="currentColor" class="bi bi-arrow-right-short" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M4 8a.5.5 0 0 1 .5-.5h5.793L8.146 5.354a.5.5 0 1 1 .708-.708l3 3a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708-.708L10.293 8.5H4.5A.5.5 0 0 1 4 8z"/>
</svg>
</button>
</div>
</ngb-alert>
</ng-template>

View File

@@ -0,0 +1,35 @@
@import "/src/theme";
form {
position: relative;
}
.alert-heading {
font-size: 80%;
font-weight: bold;
}
.alerts-hidden {
.btn {
line-height: 1;
}
}
.btn-open {
line-height: 1;
svg {
margin-top: -1px;
}
}
::ng-deep .progress {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
height: auto;
mix-blend-mode: soft-light;
pointer-events: none;
}

View File

@@ -1,14 +1,10 @@
import { HttpEventType } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop';
import { ConsumerStatusService, FileStatus, FileStatusPhase } from 'src/app/services/consumer-status.service';
import { DocumentService } from 'src/app/services/rest/document.service';
import { ToastService } from 'src/app/services/toast.service';
interface UploadStatus {
loaded: number
total: number
}
const MAX_ALERTS = 5
@Component({
selector: 'app-upload-file-widget',
@@ -16,8 +12,89 @@ interface UploadStatus {
styleUrls: ['./upload-file-widget.component.scss']
})
export class UploadFileWidgetComponent implements OnInit {
alertsExpanded = false
constructor(private documentService: DocumentService, private toastService: ToastService) { }
constructor(
private documentService: DocumentService,
private consumerStatusService: ConsumerStatusService
) { }
getStatus() {
return this.consumerStatusService.getConsumerStatus().slice(0, MAX_ALERTS)
}
getStatusSummary() {
let strings = []
let countUploadingAndProcessing = this.consumerStatusService.getConsumerStatusNotCompleted().length
let countFailed = this.getStatusFailed().length
let countSuccess = this.getStatusSuccess().length
if (countUploadingAndProcessing > 0) {
strings.push($localize`Processing: ${countUploadingAndProcessing}`)
}
if (countFailed > 0) {
strings.push($localize`Failed: ${countFailed}`)
}
if (countSuccess > 0) {
strings.push($localize`Added: ${countSuccess}`)
}
return strings.join($localize`:this string is used to separate processing, failed and added on the file upload widget:, `)
}
getStatusHidden() {
if (this.consumerStatusService.getConsumerStatus().length < MAX_ALERTS) return []
else return this.consumerStatusService.getConsumerStatus().slice(MAX_ALERTS)
}
getStatusUploading() {
return this.consumerStatusService.getConsumerStatus(FileStatusPhase.UPLOADING)
}
getStatusFailed() {
return this.consumerStatusService.getConsumerStatus(FileStatusPhase.FAILED)
}
getStatusSuccess() {
return this.consumerStatusService.getConsumerStatus(FileStatusPhase.SUCCESS)
}
getStatusCompleted() {
return this.consumerStatusService.getConsumerStatusCompleted()
}
getTotalUploadProgress() {
let current = 0
let max = 0
this.getStatusUploading().forEach(status => {
current += status.currentPhaseProgress
max += status.currentPhaseMaxProgress
})
return current / Math.max(max, 1)
}
isFinished(status: FileStatus) {
return status.phase == FileStatusPhase.FAILED || status.phase == FileStatusPhase.SUCCESS
}
getStatusColor(status: FileStatus) {
switch (status.phase) {
case FileStatusPhase.PROCESSING:
case FileStatusPhase.UPLOADING:
return "primary"
case FileStatusPhase.FAILED:
return "danger"
case FileStatusPhase.SUCCESS:
return "success"
}
}
dismiss(status: FileStatus) {
this.consumerStatusService.dismiss(status)
}
dismissCompleted() {
this.consumerStatusService.dismissCompleted()
}
ngOnInit(): void {
}
@@ -28,54 +105,39 @@ export class UploadFileWidgetComponent implements OnInit {
public fileLeave(event){
}
uploadStatus: UploadStatus[] = []
completedFiles = 0
uploadVisible = false
get loadedSum() {
return this.uploadStatus.map(s => s.loaded).reduce((a,b) => a+b, this.completedFiles > 0 ? 1 : 0)
}
get totalSum() {
return this.uploadStatus.map(s => s.total).reduce((a,b) => a+b, 1)
}
public dropped(files: NgxFileDropEntry[]) {
for (const droppedFile of files) {
if (droppedFile.fileEntry.isFile) {
let uploadStatusObject: UploadStatus = {loaded: 0, total: 1}
this.uploadStatus.push(uploadStatusObject)
this.uploadVisible = true
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
fileEntry.file((file: File) => {
let formData = new FormData()
formData.append('document', file, file.name)
let status = this.consumerStatusService.newFileUpload(file.name)
status.message = $localize`Connecting...`
this.documentService.uploadDocument(formData).subscribe(event => {
if (event.type == HttpEventType.UploadProgress) {
uploadStatusObject.loaded = event.loaded
uploadStatusObject.total = event.total
status.updateProgress(FileStatusPhase.UPLOADING, event.loaded, event.total)
status.message = $localize`Uploading...`
} else if (event.type == HttpEventType.Response) {
this.uploadStatus.splice(this.uploadStatus.indexOf(uploadStatusObject), 1)
this.completedFiles += 1
this.toastService.showInfo($localize`The document has been uploaded and will be processed by the consumer shortly.`)
status.taskId = event.body["task_id"]
status.message = $localize`Upload complete, waiting...`
}
}, error => {
this.uploadStatus.splice(this.uploadStatus.indexOf(uploadStatusObject), 1)
this.completedFiles += 1
switch (error.status) {
case 400: {
this.toastService.showInfo($localize`There was an error while uploading the document: ${error.error.document}`)
this.consumerStatusService.fail(status, error.error.document)
break;
}
default: {
this.toastService.showInfo($localize`An error has occurred while uploading the document. Sorry!`)
this.consumerStatusService.fail(status, $localize`HTTP error: ${error.status} ${error.statusText}`)
break;
}
}
})
});
}

View File

@@ -58,12 +58,12 @@
<app-input-text #inputTitle i18n-title title="Title" formControlName="title" [error]="error?.title"></app-input-text>
<app-input-number i18n-title title="Archive serial number" [error]="error?.archive_serial_number" formControlName='archive_serial_number'></app-input-number>
<app-input-date-time i18n-titleDate titleDate="Date created" formControlName="created"></app-input-date-time>
<app-input-date i18n-title title="Date created" formControlName="created" [error]="error?.created"></app-input-date>
<app-input-select [items]="correspondents" i18n-title title="Correspondent" formControlName="correspondent" [allowNull]="true"
(createNew)="createCorrespondent()"></app-input-select>
(createNew)="createCorrespondent()" [suggestions]="suggestions?.correspondents"></app-input-select>
<app-input-select [items]="documentTypes" i18n-title title="Document type" formControlName="document_type" [allowNull]="true"
(createNew)="createDocumentType()"></app-input-select>
<app-input-tags formControlName="tags"></app-input-tags>
(createNew)="createDocumentType()" [suggestions]="suggestions?.document_types"></app-input-select>
<app-input-tags formControlName="tags" [suggestions]="suggestions?.tags"></app-input-tags>
</ng-template>
</li>
@@ -145,6 +145,6 @@
<ng-container *ngIf="getContentType() == 'text/plain'">
<object [data]="previewUrl | safe" type="text/plain" class="preview-sticky" width="100%"></object>
</ng-container>
</div>
</div>

View File

@@ -19,6 +19,7 @@ import { PDFDocumentProxy } from 'ng2-pdf-viewer';
import { ToastService } from 'src/app/services/toast.service';
import { TextComponent } from '../common/input/text/text.component';
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
import { PaperlessDocumentSuggestions } from 'src/app/data/paperless-document-suggestions';
@Component({
selector: 'app-document-detail',
@@ -40,6 +41,8 @@ export class DocumentDetailComponent implements OnInit {
documentId: number
document: PaperlessDocument
metadata: PaperlessDocumentMetadata
suggestions: PaperlessDocumentSuggestions
title: string
previewUrl: string
downloadUrl: string
@@ -95,6 +98,7 @@ export class DocumentDetailComponent implements OnInit {
this.previewUrl = this.documentsService.getPreviewUrl(this.documentId)
this.downloadUrl = this.documentsService.getDownloadUrl(this.documentId)
this.downloadOriginalUrl = this.documentsService.getDownloadUrl(this.documentId, true)
this.suggestions = null
if (this.openDocumentService.getOpenDocument(this.documentId)) {
this.updateComponent(this.openDocumentService.getOpenDocument(this.documentId))
} else {
@@ -111,6 +115,13 @@ export class DocumentDetailComponent implements OnInit {
this.document = doc
this.documentsService.getMetadata(doc.id).subscribe(result => {
this.metadata = result
}, error => {
this.metadata = null
})
this.documentsService.getSuggestions(doc.id).subscribe(result => {
this.suggestions = result
}, error => {
this.suggestions = null
})
this.title = this.documentTitlePipe.transform(doc.title)
this.documentForm.patchValue(doc)
@@ -180,8 +191,8 @@ export class DocumentDetailComponent implements OnInit {
close() {
this.openDocumentService.closeDocument(this.document)
if (this.documentListViewService.savedViewId) {
this.router.navigate(['view', this.documentListViewService.savedViewId])
if (this.documentListViewService.activeSavedViewId) {
this.router.navigate(['view', this.documentListViewService.activeSavedViewId])
} else {
this.router.navigate(['documents'])
}

View File

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

View File

@@ -15,6 +15,7 @@ import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable
import { MatchingModel } from 'src/app/data/matching-model';
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
import { ToastService } from 'src/app/services/toast.service';
import { saveAs } from 'file-saver';
@Component({
selector: 'app-bulk-editor',
@@ -109,7 +110,7 @@ export class BulkEditorComponent {
if (items.length == 0) {
return ""
} else if (items.length == 1) {
return items[0].name
return $localize`"${items[0].name}"`
} else if (items.length == 2) {
return $localize`:This is for messages like 'modify "tag1" and "tag2"':"${items[0].name}" and "${items[1].name}"`
} else {
@@ -137,7 +138,7 @@ export class BulkEditorComponent {
} else {
modal.componentInstance.message = $localize`This operation will add the tags ${this._localizeList(changedTags.itemsToAdd)} and remove the tags ${this._localizeList(changedTags.itemsToRemove)} on ${this.list.selected.size} selected document(s).`
}
modal.componentInstance.btnClass = "btn-warning"
modal.componentInstance.btnCaption = $localize`Confirm`
modal.componentInstance.confirmClicked.subscribe(() => {
@@ -207,4 +208,10 @@ export class BulkEditorComponent {
this.executeBulkOperation(modal, "delete", {})
})
}
downloadSelected(content = "archive") {
this.documentService.bulkDownload(Array.from(this.list.selected), content).subscribe((result: any) => {
saveAs(result, 'documents.zip');
})
}
}

View File

@@ -1,7 +1,7 @@
<div class="card mb-3 shadow-sm" [class.card-selected]="selected" [class.document-card]="selectable">
<div class="row no-gutters">
<div class="col-md-2 d-none d-lg-block doc-img-background rounded-left" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
<img [src]="getThumbUrl()" class="card-img doc-img border-right rounded-left">
<img [src]="getThumbUrl()" class="card-img doc-img border-right rounded-left" [class.inverted]="getIsThumbInverted()">
<div style="top: 0; left: 0" class="position-absolute border-right border-bottom bg-light p-1" [class.document-card-check]="!selected">
<div class="custom-control custom-checkbox">
@@ -44,9 +44,9 @@
</svg>&nbsp;<span class="d-block d-md-inline" i18n>Edit</span>
</a>
<a class="btn btn-sm btn-outline-secondary" [href]="getPreviewUrl()">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-search" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10.442 10.442a1 1 0 0 1 1.415 0l3.85 3.85a1 1 0 0 1-1.414 1.415l-3.85-3.85a1 1 0 0 1 0-1.415z"/>
<path fill-rule="evenodd" d="M6.5 12a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM13 6.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0z"/>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
</svg>&nbsp;<span class="d-block d-md-inline" i18n>View</span>
</a>
<a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">

View File

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

View File

@@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { DocumentService } from 'src/app/services/rest/document.service';
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
@Component({
selector: 'app-document-card-large',
@@ -10,7 +11,7 @@ import { DocumentService } from 'src/app/services/rest/document.service';
})
export class DocumentCardLargeComponent implements OnInit {
constructor(private documentService: DocumentService, private sanitizer: DomSanitizer) { }
constructor(private documentService: DocumentService, private sanitizer: DomSanitizer, private settingsService: SettingsService) { }
@Input()
selected = false
@@ -53,6 +54,10 @@ export class DocumentCardLargeComponent implements OnInit {
ngOnInit(): void {
}
getIsThumbInverted() {
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
}
getDetailsAsString() {
if (typeof this.details === 'string') {
return this.details.substring(0, 500)

View File

@@ -1,7 +1,7 @@
<div class="col p-2 h-100">
<div class="card h-100 shadow-sm document-card" [class.card-selected]="selected">
<div class="border-bottom doc-img-container" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
<img class="card-img doc-img rounded-top" [src]="getThumbUrl()">
<img class="card-img doc-img rounded-top" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
<div class="border-right border-bottom bg-light p-1 rounded document-card-check">
<div class="custom-control custom-checkbox">
@@ -38,9 +38,9 @@
</svg>
</a>
<a [href]="getPreviewUrl()" class="btn btn-sm btn-outline-secondary" title="View in browser" i18n-title>
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-search" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M10.442 10.442a1 1 0 0 1 1.415 0l3.85 3.85a1 1 0 0 1-1.414 1.415l-3.85-3.85a1 1 0 0 1 0-1.415z"/>
<path fill-rule="evenodd" d="M6.5 12a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM13 6.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0z"/>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16">
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
</svg>
</a>
<a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" (click)="$event.stopPropagation()" i18n-title>

View File

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

View File

@@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { map } from 'rxjs/operators';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { DocumentService } from 'src/app/services/rest/document.service';
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
@Component({
selector: 'app-document-card-small',
@@ -10,7 +11,7 @@ import { DocumentService } from 'src/app/services/rest/document.service';
})
export class DocumentCardSmallComponent implements OnInit {
constructor(private documentService: DocumentService) { }
constructor(private documentService: DocumentService, private settingsService: SettingsService) { }
@Input()
selected = false
@@ -32,6 +33,10 @@ export class DocumentCardSmallComponent implements OnInit {
ngOnInit(): void {
}
getIsThumbInverted() {
return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED)
}
getThumbUrl() {
return this.documentService.getThumbUrl(this.document.id)
}

View File

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

View File

@@ -1,13 +1,15 @@
import { AfterViewInit, Component, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { Subscription } from 'rxjs';
import { PaperlessDocument } from 'src/app/data/paperless-document';
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive';
import { ConsumerStatusService } from 'src/app/services/consumer-status.service';
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
import { DOCUMENT_SORT_FIELDS } from 'src/app/services/rest/document.service';
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
import { Toast, ToastService } from 'src/app/services/toast.service';
import { ToastService } from 'src/app/services/toast.service';
import { FilterEditorComponent } from './filter-editor/filter-editor.component';
import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component';
@@ -16,7 +18,7 @@ import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-vi
templateUrl: './document-list.component.html',
styleUrls: ['./document-list.component.scss']
})
export class DocumentListComponent implements OnInit {
export class DocumentListComponent implements OnInit, OnDestroy {
constructor(
public list: DocumentListViewService,
@@ -24,7 +26,9 @@ export class DocumentListComponent implements OnInit {
public route: ActivatedRoute,
private router: Router,
private toastService: ToastService,
private modalService: NgbModal) { }
private modalService: NgbModal,
private consumerStatusService: ConsumerStatusService
) { }
@ViewChild("filterEditor")
private filterEditor: FilterEditorComponent
@@ -35,12 +39,14 @@ export class DocumentListComponent implements OnInit {
filterRulesModified: boolean = false
private consumptionFinishedSubscription: Subscription
get isFiltered() {
return this.list.filterRules?.length > 0
}
getTitle() {
return this.list.savedViewTitle || $localize`Documents`
return this.list.activeSavedViewTitle || $localize`Documents`
}
getSortFields() {
@@ -63,37 +69,52 @@ export class DocumentListComponent implements OnInit {
if (localStorage.getItem('document-list:displayMode') != null) {
this.displayMode = localStorage.getItem('document-list:displayMode')
}
this.consumptionFinishedSubscription = this.consumerStatusService.onDocumentConsumptionFinished().subscribe(() => {
this.list.reload()
})
this.route.paramMap.subscribe(params => {
this.list.clear()
if (params.has('id')) {
this.savedViewService.getCached(+params.get('id')).subscribe(view => {
if (!view) {
this.router.navigate(["404"])
return
}
this.list.savedView = view
this.list.activateSavedView(view)
this.list.reload()
this.rulesChanged()
})
} else {
this.list.savedView = null
this.list.activateSavedView(null)
this.list.reload()
this.rulesChanged()
}
})
}
ngOnDestroy() {
if (this.consumptionFinishedSubscription) {
this.consumptionFinishedSubscription.unsubscribe()
}
}
loadViewConfig(view: PaperlessSavedView) {
this.list.load(view)
this.list.loadSavedView(view)
this.list.reload()
this.rulesChanged()
}
saveViewConfig() {
this.savedViewService.update(this.list.savedView).subscribe(result => {
this.toastService.showInfo($localize`View "${this.list.savedView.name}" saved successfully.`)
})
if (this.list.activeSavedViewId != null) {
let savedView: PaperlessSavedView = {
id: this.list.activeSavedViewId,
filter_rules: this.list.filterRules,
sort_field: this.list.sortField,
sort_reverse: this.list.sortReverse
}
this.savedViewService.patch(savedView).subscribe(result => {
this.toastService.showInfo($localize`View "${this.list.activeSavedViewTitle}" saved successfully.`)
})
}
}
saveViewConfigAs() {
@@ -101,7 +122,7 @@ export class DocumentListComponent implements OnInit {
modal.componentInstance.defaultName = this.filterEditor.generateFilterName()
modal.componentInstance.saveClicked.subscribe(formValue => {
modal.componentInstance.buttonsEnabled = false
let savedView = {
let savedView: PaperlessSavedView = {
name: formValue.name,
show_on_dashboard: formValue.showOnDashboard,
show_in_sidebar: formValue.showInSideBar,
@@ -122,8 +143,8 @@ export class DocumentListComponent implements OnInit {
resetFilters(): void {
this.filterRulesModified = false
if (this.list.savedViewId) {
this.savedViewService.getCached(this.list.savedViewId).subscribe(viewUntouched => {
if (this.list.activeSavedViewId) {
this.savedViewService.getCached(this.list.activeSavedViewId).subscribe(viewUntouched => {
this.list.filterRules = viewUntouched.filter_rules
this.list.reload()
})
@@ -135,11 +156,11 @@ export class DocumentListComponent implements OnInit {
rulesChanged() {
let modified = false
if (this.list.savedView == null) {
if (this.list.activeSavedViewId == null) {
modified = this.list.filterRules.length > 0 // documents list is modified if it has any filters
} else {
// compare savedView current filters vs original
this.savedViewService.getCached(this.list.savedViewId).subscribe(view => {
this.savedViewService.getCached(this.list.activeSavedViewId).subscribe(view => {
let filterRulesInitial = view.filter_rules
if (this.list.filterRules.length !== filterRulesInitial.length) modified = true

View File

@@ -2,7 +2,15 @@
<div class="col mb-2 mb-xl-0">
<div class="form-inline d-flex align-items-center">
<label class="text-muted mr-2 mb-0" i18n>Filter by:</label>
<input class="form-control form-control-sm flex-fill w-auto" type="text" [(ngModel)]="titleFilter" placeholder="Title" i18n-placeholder>
<div class="input-group input-group-sm flex-fill w-auto">
<div class="input-group-prepend" ngbDropdown>
<button class="btn btn-outline-primary" ngbDropdownToggle>{{textFilterTargetName}}</button>
<div class="dropdown-menu shadow" ngbDropdownMenu>
<button *ngFor="let t of textFilterTargets" ngbDropdownItem [class.active]="textFilterTarget == t.id" (click)="changeTextFilterTarget(t.id)">{{t.name}}</button>
</div>
</div>
<input class="form-control form-control-sm" type="text" [(ngModel)]="textFilter">
</div>
</div>
</div>
<div class="w-100 d-xl-none"></div>

View File

@@ -8,10 +8,13 @@ import { DocumentTypeService } from 'src/app/services/rest/document-type.service
import { TagService } from 'src/app/services/rest/tag.service';
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
import { FilterRule } from 'src/app/data/filter-rule';
import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_ANY_TAG, FILTER_HAS_TAG, FILTER_TITLE } from 'src/app/data/filter-rule-type';
import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_ANY_TAG, FILTER_HAS_TAG, FILTER_TITLE, FILTER_TITLE_CONTENT } from 'src/app/data/filter-rule-type';
import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component';
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component';
const TEXT_FILTER_TARGET_TITLE = "title"
const TEXT_FILTER_TARGET_TITLE_CONTENT = "title-content"
@Component({
selector: 'app-filter-editor',
templateUrl: './filter-editor.component.html',
@@ -46,6 +49,8 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
return $localize`Without any tag`
}
case FILTER_TITLE:
return $localize`Title: ${rule.value}`
}
}
@@ -62,7 +67,19 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
correspondents: PaperlessCorrespondent[] = []
documentTypes: PaperlessDocumentType[] = []
_titleFilter = ""
_textFilter = ""
textFilterTargets = [
{id: TEXT_FILTER_TARGET_TITLE, name: $localize`Title`},
{id: TEXT_FILTER_TARGET_TITLE_CONTENT, name: $localize`Title & content`}
]
textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
get textFilterTargetName() {
return this.textFilterTargets.find(t => t.id == this.textFilterTarget)?.name
}
tagSelectionModel = new FilterableDropdownSelectionModel()
correspondentSelectionModel = new FilterableDropdownSelectionModel()
@@ -78,7 +95,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.documentTypeSelectionModel.clear(false)
this.tagSelectionModel.clear(false)
this.correspondentSelectionModel.clear(false)
this._titleFilter = null
this._textFilter = null
this.dateAddedBefore = null
this.dateAddedAfter = null
this.dateCreatedBefore = null
@@ -87,7 +104,12 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
value.forEach(rule => {
switch (rule.rule_type) {
case FILTER_TITLE:
this._titleFilter = rule.value
this._textFilter = rule.value
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE
break
case FILTER_TITLE_CONTENT:
this._textFilter = rule.value
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
break
case FILTER_CREATED_AFTER:
this.dateCreatedAfter = rule.value
@@ -117,10 +139,13 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
})
}
get filterRules() {
get filterRules(): FilterRule[] {
let filterRules: FilterRule[] = []
if (this._titleFilter) {
filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter})
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT) {
filterRules.push({rule_type: FILTER_TITLE_CONTENT, value: this._textFilter})
}
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE) {
filterRules.push({rule_type: FILTER_TITLE, value: this._textFilter})
}
if (this.tagSelectionModel.isNoneSelected()) {
filterRules.push({rule_type: FILTER_HAS_ANY_TAG, value: "false"})
@@ -163,15 +188,15 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.filterRulesChange.next(this.filterRules)
}
get titleFilter() {
return this._titleFilter
get textFilter() {
return this._textFilter
}
set titleFilter(value) {
this.titleFilterDebounce.next(value)
set textFilter(value) {
this.textFilterDebounce.next(value)
}
titleFilterDebounce: Subject<string>
textFilterDebounce: Subject<string>
subscription: Subscription
ngOnInit() {
@@ -179,19 +204,19 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
this.titleFilterDebounce = new Subject<string>()
this.textFilterDebounce = new Subject<string>()
this.subscription = this.titleFilterDebounce.pipe(
this.subscription = this.textFilterDebounce.pipe(
debounceTime(400),
distinctUntilChanged()
).subscribe(title => {
this._titleFilter = title
).subscribe(text => {
this._textFilter = text
this.updateRules()
})
}
ngOnDestroy() {
this.titleFilterDebounce.complete()
this.textFilterDebounce.complete()
}
resetSelected() {
@@ -221,4 +246,9 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
onDocumentTypeDropdownOpen() {
this.documentTypeSelectionModel.apply()
}
changeTextFilterTarget(target) {
this.textFilterTarget = target
this.updateRules()
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -34,7 +34,7 @@
<div class="col">
<select class="form-control" formControlName="dateLocale">
<option *ngFor="let lang of dateLocaleOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> - {{today | date:'shortDate':null:lang.code}}</span></option>
<option *ngFor="let lang of dateLocaleOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> - {{today | customDate:'shortDate':null:lang.code}}</span></option>
</select>
</div>
@@ -96,6 +96,7 @@
<div class="col">
<app-input-check i18n-title title="Use system settings" formControlName="darkModeUseSystem"></app-input-check>
<app-input-check [hidden]="settingsForm.value.darkModeUseSystem" i18n-title title="Enable dark mode" formControlName="darkModeEnabled"></app-input-check>
<app-input-check i18n-title title="Invert thumbnails in dark mode" formControlName="darkModeInvertThumbs"></app-input-check>
</div>
</div>
@@ -110,7 +111,26 @@
</ng-template>
</li>
<li [ngbNavItem]="2">
<a ngbNavLink i18n>Notifications</a>
<ng-template ngbNavContent>
<h4 i18n>Document processing</h4>
<div class="form-row form-group">
<div class="offset-md-3 col">
<app-input-check i18n-title title="Show notifications when new documents are detected" formControlName="notificationsConsumerNewDocument"></app-input-check>
<app-input-check i18n-title title="Show notifications when document processing completes successfully" formControlName="notificationsConsumerSuccess"></app-input-check>
<app-input-check i18n-title title="Show notifications when document processing fails" formControlName="notificationsConsumerFailed"></app-input-check>
<app-input-check i18n-title title="Suppress notifications on dashboard" formControlName="notificationsConsumerSuppressOnDashboard" i18n-hint hint="This will suppress all messages about document processing status on the dashboard."></app-input-check>
</div>
</div>
</ng-template>
</li>
<li [ngbNavItem]="3">
<a ngbNavLink i18n>Saved views</a>
<ng-template ngbNavContent>
@@ -148,7 +168,7 @@
</li>
</ul>
<div [ngbNavOutlet]="nav" class="border-left border-right border-bottom p-3 mb-3 shadow"></div>
<div [ngbNavOutlet]="nav" class="border-left border-right border-bottom p-3 mb-3 shadow-sm"></div>
<button type="submit" class="btn btn-primary" i18n>Save</button>
</form>

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