Compare commits

...

289 Commits

Author SHA1 Message Date
jonaswinkler
014859185c fix up CI/CD: build docker image on tags, correct docker image version, correct release tag 2021-01-14 17:24:36 +01:00
jonaswinkler
6dae900d74 I hope this works! 2021-01-14 16:09:44 +01:00
jonaswinkler
a4187b2b70 fix a test case 2021-01-14 14:54:04 +01:00
jonaswinkler
556e4f7430 update readme 2021-01-14 14:38:08 +01:00
Jonas Winkler
02c2d388ed Merge pull request #340 from jonaswinkler/translations_src-ui-messages-xlf--dev_de
Translate '/src-ui/messages.xlf' in 'de'
2021-01-14 14:36:19 +01:00
transifex-integration[bot]
c0692f03a3 Translate /src-ui/messages.xlf in de
translation completed for the source file '/src-ui/messages.xlf'
on the 'de' language.
2021-01-14 13:33:42 +00:00
jonaswinkler
a2342d98d3 fix release CI 2021-01-14 14:08:48 +01:00
jonaswinkler
24c20e38ff changelog 2021-01-14 14:04:09 +01:00
jonaswinkler
cd12c1b300 dependencies 2021-01-14 13:36:45 +01:00
jonaswinkler
a008dff368 configuration option for type of PDF viewer fixes #337 2021-01-14 13:35:43 +01:00
jonaswinkler
769356733a update docker compose files and documentation #339 2021-01-14 12:59:39 +01:00
jonaswinkler
f5be2ac4bb a couple changes to the documentation 2021-01-14 02:09:06 +01:00
jonaswinkler
2824fcb497 changelog 2021-01-14 02:08:26 +01:00
jonaswinkler
2f42eab217 move compose files 2021-01-14 01:56:10 +01:00
jonaswinkler
338324d5b6 Merge remote-tracking branch 'origin/master' into dev 2021-01-14 01:05:29 +01:00
jonaswinkler
0628d10076 background color for dark mode logs 2021-01-14 00:47:54 +01:00
jonaswinkler
bc4017d54a inconsistent shadows 2021-01-13 20:09:37 +01:00
jonaswinkler
96dc7583bf add missing dependency back into the Dockerfile. 2021-01-13 19:58:32 +01:00
jonaswinkler
deb9698ff6 added some messages to the docker startup script 2021-01-13 19:58:09 +01:00
jonaswinkler
654c7fd2ac release: updated body 2021-01-13 19:56:23 +01:00
jonaswinkler
24e34e95cd changelog 2021-01-13 17:33:32 +01:00
jonaswinkler
d093c004fb fixes #161 2021-01-13 17:17:23 +01:00
jonaswinkler
ceb9426fd4 changelog 2021-01-13 16:21:54 +01:00
jonaswinkler
e4f2016678 DEBUG logging 2021-01-13 13:35:05 +01:00
jonaswinkler
40842689fd Better deal with non-existing documents when searching 2021-01-13 13:34:52 +01:00
jonaswinkler
59f955e08b adjusted the default configuration so that at least one cpu core remains free (except on single core machines) #332 2021-01-13 00:02:20 +01:00
Jonas Winkler
91e6503503 Update README.md 2021-01-12 22:12:07 +01:00
jonaswinkler
c7c98c623d try building without libatlas 2021-01-12 21:26:39 +01:00
jonaswinkler
80aa847457 always execute all tests even if tests for one python version fail 2021-01-12 20:25:58 +01:00
jonaswinkler
f1c981338c reorganize test CI, only upload coveralls once 2021-01-12 20:23:50 +01:00
jonaswinkler
d4225a459e fix CI for new coveralls version 2021-01-12 20:05:26 +01:00
jonaswinkler
75ae37d90f update dependencies (sklearn 0.24 has aarch64 wheels!) 2021-01-12 19:48:59 +01:00
jonaswinkler
2159115679 scanners 2021-01-12 18:02:29 +01:00
Hannah Swain
0384de9a3c Update scanners list to include mobile options
(cherry picked from commit cb30fb56db445ad196eaf0dbd5ba661605f58930)
2021-01-12 17:36:38 +01:00
jonaswinkler
ae2e1fc381 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-01-12 17:32:22 +01:00
jonaswinkler
d45993c905 remove unused code 2021-01-12 17:00:53 +01:00
Jonas Winkler
4e6c7b01b6 Update README.md 2021-01-12 14:29:32 +01:00
jonaswinkler
775ce56d2e Merge branch 'master' into dev 2021-01-12 14:14:06 +01:00
Jonas Winkler
d41c466f6e Merge pull request #171 from C0nsultant/ansible-role
Provide an ansible role for deployment and configuration
2021-01-12 14:11:09 +01:00
jonaswinkler
387135ffa5 update dockerfile 2021-01-12 14:01:15 +01:00
jonaswinkler
64b176103e fixes #201 2021-01-12 13:51:44 +01:00
jonaswinkler
7f88c7d70c update docker-compose.env 2021-01-12 13:51:29 +01:00
jonaswinkler
1a89ad95e0 changelog 2021-01-12 13:08:30 +01:00
jonaswinkler
376f991db5 remove travis branch condition 2021-01-12 13:06:21 +01:00
jonaswinkler
623893b528 fixes #331 2021-01-12 13:05:49 +01:00
jonaswinkler
890f6f35b5 fixes #330 2021-01-12 12:27:58 +01:00
jonaswinkler
ab4af350dd changelog 2021-01-12 12:25:50 +01:00
Jonas Winkler
95bfc4be9a Merge pull request #329 from shamoon/fix/mobile-button-group-improvements
Mobile button group improvements
2021-01-12 11:55:12 +01:00
Michael Shamoon
95d9c8fa2b Merge remote-tracking branch 'upstream/dev' into fix/mobile-button-group-improvements 2021-01-11 16:16:12 -08:00
Michael Shamoon
83dd5f51cd Move 'Filter by:' inline with title field, icon stuff 2021-01-11 16:15:55 -08:00
jonaswinkler
5d1dddfac8 unless-stopped in local dockerfiles 2021-01-12 00:40:49 +01:00
jonaswinkler
50ba88231f documentation 2021-01-12 00:29:05 +01:00
jonaswinkler
221cf12184 only restart docker unless stopped 2021-01-12 00:21:05 +01:00
jonaswinkler
681b769a71 copy files into place 2021-01-12 00:15:20 +01:00
Michael Shamoon
27517c15ca Make bulk editor a little more compact 2021-01-11 15:01:40 -08:00
Michael Shamoon
56f566e13a select icon hidden on larger screens 2021-01-11 14:54:48 -08:00
Michael Shamoon
6131a40c31 Fix right menus getting cut off 2021-01-11 14:53:16 -08:00
jonaswinkler
040e652e6a remove obsolete steps from the documentation 2021-01-11 23:07:47 +01:00
jonaswinkler
caf0e1d15b Merge branch 'dev' into travis-multiarch-builds 2021-01-11 22:11:34 +01:00
jonaswinkler
9dd8b7cd88 fix test case 2021-01-11 22:11:01 +01:00
Michael Shamoon
8702a739b0 Change dark mode dropdown menu color for visibility 2021-01-11 12:58:59 -08:00
jonaswinkler
8104851c08 Merge branch 'dev' into travis-multiarch-builds 2021-01-11 21:57:28 +01:00
jonaswinkler
0e03c48beb pin pillow dependency 2021-01-11 21:56:50 +01:00
jonaswinkler
3fbf24a1c2 don't use dependencies from testing 2021-01-11 21:55:06 +01:00
Michael Shamoon
64a966c9a5 Fill document list buttons, show select icon because its not obvious, make some menus right 2021-01-11 12:52:42 -08:00
Michael Shamoon
18a128b917 On second thought lets make filter editor buttons fill 2021-01-11 12:22:35 -08:00
Michael Shamoon
cfd263da90 Hide page count on mobile since document is out of view anyway 2021-01-11 11:49:27 -08:00
Michael Shamoon
0f4a7f0a04 Combine sort & sort order fields to better accomodate mobile, button bar full width 2021-01-11 11:46:40 -08:00
Michael Shamoon
ae5dd62105 Hide button text in favor of icons on mobile like filter editor does 2021-01-11 11:46:07 -08:00
Michael Shamoon
b73ff095e6 Distribute filter editor buttons equally to full width on mobile 2021-01-11 11:45:23 -08:00
jonaswinkler
4503be110a try building this with updated dependencies 2021-01-11 17:13:56 +01:00
jonaswinkler
7fef27e6de fixes #305 2021-01-11 16:16:39 +01:00
jonaswinkler
d8eabd66ea lost some changes in a merge 2021-01-11 16:05:29 +01:00
jonaswinkler
a62e5f32b8 Merge branch 'master' into dev 2021-01-11 16:02:25 +01:00
Jonas Winkler
24fe5bb838 Merge pull request #323 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-11 16:00:00 +01:00
transifex-integration[bot]
5217acc471 Apply translations in de
translation completed for the source file '/src/locale/en-us/LC_MESSAGES/django.po'
on the 'de' language.
2021-01-11 14:59:29 +00:00
jonaswinkler
f96d97a637 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-01-11 15:38:57 +01:00
jonaswinkler
de38f46a53 fixes #307 2021-01-11 15:38:45 +01:00
jonaswinkler
8f94607409 a couple fixes to the ci script 2021-01-11 13:19:29 +01:00
Jonas Winkler
88ee3694f7 Merge pull request #320 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-01-11 12:02:20 +01:00
transifex-integration[bot]
584307dc54 Apply translations in fr
translation completed for the source file '/src/locale/en-us/LC_MESSAGES/django.po'
on the 'fr' language.
2021-01-11 09:12:58 +00:00
Jonas Winkler
62fd32afbc Merge pull request #318 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-11 02:20:51 +01:00
transifex-integration[bot]
bfcfcde71a 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-10 23:51:27 +00:00
jonaswinkler
6ce0945aeb enable multiarch builds, release only with ng- tags 2021-01-11 00:08:56 +01:00
jonaswinkler
f701479d77 fix upload-release-asset path 2021-01-10 23:57:20 +01:00
jonaswinkler
4e93d92e46 Merge branch 'dev' into travis-multiarch-builds 2021-01-10 23:46:31 +01:00
jonaswinkler
ebcdcc6a1e github.ref instead of GITHUB_REF 2021-01-10 23:46:19 +01:00
jonaswinkler
b5cd713e16 maybe this 2021-01-10 23:43:59 +01:00
jonaswinkler
0adb8cea91 I have no idea what I am doing 2021-01-10 23:17:53 +01:00
jonaswinkler
bce79189e5 maybe this is better? 2021-01-10 23:09:09 +01:00
jonaswinkler
8927e0185b maybe this is better? 2021-01-10 22:57:56 +01:00
jonaswinkler
df95fad9be update release name 2021-01-10 22:56:10 +01:00
jonaswinkler
e96809a08e translation for login/logout pages #212 2021-01-10 22:47:24 +01:00
jonaswinkler
7ebc1351fe updated documentation #316 2021-01-10 22:40:13 +01:00
jonaswinkler
ee31249d65 reorganized the Dockerfile 2021-01-10 22:28:46 +01:00
jonaswinkler
a9d6e7f402 add a publish release step 2021-01-10 22:22:23 +01:00
jonaswinkler
296be9135a Merge branch 'dev' into travis-multiarch-builds 2021-01-10 21:55:05 +01:00
jonaswinkler
f24640dec2 some small fixes 2021-01-10 21:53:18 +01:00
jonaswinkler
64d6e6eded wrong path to release 2021-01-10 21:35:43 +01:00
jonaswinkler
587cd80055 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-01-10 21:28:54 +01:00
jonaswinkler
527b7bee3f forgot a dependency 2021-01-10 21:27:41 +01:00
jonaswinkler
e1f52d706f fix wrong directory 2021-01-10 21:20:55 +01:00
jonaswinkler
2e2e69f07c more release archive 2021-01-10 21:17:01 +01:00
jonaswinkler
6a3e4f0857 add release archive action 2021-01-10 21:03:50 +01:00
jonaswinkler
ff3bdd7f5a fix script name 2021-01-10 20:00:11 +01:00
jonaswinkler
1e185d1502 don't build front end with docker 2021-01-10 19:59:05 +01:00
jonaswinkler
8f9aab407d Merge branch 'dev' into travis-multiarch-builds 2021-01-10 19:27:10 +01:00
jonaswinkler
4cf0ce1b06 version push 2021-01-10 19:26:38 +01:00
jonaswinkler
8430aa75da enable the other architectures 2021-01-10 17:46:43 +01:00
Jonas Winkler
f5a47de1d6 Merge pull request #315 from shamoon/fix/issue-314
Fix log colors in dark mode
2021-01-10 17:45:13 +01:00
Michael Shamoon
1fe322bbac Fix log colors in dark mode 2021-01-10 08:36:20 -08:00
jonaswinkler
f6da608154 disable tests until this is working 2021-01-10 17:19:00 +01:00
jonaswinkler
22fc16f4a3 revert to python:3.7-slim base image since ubuntu:20.04 is clearly not working for arm 2021-01-10 17:18:35 +01:00
jonaswinkler
def85a3d37 maybe use requirements instead? 2021-01-10 15:51:40 +01:00
Jonas Winkler
f638079361 Merge pull request #306 from shamoon/fix/large-card-mobile-fixes
Fixes for large cards / search results on mobile
2021-01-10 12:21:51 +01:00
Jonas Winkler
e87ab91d0e Merge pull request #310 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-10 12:21:13 +01:00
Jonas Winkler
fb56602081 Merge pull request #311 from jonaswinkler/translations_src-ui-messages-xlf--dev_nl_NL
Translate '/src-ui/messages.xlf' in 'nl_NL'
2021-01-10 12:20:57 +01:00
Jonas Winkler
2c1d9f2d7b Merge pull request #312 from jonaswinkler/translations_src-ui-messages-xlf--dev_fr
Translate '/src-ui/messages.xlf' in 'fr'
2021-01-10 12:20:43 +01:00
Jonas Winkler
49385959b8 Merge pull request #308 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-10 12:19:32 +01:00
transifex-integration[bot]
3511c86abd Translate /src-ui/messages.xlf in fr
translated for the source file '/src-ui/messages.xlf'
on the 'fr' language.
2021-01-10 11:19:21 +00:00
transifex-integration[bot]
9908580409 Translate /src-ui/messages.xlf in nl_NL
translated for the source file '/src-ui/messages.xlf'
on the 'nl_NL' language.
2021-01-10 11:19:16 +00:00
transifex-integration[bot]
5bad23d7be Apply translations in de
translated for the source file '/src/locale/en-us/LC_MESSAGES/django.po'
on the 'de' language.
2021-01-10 11:19:10 +00:00
transifex-integration[bot]
8cde2cd4b2 Apply translations in nl_NL
translated for the source file '/src/locale/en-us/LC_MESSAGES/django.po'
on the 'nl_NL' language.
2021-01-10 11:19:00 +00:00
jonaswinkler
74db555f78 fix cryphtography for armv7 2021-01-10 11:39:30 +01:00
jonaswinkler
a1a415a5d1 fix up pipfile for arm/v7 2021-01-10 11:26:14 +01:00
Michael Shamoon
49053b6ad2 Prevent long title overflow on mobile 2021-01-09 19:53:18 -08:00
Michael Shamoon
fbfdd95958 Fix large card layout on mobile 2021-01-09 19:52:31 -08:00
jonaswinkler
2c065da987 fixes #292 2021-01-10 01:59:47 +01:00
jonaswinkler
6c7281ae88 add sslmode configuration option fixes #298 2021-01-10 01:35:56 +01:00
jonaswinkler
4bb0da397a dependencies 2021-01-10 00:58:14 +01:00
jonaswinkler
6a355b8dff install pipenv 2021-01-10 00:54:48 +01:00
jonaswinkler
aa1fb63714 documentation action 2021-01-10 00:53:30 +01:00
jonaswinkler
c9ce69c1c1 documentation action 2021-01-10 00:50:06 +01:00
jonaswinkler
06ee41226d no need to run commands in pipenv when using --system 2021-01-10 00:41:11 +01:00
jonaswinkler
bf634eee66 fix python action 2021-01-10 00:32:47 +01:00
jonaswinkler
e606ae5097 try new python setup action 2021-01-10 00:28:33 +01:00
jonaswinkler
ecc40d022d test updated Pipfile 2021-01-10 00:09:10 +01:00
jonaswinkler
262e8ee999 add arm64 2021-01-09 22:53:45 +01:00
jonaswinkler
141315cf42 Revert "try separate build / push"
This reverts commit 6fa0d00b
2021-01-09 22:38:05 +01:00
jonaswinkler
6fa0d00b8f try separate build / push 2021-01-09 22:33:07 +01:00
jonaswinkler
47339c84b3 test include this branch 2021-01-09 22:03:38 +01:00
jonaswinkler
692a5081c7 rename workflow 2021-01-09 21:55:03 +01:00
jonaswinkler
28c3f8d614 fix condition 2021-01-09 21:53:13 +01:00
jonaswinkler
fb5d35e43e fix condition 2021-01-09 21:50:53 +01:00
jonaswinkler
a4bff61e5b move everything into a single file so that the docker image build can depend on the tests being successful 2021-01-09 21:49:43 +01:00
jonaswinkler
2c4bbf8a00 use correct version of cache action 2021-01-09 21:23:04 +01:00
jonaswinkler
d2ec38d5fd use correct version 2021-01-09 21:21:50 +01:00
jonaswinkler
517e037724 always update cache 2021-01-09 20:51:46 +01:00
jonaswinkler
0051d9f76e undo removing a required library 2021-01-09 20:29:29 +01:00
jonaswinkler
c0915a3dee add arm 2021-01-09 20:06:53 +01:00
jonaswinkler
4363d4c9ed simplify some things 2021-01-09 19:50:11 +01:00
jonaswinkler
d7095f7ee1 tags? 2021-01-09 19:31:41 +01:00
Jonas Winkler
ebc5f1a01d Update docker.yml 2021-01-09 19:10:09 +01:00
Jonas Winkler
d6ad41ab3a Update docker.yml 2021-01-09 19:06:00 +01:00
Jonas Winkler
9c2e55407f Update docker.yml 2021-01-09 19:02:07 +01:00
jonaswinkler
7c70659212 disable some steps 2021-01-09 18:49:21 +01:00
jonaswinkler
5822273c8d duplicate platform specification 2021-01-09 18:33:58 +01:00
jonaswinkler
001cd83ef8 new buildx setup action 2021-01-09 18:25:56 +01:00
jonaswinkler
b48c290e9c arm 2021-01-09 18:02:36 +01:00
jonaswinkler
327f00e312 now with cache? 2021-01-09 17:54:56 +01:00
jonaswinkler
b1ae8d1332 enable docker builds on this branch 2021-01-09 15:11:58 +01:00
jonaswinkler
38cb8201a1 Merge branch 'travis-multiarch-builds' of https://github.com/MarkSchmitt/paperless into travis-multiarch-builds 2021-01-09 14:59:41 +01:00
jonaswinkler
e3478edeeb Merge branch 'dev' into travis-multiarch-builds 2021-01-09 14:48:51 +01:00
Fabian Koller
69f1931f4f Update to 0.9.13 2021-01-09 11:52:39 +01:00
Jonas Winkler
6eb8908a16 Merge pull request #303 from C0nsultant/patch-4
Add gettext to  Bare Metal Route dependencies
2021-01-09 11:45:29 +01:00
Fabian Koller
88dcd889fa Add gettext to Bare Metal Route dependencies
In theory, this package is optional since not everybody wants to compile translations.
Without other changes to step 5. (i.e. making it explicit that `python3 manage.py compilemessages` is not mandatory), not installing `gettext` results in an error when blindly copy-pasting the steps.
2021-01-09 11:27:35 +01:00
Fabian Koller
9cd6235947 Disable ansible-galaxy import
Repository structure not compatible (galaxy expects the role to be at
the root of the repository, not in a subfolder).
2021-01-09 10:46:38 +01:00
Jonas Winkler
bcb2853150 Merge pull request #302 from shamoon/fix/large-card-dark-mode-buttons
Fix dark mode button display on large cards in certain browsers
2021-01-09 10:13:21 +01:00
Mark Schmitt
ad241f12a3 Run tests, documentation and frontend build also on pull_request 2021-01-09 08:06:55 +01:00
Michael Shamoon
b29627c92b Fix dark mode button display on large cards in certain browsers 2021-01-08 22:09:15 -08:00
jonaswinkler
8518d583d9 Merge branch 'dev' of github.com:jonaswinkler/paperless-ng into dev 2021-01-09 01:55:05 +01:00
jonaswinkler
81dee38e4a fixes #300 2021-01-09 01:54:51 +01:00
Jonas Winkler
fa6cc9692e Merge pull request #297 from shamoon/fix/issue-293
Fix logo size issues in certain browsers
2021-01-08 21:15:07 +01:00
Jonas Winkler
c3a92d659e Merge pull request #296 from jonaswinkler/revert-295-fix/issue-293
Revert "Fix logo size issues in certain browsers"
2021-01-08 20:59:51 +01:00
Jonas Winkler
ffed65533b Revert "Fix logo size issues in certain browsers" 2021-01-08 20:59:40 +01:00
Jonas Winkler
5a393569f0 Merge pull request #295 from shamoon/fix/issue-293
Fix logo size issues in certain browsers
2021-01-08 20:59:17 +01:00
Michael Shamoon
6cf20a93d0 Set explicit svg height, hide on mobile 2021-01-08 11:55:50 -08:00
Mark Schmitt
a2b5fb8374 Fix name of documentation workflow 2021-01-08 20:42:17 +01:00
Mark Schmitt
f4f9200fba Fix typo in yaml 2021-01-08 20:39:20 +01:00
Mark Schmitt
52640f08d2 github action improved tagging 2021-01-08 20:37:25 +01:00
Jonas Winkler
bbb5783b4f Merge pull request #289 from shamoon/fix/safari-flex-transition
Miscellaneous CSS fixes
2021-01-08 18:56:37 +01:00
Michael Shamoon
cde937a9eb Fix dark theme highlighting of ng-select dropdowns on keyboard navigation 2021-01-08 09:37:25 -08:00
Michael Shamoon
a4ee4e9b17 Prevent dashboard overflow on mobile 2021-01-08 09:33:45 -08:00
jonaswinkler
34a06435cf changelog and version 2021-01-08 13:39:12 +01:00
jonaswinkler
fad6e7284a fixes #290 2021-01-08 13:27:57 +01:00
Mark Schmitt
29f73b2dbb Give the frontend workflow a name 2021-01-08 11:34:04 +01:00
Mark Schmitt
968725f09f Separate github action into separete workflow files 2021-01-08 11:33:08 +01:00
Mark Schmitt
42064df3e0 Testing excluding docker builds if not specific ref is selected 2021-01-08 11:23:17 +01:00
Michael Shamoon
c980a52244 Fix transition for Safari 2021-01-07 19:23:59 -08:00
jonaswinkler
38f279fd2a update installation instructions 2021-01-07 21:16:16 +01:00
jonaswinkler
1170827f3d update docker ignore 2021-01-07 18:42:46 +01:00
jonaswinkler
838631b057 Merge branch 'dev' into travis-multiarch-builds 2021-01-07 17:41:44 +01:00
Mark Schmitt
89476e4479 Try to use pip cache and install dependnecies like in tests 2021-01-07 09:02:29 +01:00
Mark Schmitt
1b61765df8 Install pip 2021-01-07 08:56:48 +01:00
Mark Schmitt
40da245d8d Try to insall sphinx from pipfile 2021-01-07 08:50:04 +01:00
Mark Schmitt
1238d8b994 Add pip cache
Try to be a good citizen and use caches instead of hitting the repos all the time
2021-01-07 08:44:05 +01:00
Mark Schmitt
460d202eb0 Use pip to install sphinx
Not sure why pipenv install doesn't do the trick .. let's try this way first
2021-01-07 08:37:25 +01:00
Mark Schmitt
248e4e5eb9 Try installing sphinx simply with apt 2021-01-07 07:57:12 +01:00
Mark Schmitt
08299fb808 Simplify node build 2021-01-07 07:48:32 +01:00
Mark Schmitt
c06f6145a1 ALso install python before using pip in the documentation job 2021-01-07 07:47:42 +01:00
Mark Schmitt
6101f42ae3 ADd documentation and frontend to dependency for docker build 2021-01-07 07:45:17 +01:00
Mark Schmitt
9ffd9027fa Add missing runs-on 2021-01-07 07:44:36 +01:00
Mark Schmitt
10995788d1 Fix identation 2021-01-07 07:43:45 +01:00
Mark Schmitt
98bc51a3ec Adding documentation and frontend steps from travis build 2021-01-07 07:42:25 +01:00
Mark Schmitt
cc9f9b6e8b Docker build step depends on tests 2021-01-07 07:32:43 +01:00
Mark Schmitt
d7b3e7bd69 Use 3.8.6 2021-01-06 23:11:54 +01:00
Mark Schmitt
257ebb88f0 Looks like we need to specify the exact version 2021-01-06 23:10:06 +01:00
Mark Schmitt
9257cdac8f Remove debugging line and specify release versions 2021-01-06 23:06:40 +01:00
Mark Schmitt
44da3fd195 Fix yaml: indentation was wrong 2021-01-06 22:57:34 +01:00
Mark Schmitt
c7c167a058 Update to a possibly working version of pyenv-action
According to https://github.com/gabrielfalcao/pyenv-action/issues/136 this
version should work
2021-01-06 22:55:41 +01:00
Mark Schmitt
6f3cdbc165 Quote command 2021-01-06 22:54:11 +01:00
Mark Schmitt
5b4a3e4288 Remove comment 2021-01-06 22:53:32 +01:00
Mark Schmitt
b7ff94900f Specify exact version 2021-01-06 22:52:40 +01:00
Mark Schmitt
e761cc76fa List python versions differently so the pyenv action might light it better 2021-01-06 22:50:11 +01:00
Mark Schmitt
96da61f4e4 Trying to use pyenv action step 2021-01-06 22:48:03 +01:00
Mark Schmitt
729d2da764 Remove command - should be unnecessary 2021-01-06 10:22:24 +01:00
Mark Schmitt
2587d7fec8 Remove comment from yaml 2021-01-06 10:20:31 +01:00
Mark Schmitt
a6c6f64453 Use another python action instead 2021-01-06 09:10:45 +01:00
Mark Schmitt
b31bfeebd7 Try to make pip and pipenv use the installed python version 2021-01-06 09:05:14 +01:00
Mark Schmitt
b0588321e0 Fix yaml 2021-01-04 15:45:12 +01:00
Mark Schmitt
574ab7d696 Add GITHUB_TOKEN to python tests 2021-01-04 15:44:02 +01:00
Mark Schmitt
336b908e8f Only build for 3.6 2021-01-04 15:28:47 +01:00
Mark Schmitt
b41dd2780e Print the env 2021-01-04 15:19:00 +01:00
Mark Schmitt
7e99d7515f Try manually replacing the python-version 2021-01-04 15:17:13 +01:00
Mark Schmitt
c9de592832 Try to reproduce exactly what's done in the travis build to test 2021-01-04 15:10:16 +01:00
Mark Schmitt
69ac7b9bcf Revert python version in Pipfile to 3.6 2021-01-04 15:00:35 +01:00
Mark Schmitt
05f57249d6 Only build python 3.7 and say so in the Pipfile too 2021-01-04 12:24:48 +01:00
Mark Schmitt
e91befabc0 Revert job strategy matrix change 2021-01-04 12:22:35 +01:00
Mark Schmitt
2e641b0e30 Explicitly set ubuntu-20.04 as OS and install dev dependencies 2021-01-04 12:18:56 +01:00
Fabian Koller
586db3bc5e Deploy role to ansible galaxy
Happens only when a new tag is pushed
2021-01-04 06:42:34 +01:00
Fabian Koller
51fd19c315 Switch to github actions 2021-01-04 06:42:34 +01:00
Mark Schmitt
a21fb36cd2 github-actions: readding previously removed "-n auto"
possibly this is fixed by the wheel dependency that was missing before.
2021-01-02 11:33:56 +01:00
Mark Schmitt
28209bb474 github-actions: also install wheel
There's an error about pip wheel not being available and falling back
to some legacy version. Let's see if this helps.
2021-01-02 11:30:12 +01:00
Mark Schmitt
376efcde27 github-actions: removing unsupported parameter
I don't understand what the "-n auto" parameter does, pytest doesn't either
(in the version installed) - so removing it to see if that produces something
usable.
2021-01-02 11:27:38 +01:00
Mark Schmitt
4684d19953 github-actions: readding explicit install
explicitly install pytest, pytest-cov and coveralls.
I think travis already did that and we need to explicitly do so now.
removed installing dev-dependencies, do it explicitly.
(just trying to reproduce the travis-ci build)
2021-01-02 11:23:30 +01:00
Mark Schmitt
4bea504690 Try o make pipenv also install dev dependencies 2021-01-02 09:44:08 +01:00
Mark Schmitt
90f4ccfb94 Remove cache to see if that helps with pytest-cov issues 2021-01-02 09:41:21 +01:00
Mark Schmitt
d292f71007 Add pytest-cov to install 2021-01-02 09:31:20 +01:00
Mark Schmitt
4a45a77753 Add pytest to install step 2021-01-02 09:23:49 +01:00
Mark Schmitt
ef28ae8117 Move sphinx step below tests step as it used to be 2021-01-02 09:19:58 +01:00
Mark Schmitt
b8b0b61e67 Execute pip commands one after the other to find issue 2021-01-02 09:15:10 +01:00
Mark Schmitt
41c1b711c2 Use sudo for apt 2021-01-02 09:08:19 +01:00
Mark Schmitt
8ab251c5f7 Execute python tests 2021-01-02 09:07:05 +01:00
Mark Schmitt
97d068c3bb Removed separate build step
It looks like part of the build step is repeated in the push step
due to bad caching possibly.
2021-01-01 12:53:16 +01:00
Mark Schmitt
3f01a9e1e2 Update docker-publish.yml 2020-12-31 19:32:04 +01:00
Mark Schmitt
916ea79e57 Experimenting with github actions 2020-12-31 13:27:22 +01:00
Mark Schmitt
b855222ee1 Removing travis in favor of github actions 2020-12-31 13:24:57 +01:00
Fabian Koller
fc31195fa0 Update to 0.9.11 2020-12-31 11:47:54 +01:00
Fabian Koller
be56707a8a Change default OCRmyPDF args
Clean is hardcoded as True, anyway
Deskew breaks tesseract redo
2020-12-31 10:01:57 +01:00
Fabian Koller
a74404170d Update to 0.9.10 2020-12-31 09:53:29 +01:00
Fabian Koller
e4e4efcba7 Fix creation of user arg
json.loads is picky in that is expects true json, not yaml from ansible
2020-12-29 23:54:22 +01:00
Fabian Koller
14f87f5aee Harden systemd service files, drop perms further 2020-12-29 23:30:59 +01:00
Fabian Koller
bb569b4e78 Integrate OCRmyPDF args into ansible config 2020-12-29 22:43:52 +01:00
Fabian Koller
f075384b44 Move paperless.conf to /etc, drop permissions 2020-12-29 21:55:59 +01:00
Fabian Koller
bf3ffc29a9 Make role compatible with ansible 2.7
Recursive remote copy is supported starting with 2.8 only
Indentation behaviour in literal yaml strings seems to have changed
Regex logic for ImageMagic was flawed (no idea why this worked before)
2020-12-29 20:59:49 +01:00
jonaswinkler
9bf4ce25b2 Merge branch 'dev' into travis-multiarch-builds 2020-12-28 17:54:48 +01:00
jonaswinkler
d51848cdc0 Merge branch 'travis-multiarch-builds' of github.com:jonaswinkler/paperless-ng into travis-multiarch-builds 2020-12-28 17:54:42 +01:00
Jonas Winkler
7497e0f6b9 Merge pull request #198 from MarkSchmitt/travis-multiarch-builds
Travis multi-arch builds: only build and push docker containers on special branches and tags
2020-12-28 13:36:33 +01:00
Fabian Koller
bdf2e29843 Verify role for Ubuntu 20.04 2020-12-28 12:51:49 +01:00
Mark Schmitt
ba9b5c50d2 Add stage definitions of test jobs and add test to list of stages
Defining the stages instead of having travis guess them and implicitly build an order
ensures that the test stage is executed before the docker images are built.
2020-12-28 12:24:39 +01:00
Mark Schmitt
61834581d5 Move build condition into separate stages list 2020-12-28 12:22:35 +01:00
Fabian Koller
1276419ec6 Add molecule test for role
Only test default installation with jbig2enc and sqlite
2020-12-28 11:28:19 +01:00
Fabian Koller
ef9631ae24 Drop all permissions to paperlessng user
Also make role idempotent
2020-12-28 11:18:24 +01:00
Mark Schmitt
0560ac2a95 Use conditions to prevent builds on branches / tags other than master, dev and ng-* 2020-12-27 20:01:21 +01:00
jonaswinkler
02e81f7ab5 update Dockerfile 2020-12-23 17:03:00 +01:00
jonaswinkler
85cf931d0a Merge branch 'dev' into travis-multiarch-builds 2020-12-23 16:15:46 +01:00
Jonas Winkler
6907b91420 Merge pull request #178 from MarkSchmitt/travis-multiarch-builds
Automatic CI/CD multiarch docker builds
2020-12-23 14:35:47 +01:00
Fabian Koller
227934a7f0 Do not clear static files on every run
Django collectstatic knows when files change.
2020-12-23 14:05:35 +01:00
Fabian Koller
089a8c0498 Fix fresh installation
We can't backup a nonexistent folder.
2020-12-23 13:39:16 +01:00
Fabian Koller
92fa978735 Allow running role on all Debian releases
Dynamically template current release string where required.
2020-12-23 13:31:30 +01:00
Mark Schmitt
8135de49f0 Re-enable test steps 2020-12-23 12:13:12 +01:00
Mark Schmitt
ccfa14b9b7 Remove arm32v6 from docker manifest , it is not getting created 2020-12-23 09:14:40 +01:00
Fabian Koller
e48294a74b Update to 0.9.9 2020-12-22 19:30:53 +01:00
Fabian Koller
50c5a23de8 add basic ansible role for debian deployment
Currently only Debian 10 buster is supported.
Other Debian versions, Ubuntu and derivates should be easy to integrate.
Database deployment is considered out-of-scope and deferred to the user.
Provides basic upgrade support between releases.
2020-12-22 18:47:02 +01:00
Mark Schmitt
1f62b6d0d4 arm64-graviton2 build requires group:edge 2020-12-22 14:18:11 +01:00
Mark Schmitt
c751fe7520 Switching back to virt: vm as there seems to be a prob with the lxc ones 2020-12-22 14:15:06 +01:00
Mark Schmitt
46ecdefde8 Remove steps not necessary to build docker images 2020-12-22 13:43:51 +01:00
Mark Schmitt
e8ea373eb2 Enable manifests support 2020-12-22 13:42:21 +01:00
Mark Schmitt
162626209c Tell me, what kind of arm64 is it that produces amd64 docker images?
It's weird
2020-12-22 13:27:19 +01:00
Mark Schmitt
bf3b110804 Remove accidentially added quote 2020-12-22 12:58:35 +01:00
Mark Schmitt
7d1f931234 Use arm64-gravis instead of regular arm64. See if that is fast enough. 2020-12-22 12:49:36 +01:00
jonaswinkler
ade951a600 Adds jbig2 to the build, fixes #93 2020-12-21 23:02:26 +01:00
Mark Schmitt
48b59c8e24 Use qemu also for arm64v8 build - something might be causing trouble on the native hosts 2020-12-21 21:53:39 +01:00
jonaswinkler
b3a13cec17 Merge branch 'dev' into travis-multiarch-builds 2020-12-21 21:29:45 +01:00
Mark Schmitt
7817315f8b Adding some echos to bette runderstand where the build hangs 2020-12-21 17:08:01 +01:00
Mark Schmitt
ba18258750 Add libxslt-dev to dependencies.
At least one arm based image does not pull in dependencies the same way
the amd64 one does, resulting in a missing libxslt shared lib.
2020-12-21 16:19:24 +01:00
Mark Schmitt
23381f7d17 Added multi arch docker builds to travis ci 2020-12-21 13:14:16 +01:00
jonaswinkler
dd6aa2f775 added missing file 2020-12-20 16:01:16 +01:00
jonaswinkler
5b344963b9 reorganized docker build. 2020-12-20 15:58:29 +01:00
103 changed files with 2642 additions and 1182 deletions

View File

@@ -1,3 +1,4 @@
**/__pycache__
/src-ui/.vscode
/src-ui/node_modules
/src-ui/dist
@@ -10,3 +11,9 @@
.pytest_cache
/dist
/scripts
/resources
**/tests
**/*.spec.ts
**/htmlcov
/src/.pytest_cache
.idea

59
.github/workflows/ansible.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
---
name: Ansible Role
on: [push, pull_request]
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
with:
path: "${{ github.repository }}"
- name: Set up Python
uses: actions/setup-python@v2
- name: Set up Docker
uses: docker-practice/actions-setup-docker@master
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install molecule[ansible,docker]
ansible --version
docker --version
molecule --version
python --version
- name: Test with molecule
run: |
cd ansible
molecule test
working-directory: "${{ github.repository }}"
# # https://galaxy.ansible.com/docs/contributing/importing.html
# release:
# runs-on: ubuntu-latest
# needs:
# - test
# # https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions#github-context
# if: contains(github.ref, 'refs/tags/')
# steps:
# - name: Check out the codebase
# uses: actions/checkout@v2
# with:
# path: "${{ github.repository }}"
# - name: Set up Python
# uses: actions/setup-python@v2
# - name: Install dependencies
# run: |
# python3 -m pip install --upgrade ansible-base
# ansible --version
# python --version
# - name: Trigger a new import on Galaxy
# # TODO Check if source if pulled from cwd or imported from github
# # https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/galaxy.py
# run: |
# cd ansible
# ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} $(echo ${{ github.repository }} | cut -d/ -f1) $(echo ${{ github.repository }} | cut -d/ -f2)
# working-directory: "${{ github.repository }}"

288
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,288 @@
name: ci
on: [push, pull_request]
jobs:
documentation:
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.8
-
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 }}-pip3.8}
-
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
-
name: Make documentation
run: |
cd docs/
make html
-
name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: documentation
path: docs/_build/html/
tests:
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: ['3.6', '3.7', '3.8']
fail-fast: false
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "${{ matrix.python-version }}"
-
name: Get pip cache dir
id: pip-cache
run: |
echo "::set-output name=dir::$(pip cache dir)"
-
name: Persistent Github pip cache
uses: actions/cache@v2
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip${{ matrix.python-version }}
-
name: Prepare tests
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev unpaper tesseract-ocr imagemagick ghostscript optipng
pip install --upgrade pipenv
pipenv install --system --dev --ignore-pipfile
-
name: Tests
run: |
cd src/
pytest
-
name: Codestyle
run: |
cd src/
pycodestyle
-
name: Publish coverage results
if: matrix.python-version == '3.8'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# https://github.com/coveralls-clients/coveralls-python/issues/251
run: |
cd src/
coveralls --service=github
frontend:
runs-on: ubuntu-20.04
steps:
-
name: Checkout
uses: actions/checkout@v2
-
uses: actions/setup-node@v2
with:
node-version: '15'
-
name: Build frontend
run: ./compile-frontend.sh
-
name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: frontend-compiled
path: src/documents/static/frontend/
build-release:
needs: [frontend, documentation, tests]
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: Install dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends libpoppler-cpp-dev gettext liblept5
pip3 install -r requirements.txt
-
name: Download frontend artifact
uses: actions/download-artifact@v2
with:
name: frontend-compiled
path: src/documents/static/frontend/
-
name: Download documentation artifact
uses: actions/download-artifact@v2
with:
name: documentation
path: docs/_build/html/
-
name: Move files
run: |
mkdir dist
mkdir dist/paperless-ng
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 docker/ dist/paperless-ng/docker -r
cp scripts/*.service scripts/*.sh dist/paperless-ng/scripts/
cp src/ dist/paperless-ng/src -r
cp docs/_build/html/ dist/paperless-ng/docs -r
-
name: Compile messages
run: |
cd dist/paperless-ng/src
python3 manage.py compilemessages
-
name: Collect static files
run: |
cd dist/paperless-ng/src
python3 manage.py collectstatic --no-input
-
name: Make release package
run: |
cd dist
find . -name __pycache__ | xargs rm -r
tar -cJf paperless-ng.tar.xz paperless-ng/
-
name: Upload release artifact
uses: actions/upload-artifact@v2
with:
name: release
path: dist/paperless-ng.tar.xz
publish-release:
runs-on: ubuntu-latest
needs: build-release
if: contains(github.ref, 'refs/tags/ng-')
steps:
-
name: Download release artifact
uses: actions/download-artifact@v2
with:
name: release
path: ./
-
name: Get version
id: get_version
run: |
echo ::set-output name=version::${GITHUB_REF#refs/tags/ng-}
-
name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ng-${{ steps.get_version.outputs.version }}
release_name: Paperless-ng ${{ steps.get_version.outputs.version }}
draft: false
prerelease: true
body: |
For a complete list of changes, see the changelog at https://paperless-ng.readthedocs.io/en/latest/changelog.html.
-
name: Upload release archive
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./paperless-ng.tar.xz
asset_name: paperless-ng-${{ steps.get_version.outputs.version }}.tar.xz
asset_content_type: application/x-xz
# build and push image to docker hub.
build-docker-image:
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/ng-'))
runs-on: ubuntu-latest
needs: [frontend, tests]
steps:
-
name: Prepare
id: prepare
run: |
VERSION=edge
if [[ $GITHUB_REF == refs/tags/ng-* ]]; then
VERSION=${GITHUB_REF#refs/tags/ng-}
elif [[ $GITHUB_REF == refs/heads/master ]]; then
VERSION=latest
elif [[ $GITHUB_REF == refs/heads/* ]]; then
VERSION=${GITHUB_REF#refs/heads/}
fi
echo ::set-output name=version::${VERSION}
-
name: Checkout
uses: actions/checkout@v2
-
name: Download frontend artifact
uses: actions/download-artifact@v2
with:
name: frontend-compiled
path: src/documents/static/frontend/
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm/v7,linux/arm64
push: true
tags: jonaswinkler/paperless-ng:${{ steps.prepare.outputs.version }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
-
name: Inspect image
run: |
docker buildx imagetools inspect jonaswinkler/paperless-ng:${{ steps.prepare.outputs.version }}

4
.gitignore vendored
View File

@@ -65,8 +65,8 @@ target/
.virtualenv
virtualenv
/venv
docker-compose.env
docker-compose.yml
/docker-compose.env
/docker-compose.yml
# Used for development
scripts/import-for-development

16
.readthedocs.yml Normal file
View File

@@ -0,0 +1,16 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# Optionally set the version of Python and requirements required to build your docs
python:
version: 3.7
install:
- requirements: docs/requirements.txt

View File

@@ -1,51 +0,0 @@
language: python
dist: focal
os: linux
jobs:
include:
- name: "Paperless on Python 3.6"
python: "3.6"
- name: "Paperless on Python 3.7"
python: "3.7"
- name: "Paperless on Python 3.8"
python: "3.8"
- name: "Documentation"
script:
- cd docs/
- make html
after_success: true
- name: "Front end"
language: node_js
node_js:
- 15
before_install: true
install:
- cd src-ui/
- npm install -g @angular/cli
- npm install
script:
- ng build --prod
after_success: true
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq libpoppler-cpp-dev unpaper tesseract-ocr imagemagick ghostscript optipng
install:
- pip install --upgrade pipenv
- pipenv install --system --dev
script:
- cd src/
- pipenv run pytest --cov
- pipenv run pycodestyle
after_success:
- pipenv run coveralls

View File

@@ -1,49 +1,79 @@
FROM ubuntu:20.04 AS jbig2enc
WORKDIR /usr/src/jbig2enc
RUN apt-get update && apt-get install -y --no-install-recommends build-essential automake libtool libleptonica-dev zlib1g-dev git ca-certificates
RUN git clone https://github.com/agl/jbig2enc .
RUN ./autogen.sh
RUN ./configure && make
FROM python:3.7-slim
WORKDIR /usr/src/paperless/
COPY requirements.txt ./
#Dependencies
# Binary dependencies
RUN apt-get update \
&& apt-get -y --no-install-recommends install \
build-essential \
# Basic dependencies
curl \
file \
# fonts for text file thumbnail generation
fonts-liberation \
# for making translations further down
gettext \
ghostscript \
gnupg \
icc-profiles-free \
imagemagick \
# for Numpy
libatlas-base-dev \
liblept5 \
libmagic-dev \
libpoppler-cpp-dev \
libpq-dev \
libqpdf-dev \
libxml2 \
libxslt1-dev \
mime-support \
# thumbnail size reduction
optipng \
sudo \
tzdata \
# OCRmyPDF dependencies
ghostscript \
icc-profiles-free \
liblept5 \
libxml2 \
pngquant \
qpdf \
sudo \
tesseract-ocr \
tesseract-ocr-eng \
tesseract-ocr-deu \
tesseract-ocr-fra \
tesseract-ocr-ita \
tesseract-ocr-spa \
tzdata \
unpaper \
zlib1g \
&& pip3 install --upgrade supervisor setuptools \
&& pip install --no-cache-dir -r requirements.txt \
&& 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 \
&& 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
@@ -51,6 +81,12 @@ 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/
# copy app
COPY src/ ./src/

12
Pipfile
View File

@@ -8,9 +8,6 @@ url = "https://www.piwheels.org/simple"
verify_ssl = true
name = "piwheels"
[requires]
python_version = "3.6"
[packages]
dateparser = "~=0.7.6"
django = "~=3.1.3"
@@ -26,8 +23,9 @@ imap-tools = "*"
langdetect = "*"
pdftotext = "*"
pathvalidate = "*"
pillow = "*"
pikepdf = "*"
# 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"
python-gnupg = "*"
python-dotenv = "*"
python-dateutil = "*"
@@ -35,7 +33,7 @@ python-Levenshtein = "*"
python-magic = "*"
psycopg2-binary = "*"
redis = "*"
scikit-learn="~=0.23.2"
scikit-learn="~=0.24.0"
whitenoise = "~=5.2.0"
watchdog = "*"
whoosh="~=2.7.4"
@@ -54,6 +52,6 @@ pytest-django = "*"
pytest-env = "*"
pytest-sugar = "*"
pytest-xdist = "*"
sphinx = "~=3.3"
sphinx = "~=3.4.2"
sphinx_rtd_theme = "*"
tox = "*"

126
Pipfile.lock generated
View File

@@ -1,12 +1,10 @@
{
"_meta": {
"hash": {
"sha256": "c35d84fd7f4f1c7d599039712362935e7c41a226b0ab3d83d8c1c2fb2ad0962a"
"sha256": "3c85a487240f18b3feb44f8899395696cb79630f320f0df9ef5ee37b914c89f2"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.6"
},
"requires": {},
"sources": [
{
"name": "pypi",
@@ -253,11 +251,11 @@
},
"importlib-metadata": {
"hashes": [
"sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed",
"sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"
"sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771",
"sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"
],
"markers": "python_version < '3.8'",
"version": "==3.3.0"
"version": "==3.4.0"
},
"inotify-simple": {
"hashes": [
@@ -439,6 +437,7 @@
"pillow": {
"hashes": [
"sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6",
"sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded",
"sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865",
"sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174",
"sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032",
@@ -464,9 +463,12 @@
"sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8",
"sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59",
"sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d",
"sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7",
"sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a",
"sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0",
"sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b",
"sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d"
"sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d",
"sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae"
],
"index": "pypi",
"version": "==8.1.0"
@@ -693,26 +695,35 @@
},
"scikit-learn": {
"hashes": [
"sha256:090bbf144fd5823c1f2efa3e1a9bf180295b24294ca8f478e75b40ed54f8036e",
"sha256:0a127cc70990d4c15b1019680bfedc7fec6c23d14d3719fdf9b64b22d37cdeca",
"sha256:0d39748e7c9669ba648acf40fb3ce96b8a07b240db6888563a7cb76e05e0d9cc",
"sha256:1b8a391de95f6285a2f9adffb7db0892718950954b7149a70c783dc848f104ea",
"sha256:20766f515e6cd6f954554387dfae705d93c7b544ec0e6c6a5d8e006f6f7ef480",
"sha256:2aa95c2f17d2f80534156215c87bee72b6aa314a7f8b8fe92a2d71f47280570d",
"sha256:5ce7a8021c9defc2b75620571b350acc4a7d9763c25b7593621ef50f3bd019a2",
"sha256:6c28a1d00aae7c3c9568f61aafeaad813f0f01c729bee4fd9479e2132b215c1d",
"sha256:7671bbeddd7f4f9a6968f3b5442dac5f22bf1ba06709ef888cc9132ad354a9ab",
"sha256:914ac2b45a058d3f1338d7736200f7f3b094857758895f8667be8a81ff443b5b",
"sha256:98508723f44c61896a4e15894b2016762a55555fbf09365a0bb1870ecbd442de",
"sha256:a64817b050efd50f9abcfd311870073e500ae11b299683a519fbb52d85e08d25",
"sha256:cb3e76380312e1f86abd20340ab1d5b3cc46a26f6593d3c33c9ea3e4c7134028",
"sha256:d0dcaa54263307075cb93d0bee3ceb02821093b1b3d25f66021987d305d01dce",
"sha256:d9a1ce5f099f29c7c33181cc4386660e0ba891b21a60dc036bf369e3a3ee3aec",
"sha256:da8e7c302003dd765d92a5616678e591f347460ac7b53e53d667be7dfe6d1b10",
"sha256:daf276c465c38ef736a79bd79fc80a249f746bcbcae50c40945428f7ece074f8"
"sha256:076369634ee72b5a5941440661e2f306ff4ac30903802dc52031c7e9199ac640",
"sha256:18f7131e62265bf2691ed1d0303c640313894ccfe4278427478c6b2f45094b53",
"sha256:26f66b3726b54dfb76ea51c5d9c2431ed17ebc066cb4527662b9e851a3e7ba61",
"sha256:2951f87d35e72f007701c6e028aa230f6df6212a3194677c0c950486066a454d",
"sha256:2a5348585aa793bc8cc5a72f8e9067c9380834b0aadbd55f924843b071f13282",
"sha256:3eeff086f7329521d27249a082ea3c48c085cedb110db5f65968ab55c3ba2e09",
"sha256:4395e91b3548005f4a645018435b5a94f8cce232b5b70753020e606c6a750656",
"sha256:44e452ea8491225c5783d49577aad0f36202dfd52aec7f82c0fdfe5fbd5f7400",
"sha256:490436b44b3a1957cb625e871764b0aa330b34cc416aea4abc6c38ca63d0d682",
"sha256:5e6e3c042cea83f2e20a45e563b8eabc1f8f72446251fe23ebefdf111a173a33",
"sha256:66f27bf21202a850bcd7b6303916e4907f6e22ec59a14974ede4955aed5c7ed0",
"sha256:743b6edd98c98991be46c08e6b21df3861d5ae915f91d59f988384d93f7263e7",
"sha256:758619e49cd7c17282e6cc60d5cc73c02c072b47c9a10010bb3bb47e0d976e50",
"sha256:7f654befc5ad413690cc58f3f34a3e906caf825195ce0fda00a8e9565e1403e6",
"sha256:800aaf63f8838c00e85db2267dd226f89858594843fd03932a9eda95746d2c40",
"sha256:80ca024154b84b6ac4cfc86930ba13fdc348a209753bf2c16129db6f9eb8a80b",
"sha256:890d7d588f65acb0c4f6c083347c9076916bda5e6bd8400f06244b1afc1009af",
"sha256:905d8934d1e27a686698864a5863ff2c0e13a2ae1adb78a8a848aacc8a49927d",
"sha256:a83fcd9d59c42a2f66b307e3b0b0f08aa8e6e45be33da055697ea499f0e4f7c2",
"sha256:afeb06dc69847927634e58579b9cdc72e1390b79497336b2324b1b173f33bd47",
"sha256:b0d13fd56d26cf3de0314a4fd48037108c638fe126d813f5c1222bb0f08b6a76",
"sha256:c08b27cb78ee8d2dc781a7affed09859441f5b624f9f92da59ac0791c8774dfc",
"sha256:c912247e42114f389858ae05d63f4359d4e667ea72aaabee191aee9ad3f9774a",
"sha256:d7fe05fcb44eadd6d6c874c768f085f5de1239db3a3b7be4d3d23d12e4120589",
"sha256:d819d625832fb2969911a243e009cfa135cb8ef1e150866e417d6e9d75290087",
"sha256:e534f5f3796db6781c87e9835dcd51b7854c8c5a379c9210b93605965c1941fd"
],
"index": "pypi",
"version": "==0.23.2"
"version": "==0.24.0"
},
"scipy": {
"hashes": [
@@ -787,11 +798,11 @@
},
"tqdm": {
"hashes": [
"sha256:556c55b081bd9aa746d34125d024b73f0e2a0e62d5927ff0e400e20ee0a03b9a",
"sha256:b8b46036fd00176d0870307123ef06bb851096964fa7fc578d789f90ce82c3e4"
"sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a",
"sha256:fe3d08dd00a526850568d542ff9de9bbc2a09a791da3c334f3213d8d0bbbca65"
],
"index": "pypi",
"version": "==4.55.1"
"version": "==4.56.0"
},
"typing-extensions": {
"hashes": [
@@ -986,11 +997,11 @@
},
"coveralls": {
"hashes": [
"sha256:2301a19500b06649d2ec4f2858f9c69638d7699a4c63027c5d53daba666147cc",
"sha256:b990ba1f7bc4288e63340be0433698c1efe8217f78c689d254c2540af3d38617"
"sha256:5399c0565ab822a70a477f7031f6c88a9dd196b3de2877b3facb43b51bd13434",
"sha256:f8384968c57dee4b7133ae701ecdad88e85e30597d496dcba0d7fbb470dca41f"
],
"index": "pypi",
"version": "==2.2.0"
"version": "==3.0.0"
},
"distlib": {
"hashes": [
@@ -1032,11 +1043,11 @@
},
"faker": {
"hashes": [
"sha256:7b0c4bb678be21a68640007f254259c73d18f7996a3448267716423360519732",
"sha256:7e98483fc273ec5cfe1c9efa9b99adaa2de4c6b610fbc62d3767088e4974b0ce"
"sha256:47ac7d62d5bad8c16422a91f121430ab7656d40ca8fea9c84bcdbdf92e739b03",
"sha256:6bc44606d44f711e1d89ad9a5b42394cc6f7eedaffc765ddb5b2d22084c15733"
],
"markers": "python_version >= '3.6'",
"version": "==5.3.0"
"version": "==5.5.0"
},
"filelock": {
"hashes": [
@@ -1065,19 +1076,19 @@
},
"importlib-metadata": {
"hashes": [
"sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed",
"sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"
"sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771",
"sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"
],
"markers": "python_version < '3.8'",
"version": "==3.3.0"
"version": "==3.4.0"
},
"importlib-resources": {
"hashes": [
"sha256:0a948d0c8c3f9344de62997e3f73444dbba233b1eaf24352933c2d264b9e4182",
"sha256:6b45007a479c4ec21165ae3ffbe37faf35404e2041fac6ae1da684f38530ca73"
"sha256:4743f090ed8946e713745ec0e660249ef9fb0b9843eacc5b5ff931d2fd5aa67f",
"sha256:ea17df80a0ff04b5dbd3d96dbeab1842acfd1c6c902eaeb8c8858abf2720161e"
],
"markers": "python_version < '3.7'",
"version": "==4.1.1"
"version": "==5.0.0"
},
"iniconfig": {
"hashes": [
@@ -1172,11 +1183,11 @@
},
"pygments": {
"hashes": [
"sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716",
"sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"
"sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435",
"sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"
],
"markers": "python_version >= '3.5'",
"version": "==2.7.3"
"version": "==2.7.4"
},
"pyparsing": {
"hashes": [
@@ -1283,11 +1294,11 @@
},
"sphinx": {
"hashes": [
"sha256:77dec5ac77ca46eee54f59cf477780f4fb23327b3339ef39c8471abb829c1285",
"sha256:b8aa4eb5502c53d3b5ca13a07abeedacd887f7770c198952fd5b9530d973e767"
"sha256:41cad293f954f7d37f803d97eb184158cfd90f51195131e94875bc07cd08b93c",
"sha256:c314c857e7cd47c856d2c5adff514ac2e6495f8b8e0f886a8a37e9305dfea0d8"
],
"index": "pypi",
"version": "==3.4.2"
"version": "==3.4.3"
},
"sphinx-rtd-theme": {
"hashes": [
@@ -1369,11 +1380,20 @@
},
"tox": {
"hashes": [
"sha256:42ce19ce5dc2f6d6b1fdc5666c476e1f1e2897359b47e0aa3a5b774f335d57c2",
"sha256:4321052bfe28f9d85082341ca8e233e3ea901fdd14dab8a5d3fbd810269fbaf6"
"sha256:5efda30ad73e662c3844ac51ce1381bf28f61063773e06996aa8b6277133a7c0",
"sha256:8cccede64802e78aa6c69f81051b25f0706639d1cbbb34d9366ce00c70ee054f"
],
"index": "pypi",
"version": "==3.20.1"
"version": "==3.21.0"
},
"typing-extensions": {
"hashes": [
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
],
"markers": "python_version < '3.8'",
"version": "==3.7.4.3"
},
"urllib3": {
"hashes": [
@@ -1385,11 +1405,11 @@
},
"virtualenv": {
"hashes": [
"sha256:54b05fc737ea9c9ee9f8340f579e5da5b09fb64fd010ab5757eb90268616907c",
"sha256:b7a8ec323ee02fb2312f098b6b4c9de99559b462775bc8fe3627a73706603c1b"
"sha256:205a7577275dd0d9223c730dd498e21a8910600085c3dee97412b041fc4b853b",
"sha256:7992b8de87e544a4ab55afc2240bf8388c4e3b5765d03784dad384bfdf9097ee"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.2.2"
"version": "==20.3.0"
},
"zipp": {
"hashes": [

View File

@@ -1,4 +1,4 @@
[![Build Status](https://travis-ci.com/jonaswinkler/paperless-ng.svg?branch=master)](https://travis-ci.com/jonaswinkler/paperless-ng)
![ci](https://github.com/jonaswinkler/paperless-ng/workflows/ci/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)
@@ -56,8 +56,6 @@ For a complete list of changes from paperless, check out the [changelog](https:/
- Make the front end nice (except mobile).
- Fix whatever bugs I and you find.
- Start using CI to build the app.
- Simplify updates.
- Make the documentation nice.
## Roadmap for versions beyond 1.0
@@ -89,7 +87,7 @@ These features will probably never make it into paperless, since paperless is me
# Getting started
The recommended way to deploy paperless is docker-compose. Don't clone the repository, grab the latest release to get started instead. The dockerfiles archive contains just the docker files which will pull the image from docker hub. The source archive contains everything you need to build the docker image yourself (i.e. if you want to run on Raspberry Pi).
The recommended way to deploy paperless is docker-compose. The files in the /docker/hub directory are configured to pull the image from Docker Hub.
Read the [documentation](https://paperless-ng.readthedocs.io/en/latest/setup.html#installation) on how to get started.

38
ansible/README.md Normal file
View File

@@ -0,0 +1,38 @@
Role Name
=========
A brief description of the role goes here.
Requirements
------------
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
Role Variables
--------------
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
Dependencies
------------
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
Example Playbook
----------------
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
- hosts: servers
roles:
- { role: username.rolename, x: 42 }
License
-------
BSD
Author Information
------------------
An optional section for the role authors to include contact information, or a website (HTML is not allowed).

43
ansible/defaults/main.yml Normal file
View File

@@ -0,0 +1,43 @@
---
paperlessng_version: 0.9.13
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_filename_format:
paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv"
paperlessng_ocr_languages:
- eng
paperlessng_time_zone: Europe/Berlin
# see https://ocrmypdf.readthedocs.io/en/latest/api.html#ocrmypdf.ocr
paperlessng_ocrmypdf_args:
#- "deskew": true # https://github.com/jonaswinkler/paperless-ng/issues/231
- "optimize": 1
paperlessng_use_jbig2enc: true
paperlessng_big2enc_lossy: false
paperlessng_tika_enabled: false
paperlessng_tika_endpoint: http://localhost:9998
paperlessng_tika_gotenberg_endpoint: http://localhost:3000
paperlessng_superuser_name: paperlessng
paperlessng_superuser_email: paperlessng@example.com
paperlessng_superuser_password: paperlessng
paperlessng_system_user: paperlessng
paperlessng_system_group: paperlessng
paperlessng_listen_address: 127.0.0.1
paperlessng_listen_port: 8000
paperlessng_redis_host: localhost
paperlessng_redis_port: 6379
paperlessng_db_type: sqlite # or postgresql
# Below entries only apply for paperlessng_db_type=='postgresql'
paperlessng_db_host: localhost
paperlessng_db_port: 5432
paperlessng_db_name: paperlessng
paperlessng_db_user: paperlessng
paperlessng_db_pass: paperlessng

17
ansible/meta/main.yml Normal file
View File

@@ -0,0 +1,17 @@
dependencies: []
galaxy_info:
author: C0nsultant
description: Bare-metal deployment of paperless-ng DMS
license: license (GPLv3)
min_ansible_version: 2.7
platforms:
- name: Debian
versions:
- buster
- name: Ubuntu
versions:
- focal
galaxy_tags: [EDMS, django, python, web]

View File

@@ -0,0 +1,7 @@
---
- name: Converge
hosts: all
tasks:
- name: "Include ansible"
include_role:
name: "ansible"

View File

@@ -0,0 +1,35 @@
---
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

@@ -0,0 +1,14 @@
---
# This is an example playbook to execute Ansible tests.
- name: Verify
hosts: all
gather_facts: false
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"

428
ansible/tasks/main.yml Normal file
View File

@@ -0,0 +1,428 @@
---
- name: verify operating system
fail:
msg: Sorry, only Debian and Ubuntu supported at the moment.
when: not(ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu')
- name: install base dependencies
apt:
update_cache: yes
pkg:
# paperless-ng
- python3-dev
- python3-pip
- gettext
- fonts-liberation
- imagemagick
- unpaper
- ghostscript
- optipng
- tesseract-ocr
- gnupg
- libpoppler-cpp-dev
- libmagic-dev
- libpq-dev
# OCRmyPDF
- icc-profiles-free
- qpdf
- liblept5
- libxml2
- pngquant
- zlib1g
# dev
- sudo
- build-essential
- python3-setuptools
- python3-wheel
- python3-virtualenv
- name: install ocr languages
apt:
pkg: "{{ paperlessng_ocr_languages | map('regex_replace', '^(.*)$', 'tesseract-ocr-\\1') | list }}"
- name: set up notesalexp repository key (for jbig2enc)
apt_key:
url: https://notesalexp.org/debian/alexp_key.asc
state: present
when: paperlessng_use_jbig2enc
- name: set up notesalexp repository (for jbig2enc)
apt_repository:
repo: "deb https://notesalexp.org/debian/{{ ansible_distribution_release }}/ {{ ansible_distribution_release }} main"
state: present
when: paperlessng_use_jbig2enc
- name: set up notesalexp repository pinning
copy:
content: |
Package: *
Pin: release o=notesalexp.org
Pin-Priority: 1
Package: jbig2enc
Pin: release o=notesalexp.org
Pin-Priority: 500
dest: /etc/apt/preferences.d/notesalexp
when: paperlessng_use_jbig2enc
- name: install jbig2enc
apt:
pkg: jbig2enc
update_cache: yes
when: paperlessng_use_jbig2enc
- name: install redis
apt:
pkg: redis-server
when: paperlessng_redis_host == 'localhost' or paperlessng_redis_host == '127.0.0.1'
- name: enable redis
systemd:
name: redis-server
enabled: yes
masked: no
state: started
when: paperlessng_redis_host == 'localhost' or paperlessng_redis_host == '127.0.0.1'
- name: create paperless system group
group:
name: "{{ paperlessng_system_group }}"
- name: create paperless system user
user:
name: "{{ paperlessng_system_user }}"
groups:
- "{{ paperlessng_system_group }}"
shell: /usr/sbin/nologin
# GNUPG_HOME required due to paperless db.py
create_home: yes
- 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'
failed_when: false
ignore_errors: yes
register: paperlessng_current_version
- name: backup current paperless-ng installation
copy:
src: "{{ paperlessng_directory }}"
remote_src: yes
dest: "{{ paperlessng_directory }}-{{ ansible_date_time.iso8601 }}/"
when: '"No such file or directory" not in paperlessng_current_version.stderr and paperlessng_current_version.stdout != paperlessng_version | string'
- name: create temporary directory
tempfile:
state: directory
register: tempdir
when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
- 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: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
- 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: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
- name: move paperless-ng
command:
cmd: "cp -a {{ tempdir.path }}/paperless-ng/ {{ paperlessng_directory }}"
when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
- name: remove temporary directory
file:
path: "{{ tempdir.path }}"
state: absent
when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
- name: create paperless-ng directories and set permissions
file:
path: "{{ item }}"
state: directory
owner: "{{ paperlessng_system_user }}"
group: "{{ paperlessng_system_group }}"
mode: "750"
with_items:
- "{{ paperlessng_directory }}" # ansible `copy:` does not set correct permissions on `dest:` for recursive copies
- "{{ paperlessng_consumption_dir }}"
- "{{ paperlessng_data_dir }}"
- "{{ paperlessng_media_root }}"
- "{{ paperlessng_static_dir }}"
- name: rename initial config
command:
cmd: "mv {{ paperlessng_directory }}/paperless.conf {{ paperlessng_directory }}/paperless.conf.template"
removes: "{{ paperlessng_directory }}/paperless.conf"
- name: configure paperless-ng
lineinfile:
path: "{{ paperlessng_directory }}/paperless.conf.template"
regexp: "^#?{{ item.regexp }}="
line: "{{ item.line }}"
with_items:
- regexp: PAPERLESS_REDIS
line: "PAPERLESS_REDIS=redis://{{ paperlessng_redis_host }}:{{ paperlessng_redis_port }}"
- regexp: PAPERLESS_CONSUMPTION_DIR
line: "PAPERLESS_CONSUMPTION_DIR={{ paperlessng_consumption_dir }}"
- regexp: PAPERLESS_DATA_DIR
line: "PAPERLESS_DATA_DIR={{ paperlessng_data_dir }}"
- regexp: PAPERLESS_MEDIA_ROOT
line: "PAPERLESS_MEDIA_ROOT={{ paperlessng_media_root }}"
- regexp: PAPERLESS_STATICDIR
line: "PAPERLESS_STATICDIR={{ paperlessng_static_dir }}"
- regexp: PAPERLESS_FILENAME_FORMAT
line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}"
- regexp: PAPERLESS_OCR_LANGUAGE
line: "PAPERLESS_OCR_LANGUAGE={{ paperlessng_ocr_languages | join('+') }}"
- regexp: PAPERLESS_OCR_USER_ARGS
line: "PAPERLESS_OCR_USER_ARGS={{ paperlessng_ocrmypdf_args | combine({'jbig2_lossy': true} if paperlessng_big2enc_lossy else {}) | to_json }}"
- regexp: PAPERLESS_TIME_ZONE
line: "PAPERLESS_TIME_ZONE={{ paperlessng_time_zone }}"
- regexp: PAPERLESS_TIKA_ENABLED
line: "PAPERLESS_TIKA_ENABLED={{ paperlessng_tika_enabled }}"
no_log: yes
- name: configure paperless-ng [tika]
lineinfile:
path: "{{ paperlessng_directory }}/paperless.conf.template"
regexp: "^#?{{ item.regexp }}="
line: "'{{ item.line }}' if paperlessng_tika_enabled else '#{{ item.line }}'"
with_items:
- regexp: PAPERLESS_TIKA_ENDPOINT
line: "PAPERLESS_TIKA_ENDPOINT={{ paperlessng_tika_endpoint }}"
- regexp: PAPERLESS_TIKA_GOTENBERG_ENDPOINT
line: "PAPERLESS_TIKA_GOTENBERG_ENDPOINT={{ paperlessng_tika_endpoint }}"
- name: configure paperless-ng database [sqlite]
lineinfile:
path: "{{ paperlessng_directory }}/paperless.conf.template"
regexp: "^#?PAPERLESS_DBHOST=(.*)$"
line: '#PAPERLESS_DBHOST=\1'
backrefs: yes
when: paperlessng_db_type == 'sqlite'
- name: configure paperless-ng database [postgresql]
lineinfile:
path: "{{ paperlessng_directory }}/paperless.conf.template"
regexp: "^#?{{ item.regexp }}="
line: "{{ item.line }}"
with_items:
- regexp: PAPERLESS_DBHOST
line: "PAPERLESS_DBHOST={{ paperlessng_db_host }}"
- regexp: PAPERLESS_DBPORT
line: "PAPERLESS_DBPORT={{ paperlessng_db_port }}"
- regexp: PAPERLESS_DBNAME
line: "PAPERLESS_DBNAME={{ paperlessng_db_name }}"
- regexp: PAPERLESS_DBUSER
line: "PAPERLESS_DBUSER={{ paperlessng_db_user }}"
- regexp: PAPERLESS_DBPASS
line: "PAPERLESS_DBPASS={{ paperlessng_db_pass }}"
when: paperlessng_db_type == 'postgresql'
no_log: yes
- name: deploy paperless-ng configuration
copy:
src: "{{ paperlessng_directory }}/paperless.conf.template"
remote_src: yes
dest: /etc/paperless.conf
owner: root
group: root
mode: '0644'
register: configuration
- name: create paperlessng venv
become: yes
become_user: "{{ paperlessng_system_user }}"
command:
cmd: "python3 -m virtualenv {{ paperlessng_virtualenv }} -p /usr/bin/python3"
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: paperlessng_current_version.stdout != paperlessng_version | string
- name: collect static files
become: yes
become_user: "{{ paperlessng_system_user }}"
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py collectstatic --no-input"
when: paperlessng_current_version.stdout != paperlessng_version | string
register: static_files
changed_when: static_files.stdout is not match("0 static files copied .*")
- name: create database schema
become: yes
become_user: "{{ paperlessng_system_user }}"
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py migrate"
when: paperlessng_current_version.stdout != paperlessng_version | string
register: database_schema
changed_when: '"No migrations to apply." not in database_schema.stdout'
- name: compile translations
become: yes
become_user: "{{ paperlessng_system_user }}"
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py compilemessages"
when: paperlessng_current_version.stdout != paperlessng_version | string
- name: configure paperless superuser
become: yes
become_user: "{{ paperlessng_system_user }}"
# "manage.py createsuperuser" only works on interactive TTYs
vars:
creation_script: |
from django.contrib.auth.models import User
from django.contrib.auth.hashers import get_hasher
if User.objects.filter(username='{{ paperlessng_superuser_name }}').exists():
user = User.objects.get(username='{{ paperlessng_superuser_name }}')
old = user.__dict__.copy()
user.is_superuser = True
user.email = '{{ paperlessng_superuser_email }}'
user.set_password('{{ paperlessng_superuser_password }}')
user.save()
new = user.__dict__
algorithm, iterations, old_salt, old_hash = old['password'].split('$')
new_password_old_salt = get_hasher(algorithm).encode(password='{{ paperlessng_superuser_password }}', salt=old_salt, iterations=int(iterations))
_, _, _, new_hash = new_password_old_salt.split('$')
if not (old_hash == new_hash and old['is_superuser'] == new['is_superuser'] and old['email'] == new['email']):
print('changed')
else:
User.objects.create_superuser('{{ paperlessng_superuser_name }}', '{{ paperlessng_superuser_email }}', '{{ paperlessng_superuser_password }}')
print('changed')
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py shell -c \"{{ creation_script }}\""
register: superuser
changed_when: superuser.stdout == 'changed'
no_log: yes
- name: set ownership and permissions on paperlessng venv
file:
path: "{{ paperlessng_virtualenv }}"
state: directory
recurse: yes
owner: "{{ paperlessng_system_user }}"
group: "{{ paperlessng_system_group }}"
mode: g-w,o-rwx
when: venv.changed or paperlessng_current_version.stdout != paperlessng_version | string
- name: configure ghostscript for PDF
lineinfile:
path: /etc/ImageMagick-6/policy.xml
regexp: '(\s+)<policy domain="coder" rights=".*" pattern="PDF" />'
line: '\1<policy domain="coder" rights="read|write" pattern="PDF" />'
backrefs: yes
- name: configure systemd services
ini_file:
path: "{{ paperlessng_directory }}/scripts/{{ item[0] }}"
section: "Service"
option: "{{ item[1].option }}"
value: "{{ item[1].value }}"
with_nested:
- [
paperless-consumer.service,
paperless-scheduler.service,
paperless-webserver.service,
]
- [
# 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: "ProtectSystem",
value: "full",
},
{
option: "NoNewPrivileges",
value: "true",
},
{
option: "PrivateUsers",
value: "true",
},
{
option: "PrivateDevices",
value: "true",
}
]
- name: configure paperless-consumer service
ini_file:
path: "{{ paperlessng_directory }}/scripts/paperless-consumer.service"
section: "Service"
option: "ExecStart"
value: "{{ paperlessng_virtualenv }}/bin/python3 manage.py document_consumer"
- name: configure paperless-scheduler service
ini_file:
path: "{{ paperlessng_directory }}/scripts/paperless-scheduler.service"
section: "Service"
option: "ExecStart"
value: "{{ paperlessng_virtualenv }}/bin/python3 manage.py qcluster"
- name: configure paperless-webserver service
ini_file:
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 }}"
- name: copy systemd services
copy:
src: "{{ paperlessng_directory }}/scripts/{{ item }}"
remote_src: yes
dest: "/etc/systemd/system/{{ item }}"
with_items:
- paperless-consumer.service
- paperless-scheduler.service
- paperless-webserver.service
register: paperless_services
- name: reload systemd daemon
systemd:
name: "{{ item }}"
state: restarted
daemon_reload: yes
with_items:
- paperless-consumer
- paperless-scheduler
- paperless-webserver
when: paperless_services.changed or configuration.changed
- name: enable paperlessng services
systemd:
name: "{{ item }}"
enabled: yes
masked: no
state: started
with_items:
- paperless-consumer
- paperless-scheduler
- paperless-webserver

7
compile-frontend.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
set -e
cd src-ui
npm install
./node_modules/.bin/ng build --prod

1
docker/compose/.env Normal file
View File

@@ -0,0 +1 @@
COMPOSE_PROJECT_NAME=paperless

View File

@@ -7,7 +7,7 @@
# Additional languages to install for text recognition, separated by a
# whitespace. Note that this is
# different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines the
# default language used when guessing the language from the OCR output.
# language used for OCR.
# The container installs English, German, Italian, Spanish and French by
# default.
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster

View File

@@ -0,0 +1,90 @@
# docker-compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
# All compose files of paperless configure paperless in the following way:
#
# - Paperless is (re)started on system boot, if it was running before shutdown.
# - Docker volumes for storing data are managed by Docker.
# - Folders for importing and exporting files are created in the same directory
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8000.
#
# In addition to that, this docker-compose file adds the following optional
# configurations:
#
# - Instead of SQLite (default), PostgreSQL is used as the database server.
# - Apache Tika and Gotenberg servers are started with paperless and paperless
# is configured to use these services. These provide support for consuming
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
# parts.
#
# To install and update paperless with this file, do the following:
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
version: "3.4"
services:
broker:
image: redis:6.0
restart: unless-stopped
db:
image: postgres:13
restart: unless-stopped
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD: paperless
webserver:
image: jonaswinkler/paperless-ng:latest
restart: unless-stopped
depends_on:
- db
- broker
- gotenberg
- tika
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBHOST: db
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: thecodingmachine/gotenberg
restart: unless-stopped
environment:
DISABLE_GOOGLE_CHROME: 1
tika:
image: apache/tika
restart: unless-stopped
volumes:
data:
media:
pgdata:

View File

@@ -0,0 +1,72 @@
# docker-compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
# All compose files of paperless configure paperless in the following way:
#
# - Paperless is (re)started on system boot, if it was running before shutdown.
# - Docker volumes for storing data are managed by Docker.
# - Folders for importing and exporting files are created in the same directory
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8000.
#
# In addition to that, this docker-compose file adds the following optional
# configurations:
#
# - Instead of SQLite (default), PostgreSQL is used as the database server.
#
# To install and update paperless with this file, do the following:
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
version: "3.4"
services:
broker:
image: redis:6.0
restart: unless-stopped
db:
image: postgres:13
restart: unless-stopped
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD: paperless
webserver:
image: jonaswinkler/paperless-ng:latest
restart: unless-stopped
depends_on:
- db
- broker
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBHOST: db
volumes:
data:
media:
pgdata:

View File

@@ -0,0 +1,78 @@
# docker-compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
# All compose files of paperless configure paperless in the following way:
#
# - Paperless is (re)started on system boot, if it was running before shutdown.
# - Docker volumes for storing data are managed by Docker.
# - Folders for importing and exporting files are created in the same directory
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8000.
#
# SQLite is used as the database. The SQLite file is stored in the data volume.
#
# In addition to that, this docker-compose file adds the following optional
# configurations:
#
# - Apache Tika and Gotenberg servers are started with paperless and paperless
# is configured to use these services. These provide support for consuming
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
# parts.
#
# To install and update paperless with this file, do the following:
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
version: "3.4"
services:
broker:
image: redis:6.0
restart: unless-stopped
webserver:
image: jonaswinkler/paperless-ng:latest
restart: unless-stopped
depends_on:
- broker
- gotenberg
- tika
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: thecodingmachine/gotenberg
restart: unless-stopped
environment:
DISABLE_GOOGLE_CHROME: 1
tika:
image: apache/tika
restart: unless-stopped
volumes:
data:
media:

View File

@@ -0,0 +1,56 @@
# docker-compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
# All compose files of paperless configure paperless in the following way:
#
# - Paperless is (re)started on system boot, if it was running before shutdown.
# - Docker volumes for storing data are managed by Docker.
# - Folders for importing and exporting files are created in the same directory
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8000.
#
# SQLite is used as the database. The SQLite file is stored in the data volume.
#
# To install and update paperless with this file, do the following:
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
version: "3.4"
services:
broker:
image: redis:6.0
restart: unless-stopped
webserver:
image: jonaswinkler/paperless-ng:latest
restart: unless-stopped
depends_on:
- broker
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
volumes:
data:
media:

View File

@@ -62,6 +62,7 @@ migrations() {
# simultaneously. This also ensures that the db is ready when the command
# of the current container starts.
flock 200
echo "Apply database migrations..."
sudo -HEu paperless python3 manage.py migrate
) 200>/usr/src/paperless/data/migration_lock
@@ -85,6 +86,8 @@ initialize() {
}
install_languages() {
echo "Installing languages..."
local langs="$1"
read -ra langs <<<"$langs"
@@ -119,6 +122,8 @@ install_languages() {
done
}
echo "Paperless-ng docker container starting..."
# Install additional languages if specified
if [[ ! -z "$PAPERLESS_OCR_LANGUAGES" ]]; then
install_languages "$PAPERLESS_OCR_LANGUAGES"
@@ -127,8 +132,10 @@ fi
initialize
if [[ "$1" != "/"* ]]; then
echo Executing management command "$@"
exec sudo -HEu paperless python3 manage.py "$@"
else
echo Executing "$@"
exec "$@"
fi

View File

@@ -1,44 +0,0 @@
version: "3.4"
services:
broker:
image: redis:6.0
restart: always
db:
image: postgres:13
restart: always
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD: paperless
webserver:
image: jonaswinkler/paperless-ng:0.9.12
restart: always
depends_on:
- db
- broker
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBHOST: db
volumes:
data:
media:
pgdata:

View File

@@ -1,31 +0,0 @@
version: "3.4"
services:
broker:
image: redis:6.0
restart: always
webserver:
image: jonaswinkler/paperless-ng:0.9.12
restart: always
depends_on:
- broker
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
volumes:
data:
media:

View File

@@ -1,43 +0,0 @@
version: "3.4"
services:
broker:
image: redis:6.0
restart: always
webserver:
image: jonaswinkler/paperless-ng:0.9.12
restart: always
depends_on:
- broker
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: thecodingmachine/gotenberg
restart: unless-stopped
environment:
DISABLE_GOOGLE_CHROME: 1
tika:
image: apache/tika
restart: unless-stopped
volumes:
data:
media:

View File

@@ -1,44 +0,0 @@
version: "3.4"
services:
broker:
image: redis:6.0
restart: always
db:
image: postgres:13
restart: always
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: paperless
POSTGRES_USER: paperless
POSTGRES_PASSWORD: paperless
webserver:
build: .
restart: always
depends_on:
- db
- broker
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBHOST: db
volumes:
data:
media:
pgdata:

View File

@@ -1,31 +0,0 @@
version: "3.4"
services:
broker:
image: redis:6.0
restart: always
webserver:
build: .
restart: always
depends_on:
- broker
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
volumes:
data:
media:

View File

@@ -1,43 +0,0 @@
version: "3.4"
services:
broker:
image: redis:6.0
restart: always
webserver:
build: .
restart: always
depends_on:
- broker
ports:
- 8000:8000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
- ./export:/usr/src/paperless/export
- ./consume:/usr/src/paperless/consume
env_file: docker-compose.env
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: thecodingmachine/gotenberg
restart: unless-stopped
environment:
DISABLE_GOOGLE_CHROME: 1
tika:
image: apache/tika
restart: unless-stopped
volumes:
data:
media:

View File

@@ -48,16 +48,13 @@ Options available to bare-metal and non-docker installations:
Restoring
=========
.. _administration-updating:
Updating paperless
Updating Paperless
##################
If a new release of paperless-ng is available, upgrading depends on how you
installed paperless-ng in the first place. The releases are available at
installed paperless-ng in the first place. The releases are available at the
`release page <https://github.com/jonaswinkler/paperless-ng/releases>`_.
First of all, ensure that paperless is stopped.
@@ -69,50 +66,28 @@ First of all, ensure that paperless is stopped.
After that, :ref:`make a backup <administration-backup>`.
A. If you used the dockerfiles archive, simply download the files of the new release,
adjust the settings in the files (i.e., the path to your consumption directory),
and replace your existing docker-compose files. Then start paperless as usual,
which will pull the new image, and update your database, if necessary:
A. If you pull the image from the docker hub, all you need to do is:
.. code:: shell-session
$ cd /path/to/paperless
$ docker-compose pull
$ docker-compose up
If you see everything working, you can start paperless-ng with "-d" to have it
run in the background.
.. hint::
The released docker-compose files specify exact versions to be pulled from the hub.
This is to ensure that if the docker-compose files should change at some point
(i.e., services updates/configured differently), you wont run into trouble due to
docker pulling the ``latest`` image and running it in an older environment.
The docker-compose files refer to the ``latest`` version, which is always the latest
stable release.
B. If you built the image yourself, grab the new archive and replace your current
paperless folder with the new contents.
After that, make the necessary adjustments to the docker-compose.yml (i.e.,
adjust your consumption directory).
Build and start the new image with:
B. If you built the image yourself, do the following:
.. code:: shell-session
$ cd /path/to/paperless
$ git pull
$ ./compile-frontend.sh
$ docker-compose build
$ docker-compose up
If you see everything working, you can start paperless-ng with "-d" to have it
run in the background.
.. hint::
You can usually keep your ``docker-compose.env`` file, since this file will
never include mandatory configuration options. However, it is worth checking
out the new version of this file, since it might have new recommendations
on what to configure.
Running `docker-compose up` will also apply any new database migrations.
If you see everything working, press CTRL+C once to gracefully stop paperless.
Then you can start paperless-ng with ``-d`` to have it run in the background.
Updating paperless without docker
=================================
@@ -135,26 +110,19 @@ After grabbing the new release and unpacking the contents, do the following:
This creates a new virtual environment (or uses your existing environment)
and installs all dependencies into it.
3. Collect static files.
.. code:: shell-session
$ cd src
$ pipenv run python3 manage.py collectstatic --clear
You can also use the included ``requirements.txt`` file instead and create the virtual
environment yourself. This file includes exactly the same dependencies.
4. Migrate the database.
3. Migrate the database.
.. code:: shell-session
$ cd src
$ pipenv run python3 manage.py migrate
5. Update translation files.
.. code:: shell-session
$ cd src
$ pipenv run python3 manage.py compilemessages
This might not actually do anything. Not every new paperless version comes with new
database migrations.
Management utilities
####################

View File

@@ -5,6 +5,60 @@
Changelog
*********
paperless-ng 0.9.14
###################
Starting with this version, releases are getting built automatically. This release also comes with changes on how to install and
update paperless.
* Paperless now uses GitHub Actions to make releases and build docker images.
* Docker images are available for amd64, armhf, and aarch64.
* When you pull an image from Docker Hub, Docker will automatically select the correct image for you.
* Changes to docker installations and updates
* The ``-dockerfiles.tar.xz`` release archive is gone. Instead, simply grab the docker files from ``/docker/compose`` in the repository
if you wish to install paperless by pulling from the hub.
* The docker compose files in ``/docker/compose`` were changed to always use the ``latest`` version automatically. In order to do further
updates, simply do a ``docker-compose pull``. The documentation has been updated.
* The docker compose files were changed to restart paperless on system boot only if it was running before shutdown.
* Documentation of the docker-compose files about what they do.
* Changes to bare metal installations and updates
* The release archive is built exactly like before. However, the release now comes with already compiled translation messages and
collected static files. Therefore, the update steps ``compilemessages`` and ``collectstatic`` are now obsolete.
* Other changes
* A new configuration option ``PAPERLESS_IGNORE_DATES`` was added by `jayme-github`_. This can be used to instruct paperless to ignore
certain dates (such as your date of birth) when guessing the date from the document content. This was actually introduced in 0.9.12,
I just forgot to mention it in the changelog.
* The filter drop downs now display selected entries on top of all other entries.
* The PostgreSQL client now supports setting an explicit ``sslmode`` to force encryption of the connection to PostgreSQL.
* The docker images now come with ``jbig2enc``, which is a lossless image encoder for PDF documents and decreases the size of certain
PDF/A documents.
* When using any of the manual matching algorithms, paperless now logs messages about when and why these matching algorithms matched.
* The default settings for parallelization in paperless were adjusted to always leave one CPU core free.
* Added an option to the frontend to choose which method to use for displaying PDF documents.
* Fixes
* An issue with the tika parser not picking up files from the consumption directory was fixed.
* A couple changes to the dark mode and fixes to several other layout issues.
* An issue with the drop downs for correspondents, tags and types not properly supporting filtering with special characters was fixed.
* Fixed an issue with filenames of downloaded files: Dates where off by one day due to timezone issues.
* Searching will continue to work even when the index returns non-existing documents. This resulted in "Document does not exist" errors
before. Instead, a warning is logged, indicating the issue.
* An issue with the consumer crashing when invalid regular expression were used was fixed.
paperless-ng 0.9.13
###################
* Fixed an issue with Paperless not starting due to the new Tika integration when ``USERMAP_UID`` and ``USERMAP_GID`` was used
in the ``docker-compose.env`` file.
paperless-ng 0.9.12
###################

View File

@@ -53,6 +53,12 @@ PAPERLESS_DBPASS=<password>
Defaults to "paperless".
PAPERLESS_DBSSLMODE=<mode>
SSL mode to use when connecting to PostgreSQL.
See `the official documentation about sslmode <https://www.postgresql.org/docs/current/libpq-ssl.html>`_.
Default is ``prefer``.
Paths and folders
#################
@@ -370,8 +376,26 @@ 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 using as much cores as
possible.
with a slight favor towards threads per worker, and leaving at least one core
free for other tasks:
+----------------+---------+---------+
| CPU core count | Workers | Threads |
+----------------+---------+---------+
| 1 | 1 | 1 |
+----------------+---------+---------+
| 2 | 1 | 1 |
+----------------+---------+---------+
| 4 | 1 | 3 |
+----------------+---------+---------+
| 6 | 2 | 2 |
+----------------+---------+---------+
| 8 | 2 | 3 |
+----------------+---------+---------+
| 12 | 3 | 3 |
+----------------+---------+---------+
| 16 | 3 | 5 |
+----------------+---------+---------+
If you only specify PAPERLESS_TASK_WORKERS, paperless will adjust
PAPERLESS_THREADS_PER_WORKER automatically.
@@ -417,6 +441,9 @@ PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=<bool>
E.g. <CONSUMPTION_DIR>/foo/bar/file.pdf will add the tags "foo" and "bar" to
the consumed file. Paperless will create any tags that don't exist yet.
This is useful for sorting documents with certain tags such as ``car`` or
``todo`` prior to consumption. These folders won't be deleted.
PAPERLESS_CONSUMER_RECURSIVE must be enabled for this to work.
Defaults to false.

View File

@@ -25,8 +25,9 @@ This section describes the steps you need to take to start development on paperl
* 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-redis.sh
to use docker to fire up a redis instance.
* 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).
Back end development
====================
@@ -38,7 +39,7 @@ Install the python dependencies by performing ``pipenv install --dev`` in the sr
This will also create a virtual environment, which you can enter with ``pipenv shell`` or
execute one-shot commands in with ``pipenv run``.
In ``src/paperless.conf``, enable debug mode.
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:
@@ -102,18 +103,11 @@ In order to build the front end and serve it as part of django, execute
.. code:: shell-session
$ ng build --prod --output-path ../src/documents/static/frontend/
$ ng build --prod
This will build the front end and put it in a location from which the Django server will serve
it as static content. This way, you can verify that authentication is working.
Making a release
================
Execute the ``make-release.sh <ver>`` script.
This will test and assemble everything and also build and tag a docker image.
Extending Paperless
===================

View File

@@ -52,6 +52,8 @@ out of that folder to use them elsewhere. Here are a couple notes about that.
* PDF documents, PNG images, JPEG images, TIFF images and GIF images are processed with OCR and converted into PDF documents.
* Plain text documents are supported as well and are added verbatim
to paperless.
* With the optional Tika integration enabled (see :ref:`Configuration <configuration-tika>`), Paperless also supports various
Office documents (.docx, .doc, odt, .ppt, .pptx, .odp, .xls, .xlsx, .ods).
Paperless determines the type of a file by inspecting its content. The
file extensions do not matter.
@@ -73,10 +75,8 @@ 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:** There is no docker image for ARM available. If you know how to build
that automatically, I'm all ears. For now, you have to grab the latest release
archive from the project page and build the image yourself. The release comes
with the front end already compiled, so you don't have to do this on the Pi.
**A:** Docker images are available for arm and arm64 hardware, so just follow
the docker-compose instructions, or go the bare metal route.
**Q:** *How do I run this on unRaid?*

0
docs/requirements.txt Normal file
View File

View File

@@ -10,6 +10,9 @@ scanner you use, but sometimes finding a scanner that will write to an FTP,
NFS, or SMB server can be difficult. This page is here to help you find one
that works right for you based on recommendations from other Paperless users.
Physical scanners
=================
+---------+----------------+-----+-----+-----+----------------+
| Brand | Model | Supports | Recommended By |
+---------+----------------+-----+-----+-----+----------------+
@@ -45,3 +48,25 @@ that works right for you based on recommendations from other Paperless users.
.. _REOLDEV: https://github.com/REOLDEV
.. _Skylinar: https://github.com/Skylinar
.. _jonaswinkler: https://github.com/jonaswinkler
Mobile phone software
=====================
You can use your phone to "scan" documents. The regular camera app will work, but may have too low contrast for OCR to work well. Apps specifically for scanning are recommended.
+-------------------+----------------+-----+-----+-----+-------+--------+----------------+
| Name | OS | Supports | Recommended By |
+-------------------+----------------+-----+-----+-----+-------+--------+----------------+
| | | FTP | NFS | SMB | Email | WebDav | |
+===================+================+=====+=====+=====+=======+========+================+
| `Office Lens`_ | Android | ? | ? | ? | ? | ? | `jonaswinkler`_|
+-------------------+----------------+-----+-----+-----+-------+--------+----------------+
| `Genius Scan`_ | Android | yes | no | yes | yes | yes | `hannahswain`_ |
+-------------------+----------------+-----+-----+-----+-------+--------+----------------+
On Android, you can use these applications in combination with one of the :ref:`Paperless-ng compatible apps <usage-mobile_upload>` to "Share" the documents produced by these scanner apps with paperless.
.. _Office Lens: https://play.google.com/store/apps/details?id=com.microsoft.office.officelens
.. _Genius Scan: https://play.google.com/store/apps/details?id=com.thegrizzlylabs.geniusscan.free
.. _hannahswain: https://github.com/hannahswain

View File

@@ -3,35 +3,6 @@
Setup
*****
Download
########
Go to the project page on GitHub and download the
`latest release <https://github.com/jonaswinkler/paperless-ng/releases>`_.
There are multiple options available.
* Download the dockerfiles archive if you want to pull paperless from
Docker Hub.
* Download the dist archive and extract it if you want to build the docker image
yourself or want to install paperless without docker.
.. hint::
In contrast to paperless, the recommended way to get and update paperless-ng
is not to pull the entire git repository. Paperless-ng includes artifacts
that need to be compiled, and that's already done for you in the release.
.. admonition:: Want to try out paperless-ng before migrating?
The release contains a file ``.env`` which sets the docker-compose project
name to "paperless", which is the same as before and instructs docker-compose
to reuse and upgrade your paperless volumes.
Just rename the project name in that file to anything else and docker-compose
will create fresh volumes for you!
Overview of Paperless-ng
########################
@@ -110,22 +81,35 @@ Installation
You can go multiple routes with setting up and running Paperless:
* The `docker route`_
* The `bare metal route`_
* :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 (bare metal) <setup-bare_metal>`
The `docker route`_ is quick & easy. This is the recommended route. This configures all the stuff
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.
The `bare metal route`_ is more complicated to setup but makes it easier
The bare metal route is more 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.
.. _setup-docker_route:
.. _setup-docker_hub:
Docker Route
============
Install Paperless from Docker Hub
=================================
1. Install `Docker`_ and `docker-compose`_. [#compose]_
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
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.
Download the ``docker-compose.env`` file and the ``.env`` file as well and store them
in the same directory.
.. hint::
For new installations, it is recommended to use PostgreSQL as the database
backend.
2. Install `Docker`_ and `docker-compose`_.
.. caution::
@@ -142,15 +126,7 @@ Docker Route
.. _Docker installation guide: https://docs.docker.com/engine/installation/
.. _docker-compose installation guide: https://docs.docker.com/compose/install/
2. Copy either ``docker-compose.sqlite.yml`` or ``docker-compose.postgres.yml`` to
``docker-compose.yml``, depending on which database backend you want to use.
.. hint::
For new installations, it is recommended to use PostgreSQL as the database
backend.
2. Modify ``docker-compose.yml`` to your preferences. You may want to change the path
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
to mount the consumption directory:
@@ -167,7 +143,7 @@ Docker Route
Don't change the part after the colon or paperless wont find your documents.
3. Modify ``docker-compose.env``, following the comments in the file. The
4. 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
both the docker container and you on the host machine have write access
@@ -177,9 +153,9 @@ Docker Route
.. note::
You can use any settings from the file ``paperless.conf`` in this file.
You can use any settings from the file ``paperless.conf.example`` in this file.
Have a look at :ref:`configuration` to see whats available.
.. caution::
Certain file systems such as NFS network shares don't support file system
@@ -188,11 +164,10 @@ Docker Route
with the default configuration. You will need to use ``PAPERLESS_CONSUMER_POLLING``,
which will disable inotify. See :ref:`here <configuration-polling>`.
4. Run ``docker-compose up -d``. This will create and start the necessary
containers. This will also build the image of paperless if you grabbed the
source archive.
5. Run ``docker-compose up -d``. This will create and start the necessary
containers.
5. To be able to login, you will need a super user. To create it, execute the
6. To be able to login, you will need a super user. To create it, execute the
following command:
.. code-block:: shell-session
@@ -202,7 +177,7 @@ Docker Route
This will prompt you to set a username, an optional e-mail address and
finally a password.
6. The default ``docker-compose.yml`` exports the webserver on your local port
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.
@@ -210,11 +185,49 @@ Docker Route
.. _Docker: https://www.docker.com/
.. _docker-compose: https://docs.docker.com/compose/install/
.. [#compose] You of course don't have to use docker-compose, but it
simplifies deployment immensely. If you know your way around Docker, feel
free to tinker around without using compose!
.. _setup-docker_build:
.. _`setup-bare_metal`:
Build the docker image yourself
===============================
1. Clone the entire repository of paperless:
.. code:: shell-session
git clone https://github.com/jonaswinkler/paperless-ng
The master branch always reflects the latest stable version.
2. Copy one of the ``docker/compose/docker-compose.*.yml`` to ``docker-compose.yml`` in the root folder,
depending on which database backend you want to use. Copy
``docker-compose.env`` into the project root as well.
3. In the ``docker-compose.yml`` file, find the line that instructs docker-compose to pull the paperless image from Docker Hub:
.. code:: yaml
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
webserver:
build: .
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
.. code:: shell-session
$ docker-compose build
before that to build the image.
.. _setup-bare_metal:
Bare Metal Route
================
@@ -234,8 +247,9 @@ writing. Windows is not and will never be supported.
* ``optipng`` for optimizing thumbnails
* ``gnupg`` for handling encrypted documents
* ``libpoppler-cpp-dev`` for PDF to text conversion
* ``libmagic-dev`` for mime type detection
* ``libpq-dev`` for PostgreSQL
* ``libmagic-dev`` for mime type detection
* ``mime-support`` for mime type detection
These dependencies are required for OCRmyPDF, which is used for text recognition.
@@ -250,6 +264,11 @@ 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)
On Raspberry Pi, these libraries are required as well:
* ``libatlas-base-dev``
* ``libxslt1-dev``
You will also need ``build-essential``, ``python3-setuptools`` and ``python3-wheel``
for installing some of the python dependencies.
@@ -258,8 +277,9 @@ writing. Windows is not and will never be supported.
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.
4. Get the release archive. If you pull the git repo as it is, you also have to compile the front end by yourself.
Extract the frontend to a place from where you wish to execute it, such as ``/opt/paperless``.
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.
Extract the archive to a place from where you wish to execute it, such as ``/opt/paperless``.
5. Configure paperless. See :ref:`configuration` for details. Edit the included ``paperless.conf`` and adjust the
settings to your needs. Required settings for getting paperless running are:
@@ -272,7 +292,7 @@ writing. Windows is not and will never be supported.
paperless stores its data. If you like, you can point both to the same directory.
* ``PAPERLESS_SECRET_KEY`` should be a random sequence of characters. It's used for authentication. Failure
to do so allows third parties to forge authentication credentials.
Many more adjustments can be made to paperless, especially the OCR part. The following options are recommended
for everyone:
@@ -281,7 +301,7 @@ writing. Windows is not and will never be supported.
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
* ``/opt/paperless/media``
* ``/opt/paperless/data``
* ``/opt/paperless/consume``
@@ -295,14 +315,8 @@ writing. Windows is not and will never be supported.
.. code:: bash
# This collects static files from paperless and django.
python3 manage.py collectstatic --clear --no-input
# This creates the database schema.
python3 manage.py migrate
# This creates the translation files for paperless.
python3 manage.py compilemessages
# This creates your first paperless user
python3 manage.py createsuperuser
@@ -313,7 +327,7 @@ writing. Windows is not and will never be supported.
# This collects static files from paperless and django.
python3 manage.py runserver
and pointing your browser to http://localhost:8000/.
.. warning::
@@ -366,13 +380,18 @@ writing. Windows is not and will never be supported.
.. code::
<policy domain="coder" rights="none" pattern="PDF" />
to
.. code::
<policy domain="coder" rights="read|write" pattern="PDF" />
13. 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.
Migration to paperless-ng
#########################
@@ -406,32 +425,27 @@ 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 or use the archive to build the image yourself.
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::
The release include a ``.env`` file. This will set the
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.
4. Copy the ``docker-compose.sqlite.yml`` file to ``docker-compose.yml``.
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
SQLite database.
5. Adjust ``docker-compose.yml`` and
``docker-compose.env`` to your needs.
See `docker route`_ for details on which edits are advised.
5. Adjust ``docker-compose.yml`` and ``docker-compose.env`` to your needs.
See :ref:`setup-docker_hub` for details on which edits are advised.
6. Since ``docker-compose`` would just use the the old paperless image, we need to
manually build a new image:
.. code:: shell-session
$ docker-compose build
6. :ref:`Update paperless. <administration-updating>`
7. In order to find your existing documents with the new search feature, you need
to invoke a one-time operation that will create the search index:
@@ -439,7 +453,7 @@ Migration to paperless-ng is then performed in a few simple steps:
.. code:: shell-session
$ docker-compose run --rm webserver document_index reindex
This will migrate your database and create the search index. After that,
paperless will take care of maintaining the index by itself.
@@ -452,7 +466,7 @@ Migration to paperless-ng is then performed in a few simple steps:
This will run paperless in the background and automatically start it on system boot.
9. Paperless installed a permanent redirect to ``admin/`` in your browser. This
redirect is still in place and prevents access to the new UI. Clear
redirect is still in place and prevents access to the new UI. Clear your
browsing cache in order to fix this.
10. Optionally, follow the instructions below to migrate your existing data to PostgreSQL.
@@ -500,9 +514,9 @@ management commands as below.
$ cd /path/to/paperless
$ docker-compose run --rm webserver /bin/bash
This will launch the container and initialize the PostgreSQL database.
b) Without docker, open a shell in your virtual environment, switch to
the ``src`` directory and create the database schema:
@@ -512,7 +526,7 @@ management commands as below.
$ pipenv shell
$ cd src
$ python3 manage.py migrate
This will not copy any data yet.
4. Dump your data from SQLite:
@@ -520,7 +534,7 @@ management commands as below.
.. code:: shell-session
$ python3 manage.py dumpdata --database=sqlite --exclude=contenttypes --exclude=auth.Permission > data.json
5. Load your data into PostgreSQL:
.. code:: shell-session
@@ -571,7 +585,7 @@ as well.
Considerations for less powerful devices
########################################
Paperless runs on Raspberry Pi. However, some things are rather slow on the Pi and
Paperless runs on Raspberry Pi. However, some things are rather slow on the Pi and
configuring some options in paperless can help improve performance immensely:
* Stick with SQLite to save some resources.
@@ -593,17 +607,15 @@ configuring some options in paperless can help improve performance immensely:
For details, refer to :ref:`configuration`.
.. note::
Updating the :ref:`automatic matching algorithm <advanced-automatic_matching>`
takes quite a bit of time. However, the update mechanism checks if your
data has changed before doing the heavy lifting. If you experience the
data has changed before doing the heavy lifting. If you experience the
algorithm taking too much cpu time, consider changing the schedule in the
admin interface to daily. You can also manually invoke the task
by changing the date and time of the next run to today/now.
The actual matching of the algorithm is fast and works on Raspberry Pi as
The actual matching of the algorithm is fast and works on Raspberry Pi as
well as on any other device.
.. _redis: https://redis.io/

View File

@@ -75,6 +75,6 @@ You might encounter errors such as:
This happens when paperless does not have permission to delete files inside the consumption directory.
Ensure that ``USERMAP_UID`` and ``USERMAP_GID`` are set to the user id and group id you use on the host operating system, if these are
different from ``1000``. See :ref:`setup-docker_route`.
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.

View File

@@ -110,6 +110,8 @@ The dashboard has a file drop field to upload documents to paperless. Simply dra
onto this field or select a file with the file dialog. Multiple files are supported.
.. _usage-mobile_upload:
Mobile upload
=============
@@ -118,7 +120,7 @@ to share any documents with paperless. This can be combined with any of the mobi
scanning apps out there, such as Office Lens.
Furthermore, there is the `Paperless App <https://github.com/bauerj/paperless_app>`_ as well,
which no only has document upload, but also document editing and browsing.
which not only has document upload, but also document browsing and download features.
.. _usage-email:

View File

@@ -13,6 +13,7 @@
#PAPERLESS_DBNAME=paperless
#PAPERLESS_DBUSER=paperless
#PAPERLESS_DBPASS=paperless
#PAPERLESS_DBSSLMODE=prefer
# Paths and folders

74
requirements.txt Normal file
View File

@@ -0,0 +1,74 @@
#
# These requirements were autogenerated by pipenv
# To regenerate from the project's Pipfile, run:
#
# pipenv lock --requirements
#
-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'
asgiref==3.3.1; python_version >= '3.5'
blessed==1.17.12
certifi==2020.12.5
cffi==1.14.4
chardet==4.0.0; python_version >= '3.1'
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
django-filter==2.4.0
django-picklefield==3.0.1; python_version >= '3'
django-q==1.3.4
django==3.1.5
djangorestframework==3.12.2
filelock==3.0.12
fuzzywuzzy==0.18.0
gunicorn==20.0.4
humanfriendly==9.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
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
img2pdf==0.4.0
importlib-metadata==3.4.0; python_version < '3.8'
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'
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
pathvalidate==2.3.2
pdfminer.six==20201018; python_version >= '3.4'
pdftotext==2.1.5
pikepdf==2.2.5
pillow==8.1.0
pluggy==0.13.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
psycopg2-binary==2.8.6
pycparser==2.20; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
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
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'
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'
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'
watchdog==1.0.2
wcwidth==0.2.5
whitenoise==5.2.0
whoosh==2.7.4
zipp==3.4.0; python_version >= '3.6'

View File

@@ -1,125 +0,0 @@
#!/bin/bash
# Release checklist
# - wait for travis build.
# adjust src/paperless/version.py
# changelog in the documentation
# adjust versions in docker/hub/*
# adjust version in src-ui/src/environments/prod
# If docker-compose was modified: all compose files are the same.
# Steps:
# run release script "dev", push
# if it works: new tag, merge into master
# on master: make release "lastest", push
# on master: make release "version-tag", push
# publish release files
set -e
VERSION=$1
if [ -z "$VERSION" ]
then
echo "Need a version string."
exit 1
fi
# source root directory of paperless
PAPERLESS_ROOT=$(git rev-parse --show-toplevel)
# output directory
PAPERLESS_DIST="$PAPERLESS_ROOT/dist"
PAPERLESS_DIST_APP="$PAPERLESS_DIST/paperless-ng"
PAPERLESS_DIST_DOCKERFILES="$PAPERLESS_DIST/paperless-ng-dockerfiles"
if [ -d "$PAPERLESS_DIST" ]
then
echo "Removing $PAPERLESS_DIST"
rm "$PAPERLESS_DIST" -r
fi
mkdir "$PAPERLESS_DIST"
mkdir "$PAPERLESS_DIST_APP"
mkdir "$PAPERLESS_DIST_APP/docker"
mkdir "$PAPERLESS_DIST_APP/scripts"
mkdir "$PAPERLESS_DIST_DOCKERFILES"
# setup dependencies.
cd "$PAPERLESS_ROOT"
pipenv clean
pipenv install --dev
pipenv lock --keep-outdated -r > "$PAPERLESS_DIST_APP/requirements.txt"
# test if the application works.
cd "$PAPERLESS_ROOT/src"
#pipenv run pytest --cov
#pipenv run pycodestyle
# make the documentation.
cd "$PAPERLESS_ROOT/docs"
make clean html
# copy stuff into place
# the application itself
cp "$PAPERLESS_ROOT/.env" \
"$PAPERLESS_ROOT/.dockerignore" \
"$PAPERLESS_ROOT/CONTRIBUTING.md" \
"$PAPERLESS_ROOT/LICENSE" \
"$PAPERLESS_ROOT/Pipfile" \
"$PAPERLESS_ROOT/Pipfile.lock" \
"$PAPERLESS_ROOT/README.md" "$PAPERLESS_DIST_APP"
cp "$PAPERLESS_ROOT/paperless.conf.example" "$PAPERLESS_DIST_APP/paperless.conf"
# copy python source, templates and static files.
cd "$PAPERLESS_ROOT"
find src -name '*.po' -o -wholename '*/templates/*' -o -wholename '*/static/*' -o -name '*.py' | cpio -pdm "$PAPERLESS_DIST_APP"
# build the front end.
cd "$PAPERLESS_ROOT/src-ui"
ng build --prod --output-hashing none --sourceMap=false --output-path "$PAPERLESS_DIST_APP/src/documents/static/frontend"
# documentation
cp "$PAPERLESS_ROOT/docs/_build/html/" "$PAPERLESS_DIST_APP/docs" -r
# docker files for building the image yourself
cp "$PAPERLESS_ROOT/docker/local/"* "$PAPERLESS_DIST_APP"
cp "$PAPERLESS_ROOT/docker/docker-compose.env" "$PAPERLESS_DIST_APP"
# docker files for pulling from docker hub
cp "$PAPERLESS_ROOT/docker/hub/"* "$PAPERLESS_DIST_DOCKERFILES"
cp "$PAPERLESS_ROOT/.env" "$PAPERLESS_DIST_DOCKERFILES"
cp "$PAPERLESS_ROOT/docker/docker-compose.env" "$PAPERLESS_DIST_DOCKERFILES"
# auxiliary files required for the docker image
cp "$PAPERLESS_ROOT/docker/docker-entrypoint.sh" "$PAPERLESS_DIST_APP/docker/"
cp "$PAPERLESS_ROOT/docker/gunicorn.conf.py" "$PAPERLESS_DIST_APP/docker/"
cp "$PAPERLESS_ROOT/docker/imagemagick-policy.xml" "$PAPERLESS_DIST_APP/docker/"
cp "$PAPERLESS_ROOT/docker/supervisord.conf" "$PAPERLESS_DIST_APP/docker/"
# auxiliary files for bare metal installs
cp "$PAPERLESS_ROOT/scripts/paperless-webserver.service" "$PAPERLESS_DIST_APP/scripts/"
cp "$PAPERLESS_ROOT/scripts/paperless-consumer.service" "$PAPERLESS_DIST_APP/scripts/"
cp "$PAPERLESS_ROOT/scripts/paperless-scheduler.service" "$PAPERLESS_DIST_APP/scripts/"
# try to make the docker build.
cd "$PAPERLESS_DIST_APP"
docker build . -t "jonaswinkler/paperless-ng:$VERSION"
# works. package the app!
cd "$PAPERLESS_DIST"
tar -cJf "paperless-ng-$VERSION.tar.xz" paperless-ng/
tar -cJf "paperless-ng-$VERSION-dockerfiles.tar.xz" paperless-ng-dockerfiles/

View File

@@ -1,23 +0,0 @@
#!/bin/bash
set -e
VERSION=$1
if [ -z "$VERSION" ]
then
echo "Need a version string."
exit 1
fi
# source root directory of paperless
PAPERLESS_ROOT=$(git rev-parse --show-toplevel)
# output directory
PAPERLESS_DIST="$PAPERLESS_ROOT/dist"
PAPERLESS_DIST_APP="$PAPERLESS_DIST/paperless-ng"
cd "$PAPERLESS_DIST_APP"
docker push "jonaswinkler/paperless-ng:$VERSION"

View File

@@ -58,6 +58,7 @@
"with": "src/environments/environment.prod.ts"
}
],
"outputPath": "../src/documents/static/frontend/",
"optimization": true,
"outputHashing": "none",
"sourceMap": false,

View File

@@ -34,28 +34,28 @@
<source>Select none</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">11</context>
<context context-type="linenumber">10</context>
</context-group>
</trans-unit>
<trans-unit id="2ed8a0c2ce4968f8614151eefed20a0aa3daeeb9" datatype="html">
<source>Select page</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">12</context>
<context context-type="linenumber">11</context>
</context-group>
</trans-unit>
<trans-unit id="b463541a7e303aa4d0b1102eaff8afbaf34e7a74" datatype="html">
<source>Select all</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">13</context>
<context context-type="linenumber">12</context>
</context-group>
</trans-unit>
<trans-unit id="ec97f67072a83aaa972536b18d83179f6e4bbec9" datatype="html">
<source>Sort by</source>
<trans-unit id="5d43539fc358c3a548b9d487be821db73e2702ff" datatype="html">
<source>Sort</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">41</context>
<context context-type="linenumber">39</context>
</context-group>
</trans-unit>
<trans-unit id="b7641aed03492978b4ec6843b1e53f30464294d9" datatype="html">
@@ -83,98 +83,98 @@
<source>{VAR_PLURAL, plural, =1 {Selected <x id="INTERPOLATION"/> of one document} other {Selected <x id="INTERPOLATION"/> of <x id="INTERPOLATION_1"/> documents}}</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">87</context>
<context context-type="linenumber">85</context>
</context-group>
</trans-unit>
<trans-unit id="bb773fdeaad5e7fb8e6cd77e1cc558e1b194a0c9" datatype="html">
<source>{VAR_PLURAL, plural, =1 {One document} other {<x id="INTERPOLATION"/> documents}}</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">88</context>
<context context-type="linenumber">86</context>
</context-group>
</trans-unit>
<trans-unit id="bb91083b44e3f77dd68de773ceab467ca3d57507" datatype="html">
<source>(filtered)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">88</context>
<context context-type="linenumber">86</context>
</context-group>
</trans-unit>
<trans-unit id="ca0b795795658155d44ddca02e95f1feeeb4a88f" datatype="html">
<source>ASN</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">107</context>
<context context-type="linenumber">105</context>
</context-group>
</trans-unit>
<trans-unit id="7b5c6286aaded63fb279d6deb8aa8c704e085ced" datatype="html">
<source>Correspondent</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">113</context>
<context context-type="linenumber">111</context>
</context-group>
</trans-unit>
<trans-unit id="fdf7cbdc140d0aab0f0b6c06065a0fd448ed6a2e" datatype="html">
<source>Title</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">119</context>
<context context-type="linenumber">117</context>
</context-group>
</trans-unit>
<trans-unit id="2bd5919e8098513664a89d5b7b52d61e3063950f" datatype="html">
<source>Document type</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">125</context>
<context context-type="linenumber">123</context>
</context-group>
</trans-unit>
<trans-unit id="1b051734b0ee9021991c91b3ed4e81c244322462" datatype="html">
<source>Created</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">131</context>
<context context-type="linenumber">129</context>
</context-group>
</trans-unit>
<trans-unit id="80e3b490720757978c99a7b5af3885faf202b955" datatype="html">
<source>Added</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">137</context>
<context context-type="linenumber">135</context>
</context-group>
</trans-unit>
<trans-unit id="9021887951960049161" datatype="html">
<source>Confirm delete</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">186</context>
<context context-type="linenumber">192</context>
</context-group>
</trans-unit>
<trans-unit id="5382975254277698192" datatype="html">
<source>Do you really want to delete document &quot;<x id="PH" equiv-text="this.document.title"/>&quot;?</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">187</context>
<context context-type="linenumber">193</context>
</context-group>
</trans-unit>
<trans-unit id="6691075929777935948" datatype="html">
<source>The files for this document will be deleted permanently. This operation cannot be undone.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">188</context>
<context context-type="linenumber">194</context>
</context-group>
</trans-unit>
<trans-unit id="719892092227206532" datatype="html">
<source>Delete document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">190</context>
<context context-type="linenumber">196</context>
</context-group>
</trans-unit>
<trans-unit id="1844801255494293730" datatype="html">
<source>Error deleting document: <x id="PH" equiv-text="JSON.stringify(error)"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">197</context>
<context context-type="linenumber">203</context>
</context-group>
</trans-unit>
<trans-unit id="826b25211922a1b46436589233cb6f1a163d89b7" datatype="html">
@@ -475,21 +475,21 @@
<source>Saved view &quot;<x id="PH" equiv-text="savedView.name"/>&quot; deleted.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">54</context>
<context context-type="linenumber">55</context>
</context-group>
</trans-unit>
<trans-unit id="5647210819299459618" datatype="html">
<source>Settings saved successfully.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">74</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit id="8488620293789898901" datatype="html">
<source>Error while storing settings on server: <x id="PH" equiv-text="JSON.stringify(error.error)"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">86</context>
<context context-type="linenumber">80</context>
</context-group>
</trans-unit>
<trans-unit id="121cc5391cd2a5115bc2b3160379ee5b36cd7716" datatype="html">
@@ -510,7 +510,7 @@
<source>Saved views</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">56</context>
<context context-type="linenumber">64</context>
</context-group>
</trans-unit>
<trans-unit id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13" datatype="html">
@@ -527,81 +527,102 @@
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="c4435e56bf0289e78fedc462f1d21fb30b9de55d" datatype="html">
<source>Document editor</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit id="4903e521c9bfd11ce88e7a5575106ef638912e0d" datatype="html">
<source>Use PDF viewer provided by the browser</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
</trans-unit>
<trans-unit id="d7db07023e53f8396d18d375c2b78c25fc81c197" datatype="html">
<source>This is usually faster for displaying large PDF documents, but it might not work on some browsers.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
</trans-unit>
<trans-unit id="9ee5d1cbfd6ee168dae37aaba2b59b50bcabb2ff" datatype="html">
<source>Dark mode</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">33</context>
<context context-type="linenumber">44</context>
</context-group>
</trans-unit>
<trans-unit id="f8cb5506e70fd71fddc9bb71cee18bfff7b29637" datatype="html">
<source>Use system settings</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">36</context>
</context-group>
</trans-unit>
<trans-unit id="3863a86cd9e69a61d143d3daf51df44203df4a82" datatype="html">
<source>Bulk editing</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">44</context>
</context-group>
</trans-unit>
<trans-unit id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586" datatype="html">
<source>Show confirmation dialogs</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">48</context>
</context-group>
</trans-unit>
<trans-unit id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a" datatype="html">
<source>Deleting documents will always ask for confirmation.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">48</context>
</context-group>
</trans-unit>
<trans-unit id="8cfddc13e04f5545ac63f419ef363505d6f78c2e" datatype="html">
<source>Apply on close</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">49</context>
<context context-type="linenumber">47</context>
</context-group>
</trans-unit>
<trans-unit id="8ee474504043fa89821d626e4f3413240fa91b53" datatype="html">
<source>Enable dark mode</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">39</context>
<context context-type="linenumber">48</context>
</context-group>
</trans-unit>
<trans-unit id="3863a86cd9e69a61d143d3daf51df44203df4a82" datatype="html">
<source>Bulk editing</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">52</context>
</context-group>
</trans-unit>
<trans-unit id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586" datatype="html">
<source>Show confirmation dialogs</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a" datatype="html">
<source>Deleting documents will always ask for confirmation.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit id="8cfddc13e04f5545ac63f419ef363505d6f78c2e" datatype="html">
<source>Apply on close</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">57</context>
</context-group>
</trans-unit>
<trans-unit id="8cb90334f5dfd7fc67205085f59381e2a334ccfc" datatype="html">
<source>Appears on</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">68</context>
<context context-type="linenumber">76</context>
</context-group>
</trans-unit>
<trans-unit id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5" datatype="html">
<source>Show on dashboard</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">71</context>
<context context-type="linenumber">79</context>
</context-group>
</trans-unit>
<trans-unit id="541bfc5b123b3f8867fd681eaceefb663a811973" datatype="html">
<source>Show in sidebar</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">75</context>
<context context-type="linenumber">83</context>
</context-group>
</trans-unit>
<trans-unit id="abba764a7a595d04dc8c3b26e04b3780d4fdb540" datatype="html">
<source>No saved views defined.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">85</context>
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit id="ef60a738a565f498b858e903e42bc5ffc3cc1299" datatype="html">
@@ -910,28 +931,28 @@
<source>Filter correspondents</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
<context context-type="linenumber">19</context>
<context context-type="linenumber">20</context>
</context-group>
</trans-unit>
<trans-unit id="0ad509732aaf702b7ea8c771c7809fa84bc85908" datatype="html">
<source>Filter document types</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
<context context-type="linenumber">25</context>
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit id="2d9d55f1b70142ff4597ba32179d16888fd9c6b2" datatype="html">
<source>Reset filters</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
<context context-type="linenumber">47</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit id="7593728289020204896" datatype="html">
<source>Not assigned</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
<context context-type="linenumber">161</context>
<context context-type="linenumber">166</context>
</context-group>
<note priority="1" from="description">Filter drop down element to filter for documents with no correspondent/type/tag assigned</note>
</trans-unit>
@@ -939,7 +960,7 @@
<source>Apply</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
<context context-type="linenumber">28</context>
<context context-type="linenumber">26</context>
</context-group>
</trans-unit>
<trans-unit id="4873149362496451858" datatype="html">
@@ -1002,7 +1023,7 @@
<source>Created: <x id="INTERPOLATION" equiv-text="{{document.created | date}}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
<context context-type="linenumber">65</context>
<context context-type="linenumber">67</context>
</context-group>
</trans-unit>
<trans-unit id="cd6f3fd48957e1fea6545c2b2defc7b2435ebfa8" datatype="html">
@@ -1023,7 +1044,7 @@
<source>Score:</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
<context context-type="linenumber">61</context>
<context context-type="linenumber">62</context>
</context-group>
</trans-unit>
<trans-unit id="2840db547019ce8c76b2cdbe3a1653c5b68b06af" datatype="html">
@@ -1187,21 +1208,21 @@
<source>Select:</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">11</context>
<context context-type="linenumber">10</context>
</context-group>
</trans-unit>
<trans-unit id="dfc3c34e182ea73c5d784ff7c8135f087992dac1" datatype="html">
<source>All</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">21</context>
<context context-type="linenumber">20</context>
</context-group>
</trans-unit>
<trans-unit id="907df6a2b68daecc3c399cf40a764b358bd9fd84" datatype="html">
<source>Edit:</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">28</context>
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit id="27d158b47717ff9305d19866960418c603f19d55" datatype="html">

View File

@@ -6498,7 +6498,8 @@
},
"ini": {
"version": "1.3.5",
"resolved": "",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true
},
"inquirer": {

View File

@@ -58,6 +58,7 @@ import { MetadataCollapseComponent } from './components/document-detail/metadata
import { SelectDialogComponent } from './components/common/select-dialog/select-dialog.component';
import { NgSelectModule } from '@ng-select/ng-select';
import { NumberComponent } from './components/common/input/number/number.component';
import { SafePipe } from './pipes/safe.pipe';
@NgModule({
declarations: [
@@ -106,7 +107,8 @@ import { NumberComponent } from './components/common/input/number/number.compone
DocumentTitlePipe,
MetadataCollapseComponent,
SelectDialogComponent,
NumberComponent
NumberComponent,
SafePipe
],
imports: [
BrowserModule,

View File

@@ -113,7 +113,7 @@
background-color: rgba(0, 0, 0, 0.15);
padding-left: 1.8rem;
border-color: rgba(255, 255, 255, 0.2);
transition: flex 0.3s ease;
transition: all .3s ease, padding-left 0s ease, background-color 0s ease; // Safari requires all
max-width: 600px;
min-width: 300px; // 1/2 max

View File

@@ -1,4 +1,4 @@
<div class="btn-group" ngbDropdown role="group">
<div class="btn-group w-100" ngbDropdown role="group">
<button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="dateBefore || dateAfter ? 'btn-primary' : 'btn-outline-primary'">
{{title}}
</button>

View File

@@ -1,11 +1,9 @@
<div class="btn-group" ngbDropdown role="group" (openChange)="dropdownOpenChange($event)" #dropdown="ngbDropdown">
<div class="btn-group w-100" ngbDropdown role="group" (openChange)="dropdownOpenChange($event)" #dropdown="ngbDropdown">
<button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="!editing && selectionModel.selectionSize() > 0 ? 'btn-primary' : 'btn-outline-primary'">
<div class="d-none d-md-inline">{{title}}</div>
<div class="d-inline-block d-md-none">
<svg class="toolbaricon" fill="currentColor">
<use attr.xlink:href="assets/bootstrap-icons.svg#{{icon}}" />
</svg>
</div>
<svg class="toolbaricon" fill="currentColor">
<use attr.xlink:href="assets/bootstrap-icons.svg#{{icon}}" />
</svg>
<div class="d-none d-sm-inline">&nbsp;{{title}}</div>
<ng-container *ngIf="!editing && selectionModel.selectionSize() > 0">
<div class="badge bg-secondary text-light rounded-pill badge-corner">
{{selectionModel.selectionSize()}}
@@ -20,7 +18,7 @@
</div>
</div>
<div *ngIf="selectionModel.items" class="items">
<ng-container *ngFor="let item of (editing ? selectionModel.itemsSorted : selectionModel.items) | filter: filterText">
<ng-container *ngFor="let item of selectionModel.itemsSorted | filter: filterText">
<app-toggleable-dropdown-button *ngIf="allowSelectNone || item.id" [item]="item" [state]="selectionModel.get(item.id)" (toggle)="selectionModel.toggle(item.id)"></app-toggleable-dropdown-button>
</ng-container>
</div>

View File

@@ -19,8 +19,13 @@ export class FilterableDropdownSelectionModel {
items: MatchingModel[] = []
get itemsSorted(): MatchingModel[] {
// TODO: this is getting called very often
return this.items.sort((a,b) => {
if (this.getNonTemporary(a.id) == ToggleableItemState.NotSelected && this.getNonTemporary(b.id) != ToggleableItemState.NotSelected) {
if (a.id == null && b.id != null) {
return -1
} else if (a.id != null && b.id == null) {
return 1
} else if (this.getNonTemporary(a.id) == ToggleableItemState.NotSelected && this.getNonTemporary(b.id) != ToggleableItemState.NotSelected) {
return 1
} else if (this.getNonTemporary(a.id) != ToggleableItemState.NotSelected && this.getNonTemporary(b.id) == ToggleableItemState.NotSelected) {
return -1

View File

@@ -6,9 +6,11 @@
[style.color]="textColor"
[style.background]="backgroundColor"
[clearable]="allowNull"
[items]="items"
bindLabel="name"
bindValue="id"
(change)="onChange(value)"
(blur)="onTouched()">
<ng-option *ngFor="let i of items" [value]="i.id">{{i.name}}</ng-option>
</ng-select>
<div *ngIf="showPlusButton()" class="input-group-append">

View File

@@ -1,9 +1,9 @@
<div class="row pt-3 pb-1 mb-3 border-bottom align-items-center" >
<div class="row pt-3 pb-3 pb-md-1 mb-3 border-bottom align-items-center">
<div class="col-md text-truncate">
<p class="h2 text-truncate" style="line-height: 1.4">{{title}}</p>
<p *ngIf="subTitle" class="h5 text-truncate" style="line-height: 1.4">{{subTitle}}</p>
</div>
<div class="btn-toolbar col-auto">
<div class="btn-toolbar col col-md-auto">
<ng-content></ng-content>
</div>
</div>

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,8 @@
table {
overflow-wrap: anywhere;
table-layout: fixed;
}
th:first-child {
min-width: 5rem;
width: 25%;
}

View File

@@ -1,5 +1,5 @@
<app-page-header [(title)]="title">
<div class="input-group input-group-sm mr-5" *ngIf="getContentType() == 'application/pdf'">
<div class="input-group input-group-sm mr-5 d-none d-md-flex" *ngIf="getContentType() == 'application/pdf' && !useNativePdfViewer">
<div class="input-group-prepend">
<div class="input-group-text" i18n>Page</div>
</div>
@@ -9,7 +9,7 @@
</div>
</div>
<button type="button" class="btn btn-sm btn-outline-danger mr-2" (click)="delete()">
<button type="button" class="btn btn-sm btn-outline-danger mr-2 ml-auto" (click)="delete()">
<svg class="buttonicon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#trash" />
</svg>&nbsp;<span class="d-none d-lg-inline" i18n>Delete</span>
@@ -134,8 +134,13 @@
</div>
<div class="col-md-6 col-xl-8 mb-3">
<div class="pdf-viewer-container" *ngIf="getContentType() == 'application/pdf'">
<pdf-viewer [src]="previewUrl" [original-size]="false" [show-borders]="true" [show-all]="true" [(page)]="previewCurrentPage" [render-text-mode]="2" (after-load-complete)="pdfPreviewLoaded($event)"></pdf-viewer>
</div>
<ng-container *ngIf="getContentType() == 'application/pdf'">
<div class="preview-sticky pdf-viewer-container" *ngIf="!useNativePdfViewer ; else nativePdfViewer">
<pdf-viewer [src]="previewUrl" [original-size]="false" [show-borders]="true" [show-all]="true" [(page)]="previewCurrentPage" [render-text-mode]="2" (after-load-complete)="pdfPreviewLoaded($event)"></pdf-viewer>
</div>
<ng-template #nativePdfViewer>
<object [data]="previewUrl | safe" type="application/pdf" class="preview-sticky" width="100%"></object>
</ng-template>
</ng-container>
</div>
</div>

View File

@@ -1,6 +1,9 @@
.pdf-viewer-container {
.preview-sticky {
height: calc(100vh - 160px);
top: 70px;
position: sticky;
}
.pdf-viewer-container {
background-color: gray;
}

View File

@@ -18,6 +18,7 @@ import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/do
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';
@Component({
selector: 'app-document-detail',
@@ -70,7 +71,12 @@ export class DocumentDetailComponent implements OnInit {
private openDocumentService: OpenDocumentsService,
private documentListViewService: DocumentListViewService,
private documentTitlePipe: DocumentTitlePipe,
private toastService: ToastService) { }
private toastService: ToastService,
private settings: SettingsService) { }
get useNativePdfViewer(): boolean {
return this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)
}
getContentType() {
return this.metadata?.has_archive_version ? 'application/pdf' : this.metadata?.original_mime_type

View File

@@ -6,8 +6,7 @@
</svg>&nbsp;<ng-container i18n>Cancel</ng-container>
</button>
</div>
<div class="w-100 d-xl-none"></div>
<div class="col-auto mb-2 mb-xl-0" role="group" aria-label="Select">
<div class="col-auto mb-2 mb-xl-0 ml-auto ml-md-0" role="group" aria-label="Select">
<label class="mr-2 mb-0" i18n>Select:</label>
<div class="btn-group">
<button class="btn btn-sm btn-outline-primary" (click)="list.selectPage()">
@@ -56,9 +55,8 @@
</app-filterable-dropdown>
</div>
</div>
<div class="w-100 d-xl-none"></div>
<div class="col mb-2 mb-xl-0 d-flex">
<button type="button" class="btn btn-sm btn-outline-danger ml-0 ml-lg-auto" (click)="applyDelete()">
<div class="col-auto ml-auto mb-2 mb-xl-0 d-flex">
<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" />
</svg>&nbsp;<ng-container i18n>Delete</ng-container>

View File

@@ -31,36 +31,38 @@
</p>
<div class="d-flex align-items-center">
<div class="d-flex flex-column flex-md-row align-items-md-center">
<div class="btn-group">
<a routerLink="/search" [queryParams]="{'more_like': document.id}" class="btn btn-sm btn-outline-secondary" *ngIf="moreLikeThis">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-three-dots" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M3 9.5a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm5 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"/>
</svg>&nbsp;<ng-container i18n>More like this</ng-container>
</svg>&nbsp;<span class="d-block d-md-inline" i18n>More like this</span>
</a>
<a routerLink="/documents/{{document.id}}" class="btn btn-sm btn-outline-secondary">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5L13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175l-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/>
</svg>&nbsp;<ng-container i18n>Edit</ng-container>
</svg>&nbsp;<span class="d-block d-md-inline" i18n>Edit</span>
</a>
<a type="button" class="btn btn-sm btn-outline-secondary" [href]="getPreviewUrl()">
<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>&nbsp;<ng-container i18n>View</ng-container>
</svg>&nbsp;<span class="d-block d-md-inline" i18n>View</span>
</a>
<a type="button" class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">
<a class="btn btn-sm btn-outline-secondary" [href]="getDownloadUrl()">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-download" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
<path fill-rule="evenodd" d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
</svg>&nbsp;<ng-container i18n>Download</ng-container>
</svg>&nbsp;<span class="d-block d-md-inline" i18n>Download</span>
</a>
</div>
<small *ngIf="searchScore" class="text-muted ml-auto" i18n>Score:</small>
<div *ngIf="searchScore" class="d-flex align-items-center ml-md-auto mt-2 mt-md-0">
<small class="text-muted" i18n>Score:</small>
<ngb-progressbar *ngIf="searchScore" [type]="searchScoreClass" [value]="searchScore" class="search-score-bar mx-2" [max]="1"></ngb-progressbar>
<ngb-progressbar [type]="searchScoreClass" [value]="searchScore" class="search-score-bar mx-2" [max]="1"></ngb-progressbar>
</div>
<small class="text-muted" [class.ml-auto]="!searchScore" i18n>Created: {{document.created | date}}</small>
</div>

View File

@@ -12,10 +12,14 @@
mix-blend-mode: multiply;
}
.card-title {
word-break: break-word;
}
.search-score-bar {
width: 100px;
height: 5px;
margin-top: 2px;
margin-top: 1px;
}
.document-card-check {

View File

@@ -1,11 +1,10 @@
<app-page-header [title]="getTitle()">
<div ngbDropdown class="d-inline-block mr-2">
<button class="btn btn-sm btn-outline-primary" id="dropdownSelect" ngbDropdownToggle>
<div ngbDropdown class="mr-2 flex-fill d-flex">
<button class="btn btn-sm btn-outline-primary flex-fill" id="dropdownSelect" ngbDropdownToggle>
<svg class="toolbaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#text-indent-left" />
</svg>&nbsp;<ng-container i18n>Select</ng-container>
</button>
<div ngbDropdownMenu aria-labelledby="dropdownSelect" class="shadow">
<button ngbDropdownItem (click)="list.selectNone()" i18n>Select none</button>
@@ -14,7 +13,7 @@
</div>
</div>
<div class="btn-group btn-group-toggle" ngbRadioGroup [(ngModel)]="displayMode"
<div class="btn-group btn-group-toggle flex-fill" ngbRadioGroup [(ngModel)]="displayMode"
(ngModelChange)="saveDisplayMode()">
<label ngbButtonLabel class="btn-outline-primary btn-sm">
<input ngbButton type="radio" class="btn btn-sm" value="details">
@@ -36,43 +35,42 @@
</label>
</div>
<div class="btn-group btn-group-toggle ml-2" ngbRadioGroup [(ngModel)]="list.sortReverse">
<div ngbDropdown class="btn-group">
<button class="btn btn-outline-primary btn-sm" id="dropdownBasic1" ngbDropdownToggle i18n>Sort by</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1" class="shadow">
<div ngbDropdown class="btn-group ml-2 flex-fill">
<button class="btn btn-outline-primary btn-sm" id="dropdownBasic1" ngbDropdownToggle i18n>Sort</button>
<div ngbDropdownMenu aria-labelledby="dropdownBasic1" class="shadow dropdown-menu-right">
<div class="w-100 d-flex btn-group-toggle pb-2 mb-1 border-bottom" ngbRadioGroup [(ngModel)]="list.sortReverse">
<label ngbButtonLabel class="btn-outline-primary btn-sm mx-2 flex-fill">
<input ngbButton type="radio" class="btn btn-sm" [value]="false">
<svg class="toolbaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-down" />
</svg>
</label>
<label ngbButtonLabel class="btn-outline-primary btn-sm mr-2 flex-fill">
<input ngbButton type="radio" class="btn btn-sm" [value]="true">
<svg class="toolbaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-up-alt" />
</svg>
</label>
</div>
<div>
<button *ngFor="let f of getSortFields()" ngbDropdownItem (click)="list.sortField = f.field"
[class.active]="list.sortField == f.field">{{f.name}}</button>
[class.active]="list.sortField == f.field">{{f.name}}
</button>
</div>
</div>
<label ngbButtonLabel class="btn-outline-primary btn-sm">
<input ngbButton type="radio" class="btn btn-sm" [value]="false">
<svg class="toolbaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-down" />
</svg>
</label>
<label ngbButtonLabel class="btn-outline-primary btn-sm">
<input ngbButton type="radio" class="btn btn-sm" [value]="true">
<svg class="toolbaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#sort-alpha-up-alt" />
</svg>
</label>
</div>
<div class="btn-group ml-2">
<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">
<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>
<div class="btn-group" ngbDropdown role="group">
<button class="btn btn-sm btn-outline-primary dropdown-toggle" ngbDropdownToggle i18n>Views</button>
<div class="dropdown-menu shadow" ngbDropdownMenu>
<ng-container *ngIf="!list.savedViewId">
<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)="saveViewConfigAs()" i18n>Save as...</button>
</div>
<button ngbDropdownItem (click)="saveViewConfig()" *ngIf="list.savedViewId" i18n>Save "{{list.savedViewTitle}}"</button>
<button ngbDropdownItem (click)="saveViewConfigAs()" i18n>Save as...</button>
</div>
</div>
</app-page-header>

View File

@@ -25,3 +25,8 @@ $paperless-card-breakpoints: (
}
}
}
.dropdown-menu-right {
right: 0 !important;
left: auto !important;
}

View File

@@ -1,33 +1,36 @@
<div class="row">
<div class="col mb-2 mb-xl-0">
<div class="form-inline d-flex">
<label class="text-muted mr-2" i18n>Filter by:</label>
<input class="form-control form-control-sm flex-grow-1" type="text" [(ngModel)]="titleFilter" placeholder="Title" i18n-placeholder>
<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>
</div>
<div class="w-100 d-xl-none"></div>
<div class="col col-xl-auto mb-2 mb-xl-0">
<div class="d-flex">
<app-filterable-dropdown class="mr-2 mr-md-3" title="Tags" icon="tag-fill" i18n-title
<app-filterable-dropdown class="mr-2 flex-fill" title="Tags" icon="tag-fill" i18n-title
filterPlaceholder="Filter tags" i18n-filterPlaceholder
[items]="tags"
[(selectionModel)]="tagSelectionModel"
(selectionModelChange)="updateRules()"
[multiple]="true"
(open)="onTagsDropdownOpen()"
[allowSelectNone]="true"></app-filterable-dropdown>
<app-filterable-dropdown class="mr-2 mr-md-3" title="Correspondent" icon="person-fill" i18n-title
<app-filterable-dropdown class="mr-2 flex-fill" title="Correspondent" icon="person-fill" i18n-title
filterPlaceholder="Filter correspondents" i18n-filterPlaceholder
[items]="correspondents"
[(selectionModel)]="correspondentSelectionModel"
(selectionModelChange)="updateRules()"
(open)="onCorrespondentDropdownOpen()"
[allowSelectNone]="true"></app-filterable-dropdown>
<app-filterable-dropdown class="mr-2 mr-md-3" title="Document type" icon="file-earmark-fill" i18n-title
<app-filterable-dropdown class="mr-2 flex-fill" title="Document type" icon="file-earmark-fill" i18n-title
filterPlaceholder="Filter document types" i18n-filterPlaceholder
[items]="documentTypes"
[(selectionModel)]="documentTypeSelectionModel"
(open)="onDocumentTypeDropdownOpen()"
(selectionModelChange)="updateRules()"
[allowSelectNone]="true"></app-filterable-dropdown>
<app-date-dropdown class="mr-2 mr-md-3"
<app-date-dropdown class="mr-2"
title="Created" i18n-title
(datesSet)="updateRules()"
[(dateBefore)]="dateCreatedBefore"

View File

@@ -210,4 +210,15 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.documentTypeSelectionModel.toggle(documentTypeId)
}
onTagsDropdownOpen() {
this.tagSelectionModel.apply()
}
onCorrespondentDropdownOpen() {
this.correspondentSelectionModel.apply()
}
onDocumentTypeDropdownOpen() {
this.documentTypeSelectionModel.apply()
}
}

View File

@@ -6,7 +6,7 @@
<ngb-pagination [pageSize]="25" [collectionSize]="collectionSize" [(page)]="page" (pageChange)="reloadData()" aria-label="Default pagination"></ngb-pagination>
</div>
<table class="table table-striped border shadow">
<table class="table table-striped border shadow-sm">
<thead>
<tr>
<th scope="col" sortable="name" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Name</th>

View File

@@ -7,7 +7,7 @@
aria-label="Default pagination"></ngb-pagination>
</div>
<table class="table table-striped border shadow">
<table class="table table-striped border shadow-sm">
<thead>
<tr>
<th scope="col" sortable="name" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Name</th>

View File

@@ -5,8 +5,8 @@
<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)"
@@ -16,12 +16,12 @@
</app-page-header>
<div class="bg-dark p-3 mb-3" infiniteScroll (scrolled)="onScroll()">
<div class="bg-dark p-3 mb-3 text-light text-monospace" infiniteScroll (scrolled)="onScroll()">
<p
class="text-light text-monospace m-0 p-0 log-entry-{{log.level}}"
class="m-0 p-0 log-entry-{{log.level}}"
*ngFor="let log of logs">
{{log.created | date:'short'}}
{{getLevelText(log.level)}}
{{log.message}}
</p>
</div>
</div>

View File

@@ -28,16 +28,24 @@
</div>
</div>
<div class="form-row form-group">
<div class="col-md-3 col-form-label">
<span i18n>Document editor</span>
</div>
<div class="col">
<app-input-check i18n-title title="Use PDF viewer provided by the browser" i18n-hint hint="This is usually faster for displaying large PDF documents, but it might not work on some browsers." formControlName="useNativePdfViewer"></app-input-check>
</div>
</div>
<div class="form-row form-group">
<div class="col-md-3 col-form-label">
<span i18n>Dark mode</span>
</div>
<div class="col">
<app-input-check i18n-title title="Use system settings" formControlName="darkModeUseSystem" (change)="toggleDarkModeSetting()"></app-input-check>
<div class="custom-control custom-switch" *ngIf="!settingsForm.value.darkModeUseSystem">
<input type="checkbox" class="custom-control-input" id="darkModeEnabled" formControlName="darkModeEnabled" [checked]="settingsForm.value.darkModeEnabled">
<label class="custom-control-label" for="darkModeEnabled" i18n>Enable dark mode</label>
</div>
<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>
</div>
</div>

View File

@@ -21,6 +21,7 @@ export class SettingsComponent implements OnInit {
'documentListItemPerPage': new FormControl(this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)),
'darkModeUseSystem': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM)),
'darkModeEnabled': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED)),
'useNativePdfViewer': new FormControl(this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)),
'savedViews': this.savedViewGroup
})
@@ -55,20 +56,13 @@ export class SettingsComponent implements OnInit {
})
}
toggleDarkModeSetting() {
if (this.settingsForm.value.darkModeUseSystem) {
(this.settingsForm.controls.darkModeEnabled as FormControl).disable()
} else {
(this.settingsForm.controls.darkModeEnabled as FormControl).enable()
}
}
private saveLocalSettings() {
this.settings.set(SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, this.settingsForm.value.bulkEditApplyOnClose)
this.settings.set(SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS, this.settingsForm.value.bulkEditConfirmationDialogs)
this.settings.set(SETTINGS_KEYS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage)
this.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem)
this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString())
this.settings.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, this.settingsForm.value.useNativePdfViewer)
this.documentListViewService.updatePageSize()
this.settings.updateDarkModeSettings()
this.toastService.showInfo($localize`Settings saved successfully.`)

View File

@@ -16,11 +16,13 @@
<div *ngIf="!errorMessage" [class.result-content-searching]="searching" infiniteScroll (scrolled)="onScroll()">
<p i18n>{resultCount, plural, =0 {No results} =1 {One result} other {{{resultCount}} results}}</p>
<app-document-card-large *ngFor="let result of results"
[document]="result.document"
[details]="result.highlights"
[searchScore]="result.score / maxScore"
[moreLikeThis]="true">
<ng-container *ngFor="let result of results">
<app-document-card-large *ngIf="result.document"
[document]="result.document"
[details]="result.highlights"
[searchScore]="result.score / maxScore"
[moreLikeThis]="true">
</app-document-card-large>
</ng-container>
</app-document-card-large>
</div>

View File

@@ -22,37 +22,36 @@ export const FILTER_ASN_ISNULL = 18
export const FILTER_RULE_TYPES: FilterRuleType[] = [
{id: FILTER_TITLE, name: "Title contains", filtervar: "title__icontains", datatype: "string", multi: false, default: ""},
{id: FILTER_CONTENT, name: "Content contains", filtervar: "content__icontains", datatype: "string", multi: false, default: ""},
{id: FILTER_TITLE, filtervar: "title__icontains", datatype: "string", multi: false, default: ""},
{id: FILTER_CONTENT, filtervar: "content__icontains", datatype: "string", multi: false, default: ""},
{id: FILTER_ASN, name: "ASN is", filtervar: "archive_serial_number", datatype: "number", multi: false},
{id: FILTER_ASN, filtervar: "archive_serial_number", datatype: "number", multi: false},
{id: FILTER_CORRESPONDENT, name: "Correspondent is", filtervar: "correspondent__id", isnull_filtervar: "correspondent__isnull", datatype: "correspondent", multi: false},
{id: FILTER_DOCUMENT_TYPE, name: "Document type is", filtervar: "document_type__id", isnull_filtervar: "document_type__isnull", datatype: "document_type", multi: false},
{id: FILTER_CORRESPONDENT, filtervar: "correspondent__id", isnull_filtervar: "correspondent__isnull", datatype: "correspondent", multi: false},
{id: FILTER_DOCUMENT_TYPE, filtervar: "document_type__id", isnull_filtervar: "document_type__isnull", datatype: "document_type", multi: false},
{id: FILTER_IS_IN_INBOX, name: "Is in Inbox", filtervar: "is_in_inbox", datatype: "boolean", multi: false, default: true},
{id: FILTER_HAS_TAG, name: "Has tag", filtervar: "tags__id__all", datatype: "tag", multi: true},
{id: FILTER_DOES_NOT_HAVE_TAG, name: "Does not have tag", filtervar: "tags__id__none", datatype: "tag", multi: true},
{id: FILTER_HAS_ANY_TAG, name: "Has any tag", filtervar: "is_tagged", datatype: "boolean", multi: false, default: true},
{id: FILTER_IS_IN_INBOX, filtervar: "is_in_inbox", datatype: "boolean", multi: false, default: true},
{id: FILTER_HAS_TAG, filtervar: "tags__id__all", datatype: "tag", multi: true},
{id: FILTER_DOES_NOT_HAVE_TAG, filtervar: "tags__id__none", datatype: "tag", multi: true},
{id: FILTER_HAS_ANY_TAG, filtervar: "is_tagged", datatype: "boolean", multi: false, default: true},
{id: FILTER_CREATED_BEFORE, name: "Created before", filtervar: "created__date__lt", datatype: "date", multi: false},
{id: FILTER_CREATED_AFTER, name: "Created after", filtervar: "created__date__gt", datatype: "date", multi: false},
{id: FILTER_CREATED_BEFORE, filtervar: "created__date__lt", datatype: "date", multi: false},
{id: FILTER_CREATED_AFTER, filtervar: "created__date__gt", datatype: "date", multi: false},
{id: FILTER_CREATED_YEAR, name: "Year created is", filtervar: "created__year", datatype: "number", multi: false},
{id: FILTER_CREATED_MONTH, name: "Month created is", filtervar: "created__month", datatype: "number", multi: false},
{id: FILTER_CREATED_DAY, name: "Day created is", filtervar: "created__day", datatype: "number", multi: false},
{id: FILTER_CREATED_YEAR, filtervar: "created__year", datatype: "number", multi: false},
{id: FILTER_CREATED_MONTH, filtervar: "created__month", datatype: "number", multi: false},
{id: FILTER_CREATED_DAY, filtervar: "created__day", datatype: "number", multi: false},
{id: FILTER_ADDED_BEFORE, name: "Added before", filtervar: "added__date__lt", datatype: "date", multi: false},
{id: FILTER_ADDED_AFTER, name: "Added after", filtervar: "added__date__gt", datatype: "date", multi: false},
{id: FILTER_ADDED_BEFORE, filtervar: "added__date__lt", datatype: "date", multi: false},
{id: FILTER_ADDED_AFTER, filtervar: "added__date__gt", datatype: "date", multi: false},
{id: FILTER_MODIFIED_BEFORE, name: "Modified before", filtervar: "modified__date__lt", datatype: "date", multi: false},
{id: FILTER_MODIFIED_AFTER, name: "Modified after", filtervar: "modified__date__gt", datatype: "date", multi: false},
{id: FILTER_ASN_ISNULL, name: "ASN is null", filtervar: "archive_serial_number__isnull", datatype: "boolean", multi: false}
{id: FILTER_MODIFIED_BEFORE, filtervar: "modified__date__lt", datatype: "date", multi: false},
{id: FILTER_MODIFIED_AFTER, filtervar: "modified__date__gt", datatype: "date", multi: false},
{id: FILTER_ASN_ISNULL, filtervar: "archive_serial_number__isnull", datatype: "boolean", multi: false}
]
export interface FilterRuleType {
id: number
name: string
filtervar: string
isnull_filtervar?: string
datatype: string //number, string, boolean, date

View File

@@ -0,0 +1,8 @@
import { SafePipe } from './safe.pipe';
describe('SafePipe', () => {
it('create an instance', () => {
const pipe = new SafePipe();
expect(pipe).toBeTruthy();
});
});

View File

@@ -0,0 +1,19 @@
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
@Pipe({
name: 'safe'
})
export class SafePipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(url) {
if (url == null) {
return this.sanitizer.bypassSecurityTrustResourceUrl("")
} else {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}
}

View File

@@ -28,7 +28,11 @@ export class SearchService {
}
return this.http.get<SearchResult>(`${environment.apiBaseUrl}search/`, {params: httpParams}).pipe(
map(result => {
result.results.forEach(hit => this.documentService.addObservablesToDocument(hit.document))
result.results.forEach(hit => {
if (hit.document) {
this.documentService.addObservablesToDocument(hit.document)
}
})
return result
})
)

View File

@@ -12,7 +12,8 @@ export const SETTINGS_KEYS = {
BULK_EDIT_APPLY_ON_CLOSE: 'general-settings:bulk-edit:apply-on-close',
DOCUMENT_LIST_SIZE: 'general-settings:documentListSize',
DARK_MODE_USE_SYSTEM: 'general-settings:dark-mode:use-system',
DARK_MODE_ENABLED: 'general-settings:dark-mode:enabled'
DARK_MODE_ENABLED: 'general-settings:dark-mode:enabled',
USE_NATIVE_PDF_VIEWER: 'general-settings:document-details:native-pdf-viewer'
}
const SETTINGS: PaperlessSettings[] = [
@@ -20,7 +21,8 @@ const SETTINGS: PaperlessSettings[] = [
{key: SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE, type: "boolean", default: false},
{key: SETTINGS_KEYS.DOCUMENT_LIST_SIZE, type: "number", default: 50},
{key: SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, type: "boolean", default: true},
{key: SETTINGS_KEYS.DARK_MODE_ENABLED, type: "boolean", default: false}
{key: SETTINGS_KEYS.DARK_MODE_ENABLED, type: "boolean", default: false},
{key: SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, type: "boolean", default: false}
]
@Injectable({

View File

@@ -2,5 +2,5 @@ export const environment = {
production: true,
apiBaseUrl: "/api/",
appTitle: "Paperless-ng",
version: "0.9.12"
version: "0.9.14"
};

View File

@@ -38,7 +38,7 @@
<target>Nichts auswählen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">11</context>
<context context-type="linenumber">10</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2ed8a0c2ce4968f8614151eefed20a0aa3daeeb9">
@@ -46,7 +46,7 @@
<target>Seite auswählen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">12</context>
<context context-type="linenumber">11</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="b463541a7e303aa4d0b1102eaff8afbaf34e7a74">
@@ -54,15 +54,15 @@
<target>Alles auswählen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">13</context>
<context context-type="linenumber">12</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="ec97f67072a83aaa972536b18d83179f6e4bbec9">
<source>Sort by</source>
<trans-unit datatype="html" id="5d43539fc358c3a548b9d487be821db73e2702ff">
<source>Sort</source>
<target>Sortieren</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">41</context>
<context context-type="linenumber">39</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="b7641aed03492978b4ec6843b1e53f30464294d9">
@@ -94,7 +94,7 @@
<target>{VAR_PLURAL, plural, =1 { <x id="INTERPOLATION"/> von 1 Dokumente ausgewählt} other {<x id="INTERPOLATION"/> von <x id="INTERPOLATION_1"/> Dokumente ausgewählt}}</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">87</context>
<context context-type="linenumber">85</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="bb773fdeaad5e7fb8e6cd77e1cc558e1b194a0c9">
@@ -102,7 +102,7 @@
<target>{VAR_PLURAL, plural, =1 {Ein Dokument} other {<x id="INTERPOLATION"/> Dokumente}}</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">88</context>
<context context-type="linenumber">86</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="bb91083b44e3f77dd68de773ceab467ca3d57507">
@@ -110,7 +110,7 @@
<target>(gefiltert)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">88</context>
<context context-type="linenumber">86</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="ca0b795795658155d44ddca02e95f1feeeb4a88f">
@@ -118,7 +118,7 @@
<target>ASN</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">107</context>
<context context-type="linenumber">105</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="7b5c6286aaded63fb279d6deb8aa8c704e085ced">
@@ -126,7 +126,7 @@
<target>Korrespondent</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">113</context>
<context context-type="linenumber">111</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="fdf7cbdc140d0aab0f0b6c06065a0fd448ed6a2e">
@@ -134,7 +134,7 @@
<target>Titel</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">119</context>
<context context-type="linenumber">117</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2bd5919e8098513664a89d5b7b52d61e3063950f">
@@ -142,7 +142,7 @@
<target>Dokumenttyp</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">125</context>
<context context-type="linenumber">123</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="1b051734b0ee9021991c91b3ed4e81c244322462">
@@ -150,7 +150,7 @@
<target>Erstellt</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">131</context>
<context context-type="linenumber">129</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="80e3b490720757978c99a7b5af3885faf202b955">
@@ -158,7 +158,7 @@
<target>Hinzugefügt</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">137</context>
<context context-type="linenumber">135</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="9021887951960049161">
@@ -166,7 +166,7 @@
<target>Löschen bestätigen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">186</context>
<context context-type="linenumber">192</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="5382975254277698192">
@@ -174,7 +174,7 @@
<target>Möchten Sie das Dokument &quot;<x equiv-text="this.document.title" id="PH"/>&quot; wirklich löschen?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">187</context>
<context context-type="linenumber">193</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6691075929777935948">
@@ -182,7 +182,7 @@
<target>Die Dateien dieses Dokuments werden permanent gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">188</context>
<context context-type="linenumber">194</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="719892092227206532">
@@ -190,7 +190,7 @@
<target>Dokument löschen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">190</context>
<context context-type="linenumber">196</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="1844801255494293730">
@@ -198,7 +198,7 @@
<target>Fehler beim Löschen des Dokuments: <x equiv-text="JSON.stringify(error)" id="PH"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">197</context>
<context context-type="linenumber">203</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="826b25211922a1b46436589233cb6f1a163d89b7">
@@ -542,7 +542,7 @@
<target>Gespeicherte Ansicht &quot;<x equiv-text="savedView.name" id="PH"/>&quot; gelöscht.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">54</context>
<context context-type="linenumber">55</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="5647210819299459618">
@@ -550,7 +550,7 @@
<target>Einstellungen erfolgreich gespeichert.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">74</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8488620293789898901">
@@ -558,7 +558,7 @@
<target>Fehler beim Speichern der Einstellungen auf dem Server: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">86</context>
<context context-type="linenumber">80</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
@@ -582,7 +582,7 @@
<target>Gespeicherte Ansichten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">56</context>
<context context-type="linenumber">64</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13">
@@ -601,12 +601,36 @@
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="c4435e56bf0289e78fedc462f1d21fb30b9de55d">
<source>Document editor</source>
<target>Dokumenteditor</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4903e521c9bfd11ce88e7a5575106ef638912e0d">
<source>Use PDF viewer provided by the browser</source>
<target>Benutze PDF-Betrachter des Web Browsers</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="d7db07023e53f8396d18d375c2b78c25fc81c197">
<source>This is usually faster for displaying large PDF documents, but it might not work on some browsers.</source>
<target>Der integrierte PDF-Betrachter des Web-Browsers ist in der Regel schneller bei der Anzeige besonders großer Dokumente, funktioniert aber nicht in allen Browsern.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="9ee5d1cbfd6ee168dae37aaba2b59b50bcabb2ff">
<source>Dark mode</source>
<target>Dunkler Modus</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">33</context>
<context context-type="linenumber">44</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="f8cb5506e70fd71fddc9bb71cee18bfff7b29637">
@@ -614,39 +638,7 @@
<target>Benutze Systemeinstellungen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">36</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82">
<source>Bulk editing</source>
<target>Massenbearbeitung</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">44</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586">
<source>Show confirmation dialogs</source>
<target>Bestätigungsdialoge anzeigen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">48</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a">
<source>Deleting documents will always ask for confirmation.</source>
<target>Beim Löschen von Dokumenten wird immer nach einer Bestätigung gefragt.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">48</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e">
<source>Apply on close</source>
<target>Anwenden beim Schließen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">49</context>
<context context-type="linenumber">47</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8ee474504043fa89821d626e4f3413240fa91b53">
@@ -654,7 +646,39 @@
<target>Dunklen Modus aktivieren</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">39</context>
<context context-type="linenumber">48</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82">
<source>Bulk editing</source>
<target>Massenbearbeitung</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">52</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586">
<source>Show confirmation dialogs</source>
<target>Bestätigungsdialoge anzeigen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a">
<source>Deleting documents will always ask for confirmation.</source>
<target>Beim Löschen von Dokumenten wird immer nach einer Bestätigung gefragt.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">56</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e">
<source>Apply on close</source>
<target>Anwenden beim Schließen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">57</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="8cb90334f5dfd7fc67205085f59381e2a334ccfc">
@@ -662,7 +686,7 @@
<target>Erscheint auf</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">68</context>
<context context-type="linenumber">76</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5">
@@ -670,7 +694,7 @@
<target>Auf Startseite zeigen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">71</context>
<context context-type="linenumber">79</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="541bfc5b123b3f8867fd681eaceefb663a811973">
@@ -678,7 +702,7 @@
<target>In Seitenleiste zeigen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">75</context>
<context context-type="linenumber">83</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="abba764a7a595d04dc8c3b26e04b3780d4fdb540">
@@ -686,7 +710,7 @@
<target>Keine gespeicherten Ansichten vorhanden.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">85</context>
<context context-type="linenumber">93</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="ef60a738a565f498b858e903e42bc5ffc3cc1299">
@@ -1039,7 +1063,7 @@
<target>Korrespondenten filtern</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
<context context-type="linenumber">19</context>
<context context-type="linenumber">20</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="0ad509732aaf702b7ea8c771c7809fa84bc85908">
@@ -1047,7 +1071,7 @@
<target>Dokumenttypen filtern</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
<context context-type="linenumber">25</context>
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2d9d55f1b70142ff4597ba32179d16888fd9c6b2">
@@ -1055,7 +1079,7 @@
<target>Filter zurücksetzen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
<context context-type="linenumber">47</context>
<context context-type="linenumber">50</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="7593728289020204896">
@@ -1063,7 +1087,7 @@
<target>Nicht zugewiesen</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
<context context-type="linenumber">161</context>
<context context-type="linenumber">166</context>
</context-group>
<note from="description" priority="1">Filter drop down element to filter for documents with no correspondent/type/tag assigned</note>
</trans-unit>
@@ -1072,7 +1096,7 @@
<target>Anwenden</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
<context context-type="linenumber">28</context>
<context context-type="linenumber">26</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="4873149362496451858">
@@ -1144,7 +1168,7 @@
<target>Erstellt: <x equiv-text="{{document.created | date}}" id="INTERPOLATION"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
<context context-type="linenumber">65</context>
<context context-type="linenumber">67</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="cd6f3fd48957e1fea6545c2b2defc7b2435ebfa8">
@@ -1168,7 +1192,7 @@
<target>Relevanz:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
<context context-type="linenumber">61</context>
<context context-type="linenumber">62</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="2840db547019ce8c76b2cdbe3a1653c5b68b06af">
@@ -1355,7 +1379,7 @@
<target>Auswählen:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">11</context>
<context context-type="linenumber">10</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="dfc3c34e182ea73c5d784ff7c8135f087992dac1">
@@ -1363,7 +1387,7 @@
<target>Alle</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">21</context>
<context context-type="linenumber">20</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="907df6a2b68daecc3c399cf40a764b358bd9fd84">
@@ -1371,7 +1395,7 @@
<target>Bearbeiten:</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">28</context>
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit datatype="html" id="27d158b47717ff9305d19866960418c603f19d55">

View File

@@ -11,7 +11,7 @@
</trans-unit>
<trans-unit datatype="html" id="2155249406916744630">
<source>View &quot;<x equiv-text="this.list.savedView.name" id="PH"/>&quot; saved successfully.</source>
<target>Vue &amp;quot;<x equiv-text="this.list.savedView.name" id="PH"/>&amp;quot; enregistrée avec succès.</target>
<target>Vue &quot;<x equiv-text="this.list.savedView.name" id="PH"/>&quot; enregistrée avec succès.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">94</context>
@@ -19,7 +19,7 @@
</trans-unit>
<trans-unit datatype="html" id="6837554170707123455">
<source>View &quot;<x equiv-text="savedView.name" id="PH"/>&quot; created successfully.</source>
<target>Vue &amp;quot;<x equiv-text="savedView.name" id="PH"/>&amp;quot; créée avec succès.</target>
<target>Vue &quot;<x equiv-text="savedView.name" id="PH"/>&quot; créée avec succès.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
<context context-type="linenumber">115</context>
@@ -83,7 +83,7 @@
</trans-unit>
<trans-unit datatype="html" id="72e7d343f9165602cce1ca7faffbc565fd31ef92">
<source>Save &quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&quot;</source>
<target>Enregistrer &amp;quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&amp;quot;</target>
<target>Enregistrer &quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&quot;</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">71</context>
@@ -171,7 +171,7 @@
</trans-unit>
<trans-unit datatype="html" id="5382975254277698192">
<source>Do you really want to delete document &quot;<x equiv-text="this.document.title" id="PH"/>&quot;?</source>
<target>Voulez-vous vraiment supprimer le document &amp;quot;<x equiv-text="this.document.title" id="PH"/>&amp;quot; ?</target>
<target>Voulez-vous vraiment supprimer le document &quot;<x equiv-text="this.document.title" id="PH"/>&quot; ?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">187</context>
@@ -427,7 +427,7 @@
</trans-unit>
<trans-unit datatype="html" id="93754014749412887">
<source>Do you really want to delete the tag &quot;<x equiv-text="object.name" id="PH"/>&quot;?</source>
<target>Voulez-vous vraiment supprimer l'étiquette &amp;quot;<x equiv-text="object.name" id="PH"/>&amp;quot; ?</target>
<target>Voulez-vous vraiment supprimer l'étiquette &quot;<x equiv-text="object.name" id="PH"/>&quot; ?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.ts</context>
<context context-type="linenumber">30</context>
@@ -507,7 +507,7 @@
</trans-unit>
<trans-unit datatype="html" id="4990731724078522539">
<source>Do you really want to delete the document type &quot;<x equiv-text="object.name" id="PH"/>&quot;?</source>
<target>Voulez-vous vraiment supprimer le type de document &amp;quot;<x equiv-text="object.name" id="PH"/>&amp;quot; ?</target>
<target>Voulez-vous vraiment supprimer le type de document &quot;<x equiv-text="object.name" id="PH"/>&quot; ?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/document-type-list/document-type-list.component.ts</context>
<context context-type="linenumber">26</context>
@@ -539,7 +539,7 @@
</trans-unit>
<trans-unit datatype="html" id="5610279464668232148">
<source>Saved view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; deleted.</source>
<target>Vue &amp;quot;<x equiv-text="savedView.name" id="PH"/>&amp;quot; supprimée.</target>
<target>Vue &quot;<x equiv-text="savedView.name" id="PH"/>&quot; supprimée.</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
<context context-type="linenumber">54</context>
@@ -587,7 +587,7 @@
</trans-unit>
<trans-unit datatype="html" id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13">
<source>Appearance</source>
<target>Apparition</target>
<target>Affichage</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">13</context>
@@ -699,7 +699,7 @@
</trans-unit>
<trans-unit datatype="html" id="7427874343955308724">
<source>Do you really want to delete the correspondent &quot;<x equiv-text="object.name" id="PH"/>&quot;?</source>
<target>Voulez-vous vraiment supprimer le correspondant &amp;quot;<x equiv-text="object.name" id="PH"/>&amp;quot; ?</target>
<target>Voulez-vous vraiment supprimer le correspondant &quot;<x equiv-text="object.name" id="PH"/>&quot; ?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.ts</context>
<context context-type="linenumber">26</context>
@@ -867,7 +867,7 @@
</trans-unit>
<trans-unit datatype="html" id="afa760e48c97d64d19c1455d18b7834a2256e23f">
<source>Did you mean &quot;<x equiv-text="&lt;a [routerLink]=&quot;&quot; (click)=&quot;searchCorrectedQuery()&quot;&gt;{{correctedQuery}}" id="START_LINK"/><x equiv-text="{{correctedQuery}}&lt;/a&gt;" id="INTERPOLATION"/><x equiv-text="&lt;/a&gt;" id="CLOSE_LINK"/>&quot;?</source>
<target>Vouliez-vous dire &amp;quot;<x equiv-text="&lt;a [routerLink]=&quot;&quot; (click)=&quot;searchCorrectedQuery()&quot;&gt;{{correctedQuery}}" id="START_LINK"/><x equiv-text="{{correctedQuery}}&lt;/a&gt;" id="INTERPOLATION"/><x equiv-text="&lt;/a&gt;" id="CLOSE_LINK"/>&amp;quot; ?</target>
<target>Vouliez-vous dire &quot;<x equiv-text="&lt;a [routerLink]=&quot;&quot; (click)=&quot;searchCorrectedQuery()&quot;&gt;{{correctedQuery}}" id="START_LINK"/><x equiv-text="{{correctedQuery}}&lt;/a&gt;" id="INTERPOLATION"/><x equiv-text="&lt;/a&gt;" id="CLOSE_LINK"/>&quot; ?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
<context context-type="linenumber">13</context>
@@ -1189,7 +1189,7 @@
</trans-unit>
<trans-unit datatype="html" id="8639884465898458690">
<source>&quot;<x equiv-text="items[0].name" id="PH"/>&quot; and &quot;<x equiv-text="items[1].name" id="PH_1"/>&quot;</source>
<target>&amp;quot;<x equiv-text="items[0].name" id="PH"/>&amp;quot; et &amp;quot;<x equiv-text="items[1].name" id="PH_1"/>&amp;quot;</target>
<target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot; et &quot;<x equiv-text="items[1].name" id="PH_1"/>&quot;</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">114</context>
@@ -1198,7 +1198,7 @@
</trans-unit>
<trans-unit datatype="html" id="7894972847287473517">
<source>&quot;<x equiv-text="i.name" id="PH"/>&quot;</source>
<target>&amp;quot;<x equiv-text="i.name" id="PH"/>&amp;quot;</target>
<target>&quot;<x equiv-text="i.name" id="PH"/>&quot;</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">116</context>
@@ -1215,7 +1215,7 @@
</trans-unit>
<trans-unit datatype="html" id="1822679894391095557">
<source><x equiv-text="list" id="PH"/> and &quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&quot;</source>
<target><x equiv-text="list" id="PH"/> et &amp;quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&amp;quot;</target>
<target><x equiv-text="list" id="PH"/> et &quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&quot;</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">117</context>
@@ -1232,7 +1232,7 @@
</trans-unit>
<trans-unit datatype="html" id="6619516195038467207">
<source>This operation will add the tag &quot;<x equiv-text="tag.name" id="PH"/>&quot; to <x equiv-text="this.list.selected.size" id="PH_1"/> selected document(s).</source>
<target>Cette action affectera l'étiquette &amp;quot;<x equiv-text="tag.name" id="PH"/>&amp;quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<target>Cette action affectera l'étiquette &quot;<x equiv-text="tag.name" id="PH"/>&quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">129</context>
@@ -1248,7 +1248,7 @@
</trans-unit>
<trans-unit datatype="html" id="7181166515756808573">
<source>This operation will remove the tag &quot;<x equiv-text="tag.name" id="PH"/>&quot; from <x equiv-text="this.list.selected.size" id="PH_1"/> selected document(s).</source>
<target>Cette action supprimera l'étiquette &amp;quot;<x equiv-text="tag.name" id="PH"/>&amp;quot; de(s) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<target>Cette action supprimera l'étiquette &quot;<x equiv-text="tag.name" id="PH"/>&quot; de(s) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">134</context>
@@ -1280,7 +1280,7 @@
</trans-unit>
<trans-unit datatype="html" id="6900893559485781849">
<source>This operation will assign the correspondent &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; to <x equiv-text="this.list.selected.size" id="PH_1"/> selected document(s).</source>
<target>Cette action affectera le correspondant &amp;quot;<x equiv-text="correspondent.name" id="PH"/>&amp;quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/>document(s) sélectionné(s).</target>
<target>Cette action affectera le correspondant &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/>document(s) sélectionné(s).</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">160</context>
@@ -1304,7 +1304,7 @@
</trans-unit>
<trans-unit datatype="html" id="332180123895325027">
<source>This operation will assign the document type &quot;<x equiv-text="documentType.name" id="PH"/>&quot; to <x equiv-text="this.list.selected.size" id="PH_1"/> selected document(s).</source>
<target>Cette action affectera le type de document &amp;quot;<x equiv-text="documentType.name" id="PH"/>&amp;quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<target>Cette action affectera le type de document &quot;<x equiv-text="documentType.name" id="PH"/>&quot; au(x) <x equiv-text="this.list.selected.size" id="PH_1"/> document(s) sélectionné(s).</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">183</context>
@@ -1824,7 +1824,7 @@
</trans-unit>
<trans-unit datatype="html" id="7517655726614958140">
<source>Any: Document contains any of these words (space separated)</source>
<target>Un des mots : le document contient l'un de ces mots (séparés par des espaces)</target>
<target>Un des mots : contient l'un de ces mots (séparés par des espaces)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/matching-model.ts</context>
<context context-type="linenumber">12</context>
@@ -1840,7 +1840,7 @@
</trans-unit>
<trans-unit datatype="html" id="111914402588955480">
<source>All: Document contains all of these words (space separated)</source>
<target>Tous les mots : le document contient tous ces mots (séparés par des espaces)</target>
<target>Tous les mots : contient tous ces mots (séparés par des espaces)</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/matching-model.ts</context>
<context context-type="linenumber">13</context>
@@ -1856,7 +1856,7 @@
</trans-unit>
<trans-unit datatype="html" id="7109184332944610787">
<source>Exact: Document contains this string</source>
<target>Concordance exacte : le document contient cette chaîne de caractères</target>
<target>Concordance exacte : contient cette chaîne de caractères</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/matching-model.ts</context>
<context context-type="linenumber">14</context>
@@ -1872,7 +1872,7 @@
</trans-unit>
<trans-unit datatype="html" id="7548151332424148033">
<source>Regular expression: Document matches this regular expression</source>
<target>Expression régulière : le document correspond à cette expression régulière</target>
<target>Expression régulière : correspond à cette expression régulière</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/matching-model.ts</context>
<context context-type="linenumber">15</context>
@@ -1888,7 +1888,7 @@
</trans-unit>
<trans-unit datatype="html" id="8419167206585286450">
<source>Fuzzy: Document contains a word similar to this word</source>
<target>Mot approximatif : le document contient un mot similaire à ce mot</target>
<target>Mot approximatif : contient un mot similaire à ce mot</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/matching-model.ts</context>
<context context-type="linenumber">16</context>

View File

@@ -427,7 +427,7 @@
</trans-unit>
<trans-unit datatype="html" id="93754014749412887">
<source>Do you really want to delete the tag &quot;<x equiv-text="object.name" id="PH"/>&quot;?</source>
<target>Wil je de tag echt verwijderen &amp;quot;<x equiv-text="object.name" id="PH"/>&amp;quot;?</target>
<target>Wil je het etiket echt verwijderen &amp;quot;<x equiv-text="object.name" id="PH"/>&amp;quot;?</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.ts</context>
<context context-type="linenumber">30</context>
@@ -435,7 +435,7 @@
</trans-unit>
<trans-unit datatype="html" id="cafc87479686947e2590b9f588a88040aeaf660b">
<source>Tags</source>
<target>Tags</target>
<target>Etiketten</target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
<context context-type="linenumber">1</context>
@@ -1808,7 +1808,7 @@
</trans-unit>
<trans-unit datatype="html" id="5467489005440577210">
<source>Error while deleting element: <x equiv-text="JSON.stringify(error.error)" id="PH"/></source>
<target>Fout bij het verwijderen van een element: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<target>Fout bij het verwijderen van het element: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
<context context-type="linenumber">93</context>

View File

@@ -84,7 +84,7 @@ $border-color-dark-mode: #47494f;
}
.dropdown-menu {
background-color: $bg-dark-mode;
background-color: $bg-light-dark-mode;
.dropdown-divider {
border-color: $border-color-dark-mode;
@@ -297,7 +297,8 @@ $border-color-dark-mode: #47494f;
}
}
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option:hover {
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option:hover,
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked {
background-color: $bg-light-dark-mode;
}
@@ -347,6 +348,10 @@ $border-color-dark-mode: #47494f;
background-color: darken($danger-dark-mode, 20%);
border-color: darken($danger-dark-mode, 20%);
}
.bg-dark {
background-color: $bg-light-dark-mode !important;
}
}
body.color-scheme-dark {

View File

@@ -46,7 +46,7 @@ def _consume(filepath):
return
if not is_file_ext_supported(os.path.splitext(filepath)[1]):
logger.debug(
logger.warning(
f"Not consuming file {filepath}: Unknown file extension.")
return

View File

@@ -1,3 +1,4 @@
import logging
import re
from fuzzywuzzy import fuzz
@@ -5,49 +6,59 @@ from fuzzywuzzy import fuzz
from documents.models import MatchingModel, Correspondent, DocumentType, Tag
def match_correspondents(document_content, classifier):
logger = logging.getLogger(__name__)
def log_reason(matching_model, document, reason):
class_name = type(matching_model).__name__
logger.debug(
f"Assigning {class_name} {matching_model.name} to document "
f"{document} because {reason}")
def match_correspondents(document, classifier):
if classifier:
pred_id = classifier.predict_correspondent(document_content)
pred_id = classifier.predict_correspondent(document.content)
else:
pred_id = None
correspondents = Correspondent.objects.all()
return list(filter(
lambda o: matches(o, document_content) or o.pk == pred_id,
lambda o: matches(o, document) or o.pk == pred_id,
correspondents))
def match_document_types(document_content, classifier):
def match_document_types(document, classifier):
if classifier:
pred_id = classifier.predict_document_type(document_content)
pred_id = classifier.predict_document_type(document.content)
else:
pred_id = None
document_types = DocumentType.objects.all()
return list(filter(
lambda o: matches(o, document_content) or o.pk == pred_id,
lambda o: matches(o, document) or o.pk == pred_id,
document_types))
def match_tags(document_content, classifier):
def match_tags(document, classifier):
if classifier:
predicted_tag_ids = classifier.predict_tags(document_content)
predicted_tag_ids = classifier.predict_tags(document.content)
else:
predicted_tag_ids = []
tags = Tag.objects.all()
return list(filter(
lambda o: matches(o, document_content) or o.pk in predicted_tag_ids,
lambda o: matches(o, document) or o.pk in predicted_tag_ids,
tags))
def matches(matching_model, document_content):
def matches(matching_model, document):
search_kwargs = {}
document_content = document_content.lower()
document_content = document.content.lower()
# Check that match is not empty
if matching_model.match.strip() == "":
@@ -62,26 +73,54 @@ def matches(matching_model, document_content):
rf"\b{word}\b", document_content, **search_kwargs)
if not search_result:
return False
log_reason(
matching_model, document,
f"it contains all of these words: {matching_model.match}"
)
return True
elif matching_model.matching_algorithm == MatchingModel.MATCH_ANY:
for word in _split_match(matching_model):
if re.search(rf"\b{word}\b", document_content, **search_kwargs):
log_reason(
matching_model, document,
f"it contains this word: {word}"
)
return True
return False
elif matching_model.matching_algorithm == MatchingModel.MATCH_LITERAL:
return bool(re.search(
result = bool(re.search(
rf"\b{matching_model.match}\b",
document_content,
**search_kwargs
))
if result:
log_reason(
matching_model, document,
f"it contains this string: \"{matching_model.match}\""
)
return result
elif matching_model.matching_algorithm == MatchingModel.MATCH_REGEX:
return bool(re.search(
re.compile(matching_model.match, **search_kwargs),
document_content
))
try:
match = re.search(
re.compile(matching_model.match, **search_kwargs),
document_content
)
except re.error:
logger.error(
f"Error while processing regular expression "
f"{matching_model.match}"
)
return False
if match:
log_reason(
matching_model, document,
f"the string {match.group()} matches the regular expression "
f"{matching_model.match}"
)
return bool(match)
elif matching_model.matching_algorithm == MatchingModel.MATCH_FUZZY:
match = re.sub(r'[^\w\s]', '', matching_model.match)
@@ -89,8 +128,16 @@ def matches(matching_model, document_content):
if matching_model.is_insensitive:
match = match.lower()
text = text.lower()
return fuzz.partial_ratio(match, text) >= 90
if fuzz.partial_ratio(match, text) >= 90:
# TODO: make this better
log_reason(
matching_model, document,
f"parts of the document content somehow match the string "
f"{matching_model.match}"
)
return True
else:
return False
elif matching_model.matching_algorithm == MatchingModel.MATCH_AUTO:
# this is done elsewhere.

View File

@@ -12,6 +12,7 @@ from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
from django.utils.timezone import is_aware
from django.utils.translation import gettext_lazy as _
@@ -233,7 +234,10 @@ class Document(models.Model):
verbose_name_plural = _("documents")
def __str__(self):
created = datetime.date.isoformat(self.created)
if is_aware(self.created):
created = timezone.localdate(self.created).isoformat()
else:
created = datetime.date.isoformat(self.created)
if self.correspondent and self.title:
return f"{created} {self.correspondent} {self.title}"
else:

View File

@@ -38,7 +38,7 @@ def set_correspondent(sender,
if document.correspondent and not replace:
return
potential_correspondents = matching.match_correspondents(document.content,
potential_correspondents = matching.match_correspondents(document,
classifier)
potential_count = len(potential_correspondents)
@@ -81,7 +81,7 @@ def set_document_type(sender,
if document.document_type and not replace:
return
potential_document_type = matching.match_document_types(document.content,
potential_document_type = matching.match_document_types(document,
classifier)
potential_count = len(potential_document_type)
@@ -130,7 +130,7 @@ def set_tags(sender,
current_tags = set(document.tags.all())
matched_tags = matching.match_tags(document.content, classifier)
matched_tags = matching.match_tags(document, classifier)
relevant_tags = set(matched_tags) - current_tags

View File

@@ -1,6 +1,7 @@
<!doctype html>
{% load static %}
{% load i18n %}
<html lang="en">
<head>
@@ -16,7 +17,7 @@
<link rel="stylesheet" href="{% static styles_css %}">
</head>
<body>
<app-root>Loading...</app-root>
<app-root>{% translate "Paperless-ng is loading..." %}</app-root>
<script src="{% static runtime_js %}" defer></script>
<script src="{% static polyfills_js %}" defer></script>
<script src="{% static main_js %}" defer></script>

View File

@@ -1,6 +1,7 @@
<!doctype html>
{% load static %}
{% load i18n %}
<html lang="en">
<head>
@@ -9,7 +10,7 @@
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Jekyll v4.1.1">
<title>Paperless Sign In</title>
<title>{% translate "Paperless-ng signed out" %}</title>
<!-- Bootstrap core CSS -->
<link href="{% static 'bootstrap.min.css' %}" rel="stylesheet">
@@ -37,8 +38,8 @@
<body class="text-center">
<div class="form-signin">
<img class="mb-4" src="{% static 'frontend/en-US/assets/logo.svg' %}" alt="" width="300">
<p>You have been successfully logged out. Bye!</p>
<a href="/">Sign in again</a>
<p>{% translate "You have been successfully logged out. Bye!" %}</p>
<a href="/">{% translate "Sign in again" %}</a>
</div>
</body>
</html>

View File

@@ -1,6 +1,7 @@
<!doctype html>
{% load static %}
{% load i18n %}
<html lang="en">
<head>
@@ -9,7 +10,7 @@
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Jekyll v4.1.1">
<title>Paperless Sign In</title>
<title>{% translate "Paperless-ng sign in" %}</title>
<!-- Bootstrap core CSS -->
<link href="{% static 'bootstrap.min.css' %}" rel="stylesheet">
@@ -38,17 +39,19 @@
<form class="form-signin" method="post">
{% csrf_token %}
<img class="mb-4" src="{% static 'frontend/en-US/assets/logo.svg' %}" alt="" width="300">
<p>Please sign in.</p>
<p>{% translate "Please sign in." %}</p>
{% if form.errors %}
<div class="alert alert-danger" role="alert">
Your username and password didn't match. Please try again.
{% translate "Your username and password didn't match. Please try again." %}
</div>
{% endif %}
<label for="inputUsername" class="sr-only">Username</label>
<input type="text" name="username" id="inputUsername" class="form-control" placeholder="Username" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
{% translate "Username" as i18n_username %}
{% translate "Password" as i18n_password %}
<label for="inputUsername" class="sr-only">{{ i18n_username }}</label>
<input type="text" name="username" id="inputUsername" class="form-control" placeholder="{{ i18n_username }}" required autofocus>
<label for="inputPassword" class="sr-only">{{ i18n_password }}</label>
<input type="password" name="password" id="inputPassword" class="form-control" placeholder="{{ i18n_password }}" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% translate "Sign in" %}</button>
</form>
</body>
</html>

View File

@@ -5,12 +5,14 @@ from django.test import TestCase
from django.utils import timezone
from documents.admin import DocumentAdmin
from documents.models import Document, Tag
from documents.models import Document
from documents.tests.utils import DirectoriesMixin
class TestDocumentAdmin(TestCase):
class TestDocumentAdmin(DirectoriesMixin, TestCase):
def setUp(self) -> None:
super(TestDocumentAdmin, self).setUp()
self.doc_admin = DocumentAdmin(model=Document, admin_site=AdminSite())
@mock.patch("documents.admin.index.add_or_update_document")

View File

@@ -1,10 +1,10 @@
import shutil
import tempfile
from datetime import datetime
from pathlib import Path
from unittest import mock
from django.test import TestCase, override_settings
from django.utils import timezone
from ..models import Document, Correspondent
@@ -47,20 +47,20 @@ class TestDocument(TestCase):
def test_file_name(self):
doc = Document(mime_type="application/pdf", title="test", created=datetime(2020, 12, 25))
doc = Document(mime_type="application/pdf", title="test", created=timezone.datetime(2020, 12, 25))
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.pdf")
def test_file_name_jpg(self):
doc = Document(mime_type="image/jpeg", title="test", created=datetime(2020, 12, 25))
doc = Document(mime_type="image/jpeg", title="test", created=timezone.datetime(2020, 12, 25))
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.jpg")
def test_file_name_unknown(self):
doc = Document(mime_type="application/zip", title="test", created=datetime(2020, 12, 25))
doc = Document(mime_type="application/zip", title="test", created=timezone.datetime(2020, 12, 25))
self.assertEqual(doc.get_public_filename(), "2020-12-25 test.zip")
def test_file_name_invalid_type(self):
doc = Document(mime_type="image/jpegasd", title="test", created=datetime(2020, 12, 25))
doc = Document(mime_type="image/jpegasd", title="test", created=timezone.datetime(2020, 12, 25))
self.assertEqual(doc.get_public_filename(), "2020-12-25 test")

View File

@@ -21,13 +21,15 @@ class TestMatching(TestCase):
matching_algorithm=getattr(klass, algorithm)
)
for string in true:
doc = Document(content=string)
self.assertTrue(
matching.matches(instance, string),
matching.matches(instance, doc),
'"%s" should match "%s" but it does not' % (text, string)
)
for string in false:
doc = Document(content=string)
self.assertFalse(
matching.matches(instance, string),
matching.matches(instance, doc),
'"%s" should not match "%s" but it does' % (text, string)
)
@@ -169,7 +171,7 @@ class TestMatching(TestCase):
def test_match_regex(self):
self._test_matching(
r"alpha\w+gamma",
"alpha\w+gamma",
"MATCH_REGEX",
(
"I have alpha_and_gamma in me",
@@ -187,6 +189,16 @@ class TestMatching(TestCase):
)
)
def test_tach_invalid_regex(self):
self._test_matching(
"[[",
"MATCH_REGEX",
[],
[
"Don't match this"
]
)
def test_match_fuzzy(self):
self._test_matching(

View File

@@ -0,0 +1,34 @@
import logging
from unittest import mock
from django.test import TestCase
from paperless.settings import default_task_workers, default_threads_per_worker
class TestSettings(TestCase):
@mock.patch("paperless.settings.multiprocessing.cpu_count")
def test_single_core(self, cpu_count):
cpu_count.return_value = 1
default_workers = default_task_workers()
default_threads = default_threads_per_worker(default_workers)
self.assertEqual(default_workers, 1)
self.assertEqual(default_threads, 1)
def test_workers_threads(self):
for i in range(2, 64):
with mock.patch("paperless.settings.multiprocessing.cpu_count") as cpu_count:
cpu_count.return_value = i
default_workers = default_task_workers()
default_threads = default_threads_per_worker(default_workers)
self.assertTrue(default_workers >= 1)
self.assertTrue(default_threads >= 1)
self.assertTrue(default_workers * default_threads < i, f"{i}")

View File

@@ -1,3 +1,4 @@
import logging
import os
import tempfile
from datetime import datetime
@@ -458,12 +459,21 @@ class SearchView(APIView):
self.ix = index.open_index()
def add_infos_to_hit(self, r):
doc = Document.objects.get(id=r['id'])
try:
doc = Document.objects.get(id=r['id'])
except Document.DoesNotExist:
logging.getLogger(__name__).warning(
f"Search index returned a non-existing document: "
f"id: {r['id']}, title: {r['title']}. "
f"Search index needs reindex."
)
doc = None
return {'id': r['id'],
'highlights': r.highlights("content", text=doc.content),
'highlights': r.highlights("content", text=doc.content) if doc else None, # NOQA: E501
'score': r.score,
'rank': r.rank,
'document': DocumentSerializer(doc).data,
'document': DocumentSerializer(doc).data if doc else None,
'title': r['title']
}

View File

@@ -4,16 +4,16 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
# Translators:
# Jonas Winkler <dev@jpwinkler.de>, 2021
# Jonas Winkler, 2021
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-06 19:50+0000\n"
"POT-Creation-Date: 2021-01-10 21:41+0000\n"
"PO-Revision-Date: 2020-12-30 19:27+0000\n"
"Last-Translator: Jonas Winkler <dev@jpwinkler.de>, 2021\n"
"Last-Translator: Jonas Winkler, 2021\n"
"Language-Team: German (https://www.transifex.com/paperless/teams/115905/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -50,7 +50,7 @@ msgid "Automatic"
msgstr "Automatisch"
#: documents/models.py:41 documents/models.py:354 paperless_mail/models.py:25
#: paperless_mail/models.py:111
#: paperless_mail/models.py:109
msgid "name"
msgstr "Name"
@@ -346,6 +346,48 @@ msgstr "Filterregel"
msgid "filter rules"
msgstr "Filterregeln"
#: documents/templates/index.html:20
msgid "Paperless-ng is loading..."
msgstr "Paperless-ng wird geladen..."
#: documents/templates/registration/logged_out.html:13
msgid "Paperless-ng signed out"
msgstr "Paperless-ng abgemeldet"
#: documents/templates/registration/logged_out.html:41
msgid "You have been successfully logged out. Bye!"
msgstr "Sie wurden erfolgreich abgemeldet. Auf Wiedersehen!"
#: documents/templates/registration/logged_out.html:42
msgid "Sign in again"
msgstr "Erneut anmelden"
#: documents/templates/registration/login.html:13
msgid "Paperless-ng sign in"
msgstr "Paperless-ng Anmeldung"
#: documents/templates/registration/login.html:42
msgid "Please sign in."
msgstr "Bitte melden Sie sich an."
#: documents/templates/registration/login.html:45
msgid "Your username and password didn't match. Please try again."
msgstr ""
"Ihr Benutzername und Passwort stimmen nicht überein. Bitte versuchen Sie es "
"erneut."
#: documents/templates/registration/login.html:48
msgid "Username"
msgstr "Benutzername"
#: documents/templates/registration/login.html:49
msgid "Password"
msgstr "Passwort"
#: documents/templates/registration/login.html:54
msgid "Sign in"
msgstr "Anmelden"
#: paperless/settings.py:268
msgid "English"
msgstr "Englisch"
@@ -456,7 +498,7 @@ msgstr "Benutzername"
#: paperless_mail/models.py:50
msgid "password"
msgstr "Password"
msgstr "Passwort"
#: paperless_mail/models.py:60
msgid "mail rule"
@@ -470,79 +512,79 @@ msgstr "E-Mail-Regeln"
msgid "Only process attachments."
msgstr "Nur Anhänge verarbeiten."
#: paperless_mail/models.py:70
#: paperless_mail/models.py:68
msgid "Process all files, including 'inline' attachments."
msgstr "Alle Dateien verarbeiten, auch 'inline'-Anhänge."
#: paperless_mail/models.py:80
#: paperless_mail/models.py:78
msgid "Mark as read, don't process read mails"
msgstr "Als gelesen markieren, gelesene E-Mails nicht verarbeiten"
#: paperless_mail/models.py:81
#: paperless_mail/models.py:79
msgid "Flag the mail, don't process flagged mails"
msgstr "Als wichtig markieren, markierte E-Mails nicht verarbeiten"
#: paperless_mail/models.py:82
#: paperless_mail/models.py:80
msgid "Move to specified folder"
msgstr "In angegebenen Ordner verschieben"
#: paperless_mail/models.py:83
#: paperless_mail/models.py:81
msgid "Delete"
msgstr "Löschen"
#: paperless_mail/models.py:90
#: paperless_mail/models.py:88
msgid "Use subject as title"
msgstr "Betreff als Titel verwenden"
#: paperless_mail/models.py:91
#: paperless_mail/models.py:89
msgid "Use attachment filename as title"
msgstr "Dateiname des Anhangs als Titel verwenden"
#: paperless_mail/models.py:101
#: paperless_mail/models.py:99
msgid "Do not assign a correspondent"
msgstr "Keinen Korrespondenten zuweisen"
#: paperless_mail/models.py:103
#: paperless_mail/models.py:101
msgid "Use mail address"
msgstr "E-Mail-Adresse benutzen"
#: paperless_mail/models.py:105
#: paperless_mail/models.py:103
msgid "Use name (or mail address if not available)"
msgstr "Absendername benutzen (oder E-Mail-Adressen, wenn nicht verfügbar)"
#: paperless_mail/models.py:107
#: paperless_mail/models.py:105
msgid "Use correspondent selected below"
msgstr "Nachfolgend ausgewählten Korrespondent verwenden"
#: paperless_mail/models.py:115
#: paperless_mail/models.py:113
msgid "order"
msgstr "Reihenfolge"
#: paperless_mail/models.py:122
#: paperless_mail/models.py:120
msgid "account"
msgstr "Konto"
#: paperless_mail/models.py:126
#: paperless_mail/models.py:124
msgid "folder"
msgstr "Ordner"
#: paperless_mail/models.py:130
#: paperless_mail/models.py:128
msgid "filter from"
msgstr "Absender filtern"
#: paperless_mail/models.py:133
#: paperless_mail/models.py:131
msgid "filter subject"
msgstr "Betreff filtern"
#: paperless_mail/models.py:136
#: paperless_mail/models.py:134
msgid "filter body"
msgstr "Nachrichteninhalt filtern"
#: paperless_mail/models.py:140
#: paperless_mail/models.py:138
msgid "filter attachment filename"
msgstr "Anhang-Dateiname filtern"
#: paperless_mail/models.py:142
#: paperless_mail/models.py:140
msgid ""
"Only consume documents which entirely match this filename if specified. "
"Wildcards such as *.pdf or *invoice* are allowed. Case insensitive."
@@ -551,19 +593,19 @@ msgstr ""
"entsprechen. Platzhalter wie *.pdf oder *rechnung* sind erlaubt. Groß- und "
"Kleinschreibung ist irrelevant."
#: paperless_mail/models.py:148
#: paperless_mail/models.py:146
msgid "maximum age"
msgstr "Maximales Alter"
#: paperless_mail/models.py:150
#: paperless_mail/models.py:148
msgid "Specified in days."
msgstr "Angegeben in Tagen."
#: paperless_mail/models.py:153
#: paperless_mail/models.py:151
msgid "attachment type"
msgstr "Dateianhangs-Typ"
msgstr "Dateianhangstyp"
#: paperless_mail/models.py:156
#: paperless_mail/models.py:154
msgid ""
"Inline attachments include embedded images, so it's best to combine this "
"option with a filename filter."
@@ -571,15 +613,15 @@ msgstr ""
"'Inline'-Anhänge schließen eingebettete Bilder mit ein, daher sollte diese "
"Einstellung mit einem Dateinamenfilter kombiniert werden."
#: paperless_mail/models.py:161
#: paperless_mail/models.py:159
msgid "action"
msgstr "Aktion"
#: paperless_mail/models.py:167
#: paperless_mail/models.py:165
msgid "action parameter"
msgstr "Parameter für Aktion"
#: paperless_mail/models.py:169
#: paperless_mail/models.py:167
msgid ""
"Additional parameter for the action selected above, i.e., the target folder "
"of the move to folder action."
@@ -587,22 +629,22 @@ msgstr ""
"Zusätzlicher Parameter für die oben ausgewählte Aktion, zum Beispiel der "
"Zielordner für die Aktion \"In angegebenen Ordner verschieben\""
#: paperless_mail/models.py:175
#: paperless_mail/models.py:173
msgid "assign title from"
msgstr "Titel zuweisen von"
#: paperless_mail/models.py:185
#: paperless_mail/models.py:183
msgid "assign this tag"
msgstr "Dieses Tag zuweisen"
#: paperless_mail/models.py:193
#: paperless_mail/models.py:191
msgid "assign this document type"
msgstr "Diesen Dokumenttyp zuweisen"
#: paperless_mail/models.py:197
#: paperless_mail/models.py:195
msgid "assign correspondent from"
msgstr "Korrespondent zuweisen von"
#: paperless_mail/models.py:207
#: paperless_mail/models.py:205
msgid "assign this correspondent"
msgstr "Diesen Korrespondent zuweisen"

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-06 19:50+0000\n"
"POT-Creation-Date: 2021-01-10 21:41+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -46,7 +46,7 @@ msgid "Automatic"
msgstr ""
#: documents/models.py:41 documents/models.py:354 paperless_mail/models.py:25
#: paperless_mail/models.py:111
#: paperless_mail/models.py:109
msgid "name"
msgstr ""
@@ -338,6 +338,46 @@ msgstr ""
msgid "filter rules"
msgstr ""
#: documents/templates/index.html:20
msgid "Paperless-ng is loading..."
msgstr ""
#: documents/templates/registration/logged_out.html:13
msgid "Paperless-ng signed out"
msgstr ""
#: documents/templates/registration/logged_out.html:41
msgid "You have been successfully logged out. Bye!"
msgstr ""
#: documents/templates/registration/logged_out.html:42
msgid "Sign in again"
msgstr ""
#: documents/templates/registration/login.html:13
msgid "Paperless-ng sign in"
msgstr ""
#: documents/templates/registration/login.html:42
msgid "Please sign in."
msgstr ""
#: documents/templates/registration/login.html:45
msgid "Your username and password didn't match. Please try again."
msgstr ""
#: documents/templates/registration/login.html:48
msgid "Username"
msgstr ""
#: documents/templates/registration/login.html:49
msgid "Password"
msgstr ""
#: documents/templates/registration/login.html:54
msgid "Sign in"
msgstr ""
#: paperless/settings.py:268
msgid "English"
msgstr ""
@@ -451,132 +491,132 @@ msgstr ""
msgid "Only process attachments."
msgstr ""
#: paperless_mail/models.py:70
#: paperless_mail/models.py:68
msgid "Process all files, including 'inline' attachments."
msgstr ""
#: paperless_mail/models.py:80
#: paperless_mail/models.py:78
msgid "Mark as read, don't process read mails"
msgstr ""
#: paperless_mail/models.py:81
#: paperless_mail/models.py:79
msgid "Flag the mail, don't process flagged mails"
msgstr ""
#: paperless_mail/models.py:82
#: paperless_mail/models.py:80
msgid "Move to specified folder"
msgstr ""
#: paperless_mail/models.py:83
#: paperless_mail/models.py:81
msgid "Delete"
msgstr ""
#: paperless_mail/models.py:90
#: paperless_mail/models.py:88
msgid "Use subject as title"
msgstr ""
#: paperless_mail/models.py:91
#: paperless_mail/models.py:89
msgid "Use attachment filename as title"
msgstr ""
#: paperless_mail/models.py:101
#: paperless_mail/models.py:99
msgid "Do not assign a correspondent"
msgstr ""
#: paperless_mail/models.py:103
#: paperless_mail/models.py:101
msgid "Use mail address"
msgstr ""
#: paperless_mail/models.py:105
#: paperless_mail/models.py:103
msgid "Use name (or mail address if not available)"
msgstr ""
#: paperless_mail/models.py:107
#: paperless_mail/models.py:105
msgid "Use correspondent selected below"
msgstr ""
#: paperless_mail/models.py:115
#: paperless_mail/models.py:113
msgid "order"
msgstr ""
#: paperless_mail/models.py:122
#: paperless_mail/models.py:120
msgid "account"
msgstr ""
#: paperless_mail/models.py:126
#: paperless_mail/models.py:124
msgid "folder"
msgstr ""
#: paperless_mail/models.py:130
#: paperless_mail/models.py:128
msgid "filter from"
msgstr ""
#: paperless_mail/models.py:133
#: paperless_mail/models.py:131
msgid "filter subject"
msgstr ""
#: paperless_mail/models.py:136
#: paperless_mail/models.py:134
msgid "filter body"
msgstr ""
#: paperless_mail/models.py:140
#: paperless_mail/models.py:138
msgid "filter attachment filename"
msgstr ""
#: paperless_mail/models.py:142
#: paperless_mail/models.py:140
msgid ""
"Only consume documents which entirely match this filename if specified. "
"Wildcards such as *.pdf or *invoice* are allowed. Case insensitive."
msgstr ""
#: paperless_mail/models.py:148
#: paperless_mail/models.py:146
msgid "maximum age"
msgstr ""
#: paperless_mail/models.py:150
#: paperless_mail/models.py:148
msgid "Specified in days."
msgstr ""
#: paperless_mail/models.py:153
#: paperless_mail/models.py:151
msgid "attachment type"
msgstr ""
#: paperless_mail/models.py:156
#: paperless_mail/models.py:154
msgid ""
"Inline attachments include embedded images, so it's best to combine this "
"option with a filename filter."
msgstr ""
#: paperless_mail/models.py:161
#: paperless_mail/models.py:159
msgid "action"
msgstr ""
#: paperless_mail/models.py:167
#: paperless_mail/models.py:165
msgid "action parameter"
msgstr ""
#: paperless_mail/models.py:169
#: paperless_mail/models.py:167
msgid ""
"Additional parameter for the action selected above, i.e., the target folder "
"of the move to folder action."
msgstr ""
#: paperless_mail/models.py:175
#: paperless_mail/models.py:173
msgid "assign title from"
msgstr ""
#: paperless_mail/models.py:185
#: paperless_mail/models.py:183
msgid "assign this tag"
msgstr ""
#: paperless_mail/models.py:193
#: paperless_mail/models.py:191
msgid "assign this document type"
msgstr ""
#: paperless_mail/models.py:197
#: paperless_mail/models.py:195
msgid "assign correspondent from"
msgstr ""
#: paperless_mail/models.py:207
#: paperless_mail/models.py:205
msgid "assign this correspondent"
msgstr ""

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-06 19:50+0000\n"
"POT-Creation-Date: 2021-01-10 21:41+0000\n"
"PO-Revision-Date: 2020-12-30 19:27+0000\n"
"Last-Translator: Philmo67, 2021\n"
"Language-Team: French (https://www.transifex.com/paperless/teams/115905/fr/)\n"
@@ -51,7 +51,7 @@ msgid "Automatic"
msgstr "Automatique"
#: documents/models.py:41 documents/models.py:354 paperless_mail/models.py:25
#: paperless_mail/models.py:111
#: paperless_mail/models.py:109
msgid "name"
msgstr "nom"
@@ -348,6 +348,48 @@ msgstr "règle de filtrage"
msgid "filter rules"
msgstr "règles de filtrage"
#: documents/templates/index.html:20
msgid "Paperless-ng is loading..."
msgstr "Paperless-ng est en cours de chargement..."
#: documents/templates/registration/logged_out.html:13
msgid "Paperless-ng signed out"
msgstr "Déconnecté de Paperless-ng"
#: documents/templates/registration/logged_out.html:41
msgid "You have been successfully logged out. Bye!"
msgstr "Vous avez été déconnecté avec succès. Au revoir !"
#: documents/templates/registration/logged_out.html:42
msgid "Sign in again"
msgstr "Se reconnecter"
#: documents/templates/registration/login.html:13
msgid "Paperless-ng sign in"
msgstr "Connexion à Paperless-ng"
#: documents/templates/registration/login.html:42
msgid "Please sign in."
msgstr "Veuillez vous connecter."
#: documents/templates/registration/login.html:45
msgid "Your username and password didn't match. Please try again."
msgstr ""
"Votre nom d'utilisateur et votre mot de passe ne correspondent pas. Veuillez"
" réessayer."
#: documents/templates/registration/login.html:48
msgid "Username"
msgstr "Nom d'utilisateur"
#: documents/templates/registration/login.html:49
msgid "Password"
msgstr "Mot de passe"
#: documents/templates/registration/login.html:54
msgid "Sign in"
msgstr "S'identifier"
#: paperless/settings.py:268
msgid "English"
msgstr "Anglais"
@@ -404,9 +446,9 @@ msgid ""
"process all matching rules that you have defined."
msgstr ""
"Affecter automatiquement des métadonnées aux documents traités à partir de "
"cette règle. Si vous n'affectez pas d'étiquettes, de types ou de "
"correspondants ici, Paperless-ng traitera quand même toutes les règles de "
"rapprochement que vous avez définies."
"cette règle. Si vous n'affectez pas d'étiquette, de type ou de correspondant"
" ici, Paperless-ng appliquera toutes les autres règles de rapprochement que "
"vous avez définies."
#: paperless_mail/apps.py:9
msgid "Paperless mail"
@@ -472,79 +514,79 @@ msgstr "règles de courriel"
msgid "Only process attachments."
msgstr "Ne traiter que les pièces jointes."
#: paperless_mail/models.py:70
#: paperless_mail/models.py:68
msgid "Process all files, including 'inline' attachments."
msgstr "Traiter tous les fichiers, y compris les pièces jointes \"en ligne\"."
#: paperless_mail/models.py:80
#: paperless_mail/models.py:78
msgid "Mark as read, don't process read mails"
msgstr "Marquer comme lu, ne pas traiter les courriels lus"
#: paperless_mail/models.py:81
#: paperless_mail/models.py:79
msgid "Flag the mail, don't process flagged mails"
msgstr "Marquer le courriel, ne pas traiter les courriels marqués"
#: paperless_mail/models.py:82
#: paperless_mail/models.py:80
msgid "Move to specified folder"
msgstr "Déplacer vers le dossier spécifié"
#: paperless_mail/models.py:83
#: paperless_mail/models.py:81
msgid "Delete"
msgstr "Supprimer"
#: paperless_mail/models.py:90
#: paperless_mail/models.py:88
msgid "Use subject as title"
msgstr "Utiliser le sujet en tant que titre"
#: paperless_mail/models.py:91
#: paperless_mail/models.py:89
msgid "Use attachment filename as title"
msgstr "Utiliser le nom de la pièce jointe en tant que titre"
#: paperless_mail/models.py:101
#: paperless_mail/models.py:99
msgid "Do not assign a correspondent"
msgstr "Ne pas affecter de correspondant"
#: paperless_mail/models.py:103
#: paperless_mail/models.py:101
msgid "Use mail address"
msgstr "Utiliser l'adresse électronique"
#: paperless_mail/models.py:105
#: paperless_mail/models.py:103
msgid "Use name (or mail address if not available)"
msgstr "Utiliser le nom (ou l'adresse électronique s'il n'est pas disponible)"
#: paperless_mail/models.py:107
#: paperless_mail/models.py:105
msgid "Use correspondent selected below"
msgstr "Utiliser le correspondant sélectionné ci-dessous"
#: paperless_mail/models.py:115
#: paperless_mail/models.py:113
msgid "order"
msgstr "ordre"
#: paperless_mail/models.py:122
#: paperless_mail/models.py:120
msgid "account"
msgstr "compte"
#: paperless_mail/models.py:126
#: paperless_mail/models.py:124
msgid "folder"
msgstr "répertoire"
#: paperless_mail/models.py:130
#: paperless_mail/models.py:128
msgid "filter from"
msgstr "filtrer l'expéditeur"
#: paperless_mail/models.py:133
#: paperless_mail/models.py:131
msgid "filter subject"
msgstr "filtrer le sujet"
#: paperless_mail/models.py:136
#: paperless_mail/models.py:134
msgid "filter body"
msgstr "filtrer le corps du message"
#: paperless_mail/models.py:140
#: paperless_mail/models.py:138
msgid "filter attachment filename"
msgstr "filtrer le nom de fichier de la pièce jointe"
#: paperless_mail/models.py:142
#: paperless_mail/models.py:140
msgid ""
"Only consume documents which entirely match this filename if specified. "
"Wildcards such as *.pdf or *invoice* are allowed. Case insensitive."
@@ -553,19 +595,19 @@ msgstr ""
" s'il est spécifié. Les jokers tels que *.pdf ou *facture* sont autorisés. "
"La casse n'est pas prise en compte."
#: paperless_mail/models.py:148
#: paperless_mail/models.py:146
msgid "maximum age"
msgstr "âge maximum"
#: paperless_mail/models.py:150
#: paperless_mail/models.py:148
msgid "Specified in days."
msgstr "En jours."
#: paperless_mail/models.py:153
#: paperless_mail/models.py:151
msgid "attachment type"
msgstr "type de pièce jointe"
#: paperless_mail/models.py:156
#: paperless_mail/models.py:154
msgid ""
"Inline attachments include embedded images, so it's best to combine this "
"option with a filename filter."
@@ -573,15 +615,15 @@ msgstr ""
"Les pièces jointes en ligne comprennent les images intégrées, il est donc "
"préférable de combiner cette option avec un filtre de nom de fichier."
#: paperless_mail/models.py:161
#: paperless_mail/models.py:159
msgid "action"
msgstr "action"
#: paperless_mail/models.py:167
#: paperless_mail/models.py:165
msgid "action parameter"
msgstr "paramètre d'action"
#: paperless_mail/models.py:169
#: paperless_mail/models.py:167
msgid ""
"Additional parameter for the action selected above, i.e., the target folder "
"of the move to folder action."
@@ -589,22 +631,22 @@ msgstr ""
"Paramètre supplémentaire pour l'action sélectionnée ci-dessus, par exemple "
"le dossier cible de l'action de déplacement vers un dossier."
#: paperless_mail/models.py:175
#: paperless_mail/models.py:173
msgid "assign title from"
msgstr "affecter le titre depuis"
#: paperless_mail/models.py:185
#: paperless_mail/models.py:183
msgid "assign this tag"
msgstr "affecter cette étiquette"
#: paperless_mail/models.py:193
#: paperless_mail/models.py:191
msgid "assign this document type"
msgstr "affecter ce type de document"
#: paperless_mail/models.py:197
#: paperless_mail/models.py:195
msgid "assign correspondent from"
msgstr "affecter le correspondant depuis"
#: paperless_mail/models.py:207
#: paperless_mail/models.py:205
msgid "assign this correspondent"
msgstr "affecter ce correspondant"

View File

@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-01-06 19:50+0000\n"
"POT-Creation-Date: 2021-01-10 21:41+0000\n"
"PO-Revision-Date: 2020-12-30 19:27+0000\n"
"Last-Translator: Jo Vandeginste <jo.vandeginste@gmail.com>, 2021\n"
"Language-Team: Dutch (Netherlands) (https://www.transifex.com/paperless/teams/115905/nl_NL/)\n"
@@ -51,7 +51,7 @@ msgid "Automatic"
msgstr "Automatisch"
#: documents/models.py:41 documents/models.py:354 paperless_mail/models.py:25
#: paperless_mail/models.py:111
#: paperless_mail/models.py:109
msgid "name"
msgstr "naam"
@@ -93,11 +93,11 @@ msgstr ""
#: documents/models.py:114
msgid "tag"
msgstr "tag"
msgstr "etiket"
#: documents/models.py:115 documents/models.py:171
msgid "tags"
msgstr "tags"
msgstr "etiketten"
#: documents/models.py:121 documents/models.py:153
msgid "document type"
@@ -285,11 +285,11 @@ msgstr "zit in \"Postvak in\""
#: documents/models.py:380
msgid "has tag"
msgstr "heeft tag"
msgstr "heeft etiket"
#: documents/models.py:381
msgid "has any tag"
msgstr "heeft elke tag"
msgstr "heeft één van de etiketten"
#: documents/models.py:382
msgid "created before"
@@ -329,7 +329,7 @@ msgstr "gewijzigd na"
#: documents/models.py:391
msgid "does not have tag"
msgstr "heeft geen tag"
msgstr "heeft geen etiket"
#: documents/models.py:402
msgid "rule type"
@@ -347,6 +347,46 @@ msgstr "filterregel"
msgid "filter rules"
msgstr "filterregels"
#: documents/templates/index.html:20
msgid "Paperless-ng is loading..."
msgstr "Paperless-ng is aan het laden..."
#: documents/templates/registration/logged_out.html:13
msgid "Paperless-ng signed out"
msgstr "Paperless-ng - afmelden"
#: documents/templates/registration/logged_out.html:41
msgid "You have been successfully logged out. Bye!"
msgstr "Je bent nu afgemeld. Tot later!"
#: documents/templates/registration/logged_out.html:42
msgid "Sign in again"
msgstr "Meld je opnieuw aan"
#: documents/templates/registration/login.html:13
msgid "Paperless-ng sign in"
msgstr "Paperless-ng - aanmelden"
#: documents/templates/registration/login.html:42
msgid "Please sign in."
msgstr "Gelieve aan te melden."
#: documents/templates/registration/login.html:45
msgid "Your username and password didn't match. Please try again."
msgstr "Je gebruikersnaam en wachtwoord komen niet overeen. Probeer opnieuw."
#: documents/templates/registration/login.html:48
msgid "Username"
msgstr "Gebruikersnaam"
#: documents/templates/registration/login.html:49
msgid "Password"
msgstr "Wachtwoord"
#: documents/templates/registration/login.html:54
msgid "Sign in"
msgstr "Aanmelden"
#: paperless/settings.py:268
msgid "English"
msgstr "Engels"
@@ -469,79 +509,79 @@ msgstr "email-regels"
msgid "Only process attachments."
msgstr "Alleen bijlagen verwerken"
#: paperless_mail/models.py:70
#: paperless_mail/models.py:68
msgid "Process all files, including 'inline' attachments."
msgstr "Verwerk alle bestanden, inclusief 'inline' bijlagen."
#: paperless_mail/models.py:80
#: paperless_mail/models.py:78
msgid "Mark as read, don't process read mails"
msgstr "Markeer als gelezen, verwerk geen gelezen mails"
#: paperless_mail/models.py:81
#: paperless_mail/models.py:79
msgid "Flag the mail, don't process flagged mails"
msgstr "Markeer de mail, verwerk geen mails met markering"
#: paperless_mail/models.py:82
#: paperless_mail/models.py:80
msgid "Move to specified folder"
msgstr "Verplaats naar gegeven map"
#: paperless_mail/models.py:83
#: paperless_mail/models.py:81
msgid "Delete"
msgstr "Verwijder"
#: paperless_mail/models.py:90
#: paperless_mail/models.py:88
msgid "Use subject as title"
msgstr "Gebruik onderwerp als titel"
#: paperless_mail/models.py:91
#: paperless_mail/models.py:89
msgid "Use attachment filename as title"
msgstr "Gebruik naam van bijlage als titel"
#: paperless_mail/models.py:101
#: paperless_mail/models.py:99
msgid "Do not assign a correspondent"
msgstr "Wijs geen correspondent toe"
#: paperless_mail/models.py:103
#: paperless_mail/models.py:101
msgid "Use mail address"
msgstr "Gebruik het email-adres"
#: paperless_mail/models.py:105
#: paperless_mail/models.py:103
msgid "Use name (or mail address if not available)"
msgstr "Gebruik de naam, en anders het email-adres"
#: paperless_mail/models.py:107
#: paperless_mail/models.py:105
msgid "Use correspondent selected below"
msgstr "Gebruik de hieronder aangeduide correspondent"
#: paperless_mail/models.py:115
#: paperless_mail/models.py:113
msgid "order"
msgstr "volgorde"
#: paperless_mail/models.py:122
#: paperless_mail/models.py:120
msgid "account"
msgstr "account"
#: paperless_mail/models.py:126
#: paperless_mail/models.py:124
msgid "folder"
msgstr "map"
#: paperless_mail/models.py:130
#: paperless_mail/models.py:128
msgid "filter from"
msgstr "filter afzender"
#: paperless_mail/models.py:133
#: paperless_mail/models.py:131
msgid "filter subject"
msgstr "filter onderwerp"
#: paperless_mail/models.py:136
#: paperless_mail/models.py:134
msgid "filter body"
msgstr "filter inhoud"
#: paperless_mail/models.py:140
#: paperless_mail/models.py:138
msgid "filter attachment filename"
msgstr "Filter bestandsnaam van bijlage"
#: paperless_mail/models.py:142
#: paperless_mail/models.py:140
msgid ""
"Only consume documents which entirely match this filename if specified. "
"Wildcards such as *.pdf or *invoice* are allowed. Case insensitive."
@@ -550,19 +590,19 @@ msgstr ""
" kunt jokertekens gebruiken, zoals *.pdf of *factuur*. Dit is niet "
"hoofdlettergevoelig."
#: paperless_mail/models.py:148
#: paperless_mail/models.py:146
msgid "maximum age"
msgstr "Maximale leeftijd"
#: paperless_mail/models.py:150
#: paperless_mail/models.py:148
msgid "Specified in days."
msgstr "Aangegeven in dagen"
#: paperless_mail/models.py:153
#: paperless_mail/models.py:151
msgid "attachment type"
msgstr "Type bijlage"
#: paperless_mail/models.py:156
#: paperless_mail/models.py:154
msgid ""
"Inline attachments include embedded images, so it's best to combine this "
"option with a filename filter."
@@ -570,15 +610,15 @@ msgstr ""
"\"Inline\" bijlagen bevatten vaak ook afbeeldingen. In dit geval valt het "
"aan te raden om ook een filter voor de bestandsnaam op te geven."
#: paperless_mail/models.py:161
#: paperless_mail/models.py:159
msgid "action"
msgstr "actie"
#: paperless_mail/models.py:167
#: paperless_mail/models.py:165
msgid "action parameter"
msgstr "actie parameters"
#: paperless_mail/models.py:169
#: paperless_mail/models.py:167
msgid ""
"Additional parameter for the action selected above, i.e., the target folder "
"of the move to folder action."
@@ -586,22 +626,22 @@ msgstr ""
"Extra parameters voor de hierboven gekozen actie, met andere woorden: de "
"bestemmingsmap voor de verplaats-actie."
#: paperless_mail/models.py:175
#: paperless_mail/models.py:173
msgid "assign title from"
msgstr "wijs titel toe van"
#: paperless_mail/models.py:185
#: paperless_mail/models.py:183
msgid "assign this tag"
msgstr "wijs dit etiket toe"
#: paperless_mail/models.py:193
#: paperless_mail/models.py:191
msgid "assign this document type"
msgstr "wijs dit documenttype toe"
#: paperless_mail/models.py:197
#: paperless_mail/models.py:195
msgid "assign correspondent from"
msgstr "wijs correspondent toe van"
#: paperless_mail/models.py:207
#: paperless_mail/models.py:205
msgid "assign this correspondent"
msgstr "wijs deze correspondent toe"

View File

@@ -90,7 +90,6 @@ INSTALLED_APPS = [
"documents.apps.DocumentsConfig",
"paperless_tesseract.apps.PaperlessTesseractConfig",
"paperless_text.apps.PaperlessTextConfig",
"paperless_tika.apps.PaperlessTikaConfig",
"paperless_mail.apps.PaperlessMailConfig",
"django.contrib.admin",
@@ -180,6 +179,12 @@ if ENABLE_HTTP_REMOTE_USER:
'rest_framework.authentication.RemoteUserAuthentication'
)
# X-Frame options for embedded PDF display:
if DEBUG:
X_FRAME_OPTIONS = 'ANY'
else:
X_FRAME_OPTIONS = 'SAMEORIGIN'
# We allow CORS from localhost:8080
CORS_ALLOWED_ORIGINS = tuple(os.getenv("PAPERLESS_CORS_ALLOWED_HOSTS", "http://localhost:8000").split(","))
@@ -254,6 +259,7 @@ if os.getenv("PAPERLESS_DBHOST"):
"NAME": os.getenv("PAPERLESS_DBNAME", "paperless"),
"USER": os.getenv("PAPERLESS_DBUSER", "paperless"),
"PASSWORD": os.getenv("PAPERLESS_DBPASS", "paperless"),
'OPTIONS': {'sslmode': os.getenv("PAPERLESS_DBSSLMODE", "prefer")},
}
if os.getenv("PAPERLESS_DBPORT"):
DATABASES["default"]["PORT"] = os.getenv("PAPERLESS_DBPORT")
@@ -308,7 +314,7 @@ LOGGING = {
"class": "documents.loggers.PaperlessHandler",
},
"console": {
"level": "INFO",
"level": "DEBUG" if DEBUG else "INFO",
"class": "logging.StreamHandler",
"formatter": "verbose",
}
@@ -345,10 +351,13 @@ LOGGING = {
# Favors threads per worker on smaller systems and never exceeds cpu_count()
# in total.
def default_task_workers():
# always leave one core open
available_cores = max(multiprocessing.cpu_count() - 1, 1)
try:
return max(
math.floor(math.sqrt(multiprocessing.cpu_count())),
math.floor(math.sqrt(available_cores)),
1
)
except NotImplementedError:
@@ -365,17 +374,19 @@ Q_CLUSTER = {
}
def default_threads_per_worker():
def default_threads_per_worker(task_workers):
# always leave one core open
available_cores = max(multiprocessing.cpu_count() - 1, 1)
try:
return max(
math.floor(multiprocessing.cpu_count() / TASK_WORKERS),
math.floor(available_cores / task_workers),
1
)
except NotImplementedError:
return 1
THREADS_PER_WORKER = os.getenv("PAPERLESS_THREADS_PER_WORKER", default_threads_per_worker())
THREADS_PER_WORKER = os.getenv("PAPERLESS_THREADS_PER_WORKER", default_threads_per_worker(TASK_WORKERS))
###############################################################################
# Paperless Specific Settings #
@@ -461,6 +472,9 @@ PAPERLESS_TIKA_GOTENBERG_ENDPOINT = os.getenv(
"PAPERLESS_TIKA_GOTENBERG_ENDPOINT", "http://localhost:3000"
)
if PAPERLESS_TIKA_ENABLED:
INSTALLED_APPS.append("paperless_tika.apps.PaperlessTikaConfig")
# List dates that should be ignored when trying to parse date from document text
IGNORE_DATES = set()
for s in os.getenv("PAPERLESS_IGNORE_DATES", "").split(","):

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