Compare commits

..

378 Commits

Author SHA1 Message Date
Trenton H
e5106bdca0 Updates the version strings to 1.10.0 2022-11-09 14:00:09 -08:00
Trenton H
ba1366f49a Merge branch 'dev' into beta 2022-11-09 13:51:10 -08:00
shamoon
f3b3db30a2 Merge pull request #1689 from paperless-ngx/l10n_dev
New Crowdin updates
2022-11-09 13:45:25 -08:00
Michael Shamoon
69241ce394 Merge branch 'dev' into l10n_dev 2022-11-09 13:43:29 -08:00
Trenton H
10f6195bac Always use pikepdf, then pdf2image if needed to check for barcodes instead of requiring/allowing configuration 2022-11-09 13:01:39 -08:00
Trenton H
1d0cf77e7e Downgrades cryptography to the last built version on piwheels 2022-11-09 12:12:23 -08:00
Trenton H
beea3eb7eb Releases constraints on some packages and updates everything 2022-11-09 08:51:46 -08:00
Trenton H
a7b5b98174 Fixes the paths to binaries 2022-11-08 08:52:01 -08:00
Trenton Holmes
046d43fbe8 Limits tamper check find to depth 1, as only files at the first level could be run 2022-11-08 08:52:01 -08:00
Trenton Holmes
8023aae738 Adds local and readonly to almost everything. Fully qualifies the path to binaries 2022-11-08 08:52:01 -08:00
Trenton Holmes
2a9bb55559 It's not a customer 2022-11-08 08:52:01 -08:00
Trenton Holmes
e635bfedc5 Corrects the ls command redirection 2022-11-08 08:52:01 -08:00
Trenton Holmes
be64552092 Adds an untested custom startup functionality 2022-11-08 08:52:01 -08:00
Michael Shamoon
91a2dedfec Celery JSON serializable object for override_tag_ids 2022-11-07 07:41:23 -08:00
Paperless-ngx Translation Bot [bot]
069e0a1903 New translations messages.xlf (Serbian (Latin))
[ci skip]
2022-11-06 05:11:35 -08:00
shamoon
39149a891c Merge pull request #1934 from paperless-ngx/fix/issue-1933
Fix mail consumption broken in `dev` after move to celery
2022-11-05 21:47:39 -07:00
Michael Shamoon
daa49ee7c8 Remote outdated task_name parameter 2022-11-05 17:51:56 -07:00
Trenton H
7e3e0a0fa6 Refresh the instance from the database before doing any file handling with it 2022-11-03 11:32:27 -07:00
Trenton Holmes
3c325582d9 Try waiting a little bit after a parser error during the live testing 2022-11-02 15:55:12 -07:00
Paperless-ngx Translation Bot [bot]
e5012cdc5f New translations messages.xlf (Portuguese)
[ci skip]
2022-11-02 05:07:00 -07:00
dependabot[bot]
853d13b6f2 Bump tj-actions/changed-files from 32 to 34
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 32 to 34.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](https://github.com/tj-actions/changed-files/compare/v32...v34)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 15:34:17 -07:00
dependabot[bot]
449fa9bf48 Bump scikit-learn from 1.1.2 to 1.1.3
Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 1.1.2 to 1.1.3.
- [Release notes](https://github.com/scikit-learn/scikit-learn/releases)
- [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.1.2...1.1.3)

---
updated-dependencies:
- dependency-name: scikit-learn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 14:51:50 -07:00
dependabot[bot]
0a6828517a Merge pull request #1910 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/angular/platform-browser-14.2.8 2022-11-01 21:18:19 +00:00
shamoon
8585e77ccd Merge pull request #1911 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/ngx-ui-tour-ng-bootstrap-11.1.0
Bump ngx-ui-tour-ng-bootstrap from 11.0.0 to 11.1.0 in /src-ui
2022-11-01 14:08:56 -07:00
Michael Shamoon
db32431bcc Bump angular packages 2022-11-01 14:07:55 -07:00
dependabot[bot]
e2d826b4ea Bump ngx-ui-tour-ng-bootstrap from 11.0.0 to 11.1.0 in /src-ui
Bumps [ngx-ui-tour-ng-bootstrap](https://github.com/hakimio/ngx-ui-tour) from 11.0.0 to 11.1.0.
- [Release notes](https://github.com/hakimio/ngx-ui-tour/releases)
- [Commits](https://github.com/hakimio/ngx-ui-tour/commits)

---
updated-dependencies:
- dependency-name: ngx-ui-tour-ng-bootstrap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 21:05:11 +00:00
shamoon
06f1a4f744 Merge pull request #1914 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/jest-environment-jsdom-29.2.2
Bump jest-environment-jsdom from 29.1.2 to 29.2.2 in /src-ui
2022-11-01 14:02:59 -07:00
dependabot[bot]
d09bb563a7 Bump jest-environment-jsdom from 29.1.2 to 29.2.2 in /src-ui
Bumps [jest-environment-jsdom](https://github.com/facebook/jest/tree/HEAD/packages/jest-environment-jsdom) from 29.1.2 to 29.2.2.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.2.2/packages/jest-environment-jsdom)

---
updated-dependencies:
- dependency-name: jest-environment-jsdom
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 20:17:34 +00:00
dependabot[bot]
0fafecc6a4 Bump @angular/platform-browser from 14.2.4 to 14.2.8 in /src-ui
Bumps [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) from 14.2.4 to 14.2.8.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/14.2.8/packages/platform-browser)

---
updated-dependencies:
- dependency-name: "@angular/platform-browser"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 20:16:10 +00:00
Trenton Holmes
66b60654d9 Fixes missing lxml and tweaks commenting 2022-11-01 12:42:03 -07:00
Trenton Holmes
b479027f3d Adds a couple build args to force pikepdf to rebuild dependent wheels if the versions change 2022-11-01 12:42:03 -07:00
Trenton Holmes
0a81439415 No need for an extra import, the object is smart already 2022-11-01 08:44:30 -07:00
Trenton Holmes
4fcaa72886 Adds more options for the filename formatting 2022-11-01 08:44:30 -07:00
dependabot[bot]
9acb00dcba Bump pillow from 9.2.0 to 9.3.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 9.2.0 to 9.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.2.0...9.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-01 08:04:25 -07:00
dependabot[bot]
ebc453d720 Bump pytest from 7.1.3 to 7.2.0
Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.1.3 to 7.2.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/7.1.3...7.2.0)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-31 19:00:17 -07:00
dependabot[bot]
770d72c3e8 Bump tox from 3.26.0 to 3.27.0
Bumps [tox](https://github.com/tox-dev/tox) from 3.26.0 to 3.27.0.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/master/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/3.26.0...3.27.0)

---
updated-dependencies:
- dependency-name: tox
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-31 17:56:19 -07:00
Max Bachmann
e97c04c03d directly use rapidfuzz 2022-10-31 13:17:10 -07:00
shamoon
34a0111ff5 update logs section 2022-10-31 13:06:17 -07:00
Trenton H
9214b41255 Fixes deleting images if the branch API returns an error code and makes the error code a failure 2022-10-31 12:46:25 -07:00
shamoon
0d941bfb05 Merge pull request #1886 from paperless-ngx/1.9.2-ui-tweaks
Feature: 1.9.2 UI tweaks
2022-10-31 12:42:13 -07:00
Paperless-ngx Translation Bot [bot]
4c68d28a6f New translations messages.xlf (German)
[ci skip]
2022-10-30 15:40:57 -07:00
shamoon
b511b084d0 Update matrix url
[ci skip]
2022-10-30 06:48:41 -07:00
Paperless-ngx Translation Bot [bot]
f2939583d7 New translations messages.xlf (Arabic)
[ci skip]
2022-10-29 23:28:44 -07:00
Paperless-ngx Translation Bot [bot]
5951b0d946 New translations messages.xlf (Romanian)
[ci skip]
2022-10-29 23:28:43 -07:00
Paperless-ngx Translation Bot [bot]
bf088c427a New translations messages.xlf (French)
[ci skip]
2022-10-29 23:28:42 -07:00
Paperless-ngx Translation Bot [bot]
ef1eead52e New translations messages.xlf (Spanish)
[ci skip]
2022-10-29 23:28:40 -07:00
Paperless-ngx Translation Bot [bot]
f77a431554 New translations messages.xlf (Belarusian)
[ci skip]
2022-10-29 23:28:39 -07:00
Paperless-ngx Translation Bot [bot]
cb930d1e76 New translations messages.xlf (Czech)
[ci skip]
2022-10-29 23:28:38 -07:00
Paperless-ngx Translation Bot [bot]
c4db7f7b6d New translations messages.xlf (Danish)
[ci skip]
2022-10-29 23:28:36 -07:00
Paperless-ngx Translation Bot [bot]
a24524fb81 New translations messages.xlf (Finnish)
[ci skip]
2022-10-29 23:28:35 -07:00
Paperless-ngx Translation Bot [bot]
9e253fcc62 New translations messages.xlf (Hebrew)
[ci skip]
2022-10-29 23:28:34 -07:00
Paperless-ngx Translation Bot [bot]
490dee2e90 New translations messages.xlf (Italian)
[ci skip]
2022-10-29 23:28:32 -07:00
Paperless-ngx Translation Bot [bot]
a2032b9979 New translations messages.xlf (Dutch)
[ci skip]
2022-10-29 23:28:31 -07:00
Paperless-ngx Translation Bot [bot]
1561f561d9 New translations messages.xlf (German)
[ci skip]
2022-10-29 23:28:30 -07:00
Paperless-ngx Translation Bot [bot]
6246bfaf28 New translations messages.xlf (Norwegian)
[ci skip]
2022-10-29 23:28:29 -07:00
Paperless-ngx Translation Bot [bot]
57763d0c0b New translations messages.xlf (Portuguese)
[ci skip]
2022-10-29 23:28:28 -07:00
Paperless-ngx Translation Bot [bot]
d441c8a26e New translations messages.xlf (Slovenian)
[ci skip]
2022-10-29 23:28:26 -07:00
Paperless-ngx Translation Bot [bot]
54abeff63a New translations messages.xlf (Swedish)
[ci skip]
2022-10-29 23:28:25 -07:00
Paperless-ngx Translation Bot [bot]
dc045761d9 New translations messages.xlf (Turkish)
[ci skip]
2022-10-29 23:28:24 -07:00
Paperless-ngx Translation Bot [bot]
393c208cfd New translations messages.xlf (Chinese Simplified)
[ci skip]
2022-10-29 23:28:22 -07:00
Paperless-ngx Translation Bot [bot]
11f9bc898a New translations messages.xlf (Portuguese, Brazilian)
[ci skip]
2022-10-29 23:28:21 -07:00
Paperless-ngx Translation Bot [bot]
a31089ca6e New translations messages.xlf (Croatian)
[ci skip]
2022-10-29 23:28:19 -07:00
Paperless-ngx Translation Bot [bot]
0355fa2cb6 New translations messages.xlf (Luxembourgish)
[ci skip]
2022-10-29 23:28:18 -07:00
Paperless-ngx Translation Bot [bot]
73b945a9f3 New translations messages.xlf (Serbian (Latin))
[ci skip]
2022-10-29 23:28:17 -07:00
Paperless-ngx Translation Bot [bot]
396dd9ae1c New translations messages.xlf (Polish)
[ci skip]
2022-10-29 23:28:16 -07:00
Paperless-ngx Translation Bot [bot]
b026dcc6ae New translations messages.xlf (Russian)
[ci skip]
2022-10-29 23:28:15 -07:00
Michael Shamoon
36d2286d03 Update messages.xlf 2022-10-29 23:19:35 -07:00
shamoon
ea992e92f5 Merge pull request #1860 from paperless-ngx/dependabot/pip/dev/zipp-3.10.0
Bump zipp from 3.9.0 to 3.10.0
2022-10-29 23:17:02 -07:00
shamoon
d6b5c733f3 Merge pull request #1868 from paperless-ngx/fix/issue-1866
Fix: independent control of saved views
2022-10-29 23:15:28 -07:00
Michael Shamoon
7efdce44f7 Implement warning on close unsaved view 2022-10-29 23:14:33 -07:00
Michael Shamoon
9b3243533c better positioning in clearable badges 2022-10-29 15:29:50 -07:00
Michael Shamoon
0993fc07a3 Add "clearable" badge 2022-10-29 15:03:27 -07:00
Michael Shamoon
28f7b0dc13 Use check badge for dropdowns 2022-10-29 14:07:45 -07:00
Michael Shamoon
bd64684fa4 support esc and x button for main search bar 2022-10-29 14:07:31 -07:00
Michael Shamoon
a9abffaddc Support keyboard esc and X button for filter editor text field 2022-10-29 14:07:31 -07:00
Michael Shamoon
89e0f8e3ef fix filterable dropdown corner radius 2022-10-29 14:07:31 -07:00
shamoon
9e91440245 Merge pull request #1865 from paperless-ngx/fix-relative-dates
Fix: frontend relative date searches
2022-10-29 14:05:35 -07:00
shamoon
4a24ba51c5 Merge pull request #1876 from astubenbord/main
Added new application to list of affiliated projects
2022-10-28 22:22:08 -07:00
Anton Stubenbord
d5fb98b7c4 Added new application to list of affiliated projects 2022-10-28 22:21:27 -07:00
Sblop
cda0a19b99 Update settings.py
Comment too long.
2022-10-28 14:40:48 -07:00
Sblop
932a285b82 Update settings.py 2022-10-28 14:40:48 -07:00
Sblop
c414de9c35 Update settings.py
Django gives a system error on MariaDB on VARCHARs longer than 255 chars. This was a limitation in older versions of mysql.
Meaning: You cannot run Paperless-NGX on older version were this limitation were present, meaning Django plays it extremely safe by giving an error.
This fixes this problem.
2022-10-28 14:40:48 -07:00
Trenton Holmes
9b82ab95fb Updates to the latest pre-commit hook versions 2022-10-27 13:50:08 -07:00
Trenton Holmes
aa5aff246b Locks pipenv to the latest, last working version 2022-10-27 13:50:08 -07:00
Paperless-ngx Translation Bot [bot]
c6a484439d New translations django.po (Dutch)
[ci skip]
2022-10-27 02:51:17 -07:00
Michael Shamoon
1b55717cc7 Allow independent saved view control 2022-10-26 12:54:44 -07:00
Michael Shamoon
dc1da7cb24 Retain saved view filters when changing a saved view 2022-10-26 10:39:09 -07:00
Michael Shamoon
8652b7ddb0 Fix switch from title_content search 2022-10-25 15:31:01 -07:00
Michael Shamoon
84b3fee0f9 Fix losing search when adding relative date 2022-10-25 13:43:13 -07:00
Michael Shamoon
b52cb193e1 Remove relative date query strings from dropdown 2022-10-25 13:34:36 -07:00
Michael Shamoon
6a00d5e08a Implement relative date querying 2022-10-25 12:45:15 -07:00
dependabot[bot]
3357fa19f3 Bump pytest-env from 0.6.2 to 0.8.1
Bumps [pytest-env](https://github.com/pytest-dev/pytest-env) from 0.6.2 to 0.8.1.
- [Release notes](https://github.com/pytest-dev/pytest-env/releases)
- [Commits](https://github.com/pytest-dev/pytest-env/compare/0.6.2...0.8.1)

---
updated-dependencies:
- dependency-name: pytest-env
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-25 07:26:43 -07:00
shamoon
37a892d461 Merge pull request #1858 from paperless-ngx/fix/1850-barcode-password
Bugfix: Handle password protected PDFs during barcode detection
2022-10-24 23:17:48 -07:00
dependabot[bot]
f149f9ccb1 Bump zipp from 3.9.0 to 3.10.0
Bumps [zipp](https://github.com/jaraco/zipp) from 3.9.0 to 3.10.0.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/CHANGES.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.9.0...v3.10.0)

---
updated-dependencies:
- dependency-name: zipp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 20:34:44 +00:00
Trenton H
d52fbbb040 More smoothly handle the case of a password protected PDF for barcodes 2022-10-24 13:16:14 -07:00
Paperless-ngx Translation Bot [bot]
446eca4ac3 New translations messages.xlf (German)
[ci skip]
2022-10-24 13:11:59 -07:00
Trenton H
f8ce6285df Allows using pdf2image instead of pikepdf if desired 2022-10-24 09:58:34 -07:00
Trenton H
0a19ad4edb Resolves the conflicts with a lock 2022-10-24 09:17:38 -07:00
Trenton H
ab69961b5c Integrates an optional starting of Flower into the Docker image 2022-10-24 09:17:38 -07:00
Michael Shamoon
1400dba12c Fix frontend task name display 2022-10-24 09:10:10 -07:00
Trenton H
a72cc5da83 Connects up the celery signals to support pending, started and success/failure, without relying on django-celery-results 2022-10-24 09:10:10 -07:00
Paperless-ngx Translation Bot [bot]
630b8fa675 New translations messages.xlf (Arabic)
[ci skip]
2022-10-23 22:19:58 -07:00
Paperless-ngx Translation Bot [bot]
43514ad477 New translations messages.xlf (Romanian)
[ci skip]
2022-10-23 22:19:57 -07:00
Paperless-ngx Translation Bot [bot]
0ed95547d5 New translations messages.xlf (French)
[ci skip]
2022-10-23 22:19:56 -07:00
Paperless-ngx Translation Bot [bot]
250e2d54b4 New translations messages.xlf (Spanish)
[ci skip]
2022-10-23 22:19:54 -07:00
Paperless-ngx Translation Bot [bot]
b02cd541a8 New translations messages.xlf (Belarusian)
[ci skip]
2022-10-23 22:19:53 -07:00
Paperless-ngx Translation Bot [bot]
94f2a2ce33 New translations messages.xlf (Czech)
[ci skip]
2022-10-23 22:19:52 -07:00
Paperless-ngx Translation Bot [bot]
6a7c0279bf New translations messages.xlf (Danish)
[ci skip]
2022-10-23 22:19:50 -07:00
Paperless-ngx Translation Bot [bot]
5ba11b1161 New translations messages.xlf (Finnish)
[ci skip]
2022-10-23 22:19:49 -07:00
Paperless-ngx Translation Bot [bot]
917bce301c New translations messages.xlf (Hebrew)
[ci skip]
2022-10-23 22:19:48 -07:00
Paperless-ngx Translation Bot [bot]
3fb32c5cf1 New translations messages.xlf (Italian)
[ci skip]
2022-10-23 22:19:47 -07:00
Paperless-ngx Translation Bot [bot]
19a1bf0f5f New translations messages.xlf (Dutch)
[ci skip]
2022-10-23 22:19:45 -07:00
Paperless-ngx Translation Bot [bot]
abe8e678ea New translations messages.xlf (German)
[ci skip]
2022-10-23 22:19:44 -07:00
Paperless-ngx Translation Bot [bot]
d16dee61fa New translations messages.xlf (Norwegian)
[ci skip]
2022-10-23 22:19:43 -07:00
Paperless-ngx Translation Bot [bot]
e7b22b15c6 New translations messages.xlf (Portuguese)
[ci skip]
2022-10-23 22:19:41 -07:00
Paperless-ngx Translation Bot [bot]
0f62770bce New translations messages.xlf (Slovenian)
[ci skip]
2022-10-23 22:19:40 -07:00
Paperless-ngx Translation Bot [bot]
c67cff4f0e New translations messages.xlf (Swedish)
[ci skip]
2022-10-23 22:19:39 -07:00
Paperless-ngx Translation Bot [bot]
dccb9227a2 New translations messages.xlf (Turkish)
[ci skip]
2022-10-23 22:19:37 -07:00
Paperless-ngx Translation Bot [bot]
b7db1cf2c1 New translations messages.xlf (Chinese Simplified)
[ci skip]
2022-10-23 22:19:36 -07:00
Paperless-ngx Translation Bot [bot]
3d683d13b8 New translations messages.xlf (Portuguese, Brazilian)
[ci skip]
2022-10-23 22:19:35 -07:00
Paperless-ngx Translation Bot [bot]
310c89ffdf New translations messages.xlf (Croatian)
[ci skip]
2022-10-23 22:19:33 -07:00
Paperless-ngx Translation Bot [bot]
dd069d753b New translations messages.xlf (Luxembourgish)
[ci skip]
2022-10-23 22:19:32 -07:00
Paperless-ngx Translation Bot [bot]
27a24f10b3 New translations messages.xlf (Serbian (Latin))
[ci skip]
2022-10-23 22:19:31 -07:00
Paperless-ngx Translation Bot [bot]
b6b9bf0e3c New translations messages.xlf (Polish)
[ci skip]
2022-10-23 22:19:29 -07:00
Paperless-ngx Translation Bot [bot]
5fc7350ea0 New translations messages.xlf (Russian)
[ci skip]
2022-10-23 22:19:28 -07:00
Michael Shamoon
9b01c96846 Grammar error, updated frontend translation
https://crowdin.com/translate/paperless-ngx/16/en-it?filter=basic&value=0#
2022-10-23 22:11:24 -07:00
Paperless-ngx Translation Bot [bot]
96aba9acdc New translations messages.xlf (Italian)
[ci skip]
2022-10-23 07:21:03 -07:00
Paperless-ngx Translation Bot [bot]
43e78c8e69 New translations messages.xlf (Italian)
[ci skip]
2022-10-23 06:22:29 -07:00
Paperless-ngx Translation Bot [bot]
47d0c77970 New translations messages.xlf (Dutch)
[ci skip]
2022-10-22 00:48:54 -07:00
Paperless-ngx Translation Bot [bot]
f825b5772d New translations messages.xlf (Arabic)
[ci skip]
2022-10-20 17:27:27 -07:00
Paperless-ngx Translation Bot [bot]
f9cb95e79c New translations messages.xlf (Arabic)
[ci skip]
2022-10-20 16:30:33 -07:00
Paperless-ngx Translation Bot [bot]
73845ef968 New translations django.po (Arabic)
[ci skip]
2022-10-20 16:30:32 -07:00
Michael Shamoon
8be6c707de Update django.po
[ci skip]
2022-10-20 15:33:16 -07:00
Michael Shamoon
60f76d3e1f rename backend Arabic translation file
[ci skip]
2022-10-20 15:31:28 -07:00
Paperless-ngx Translation Bot [bot]
912dc9a847 New translations messages.xlf (Arabic)
[ci skip]
2022-10-20 15:29:29 -07:00
Paperless-ngx Translation Bot [bot]
70ef6412eb New translations django.po (Arabic)
[ci skip]
2022-10-20 15:28:49 -07:00
Michael Shamoon
99db828d49 Merge branch 'dev' of github.com:paperless-ngx/paperless-ngx into dev 2022-10-20 15:28:15 -07:00
Michael Shamoon
3c5b647303 rename Arabic translation file
[skip ci]
2022-10-20 15:28:10 -07:00
Trenton Holmes
d1aa08850d Reverts the change around skip_noarchive to align with how it is documented to work 2022-10-20 13:34:41 -07:00
dependabot[bot]
53e8d84af2 Bump sphinx from 5.2.3 to 5.3.0
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 5.2.3 to 5.3.0.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v5.2.3...v5.3.0)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-20 13:10:14 -07:00
Trenton H
4c7242df6d Even more commenting 2022-10-20 12:42:40 -07:00
Trenton H
a231b92644 Corrects the script name 2022-10-20 12:42:40 -07:00
Trenton H
c3a62268c7 Simplifies the image tag cleanup into classes, fixes the library images caring about feature branches 2022-10-20 12:42:40 -07:00
Michael Shamoon
69913ae250 Remove legacy translation files
[ci ckip]
2022-10-19 21:44:47 -07:00
Michael Shamoon
a2da1acdd7 Fix button styles broken by bootstrap update 2022-10-18 14:30:31 -07:00
Paperless-ngx Translation Bot [bot]
a67ea8ffd9 New translations messages.xlf (Czech)
[ci skip]
2022-10-18 14:06:43 -07:00
Paperless-ngx Translation Bot [bot]
0050a20710 New translations django.po (Czech)
[ci skip]
2022-10-18 13:06:11 -07:00
Paperless-ngx Translation Bot [bot]
ad65360a55 New translations messages.xlf (Czech)
[ci skip]
2022-10-18 13:06:10 -07:00
shamoon
15bcb2491c Merge pull request #1792 from paperless-ngx/fix/issue-1791
Fix: missing loadViewConfig breaks loading saved view
2022-10-17 14:30:30 -07:00
Paperless-ngx Translation Bot [bot]
2a2fa90cf9 New translations messages.xlf (French)
[ci skip]
2022-10-17 12:49:11 -07:00
Paperless-ngx Translation Bot [bot]
6e9fbdb8ed New translations messages.xlf (Belarusian)
[ci skip]
2022-10-16 07:46:24 -07:00
Paperless-ngx Translation Bot [bot]
097ab55f7a New translations django.po (Belarusian)
[ci skip]
2022-10-16 06:46:10 -07:00
Paperless-ngx Translation Bot [bot]
dddb82af23 New translations messages.xlf (Belarusian)
[ci skip]
2022-10-16 06:46:09 -07:00
Paperless-ngx Translation Bot [bot]
377c37dfab New translations django.po (Belarusian)
[ci skip]
2022-10-16 05:44:52 -07:00
Trenton H
0df0deb445 Downgrades and locks channels-redis to 3.4.1 to resolve issues with async_to_sync 2022-10-14 16:48:48 -07:00
Paperless-ngx Translation Bot [bot]
d05f803c16 New translations messages.xlf (French)
[ci skip]
2022-10-14 02:11:20 -07:00
Paperless-ngx Translation Bot [bot]
25e2ca5295 New translations messages.xlf (French)
[ci skip]
2022-10-14 01:01:03 -07:00
Michael Shamoon
02e8157fb9 Fix missing loadViewConfig breaks loading saved view 2022-10-13 12:27:41 -07:00
Paperless-ngx Translation Bot [bot]
708cac4683 New translations messages.xlf (German)
[ci skip]
2022-10-13 05:45:07 -07:00
Paperless-ngx Translation Bot [bot]
66d79ccd3f New translations messages.xlf (German)
[ci skip]
2022-10-13 04:41:06 -07:00
Paperless-ngx Translation Bot [bot]
9c1195fc59 New translations messages.xlf (Serbian (Latin))
[ci skip]
2022-10-12 14:21:46 -07:00
Trenton H
deaf02780e Updates to the new method to set step outputs 2022-10-12 14:04:54 -07:00
Trenton H
5602031b67 Updates to the Docker login v2 action, adds workflow level concurrency 2022-10-12 14:04:54 -07:00
Paperless-ngx Translation Bot [bot]
4c1bf240d6 New translations messages.xlf (Serbian (Latin))
[ci skip]
2022-10-12 12:56:10 -07:00
shamoon
b13ced93ed Merge pull request #1745 from paperless-ngx/fix/1743
Bugfix: Fallback to pdf2image if pikepdf fails
2022-10-12 07:38:43 -07:00
shamoon
87472b31d2 Merge pull request #1780 from paperless-ngx/fix/issue-1647
Documentation: Add note re MS exchange servers
2022-10-11 19:59:46 -07:00
shamoon
7f1d01e443 Merge pull request #1777 from paperless-ngx/fix/issue-1776
Fix: creating new storage path on document edit fails to update menu
2022-10-11 19:59:02 -07:00
Michael Shamoon
1024d7e6e2 Add note re MS exchange servers 2022-10-11 15:12:22 -07:00
Trenton Holmes
4cc2976614 Adds specific handling for CCITT Group 4, which pikepdf decodes, but not correctly 2022-10-11 13:51:14 -07:00
Trenton H
caf4b54bc7 In case pikepdf fails to convert an image to a PIL image, fall back to converting pages to PIL images 2022-10-11 13:51:13 -07:00
shamoon
fb2efe5ab8 Merge pull request #1773 from paperless-ngx/library-updates-fixes
Chore: Python library update + test fixes
2022-10-11 13:36:54 -07:00
Michael Shamoon
98f1722d1b Fix slim sidebar close button position
[ci skip]
2022-10-11 12:34:15 -07:00
Michael Shamoon
a6dc8a373e Fix new storagepaths not showing in doc detail 2022-10-11 12:23:15 -07:00
Trenton H
91e96bc88f Bumps tj-actions/changed-files which should resolve the CI warning about Node 12 2022-10-10 14:45:46 -07:00
Trenton H
8c06858807 Updates workflow to clone all history so changed-files can work (probably) 2022-10-10 14:30:55 -07:00
Trenton H
8025df5fe3 Catch the new error raised by redis when it can't find the broker and stub out the call for testing 2022-10-10 14:21:42 -07:00
Trenton H
5aeb656a48 Fixes usage of a depracated logger method 2022-10-10 14:20:19 -07:00
Trenton H
f96ee4f7a0 Updates the pre-commit hooks to ensure alignment with Pipfile 2022-10-10 14:20:19 -07:00
Trenton H
fcdb1dc30c Adds hiredis extra to redis-py and updates everything 2022-10-10 14:16:14 -07:00
Trenton H
dafefa33d6 Adds step to bare metal setup regarding downloading the required NLTK data 2022-10-10 08:58:23 -07:00
Trenton H
d08eb0c66b Adds skipping of NLTK data download if the feature appears disabled 2022-10-10 08:58:23 -07:00
Trenton H
d1a17480ea Account for plusses in the OCR language setting 2022-10-10 08:58:23 -07:00
Trenton H
1e891414a3 Allows disabling NLTK, adds it as a consideration for low power devices 2022-10-10 08:58:23 -07:00
Trenton Holmes
c44c914d3d Changes the NLTK language to be based on the Tesseract OCR language, with fallback to the default processing 2022-10-10 08:58:23 -07:00
Trenton H
d10d2f5a54 Allows configuration of the NLTK processing language 2022-10-10 08:58:23 -07:00
Trenton Holmes
6523cf0c4b Fixes the download and usage of the downloaded data 2022-10-10 08:58:23 -07:00
Trenton Holmes
1262c121f0 Missed one mock 2022-10-10 08:58:23 -07:00
Trenton Holmes
f7cd6974c5 Mock out the nltk portions so the data doesn't need to be downloaded 2022-10-10 08:58:23 -07:00
Trenton Holmes
a7e1ba82d6 Fixes CI unit testing 2022-10-10 08:58:23 -07:00
Trenton Holmes
d856e48045 Updates the pre-processing of document content to be much more robust, with tokenization, stemming and stop word removal 2022-10-10 08:58:23 -07:00
Paperless-ngx Translation Bot [bot]
87972ee7fe New translations messages.xlf (Arabic, Saudi Arabia)
[ci skip]
2022-10-10 00:36:14 -07:00
Paperless-ngx Translation Bot [bot]
e6332944ce New translations messages.xlf (Romanian)
[ci skip]
2022-10-10 00:36:13 -07:00
Paperless-ngx Translation Bot [bot]
96d7dc273e New translations messages.xlf (French)
[ci skip]
2022-10-10 00:36:12 -07:00
Paperless-ngx Translation Bot [bot]
4f4a08ccc7 New translations messages.xlf (Spanish)
[ci skip]
2022-10-10 00:36:10 -07:00
Paperless-ngx Translation Bot [bot]
1716a11f45 New translations messages.xlf (Belarusian)
[ci skip]
2022-10-10 00:36:09 -07:00
Paperless-ngx Translation Bot [bot]
9ed0d5f7d6 New translations messages.xlf (Czech)
[ci skip]
2022-10-10 00:36:08 -07:00
Paperless-ngx Translation Bot [bot]
9b461990b7 New translations messages.xlf (Danish)
[ci skip]
2022-10-10 00:36:06 -07:00
Paperless-ngx Translation Bot [bot]
8a47e5b8e4 New translations messages.xlf (Finnish)
[ci skip]
2022-10-10 00:36:05 -07:00
Paperless-ngx Translation Bot [bot]
5f12087779 New translations messages.xlf (Hebrew)
[ci skip]
2022-10-10 00:36:04 -07:00
Paperless-ngx Translation Bot [bot]
8aa9a4db65 New translations messages.xlf (Italian)
[ci skip]
2022-10-10 00:36:02 -07:00
Paperless-ngx Translation Bot [bot]
75a54fab2c New translations messages.xlf (Japanese)
[ci skip]
2022-10-10 00:36:01 -07:00
Paperless-ngx Translation Bot [bot]
085b5eb9f1 New translations messages.xlf (German)
[ci skip]
2022-10-10 00:35:59 -07:00
Paperless-ngx Translation Bot [bot]
543e221d3e New translations messages.xlf (Dutch)
[ci skip]
2022-10-10 00:35:58 -07:00
Paperless-ngx Translation Bot [bot]
e25584a202 New translations messages.xlf (Polish)
[ci skip]
2022-10-10 00:35:55 -07:00
Paperless-ngx Translation Bot [bot]
4c268d5883 New translations messages.xlf (Portuguese)
[ci skip]
2022-10-10 00:35:54 -07:00
Paperless-ngx Translation Bot [bot]
bf8fc3ca29 New translations messages.xlf (Slovenian)
[ci skip]
2022-10-10 00:35:53 -07:00
Paperless-ngx Translation Bot [bot]
65c1b84508 New translations messages.xlf (Swedish)
[ci skip]
2022-10-10 00:35:51 -07:00
Paperless-ngx Translation Bot [bot]
f78ade5007 New translations messages.xlf (Turkish)
[ci skip]
2022-10-10 00:35:50 -07:00
Paperless-ngx Translation Bot [bot]
6f73aef262 New translations messages.xlf (Chinese Simplified)
[ci skip]
2022-10-10 00:35:49 -07:00
Paperless-ngx Translation Bot [bot]
4d715ddfbc New translations messages.xlf (Portuguese, Brazilian)
[ci skip]
2022-10-10 00:35:48 -07:00
Paperless-ngx Translation Bot [bot]
cdf99166f4 New translations messages.xlf (Croatian)
[ci skip]
2022-10-10 00:35:46 -07:00
Paperless-ngx Translation Bot [bot]
fc267472b3 New translations messages.xlf (Luxembourgish)
[ci skip]
2022-10-10 00:35:45 -07:00
Paperless-ngx Translation Bot [bot]
a0fde023f5 New translations messages.xlf (Serbian (Latin))
[ci skip]
2022-10-10 00:35:44 -07:00
Paperless-ngx Translation Bot [bot]
4a98156731 New translations messages.xlf (Norwegian)
[ci skip]
2022-10-10 00:35:43 -07:00
Paperless-ngx Translation Bot [bot]
ecb190d1b8 New translations messages.xlf (Russian)
[ci skip]
2022-10-10 00:35:41 -07:00
Michael Shamoon
14d82bd8ff Update frontend translation strings 2022-10-10 00:19:01 -07:00
shamoon
6f50285f47 Merge pull request #1648 from paperless-ngx/feature-use-celery
Feature: Transition to celery for background tasks
2022-10-10 00:07:55 -07:00
Michael Shamoon
836ab7a9e7 prevent duplicate tourAnchor race condition 2022-10-10 00:07:38 -07:00
Michael Shamoon
06d9e38457 Fix tour / slim sidebar compatibility 2022-10-09 23:58:41 -07:00
shamoon
9aa7e8e37e Merge pull request #1768 from paperless-ngx/chore-update-gotenbreg
Chore: Updates Gotenberg versions
2022-10-09 23:51:04 -07:00
shamoon
fa526dd702 Merge pull request #1644 from paperless-ngx/feature/welcome-tour
Feature: UI Welcome Tour
2022-10-09 23:50:39 -07:00
Michael Shamoon
8325dc5977 Slim sidebar compatibility 2022-10-09 23:48:22 -07:00
Michael Shamoon
abd34d8d17 Fix code style 2022-10-09 23:47:45 -07:00
Michael Shamoon
d19b598371 Add highlighting, move some anchors for visibility 2022-10-09 23:47:45 -07:00
Michael Shamoon
833a301948 Update translation strings for welcome tour 2022-10-09 23:47:45 -07:00
Michael Shamoon
0e03633ed0 fix tour placement issues, basic offerTour method 2022-10-09 23:47:45 -07:00
Michael Shamoon
5e45b1f230 Add admin tour step, remove step titles, cleaner look 2022-10-09 23:47:45 -07:00
Michael Shamoon
07e2329068 Complete welcome tour 2022-10-09 23:47:31 -07:00
Michael Shamoon
4e56fe339e Core welcome tour functionality 2022-10-09 23:46:26 -07:00
shamoon
e7ebc33090 Merge pull request #1641 from paperless-ngx/feature/slim-sidebar
Feature: slim sidebar
2022-10-09 23:24:57 -07:00
Michael Shamoon
3b07e0fe15 Fix merge conflict 2022-10-09 23:24:36 -07:00
Michael Shamoon
a246b3b598 Merge branch 'dev' into feature/slim-sidebar 2022-10-09 23:15:48 -07:00
Trenton Holmes
11ab469a39 Updates a few more instances of the container 2022-10-09 18:03:29 -07:00
Trenton Holmes
694ad53ef9 Updates Gotenberg container to the latest 2022-10-09 17:55:09 -07:00
Trenton Holmes
a3be3bb71a Re-lock pipenv version to the last working version 2022-10-09 17:43:58 -07:00
Trenton Holmes
77b3aa5011 Fixes is_relative_to not being availible for 3.8 2022-10-09 17:43:58 -07:00
Trenton Holmes
9aefff38e7 If the original file containing a barcode was in the temporary scratch dir, move the split files to consume dir 2022-10-09 17:43:58 -07:00
shamoon
462b243531 Merge pull request #1754 from NiFNi/feature/change-default-matching-algo
change default matching algo to auto and move to constant
2022-10-08 09:35:30 -07:00
shamoon
430c5c3b87 Merge pull request #1761 from paperless-ngx/docs/lsio-tweak
Documentation: Tweak LinuxServer
2022-10-07 23:45:51 -07:00
Trenton H
97ceb1a8a6 Enable some testing against a real email server to hopefully catch things earlier 2022-10-07 18:28:11 -07:00
Trenton H
55089aab32 Fixes handling of gmail label extension to IMAP 2022-10-07 18:28:11 -07:00
Trenton Holmes
b7c335507f Fixes the LSIO migration setting for the media root 2022-10-07 18:18:47 -07:00
Trenton Holmes
9c0c734b34 Enables some basic live testing against a tika server with actual sample documents to catch some more errors mocking won't catch 2022-10-07 18:06:06 -07:00
Michael Shamoon
7f5a3ca1a3 Fix code style 2022-10-07 06:29:31 -07:00
shamoon
0b5c6d3532 Merge pull request #1731 from paperless-ngx/fix/1624
Documentation: Adds troubleshooting note about Kubernetes and ports
2022-10-06 13:22:58 -07:00
shamoon
5357775d42 Merge pull request #1692 from paperless-ngx/feature-frontend-update-checking
Feature: frontend update checking settings
2022-10-05 13:46:32 -07:00
Trenton H
0f25260163 Updates to qpdf 11.1.1, pikepdf 6.1.0, and a few other updates 2022-10-05 10:03:40 -07:00
shamoon
964cfcd4fb Merge pull request #1744 from paperless-ngx/fix/issue-1725
Fix: allow preview for .csv files
2022-10-04 13:16:18 -07:00
Michael Shamoon
c42388f7e2 Use text mime type for csv files for browser preview
Co-Authored-By: Trenton H <797416+stumpylog@users.noreply.github.com>
Co-Authored-By: bin101 <12427722+bin101@users.noreply.github.com>
2022-10-04 13:01:06 -07:00
Trenton H
ff7d4d15cd Fixes migration error if some tasks are defined already 2022-10-04 07:56:40 -07:00
shamoon
5e4a9311ed Merge branch 'dev' into feature-use-celery 2022-10-03 18:00:54 -07:00
shamoon
4e07280102 Merge pull request #1642 from paperless-ngx/feature-qpdf-11
Feature: Upgrade to qpdf 11, pikepdf 6 & ocrmypdf 14
2022-10-03 17:36:30 -07:00
shamoon
fdac108cab Merge pull request #1733 from paperless-ngx/docs-lsio-migrate
Documentation: LinuxServer.io Migration
2022-10-03 17:07:06 -07:00
dependabot[bot]
318f74b34a Bump myst-parser from 0.18.0 to 0.18.1
Bumps [myst-parser](https://github.com/executablebooks/MyST-Parser) from 0.18.0 to 0.18.1.
- [Release notes](https://github.com/executablebooks/MyST-Parser/releases)
- [Changelog](https://github.com/executablebooks/MyST-Parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/executablebooks/MyST-Parser/compare/v0.18.0...v0.18.1)

---
updated-dependencies:
- dependency-name: myst-parser
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-03 15:51:43 -07:00
Trenton H
788b3a5ba8 Fixes django-q still in the Pipfile.lock 2022-10-03 13:35:01 -07:00
Trenton H
19d4b85961 Fixes up some issues with the migrations and type mismatches 2022-10-03 13:18:25 -07:00
Trenton H
821c14fbce Corrects how the link to example compose files looks 2022-10-03 11:14:18 -07:00
Trenton H
8c03d9c638 Corrects a re-numbered step 2022-10-03 11:11:51 -07:00
Trenton H
174a609449 Adds a few steps for migration from the LinuxServer.io image 2022-10-03 10:16:53 -07:00
Trenton H
5fd394726e Adds troubleshooting note for Kubernetes about needing to set the port again 2022-10-03 09:01:07 -07:00
Michael Shamoon
1f27f7c12d Update paperless-document.ts 2022-10-03 00:27:57 -07:00
Jens van Almsick
ad6ef7314b fix: csv recognition by consumer
paperless-ngx detects the file format via the mime-type based on the response of python-magic which rely on the response of the file command.
In version 5.39 (which is shipped with debian bullseye and I think many more non-rolling distributions) of the file command a *.csv will be detected as "application/csv" instead of "text/csv" as in newer versions.
2022-10-02 16:09:07 -07:00
Paperless-ngx Translation Bot [bot]
98c306a315 New translations messages.xlf (German)
[ci skip]
2022-10-02 13:21:13 -07:00
Michael Shamoon
11ad8ada79 add id to document duplicate error message 2022-10-02 10:27:45 -07:00
Trenton Holmes
905b28c1d7 When a document is a duplicate, include the title of the existing document in the fail message 2022-10-02 10:27:45 -07:00
dependabot[bot]
84b01b2e4e Bump leonsteinhaeuser/project-beta-automations from 1.3.0 to 2.0.1
Bumps [leonsteinhaeuser/project-beta-automations](https://github.com/leonsteinhaeuser/project-beta-automations) from 1.3.0 to 2.0.1.
- [Release notes](https://github.com/leonsteinhaeuser/project-beta-automations/releases)
- [Commits](https://github.com/leonsteinhaeuser/project-beta-automations/compare/v1.3.0...v2.0.1)

---
updated-dependencies:
- dependency-name: leonsteinhaeuser/project-beta-automations
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 10:27:02 -07:00
dependabot[bot]
5e9928e58b Bump tj-actions/changed-files from 29.0.2 to 31.0.2
Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 29.0.2 to 31.0.2.
- [Release notes](https://github.com/tj-actions/changed-files/releases)
- [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
- [Commits](https://github.com/tj-actions/changed-files/compare/v29.0.2...v31.0.2)

---
updated-dependencies:
- dependency-name: tj-actions/changed-files
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 10:26:39 -07:00
dependabot[bot]
0457259e7e Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 10:26:21 -07:00
dependabot[bot]
1f73a6913f Bump actions/setup-python from 3 to 4
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3 to 4.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 10:25:49 -07:00
dependabot[bot]
b29593bad9 Merge pull request #1720 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/rxjs-7.5.7 2022-10-02 07:05:45 +00:00
dependabot[bot]
bd378f79f4 Bump rxjs from 7.5.6 to 7.5.7 in /src-ui
Bumps [rxjs](https://github.com/reactivex/rxjs) from 7.5.6 to 7.5.7.
- [Release notes](https://github.com/reactivex/rxjs/releases)
- [Changelog](https://github.com/ReactiveX/rxjs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/reactivex/rxjs/compare/7.5.6...7.5.7)

---
updated-dependencies:
- dependency-name: rxjs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 06:52:18 +00:00
shamoon
699ed62af4 Merge pull request #1716 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/uuid-9.0.0
Bump uuid from 8.3.2 to 9.0.0 in /src-ui
2022-10-01 23:51:18 -07:00
dependabot[bot]
d6ae4102b4 Bump uuid from 8.3.2 to 9.0.0 in /src-ui
Bumps [uuid](https://github.com/uuidjs/uuid) from 8.3.2 to 9.0.0.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v8.3.2...v9.0.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 06:49:30 +00:00
shamoon
97eed9c66d Merge pull request #1717 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/ng2-pdf-viewer-9.1.2
Bump ng2-pdf-viewer from 9.1.0 to 9.1.2 in /src-ui
2022-10-01 23:47:35 -07:00
dependabot[bot]
b79a560816 Bump ng2-pdf-viewer from 9.1.0 to 9.1.2 in /src-ui
Bumps [ng2-pdf-viewer](https://github.com/VadimDez/ng2-pdf-viewer) from 9.1.0 to 9.1.2.
- [Release notes](https://github.com/VadimDez/ng2-pdf-viewer/releases)
- [Changelog](https://github.com/VadimDez/ng2-pdf-viewer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/VadimDez/ng2-pdf-viewer/compare/9.1.0...9.1.2)

---
updated-dependencies:
- dependency-name: ng2-pdf-viewer
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 06:25:59 +00:00
shamoon
a6045bb8e8 Merge pull request #1715 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/ngx-color-8.0.3
Bump ngx-color from 8.0.2 to 8.0.3 in /src-ui
2022-10-01 23:25:00 -07:00
shamoon
9f770e42ba Merge pull request #1719 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/concurrently-7.4.0
Bump concurrently from 7.3.0 to 7.4.0 in /src-ui
2022-10-01 20:03:53 -07:00
dependabot[bot]
b20fe9f09b Bump concurrently from 7.3.0 to 7.4.0 in /src-ui
Bumps [concurrently](https://github.com/open-cli-tools/concurrently) from 7.3.0 to 7.4.0.
- [Release notes](https://github.com/open-cli-tools/concurrently/releases)
- [Commits](https://github.com/open-cli-tools/concurrently/compare/v7.3.0...v7.4.0)

---
updated-dependencies:
- dependency-name: concurrently
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 02:41:50 +00:00
dependabot[bot]
48dbdc6c00 Merge pull request #1718 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/types/node-18.7.23 2022-10-02 02:40:34 +00:00
dependabot[bot]
d33f993809 Bump @types/node from 18.7.14 to 18.7.23 in /src-ui
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.7.14 to 18.7.23.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 02:22:40 +00:00
dependabot[bot]
8ba44b3f71 Bump ngx-color from 8.0.2 to 8.0.3 in /src-ui
Bumps [ngx-color](https://github.com/scttcper/ngx-color) from 8.0.2 to 8.0.3.
- [Release notes](https://github.com/scttcper/ngx-color/releases)
- [Commits](https://github.com/scttcper/ngx-color/compare/v8.0.2...v8.0.3)

---
updated-dependencies:
- dependency-name: ngx-color
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 02:22:20 +00:00
shamoon
83e0a6e179 Merge pull request #1714 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/jest-environment-jsdom-29.1.2
Bump jest-environment-jsdom from 29.0.1 to 29.1.2 in /src-ui
2022-10-01 19:20:36 -07:00
dependabot[bot]
8e2c4da55e Bump jest-environment-jsdom from 29.0.1 to 29.1.2 in /src-ui
Bumps [jest-environment-jsdom](https://github.com/facebook/jest/tree/HEAD/packages/jest-environment-jsdom) from 29.0.1 to 29.1.2.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.1.2/packages/jest-environment-jsdom)

---
updated-dependencies:
- dependency-name: jest-environment-jsdom
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 01:09:49 +00:00
dependabot[bot]
cae79e0555 Merge pull request #1708 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/angular/compiler-14.2.4 2022-10-02 00:51:27 +00:00
Michael Shamoon
5b79aec065 Update @angular/cli @angular/core 2022-10-01 17:36:08 -07:00
dependabot[bot]
91de061c06 Bump @angular/compiler from 14.2.0 to 14.2.4 in /src-ui
Bumps [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) from 14.2.0 to 14.2.4.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/14.2.4/packages/compiler)

---
updated-dependencies:
- dependency-name: "@angular/compiler"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 17:35:34 -07:00
shamoon
f7d6f0bf21 Merge pull request #1707 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/cypress-10.9.0
Bump cypress from 10.7.0 to 10.9.0 in /src-ui
2022-10-01 17:28:55 -07:00
dependabot[bot]
551c765358 Bump cypress from 10.7.0 to 10.9.0 in /src-ui
Bumps [cypress](https://github.com/cypress-io/cypress) from 10.7.0 to 10.9.0.
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/cypress/compare/v10.7.0...v10.9.0)

---
updated-dependencies:
- dependency-name: cypress
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 00:10:16 +00:00
shamoon
34ee26084d Merge pull request #1710 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/bootstrap-5.2.1
Bump bootstrap from 5.2.0 to 5.2.1 in /src-ui
2022-10-01 17:09:22 -07:00
dependabot[bot]
47a76412e2 Bump bootstrap from 5.2.0 to 5.2.1 in /src-ui
Bumps [bootstrap](https://github.com/twbs/bootstrap) from 5.2.0 to 5.2.1.
- [Release notes](https://github.com/twbs/bootstrap/releases)
- [Commits](https://github.com/twbs/bootstrap/compare/v5.2.0...v5.2.1)

---
updated-dependencies:
- dependency-name: bootstrap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-02 00:03:12 +00:00
shamoon
45e6a419b3 Merge pull request #1706 from paperless-ngx/dependabot/npm_and_yarn/src-ui/dev/typescript-4.8.4
Bump typescript from 4.7.4 to 4.8.4 in /src-ui
2022-10-01 16:11:17 -07:00
dependabot[bot]
c34f982496 Bump typescript from 4.7.4 to 4.8.4 in /src-ui
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.7.4 to 4.8.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.7.4...v4.8.4)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-01 20:07:21 +00:00
Michael Shamoon
f26fda9485 Fix python + frontend tests 2022-09-30 18:32:21 -07:00
Michael Shamoon
06a29cd45c Handle settings live refresh, unsubscribe subscriptions 2022-09-30 15:39:02 -07:00
Michael Shamoon
98ab770437 Update documentation re. PAPERLESS_ENABLE_UPDATE_CHECK 2022-09-30 15:10:52 -07:00
Michael Shamoon
c87f60c605 Better migration of update checking settings, offer reload, strip backend_setting from db 2022-09-30 14:03:59 -07:00
Michael Shamoon
9e2430da46 Frontend update checking settings 2022-09-30 12:30:23 -07:00
Paperless-ngx Translation Bot [bot]
f59abadbc4 New translations messages.xlf (Russian)
[ci skip]
2022-09-30 10:20:29 -07:00
Paperless-ngx Translation Bot [bot]
4a3a55b923 New translations messages.xlf (Russian)
[ci skip]
2022-09-30 07:56:26 -07:00
Paperless-ngx Translation Bot [bot]
9bd031fbd7 New translations messages.xlf (Russian)
[ci skip]
2022-09-30 06:55:45 -07:00
Trenton H
436f9e891e Changes MariaDB encoding to use utf8mb4 2022-09-29 13:53:44 -07:00
Michael Shamoon
04faa10e3b Update references to qcluster t celery 2022-09-28 20:13:19 -07:00
Trenton Holmes
38ba1d1a52 Cleans up and documnets installation on bare metal, includes systemd service file for the celery and celery beat 2022-09-28 19:38:41 -07:00
Trenton H
4422bb3f69 Fix logger location tag
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2022-09-28 11:02:34 -07:00
Trenton H
5b66ef0a74 Updates how task_args and task_kwargs are parsed, adds testing to cover everything I can think of 2022-09-28 10:40:55 -07:00
shamoon
5639659b63 Merge pull request #1683 from paperless-ngx/fix/issue-1660
Mariadb compose files should use `PAPERLESS_DBPASS`
2022-09-28 09:48:20 -07:00
Michael Shamoon
7ba9cdbe23 Mariadb compose files should use PAPERLESS_DBPASS 2022-09-28 09:38:13 -07:00
Michael Shamoon
6f6f006704 re-enable frontend tests and fix tasks fixture 2022-09-27 21:01:16 -07:00
Michael Shamoon
4fe37f6aee Add related_document and direct link from task UI 2022-09-27 20:50:26 -07:00
Michael Shamoon
5162bdd404 Filter out old migrated tasks 2022-09-27 19:41:23 -07:00
Michael Shamoon
c8f252d165 Add document name & error result parsing to PaperlessTask serializer 2022-09-27 19:40:24 -07:00
Michael Shamoon
c289439cab Fix tab ids & date parsing 2022-09-27 17:56:04 -07:00
shamoon
807b7130e5 Merge pull request #1671 from paperless-ngx/v1.9.2-changelog
[Documentation] Add v1.9.2 changelog
2022-09-27 16:50:23 -07:00
Trenton H
14b6216b49 Ensures all existing one to one fields are nulled before altering the field 2022-09-27 14:17:42 -07:00
Trenton H
9188e25dc5 Fixes migration order back to the right way 2022-09-27 13:55:31 -07:00
Trenton H
fad1b03458 Finalizes what the PaperlessTask will look like to the frontend 2022-09-27 12:44:01 -07:00
Michael Shamoon
49054c61a4 Add failed tasks badge, better animation 2022-09-27 11:00:02 -07:00
Michael Shamoon
e2d593c023 Fix formatting, add note about 1.9.1 version string 2022-09-27 10:04:41 -07:00
github-actions
7455963124 Changelog - GHA 2022-09-27 17:01:37 +00:00
Michael Shamoon
6771e57fca Merge branch 'main' into dev 2022-09-27 09:36:39 -07:00
Michael Shamoon
9d117ee11b Merge pull request #1666 from paperless-ngx/fix/1664 2022-09-27 09:34:34 -07:00
shamoon
7d4b2c2413 Merge pull request #1670 from paperless-ngx/fix/issue-1667
Bugfix: Allow PAPERLESS_OCR_CLEAN=none
2022-09-27 09:29:49 -07:00
Michael Shamoon
5bb1824613 Allow PAPERLESS_OCR_CLEAN=none 2022-09-27 08:48:04 -07:00
Trenton H
8c07b76e6a Bumps version numbers to 1.9.2 2022-09-27 08:06:35 -07:00
shamoon
8cb58b4ff8 Merge pull request #1650 from paperless-ngx/fix/changelog-formatting
Chore: Ensure the generated changelog is formatted
2022-09-26 16:02:47 -07:00
shamoon
426178b6e5 Merge pull request #1649 from paperless-ngx/v1.9.1-changelog 2022-09-26 13:03:29 -07:00
Trenton H
07ec74a5d6 Runs the pre-commit hooks against the changelog before committing it 2022-09-26 12:12:57 -07:00
Trenton H
a865f2af7d Fixes changelog linting 2022-09-26 12:06:42 -07:00
github-actions
3409d19139 Changelog - GHA 2022-09-26 19:04:29 +00:00
Trenton H
71b4571524 Merge branch 'main' into dev 2022-09-26 11:31:18 -07:00
Trenton Holmes
9247300230 Transitions the backend to celery and celery beat 2022-09-26 11:25:34 -07:00
Trenton H
8967f07c8d Fixes a missing option for OCR mode and incorrect clean mode 2022-09-26 11:05:19 -07:00
shamoon
6e21d3dbee Merge pull request #1646 from paperless-ngx/fix/reset-button-padding
Fix reset button padding on small screens
2022-09-26 11:03:52 -07:00
Michael Shamoon
45b3422506 Fix reset button padding on small screens 2022-09-26 10:55:22 -07:00
Trenton H
e5de658e78 Updates to qpdf 11.1, pikepdf 6 and OCRMyPDF 14 2022-09-26 09:49:03 -07:00
Michael Shamoon
e0f93c26d6 Nicer slim sidebar transitioning, dark mode fixes, fix tests by making settings taller 2022-09-26 09:33:05 -07:00
Trenton H
df7e4d85c6 Fixes the linting issue 2022-09-26 09:15:22 -07:00
janis-ax
ae736f8f68 Update docs/advanced_usage.rst
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2022-09-26 09:06:36 -07:00
janis-ax
e2674c29a6 Improve docs with exampe
Filename ending comes automatically
2022-09-26 09:06:36 -07:00
Michael Shamoon
aa55162e2e Settings UI with live updating 2022-09-26 09:05:53 -07:00
Michael Shamoon
1330390b4f slim sidebar with doc list adjusting 2022-09-26 09:05:53 -07:00
Michael Shamoon
617055fca7 Save slimsidebar settings to db on change 2022-09-26 09:05:53 -07:00
shamoon
185b352264 Merge pull request #1640 from paperless-ngx/feature-combo-deps-update
Chore: Python dependencies update
2022-09-26 09:04:45 -07:00
shamoon
58afac2312 Merge pull request #1639 from paperless-ngx/v1.9.0-changelog
[Documentation] Add v1.9.0 changelog
2022-09-26 08:49:43 -07:00
Trenton H
ea60b83336 Fixes missing markdown text from a PR title 2022-09-26 08:44:42 -07:00
Trenton Holmes
05a00b3057 Updates Python deps all at once 2022-09-26 08:26:55 -07:00
github-actions
09139fe434 Changelog - GHA 2022-09-26 15:24:50 +00:00
Michael Shamoon
d003d26a67 Update environment.prod.ts 2022-09-26 07:56:05 -07:00
Michael Shamoon
ef2789cf57 Merge branch 'main' into dev 2022-09-26 07:55:49 -07:00
Michael Shamoon
7d4ce40a37 v1.9.0 2022-09-26 07:54:10 -07:00
shamoon
8feada6907 Merge pull request #1560 from paperless-ngx/beta
[Beta] Paperless-ngx v1.9.0 Release Candidate
2022-09-26 07:53:16 -07:00
Paperless-ngx Translation Bot [bot]
ed9b0c32d8 New Crowdin updates (#1633)
* bugfix: increase delay

partially reverts 86358d5561
re-implements 4fbabe43ea

Signed-off-by: Florian Brandes <florian.brandes@posteo.de>

* New translations messages.xlf (Slovenian)
[ci skip]

* New translations messages.xlf (Slovenian)
[ci skip]

Signed-off-by: Florian Brandes <florian.brandes@posteo.de>
Co-authored-by: Florian Brandes <florian.brandes@posteo.de>
2022-09-26 07:52:44 -07:00
Felix E
8088394d16 Merge pull request #1629 from paperless-ngx/feature-better-pipenv-docker
Chore: Moves pipenv into its own Docker stage
2022-09-25 09:54:40 +02:00
Trenton Holmes
cb2823ff45 Moves the pipenv generation of requirements.txt into a seperate build stage, disconnecting the main image from its dependencies 2022-09-21 09:43:40 -07:00
lemmi
27906df149 paperless_cmd.sh: use exec to run supervisord
Without exec, signals aren't passed through properly. Stopping the
container won't have any effect unless killed.

closes #1616
2022-09-20 07:23:04 -07:00
Paperless-ngx Translation Bot [bot]
b8e7f0b45f New Crowdin updates (#1607)
* bugfix: increase delay

partially reverts 86358d5561
re-implements 4fbabe43ea

Signed-off-by: Florian Brandes <florian.brandes@posteo.de>

* New translations messages.xlf (Finnish) [ci skip]

Signed-off-by: Florian Brandes <florian.brandes@posteo.de>
Co-authored-by: Florian Brandes <florian.brandes@posteo.de>
2022-09-16 15:45:17 -07:00
Trenton H
355b3fcb3d Fixes grammar in comment
Co-authored-by: Florian <florian.brandes@posteo.de>
2022-09-16 09:08:16 -07:00
Trenton Holmes
7aa0e5650b Updates how barcodes are detected, using pikepdf images, instead of converting each page to an image 2022-09-16 09:08:16 -07:00
Paperless-ngx Translation Bot [bot]
f9a0adc64e New Crowdin updates (#1580)
* New translations messages.xlf (French)
[ci skip]

* New translations messages.xlf (Finnish)
[ci skip]

* New translations messages.xlf (Finnish)
[ci skip]
2022-09-16 08:37:07 -07:00
shamoon
3b34aed64f Merge pull request #1605 from paperless-ngx/fix/1599-consume-permissions
Fix: Consume directory permissions were not updated
2022-09-16 08:31:41 -07:00
Florian Brandes
8a8edfb108 bugfix: increase delay
partially reverts 86358d5561
re-implements 4fbabe43ea

Signed-off-by: Florian Brandes <florian.brandes@posteo.de>
2022-09-16 08:00:26 -07:00
Trenton H
3a7cbd3a42 Fixes an issue where the consume directory wasn't included in the permissions fix at Docker entry 2022-09-16 07:52:33 -07:00
Trenton H
0e443ba017 Merge pull request #1596 from paperless-ngx/fix/1590-barcodes
Fix: Double barcode separation creates empty file
2022-09-15 14:18:01 -07:00
shamoon
8ed401aec1 Merge pull request #1591 from paperless-ngx/fix/1583-tika-str
Fix: Parsing Tika documents fails with AttributeError
2022-09-14 21:52:49 -07:00
Trenton Holmes
9ae847039b Fixes the seperation of files by barcode, during the case where 2 barcodes appear back to back 2022-09-14 14:00:37 -07:00
Trenton Holmes
d4cb84ff76 Ensure the tika parse function gets a string, not a PathLike 2022-09-14 07:48:12 -07:00
shamoon
17ae2aacbf Merge pull request #1576 from paperless-ngx/fix/slow-classifier
Fix: Resolve issue with slow classifier
2022-09-13 11:57:58 -07:00
Michael Shamoon
82d03f2dc6 fix tag list vertical space 2022-09-13 11:54:25 -07:00
Trenton Holmes
3cf2aaf8ff Locks numpy version until an upstream fix or resolution to aarch64 classifier slowdown 2022-09-13 10:33:07 -07:00
shamoon
0c89721133 Merge pull request #1567 from paperless-ngx/feature-process-names
Feature: Display django-q process names
2022-09-12 12:21:49 -07:00
Trenton H
e206687070 Updates django-q and add optional setproctitle to allow display 2022-09-12 12:18:41 -07:00
Trenton Holmes
cdb9c48545 Minor logging updates 2022-09-12 11:44:33 -07:00
Trenton Holmes
0b8eff9643 Extends the cleanup of image versions to the library images and all the registry cache images as well 2022-09-12 11:44:33 -07:00
shamoon
16882b8fa9 Merge pull request #1566 from paperless-ngx/fix/issue-1564
Fix document comments not updating on document navigation
2022-09-12 11:37:30 -07:00
Michael Shamoon
2afa5940e3 Fix live updating of comments on doc change 2022-09-12 08:35:13 -07:00
Michael Shamoon
8fa7bc3dab Remove user dropdown border 2022-09-12 08:35:04 -07:00
Michael Shamoon
4a9dc1e33a Update messages.xlf 2022-09-11 20:47:47 -07:00
Nico Fricke
36158609f0 change default matching algo to auto and move to constant 2022-08-01 09:22:13 +02:00
180 changed files with 20645 additions and 24476 deletions

View File

@@ -1,6 +1,6 @@
{
"qpdf": {
"version": "10.6.3"
"version": "11.1.1"
},
"jbig2enc": {
"version": "0.29",

View File

@@ -13,6 +13,7 @@ body:
- [The troubleshooting documentation](https://paperless-ngx.readthedocs.io/en/latest/troubleshooting.html).
- [The installation instructions](https://paperless-ngx.readthedocs.io/en/latest/setup.html#installation).
- [Existing issues and discussions](https://github.com/paperless-ngx/paperless-ngx/search?q=&type=issues).
- Disable any customer container initialization scripts, if using any
If you encounter issues while installing or configuring Paperless-ngx, please post in the ["Support" section of the discussions](https://github.com/paperless-ngx/paperless-ngx/discussions/new?category=support).
- type: textarea
@@ -41,7 +42,15 @@ body:
id: logs
attributes:
label: Webserver logs
description: If available, post any logs from the web server related to your issue.
description: Logs from the web server related to your issue.
render: bash
validations:
required: true
- type: textarea
id: logs_browser
attributes:
label: Browser logs
description: Logs from the web browser related to your issue, if needed
render: bash
- type: input
id: version

View File

@@ -1,168 +1,312 @@
#!/usr/bin/env python3
import functools
import json
import logging
import os
import re
import shutil
import subprocess
from argparse import ArgumentParser
from typing import Dict
from typing import Final
from typing import List
from urllib.parse import quote
from typing import Optional
import requests
from common import get_log_level
from github import ContainerPackage
from github import GithubBranchApi
from github import GithubContainerRegistryApi
logger = logging.getLogger("cleanup-tags")
class ContainerPackage:
def __init__(self, data: Dict):
class DockerManifest2:
"""
Data class wrapping the Docker Image Manifest Version 2.
See https://docs.docker.com/registry/spec/manifest-v2-2/
"""
def __init__(self, data: Dict) -> None:
self._data = data
self.name = self._data["name"]
self.id = self._data["id"]
self.url = self._data["url"]
self.tags = self._data["metadata"]["container"]["tags"]
@functools.cached_property
def untagged(self) -> bool:
return len(self.tags) == 0
@functools.cache
def tag_matches(self, pattern: str) -> bool:
for tag in self.tags:
if re.match(pattern, tag) is not None:
return True
return False
def __repr__(self):
return f"Package {self.name}"
# This is the sha256: digest string. Corresponds to GitHub API name
# if the package is an untagged package
self.digest = self._data["digest"]
platform_data_os = self._data["platform"]["os"]
platform_arch = self._data["platform"]["architecture"]
platform_variant = self._data["platform"].get(
"variant",
"",
)
self.platform = f"{platform_data_os}/{platform_arch}{platform_variant}"
class GithubContainerRegistry:
class RegistryTagsCleaner:
"""
This is the base class for the image registry cleaning. Given a package
name, it will keep all images which are tagged and all untagged images
referred to by a manifest. This results in only images which have been untagged
and cannot be referenced except by their SHA in being removed. None of these
images should be referenced, so it is fine to delete them.
"""
def __init__(
self,
session: requests.Session,
token: str,
owner_or_org: str,
):
self._session: requests.Session = session
self._token = token
self._owner_or_org = owner_or_org
# https://docs.github.com/en/rest/branches/branches
self._BRANCHES_ENDPOINT = "https://api.github.com/repos/{OWNER}/{REPO}/branches"
if self._owner_or_org == "paperless-ngx":
# https://docs.github.com/en/rest/packages#get-all-package-versions-for-a-package-owned-by-an-organization
self._PACKAGES_VERSIONS_ENDPOINT = "https://api.github.com/orgs/{ORG}/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions"
# https://docs.github.com/en/rest/packages#delete-package-version-for-an-organization
self._PACKAGE_VERSION_DELETE_ENDPOINT = "https://api.github.com/orgs/{ORG}/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions/{PACKAGE_VERSION_ID}"
else:
# https://docs.github.com/en/rest/packages#get-all-package-versions-for-a-package-owned-by-the-authenticated-user
self._PACKAGES_VERSIONS_ENDPOINT = "https://api.github.com/user/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions"
# https://docs.github.com/en/rest/packages#delete-a-package-version-for-the-authenticated-user
self._PACKAGE_VERSION_DELETE_ENDPOINT = "https://api.github.com/user/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions/{PACKAGE_VERSION_ID}"
def __enter__(self):
"""
Sets up the required headers for auth and response
type from the API
"""
self._session.headers.update(
{
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {self._token}",
},
)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
Ensures the authorization token is cleaned up no matter
the reason for the exit
"""
if "Accept" in self._session.headers:
del self._session.headers["Accept"]
if "Authorization" in self._session.headers:
del self._session.headers["Authorization"]
def _read_all_pages(self, endpoint):
"""
Internal function to read all pages of an endpoint, utilizing the
next.url until exhausted
"""
internal_data = []
while True:
resp = self._session.get(endpoint)
if resp.status_code == 200:
internal_data += resp.json()
if "next" in resp.links:
endpoint = resp.links["next"]["url"]
else:
logger.debug("Exiting pagination loop")
break
else:
logger.warning(f"Request to {endpoint} return HTTP {resp.status_code}")
break
return internal_data
def get_branches(self, repo: str):
"""
Returns all current branches of the given repository
"""
endpoint = self._BRANCHES_ENDPOINT.format(OWNER=self._owner_or_org, REPO=repo)
internal_data = self._read_all_pages(endpoint)
return internal_data
def filter_branches_by_name_pattern(self, branch_data, pattern: str):
"""
Filters the given list of branches to those which start with the given
pattern. Future enhancement could use regex patterns instead.
"""
matches = {}
for branch in branch_data:
if branch["name"].startswith(pattern):
matches[branch["name"]] = branch
return matches
def get_package_versions(
self,
package_name: str,
package_type: str = "container",
) -> List[ContainerPackage]:
"""
Returns all the versions of a given package (container images) from
the API
"""
package_name = quote(package_name, safe="")
endpoint = self._PACKAGES_VERSIONS_ENDPOINT.format(
ORG=self._owner_or_org,
PACKAGE_TYPE=package_type,
PACKAGE_NAME=package_name,
repo_owner: str,
repo_name: str,
package_api: GithubContainerRegistryApi,
branch_api: Optional[GithubBranchApi],
):
self.actually_delete = False
self.package_api = package_api
self.branch_api = branch_api
self.package_name = package_name
self.repo_owner = repo_owner
self.repo_name = repo_name
self.tags_to_delete: List[str] = []
self.tags_to_keep: List[str] = []
# Get the information about all versions of the given package
# These are active, not deleted, the default returned from the API
self.all_package_versions = self.package_api.get_active_package_versions(
self.package_name,
)
pkgs = []
# Get a mapping from a tag like "1.7.0" or "feature-xyz" to the ContainerPackage
# tagged with it. It makes certain lookups easy
self.all_pkgs_tags_to_version: Dict[str, ContainerPackage] = {}
for pkg in self.all_package_versions:
for tag in pkg.tags:
self.all_pkgs_tags_to_version[tag] = pkg
logger.info(
f"Located {len(self.all_package_versions)} versions of package {self.package_name}",
)
for data in self._read_all_pages(endpoint):
pkgs.append(ContainerPackage(data))
self.decide_what_tags_to_keep()
return pkgs
def delete_package_version(self, package_data: ContainerPackage):
def clean(self):
"""
Deletes the given package version from the GHCR
This method will delete image versions, based on the selected tags to delete
"""
resp = self._session.delete(package_data.url)
if resp.status_code != 204:
logger.warning(
f"Request to delete {package_data.url} returned HTTP {resp.status_code}",
for tag_to_delete in self.tags_to_delete:
package_version_info = self.all_pkgs_tags_to_version[tag_to_delete]
if self.actually_delete:
logger.info(
f"Deleting {tag_to_delete} (id {package_version_info.id})",
)
self.package_api.delete_package_version(
package_version_info,
)
else:
logger.info(
f"Would delete {tag_to_delete} (id {package_version_info.id})",
)
else:
logger.info("No tags to delete")
def clean_untagged(self, is_manifest_image: bool):
"""
This method will delete untagged images, that is those which are not named. It
handles if the image tag is actually a manifest, which points to images that look otherwise
untagged.
"""
def _clean_untagged_manifest():
"""
Handles the deletion of untagged images, but where the package is a manifest, ie a multi
arch image, which means some "untagged" images need to exist still.
Ok, bear with me, these are annoying.
Our images are multi-arch, so the manifest is more like a pointer to a sha256 digest.
These images are untagged, but pointed to, and so should not be removed (or every pull fails).
So for each image getting kept, parse the manifest to find the digest(s) it points to. Then
remove those from the list of untagged images. The final result is the untagged, not pointed to
version which should be safe to remove.
Example:
Tag: ghcr.io/paperless-ngx/paperless-ngx:1.7.1 refers to
amd64: sha256:b9ed4f8753bbf5146547671052d7e91f68cdfc9ef049d06690b2bc866fec2690
armv7: sha256:81605222df4ba4605a2ba4893276e5d08c511231ead1d5da061410e1bbec05c3
arm64: sha256:374cd68db40734b844705bfc38faae84cc4182371de4bebd533a9a365d5e8f3b
each of which appears as untagged image, but isn't really.
So from the list of untagged packages, remove those digests. Once all tags which
are being kept are checked, the remaining untagged packages are actually untagged
with no referrals in a manifest to them.
"""
# Simplify the untagged data, mapping name (which is a digest) to the version
# At the moment, these are the images which APPEAR untagged.
untagged_versions = {}
for x in self.all_package_versions:
if x.untagged:
untagged_versions[x.name] = x
skips = 0
# Parse manifests to locate digests pointed to
for tag in sorted(self.tags_to_keep):
full_name = f"ghcr.io/{self.repo_owner}/{self.package_name}:{tag}"
logger.info(f"Checking manifest for {full_name}")
try:
proc = subprocess.run(
[
shutil.which("docker"),
"manifest",
"inspect",
full_name,
],
capture_output=True,
)
manifest_list = json.loads(proc.stdout)
for manifest_data in manifest_list["manifests"]:
manifest = DockerManifest2(manifest_data)
if manifest.digest in untagged_versions:
logger.info(
f"Skipping deletion of {manifest.digest},"
f" referred to by {full_name}"
f" for {manifest.platform}",
)
del untagged_versions[manifest.digest]
skips += 1
except Exception as err:
self.actually_delete = False
logger.exception(err)
return
logger.info(
f"Skipping deletion of {skips} packages referred to by a manifest",
)
# Delete the untagged and not pointed at packages
logger.info(f"Deleting untagged packages of {self.package_name}")
for to_delete_name in untagged_versions:
to_delete_version = untagged_versions[to_delete_name]
if self.actually_delete:
logger.info(
f"Deleting id {to_delete_version.id} named {to_delete_version.name}",
)
self.package_api.delete_package_version(
to_delete_version,
)
else:
logger.info(
f"Would delete {to_delete_name} (id {to_delete_version.id})",
)
def _clean_untagged_non_manifest():
"""
If the package is not a multi-arch manifest, images without tags are safe to delete.
"""
for package in self.all_package_versions:
if package.untagged:
if self.actually_delete:
logger.info(
f"Deleting id {package.id} named {package.name}",
)
self.package_api.delete_package_version(
package,
)
else:
logger.info(
f"Would delete {package.name} (id {package.id})",
)
else:
logger.info(
f"Not deleting tag {package.tags[0]} of package {self.package_name}",
)
logger.info("Beginning untagged image cleaning")
if is_manifest_image:
_clean_untagged_manifest()
else:
_clean_untagged_non_manifest()
def decide_what_tags_to_keep(self):
"""
This method holds the logic to delete what tags to keep and there fore
what tags to delete.
By default, any image with at least 1 tag will be kept
"""
# By default, keep anything which is tagged
self.tags_to_keep = list(set(self.all_pkgs_tags_to_version.keys()))
class MainImageTagsCleaner(RegistryTagsCleaner):
def decide_what_tags_to_keep(self):
"""
Overrides the default logic for deciding what images to keep. Images tagged as "feature-"
will be removed, if the corresponding branch no longer exists.
"""
# Default to everything gets kept still
super().decide_what_tags_to_keep()
# Locate the feature branches
feature_branches = {}
for branch in self.branch_api.get_branches(
repo=self.repo_name,
):
if branch.name.startswith("feature-"):
logger.debug(f"Found feature branch {branch.name}")
feature_branches[branch.name] = branch
logger.info(f"Located {len(feature_branches)} feature branches")
if not len(feature_branches):
# Our work here is done, delete nothing
return
# Filter to packages which are tagged with feature-*
packages_tagged_feature: List[ContainerPackage] = []
for package in self.all_package_versions:
if package.tag_matches("feature-"):
packages_tagged_feature.append(package)
# Map tags like "feature-xyz" to a ContainerPackage
feature_pkgs_tags_to_versions: Dict[str, ContainerPackage] = {}
for pkg in packages_tagged_feature:
for tag in pkg.tags:
feature_pkgs_tags_to_versions[tag] = pkg
logger.info(
f'Located {len(feature_pkgs_tags_to_versions)} versions of package {self.package_name} tagged "feature-"',
)
# All the feature tags minus all the feature branches leaves us feature tags
# with no corresponding branch
self.tags_to_delete = list(
set(feature_pkgs_tags_to_versions.keys()) - set(feature_branches.keys()),
)
# All the tags minus the set of going to be deleted tags leaves us the
# tags which will be kept around
self.tags_to_keep = list(
set(self.all_pkgs_tags_to_version.keys()) - set(self.tags_to_delete),
)
logger.info(
f"Located {len(self.tags_to_delete)} versions of package {self.package_name} to delete",
)
class LibraryTagsCleaner(RegistryTagsCleaner):
"""
Exists for the off change that someday, the installer library images
will need their own logic
"""
pass
def _main():
parser = ArgumentParser(
@@ -187,6 +331,15 @@ def _main():
help="If provided, delete untagged containers as well",
)
# If given, the package is assumed to be a multi-arch manifest. Cache packages are
# not multi-arch, all other types are
parser.add_argument(
"--is-manifest",
action="store_true",
default=False,
help="If provided, the package is assumed to be a multi-arch manifest following schema v2",
)
# Allows configuration of log level for debugging
parser.add_argument(
"--loglevel",
@@ -194,6 +347,12 @@ def _main():
help="Configures the logging level",
)
# Get the name of the package being processed this round
parser.add_argument(
"package",
help="The package to process",
)
args = parser.parse_args()
logging.basicConfig(
@@ -207,181 +366,36 @@ def _main():
repo: Final[str] = os.environ["GITHUB_REPOSITORY"]
gh_token: Final[str] = os.environ["TOKEN"]
with requests.session() as sess:
with GithubContainerRegistry(sess, gh_token, repo_owner) as gh_api:
# Step 1 - Get branch information
# Step 1.1 - Locate all branches of the repo
all_branches = gh_api.get_branches("paperless-ngx")
logger.info(f"Located {len(all_branches)} branches of {repo_owner}/{repo} ")
# Step 1.2 - Filter branches to those starting with "feature-"
feature_branches = gh_api.filter_branches_by_name_pattern(
all_branches,
"feature-",
)
logger.info(f"Located {len(feature_branches)} feature branches")
# Step 2 - Deal with package information
for package_name in ["paperless-ngx", "paperless-ngx/builder/cache/app"]:
# Step 2.1 - Location all versions of the given package
all_package_versions = gh_api.get_package_versions(package_name)
# Faster lookup, map the tag to their container
all_pkgs_tags_to_version = {}
for pkg in all_package_versions:
for tag in pkg.tags:
all_pkgs_tags_to_version[tag] = pkg
logger.info(
f"Located {len(all_package_versions)} versions of package {package_name}",
# Find all branches named feature-*
# Note: Only relevant to the main application, but simpler to
# leave in for all packages
with GithubBranchApi(gh_token) as branch_api:
with GithubContainerRegistryApi(gh_token, repo_owner) as container_api:
if args.package in {"paperless-ngx", "paperless-ngx/builder/cache/app"}:
cleaner = MainImageTagsCleaner(
args.package,
repo_owner,
repo,
container_api,
branch_api,
)
else:
cleaner = LibraryTagsCleaner(
args.package,
repo_owner,
repo,
container_api,
None,
)
# Step 2.2 - Location package versions which have a tag of "feature-"
packages_tagged_feature = []
for package in all_package_versions:
if package.tag_matches("feature-"):
packages_tagged_feature.append(package)
# Set if actually doing a delete vs dry run
cleaner.actually_delete = args.delete
logger.info(
f'Located {len(packages_tagged_feature)} versions of package {package_name} tagged "feature-"',
)
# Clean images with tags
cleaner.clean()
# Faster lookup, map feature- tags to their container
feature_pkgs_tags_to_versions = {}
for pkg in packages_tagged_feature:
for tag in pkg.tags:
feature_pkgs_tags_to_versions[tag] = pkg
# Step 2.3 - Determine which package versions have no matching branch and which tags we're keeping
tags_to_delete = list(
set(feature_pkgs_tags_to_versions.keys())
- set(feature_branches.keys()),
)
tags_to_keep = list(
set(all_pkgs_tags_to_version.keys()) - set(tags_to_delete),
)
logger.info(
f"Located {len(tags_to_delete)} versions of package {package_name} to delete",
)
# Step 2.4 - Delete certain package versions
for tag_to_delete in tags_to_delete:
package_version_info = feature_pkgs_tags_to_versions[tag_to_delete]
if args.delete:
logger.info(
f"Deleting {tag_to_delete} (id {package_version_info.id})",
)
gh_api.delete_package_version(
package_version_info,
)
else:
logger.info(
f"Would delete {tag_to_delete} (id {package_version_info.id})",
)
# Step 3 - Deal with untagged and dangling packages
if args.untagged:
"""
Ok, bear with me, these are annoying.
Our images are multi-arch, so the manifest is more like a pointer to a sha256 digest.
These images are untagged, but pointed to, and so should not be removed (or every pull fails).
So for each image getting kept, parse the manifest to find the digest(s) it points to. Then
remove those from the list of untagged images. The final result is the untagged, not pointed to
version which should be safe to remove.
Example:
Tag: ghcr.io/paperless-ngx/paperless-ngx:1.7.1 refers to
amd64: sha256:b9ed4f8753bbf5146547671052d7e91f68cdfc9ef049d06690b2bc866fec2690
armv7: sha256:81605222df4ba4605a2ba4893276e5d08c511231ead1d5da061410e1bbec05c3
arm64: sha256:374cd68db40734b844705bfc38faae84cc4182371de4bebd533a9a365d5e8f3b
each of which appears as untagged image
"""
# Step 3.1 - Simplify the untagged data, mapping name (which is a digest) to the version
untagged_versions = {}
for x in all_package_versions:
if x.untagged:
untagged_versions[x.name] = x
skips = 0
# Extra security to not delete on an unexpected error
actually_delete = True
logger.info(
f"Located {len(tags_to_keep)} tags of package {package_name} to keep",
)
# Step 3.2 - Parse manifests to locate digests pointed to
for tag in tags_to_keep:
full_name = f"ghcr.io/{repo_owner}/{package_name}:{tag}"
logger.info(f"Checking manifest for {full_name}")
try:
proc = subprocess.run(
[
shutil.which("docker"),
"manifest",
"inspect",
full_name,
],
capture_output=True,
)
manifest_list = json.loads(proc.stdout)
for manifest in manifest_list["manifests"]:
digest = manifest["digest"]
platform_data_os = manifest["platform"]["os"]
platform_arch = manifest["platform"]["architecture"]
platform_variant = manifest["platform"].get(
"variant",
"",
)
platform = f"{platform_data_os}/{platform_arch}{platform_variant}"
if digest in untagged_versions:
logger.debug(
f"Skipping deletion of {digest}, referred to by {full_name} for {platform}",
)
del untagged_versions[digest]
skips += 1
except json.decoder.JSONDecodeError as err:
# This is probably for a cache image, which isn't a multi-arch digest
# These are ok to delete all on
logger.debug(f"{err} on {full_name}")
continue
except Exception as err:
actually_delete = False
logger.exception(err)
continue
logger.info(f"Skipping deletion of {skips} packages")
# Step 3.3 - Delete the untagged and not pointed at packages
logger.info(f"Deleting untagged packages of {package_name}")
for to_delete_name in untagged_versions:
to_delete_version = untagged_versions[to_delete_name]
if args.delete and actually_delete:
logger.info(
f"Deleting id {to_delete_version.id} named {to_delete_version.name}",
)
gh_api.delete_package_version(
to_delete_version,
)
else:
logger.info(
f"Would delete {to_delete_name} (id {to_delete_version.id})",
)
else:
logger.info("Leaving untagged images untouched")
# Clean images which are untagged
cleaner.clean_untagged(args.is_manifest)
if __name__ == "__main__":

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env python3
import logging
from argparse import ArgumentError
def get_image_tag(
@@ -30,6 +29,11 @@ def get_cache_image_tag(
def get_log_level(args) -> int:
"""
Returns a logging level, based
:param args:
:return:
"""
levels = {
"critical": logging.CRITICAL,
"error": logging.ERROR,

274
.github/scripts/github.py vendored Normal file
View File

@@ -0,0 +1,274 @@
#!/usr/bin/env python3
"""
This module contains some useful classes for interacting with the Github API.
The full documentation for the API can be found here: https://docs.github.com/en/rest
Mostly, this focusses on two areas, repo branches and repo packages, as the use case
is cleaning up container images which are no longer referred to.
"""
import functools
import logging
import re
import urllib.parse
from typing import Dict
from typing import List
from typing import Optional
import httpx
logger = logging.getLogger("github-api")
class _GithubApiBase:
"""
A base class for interacting with the Github API. It
will handle the session and setting authorization headers.
"""
def __init__(self, token: str) -> None:
self._token = token
self._client: Optional[httpx.Client] = None
def __enter__(self) -> "_GithubApiBase":
"""
Sets up the required headers for auth and response
type from the API
"""
self._client = httpx.Client()
self._client.headers.update(
{
"Accept": "application/vnd.github.v3+json",
"Authorization": f"token {self._token}",
},
)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
Ensures the authorization token is cleaned up no matter
the reason for the exit
"""
if "Accept" in self._client.headers:
del self._client.headers["Accept"]
if "Authorization" in self._client.headers:
del self._client.headers["Authorization"]
# Close the session as well
self._client.close()
self._client = None
def _read_all_pages(self, endpoint):
"""
Helper function to read all pages of an endpoint, utilizing the
next.url until exhausted. Assumes the endpoint returns a list
"""
internal_data = []
while True:
resp = self._client.get(endpoint)
if resp.status_code == 200:
internal_data += resp.json()
if "next" in resp.links:
endpoint = resp.links["next"]["url"]
else:
logger.debug("Exiting pagination loop")
break
else:
logger.warning(f"Request to {endpoint} return HTTP {resp.status_code}")
resp.raise_for_status()
return internal_data
class _EndpointResponse:
"""
For all endpoint JSON responses, store the full
response data, for ease of extending later, if need be.
"""
def __init__(self, data: Dict) -> None:
self._data = data
class GithubBranch(_EndpointResponse):
"""
Simple wrapper for a repository branch, only extracts name information
for now.
"""
def __init__(self, data: Dict) -> None:
super().__init__(data)
self.name = self._data["name"]
class GithubBranchApi(_GithubApiBase):
"""
Wrapper around branch API.
See https://docs.github.com/en/rest/branches/branches
"""
def __init__(self, token: str) -> None:
super().__init__(token)
self._ENDPOINT = "https://api.github.com/repos/{REPO}/branches"
def get_branches(self, repo: str) -> List[GithubBranch]:
"""
Returns all current branches of the given repository owned by the given
owner or organization.
"""
# The environment GITHUB_REPOSITORY already contains the owner in the correct location
endpoint = self._ENDPOINT.format(REPO=repo)
internal_data = self._read_all_pages(endpoint)
return [GithubBranch(branch) for branch in internal_data]
class ContainerPackage(_EndpointResponse):
"""
Data class wrapping the JSON response from the package related
endpoints
"""
def __init__(self, data: Dict):
super().__init__(data)
# This is a numerical ID, required for interactions with this
# specific package, including deletion of it or restoration
self.id: int = self._data["id"]
# A string name. This might be an actual name or it could be a
# digest string like "sha256:"
self.name: str = self._data["name"]
# URL to the package, including its ID, can be used for deletion
# or restoration without needing to build up a URL ourselves
self.url: str = self._data["url"]
# The list of tags applied to this image. Maybe an empty list
self.tags: List[str] = self._data["metadata"]["container"]["tags"]
@functools.cached_property
def untagged(self) -> bool:
"""
Returns True if the image has no tags applied to it, False otherwise
"""
return len(self.tags) == 0
@functools.cache
def tag_matches(self, pattern: str) -> bool:
"""
Returns True if the image has at least one tag which matches the given regex,
False otherwise
"""
for tag in self.tags:
if re.match(pattern, tag) is not None:
return True
return False
def __repr__(self):
return f"Package {self.name}"
class GithubContainerRegistryApi(_GithubApiBase):
"""
Class wrapper to deal with the Github packages API. This class only deals with
container type packages, the only type published by paperless-ngx.
"""
def __init__(self, token: str, owner_or_org: str) -> None:
super().__init__(token)
self._owner_or_org = owner_or_org
if self._owner_or_org == "paperless-ngx":
# https://docs.github.com/en/rest/packages#get-all-package-versions-for-a-package-owned-by-an-organization
self._PACKAGES_VERSIONS_ENDPOINT = "https://api.github.com/orgs/{ORG}/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions"
# https://docs.github.com/en/rest/packages#delete-package-version-for-an-organization
self._PACKAGE_VERSION_DELETE_ENDPOINT = "https://api.github.com/orgs/{ORG}/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions/{PACKAGE_VERSION_ID}"
else:
# https://docs.github.com/en/rest/packages#get-all-package-versions-for-a-package-owned-by-the-authenticated-user
self._PACKAGES_VERSIONS_ENDPOINT = "https://api.github.com/user/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions"
# https://docs.github.com/en/rest/packages#delete-a-package-version-for-the-authenticated-user
self._PACKAGE_VERSION_DELETE_ENDPOINT = "https://api.github.com/user/packages/{PACKAGE_TYPE}/{PACKAGE_NAME}/versions/{PACKAGE_VERSION_ID}"
self._PACKAGE_VERSION_RESTORE_ENDPOINT = (
f"{self._PACKAGE_VERSION_DELETE_ENDPOINT}/restore"
)
def get_active_package_versions(
self,
package_name: str,
) -> List[ContainerPackage]:
"""
Returns all the versions of a given package (container images) from
the API
"""
package_type: str = "container"
# Need to quote this for slashes in the name
package_name = urllib.parse.quote(package_name, safe="")
endpoint = self._PACKAGES_VERSIONS_ENDPOINT.format(
ORG=self._owner_or_org,
PACKAGE_TYPE=package_type,
PACKAGE_NAME=package_name,
)
pkgs = []
for data in self._read_all_pages(endpoint):
pkgs.append(ContainerPackage(data))
return pkgs
def get_deleted_package_versions(
self,
package_name: str,
) -> List[ContainerPackage]:
package_type: str = "container"
# Need to quote this for slashes in the name
package_name = urllib.parse.quote(package_name, safe="")
endpoint = (
self._PACKAGES_VERSIONS_ENDPOINT.format(
ORG=self._owner_or_org,
PACKAGE_TYPE=package_type,
PACKAGE_NAME=package_name,
)
+ "?state=deleted"
)
pkgs = []
for data in self._read_all_pages(endpoint):
pkgs.append(ContainerPackage(data))
return pkgs
def delete_package_version(self, package_data: ContainerPackage):
"""
Deletes the given package version from the GHCR
"""
resp = self._client.delete(package_data.url)
if resp.status_code != 204:
logger.warning(
f"Request to delete {package_data.url} returned HTTP {resp.status_code}",
)
def restore_package_version(
self,
package_name: str,
package_data: ContainerPackage,
):
package_type: str = "container"
endpoint = self._PACKAGE_VERSION_RESTORE_ENDPOINT.format(
ORG=self._owner_or_org,
PACKAGE_TYPE=package_type,
PACKAGE_NAME=package_name,
PACKAGE_VERSION_ID=package_data.id,
)
resp = self._client.post(endpoint)
if resp.status_code != 204:
logger.warning(
f"Request to delete {endpoint} returned HTTP {resp.status_code}",
)

View File

@@ -44,8 +44,7 @@ jobs:
-
name: Install pipenv
run: |
pipx install pipenv==2022.8.5
pipenv --version
pipx install pipenv==2022.10.12
-
name: Set up Python
uses: actions/setup-python@v4
@@ -82,17 +81,32 @@ jobs:
matrix:
python-version: ['3.8', '3.9', '3.10']
fail-fast: false
services:
tika:
image: ghcr.io/paperless-ngx/tika:latest
ports:
- "9998:9998/tcp"
gotenberg:
image: docker.io/gotenberg/gotenberg:7.6
ports:
- "3000:3000/tcp"
env:
# Enable Tika end to end testing
TIKA_LIVE: 1
# Enable paperless_mail testing against real server
PAPERLESS_MAIL_TEST_HOST: ${{ secrets.TEST_MAIL_HOST }}
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
steps:
-
name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 2
fetch-depth: 0
-
name: Install pipenv
run: |
pipx install pipenv==2022.8.5
pipenv --version
pipx install pipenv==2022.10.12
-
name: Set up Python
uses: actions/setup-python@v4
@@ -117,11 +131,11 @@ jobs:
name: Tests
run: |
cd src/
pipenv run pytest
pipenv run pytest -rfEp
-
name: Get changed files
id: changed-files-specific
uses: tj-actions/changed-files@v29.0.2
uses: tj-actions/changed-files@v34
with:
files: |
src/**
@@ -180,7 +194,7 @@ jobs:
id: set-ghcr-repository
run: |
ghcr_name=$(echo "${GITHUB_REPOSITORY}" | awk '{ print tolower($0) }')
echo ::set-output name=repository::${ghcr_name}
echo "repository=${ghcr_name}" >> $GITHUB_OUTPUT
-
name: Checkout
uses: actions/checkout@v3
@@ -197,7 +211,7 @@ jobs:
echo ${build_json}
echo ::set-output name=qpdf-json::${build_json}
echo "qpdf-json=${build_json}" >> $GITHUB_OUTPUT
-
name: Setup psycopg2 image
id: psycopg2-setup
@@ -206,7 +220,7 @@ jobs:
echo ${build_json}
echo ::set-output name=psycopg2-json::${build_json}
echo "psycopg2-json=${build_json}" >> $GITHUB_OUTPUT
-
name: Setup pikepdf image
id: pikepdf-setup
@@ -215,7 +229,7 @@ jobs:
echo ${build_json}
echo ::set-output name=pikepdf-json::${build_json}
echo "pikepdf-json=${build_json}" >> $GITHUB_OUTPUT
-
name: Setup jbig2enc image
id: jbig2enc-setup
@@ -224,7 +238,7 @@ jobs:
echo ${build_json}
echo ::set-output name=jbig2enc-json::${build_json}
echo "jbig2enc-json=${build_json}" >> $GITHUB_OUTPUT
outputs:
@@ -259,10 +273,10 @@ jobs:
run: |
if [[ ${{ needs.prepare-docker-build.outputs.ghcr-repository }} == "paperless-ngx/paperless-ngx" && ( ${{ github.ref_name }} == "main" || ${{ github.ref_name }} == "dev" || ${{ github.ref_name }} == "beta" || ${{ startsWith(github.ref, 'refs/tags/v') }} == "true" ) ]] ; then
echo "Enabling DockerHub image push"
echo ::set-output name=enable::"true"
echo "enable=true" >> $GITHUB_OUTPUT
else
echo "Not pushing to DockerHub"
echo ::set-output name=enable::"false"
echo "enable=false" >> $GITHUB_OUTPUT
fi
-
name: Gather Docker metadata
@@ -443,11 +457,11 @@ jobs:
name: Get version
id: get_version
run: |
echo ::set-output name=version::${{ github.ref_name }}
echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
if [[ ${{ contains(github.ref_name, '-beta.rc') }} == 'true' ]]; then
echo ::set-output name=prerelease::true
echo "prerelease=true" >> $GITHUB_OUTPUT
else
echo ::set-output name=prerelease::false
echo "prerelease=false" >> $GITHUB_OUTPUT
fi
-
name: Create Release and Changelog
@@ -484,6 +498,18 @@ jobs:
uses: actions/checkout@v3
with:
ref: main
-
name: Install pipenv
run: |
pip3 install --upgrade pip setuptools wheel pipx
pipx install pipenv
-
name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.9
cache: "pipenv"
cache-dependency-path: 'Pipfile.lock'
-
name: Append Changelog to docs
id: append-Changelog
@@ -497,9 +523,10 @@ jobs:
CURRENT_CHANGELOG=`tail --lines +2 changelog.md`
echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
mv changelog-new.md changelog.md
pipenv run pre-commit --files changelog.md
git config --global user.name "github-actions"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -am "Changelog ${{ steps.get_version.outputs.version }} - GHA"
git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA"
git push origin ${{ needs.publish-release.outputs.version }}-changelog
-
name: Create Pull Request

View File

@@ -16,12 +16,34 @@ on:
paths:
- ".github/workflows/cleanup-tags.yml"
- ".github/scripts/cleanup-tags.py"
- ".github/scripts/github.py"
- ".github/scripts/common.py"
concurrency:
group: registry-tags-cleanup
cancel-in-progress: false
jobs:
cleanup:
name: Cleanup Image Tags
runs-on: ubuntu-20.04
cleanup-images:
name: Cleanup Image Tags for ${{ matrix.primary-name }}
runs-on: ubuntu-latest
strategy:
matrix:
include:
- primary-name: "paperless-ngx"
cache-name: "paperless-ngx/builder/cache/app"
- primary-name: "paperless-ngx/builder/qpdf"
cache-name: "paperless-ngx/builder/cache/qpdf"
- primary-name: "paperless-ngx/builder/pikepdf"
cache-name: "paperless-ngx/builder/cache/pikepdf"
- primary-name: "paperless-ngx/builder/jbig2enc"
cache-name: "paperless-ngx/builder/cache/jbig2enc"
- primary-name: "paperless-ngx/builder/psycopg2"
cache-name: "paperless-ngx/builder/cache/psycopg2"
env:
# Requires a personal access token with the OAuth scope delete:packages
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
@@ -31,28 +53,43 @@ jobs:
uses: actions/checkout@v3
-
name: Login to Github Container Registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Set up Python
uses: actions/setup-python@v3
uses: actions/setup-python@v4
with:
python-version: "3.10"
-
name: Install requests
name: Install httpx
run: |
python -m pip install requests
python -m pip install httpx
#
# Clean up primary package
#
-
name: Cleanup feature tags
# Only run if the token is not empty
name: Cleanup for package "${{ matrix.primary-name }}"
if: "${{ env.TOKEN != '' }}"
run: |
python ${GITHUB_WORKSPACE}/.github/scripts/cleanup-tags.py --loglevel info --untagged --delete
python ${GITHUB_WORKSPACE}/.github/scripts/cleanup-tags.py --untagged --is-manifest --delete "${{ matrix.primary-name }}"
#
# Clean up registry cache package
#
-
name: Cleanup for package "${{ matrix.cache-name }}"
if: "${{ env.TOKEN != '' }}"
run: |
python ${GITHUB_WORKSPACE}/.github/scripts/cleanup-tags.py --untagged --delete "${{ matrix.cache-name }}"
#
# Verify tags which are left still pull
#
-
name: Check all tags still pull
run: |
ghcr_name=$(echo "${GITHUB_REPOSITORY}" | awk '{ print tolower($0) }')
docker pull --quiet --all-tags ghcr.io/${ghcr_name}
ghcr_name=$(echo "ghcr.io/${GITHUB_REPOSITORY_OWNER}/${{ matrix.primary-name }}" | awk '{ print tolower($0) }')
echo "Pulling all tags of ${ghcr_name}"
docker pull --quiet --all-tags ${ghcr_name}
docker image list

View File

@@ -38,7 +38,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -41,7 +41,7 @@ jobs:
id: set-ghcr-repository
run: |
ghcr_name=$(echo "${GITHUB_REPOSITORY}" | awk '{ print tolower($0) }')
echo ::set-output name=repository::${ghcr_name}
echo "repository=${ghcr_name}" >> $GITHUB_OUTPUT
-
name: Checkout
uses: actions/checkout@v3
@@ -50,6 +50,11 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: "3.9"
-
name: Install jq
run: |
sudo apt-get update
sudo apt-get install jq
-
name: Setup qpdf image
id: qpdf-setup
@@ -58,7 +63,7 @@ jobs:
echo ${build_json}
echo ::set-output name=qpdf-json::${build_json}
echo "qpdf-json=${build_json}" >> $GITHUB_OUTPUT
-
name: Setup psycopg2 image
id: psycopg2-setup
@@ -67,7 +72,7 @@ jobs:
echo ${build_json}
echo ::set-output name=psycopg2-json::${build_json}
echo "psycopg2-json=${build_json}" >> $GITHUB_OUTPUT
-
name: Setup pikepdf image
id: pikepdf-setup
@@ -76,7 +81,7 @@ jobs:
echo ${build_json}
echo ::set-output name=pikepdf-json::${build_json}
echo "pikepdf-json=${build_json}" >> $GITHUB_OUTPUT
-
name: Setup jbig2enc image
id: jbig2enc-setup
@@ -85,7 +90,19 @@ jobs:
echo ${build_json}
echo ::set-output name=jbig2enc-json::${build_json}
echo "jbig2enc-json=${build_json}" >> $GITHUB_OUTPUT
-
name: Setup other versions
id: cache-bust-setup
run: |
pillow_version=$(jq ".default.pillow.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g')
lxml_version=$(jq ".default.lxml.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g')
echo "Pillow is ${pillow_version}"
echo "lxml is ${lxml_version}"
echo "pillow-version=${pillow_version}" >> $GITHUB_OUTPUT
echo "lxml-version=${lxml_version}" >> $GITHUB_OUTPUT
outputs:
@@ -97,7 +114,11 @@ jobs:
psycopg2-json: ${{ steps.psycopg2-setup.outputs.psycopg2-json }}
jbig2enc-json: ${{ steps.jbig2enc-setup.outputs.jbig2enc-json}}
jbig2enc-json: ${{ steps.jbig2enc-setup.outputs.jbig2enc-json }}
pillow-version: ${{ steps.cache-bust-setup.outputs.pillow-version }}
lxml-version: ${{ steps.cache-bust-setup.outputs.lxml-version }}
build-qpdf-debs:
name: qpdf
@@ -145,3 +166,5 @@ jobs:
REPO=${{ needs.prepare-docker-build.outputs.ghcr-repository }}
QPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.qpdf-json).version }}
PIKEPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.pikepdf-json).version }}
PILLOW_VERSION=${{ needs.prepare-docker-build.outputs.pillow-version }}
LXML_VERSION=${{ needs.prepare-docker-build.outputs.lxml-version }}

View File

@@ -28,7 +28,7 @@ jobs:
if: github.event_name == 'issues' && (github.event.action == 'opened' || github.event.action == 'reopened')
steps:
- name: Add issue to project and set status to ${{ env.todo }}
uses: leonsteinhaeuser/project-beta-automations@v1.3.0
uses: leonsteinhaeuser/project-beta-automations@v2.0.1
with:
gh_token: ${{ secrets.GH_TOKEN }}
organization: paperless-ngx
@@ -44,7 +44,7 @@ jobs:
if: github.event_name == 'pull_request_target' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request.user.login != 'dependabot'
steps:
- name: Add PR to project and set status to "Needs Review"
uses: leonsteinhaeuser/project-beta-automations@v1.3.0
uses: leonsteinhaeuser/project-beta-automations@v2.0.1
with:
gh_token: ${{ secrets.GH_TOKEN }}
organization: paperless-ngx

3
.gitignore vendored
View File

@@ -93,3 +93,6 @@ scripts/nuke
# mac os
.DS_Store
# celery schedule file
celerybeat-schedule*

View File

@@ -37,7 +37,7 @@ repos:
exclude: "(^Pipfile\\.lock$)"
# Python hooks
- repo: https://github.com/asottile/reorder_python_imports
rev: v3.8.2
rev: v3.9.0
hooks:
- id: reorder-python-imports
exclude: "(migrations)"
@@ -47,7 +47,7 @@ repos:
- id: yesqa
exclude: "(migrations)"
- repo: https://github.com/asottile/add-trailing-comma
rev: "v2.2.3"
rev: "v2.3.0"
hooks:
- id: add-trailing-comma
exclude: "(migrations)"
@@ -59,11 +59,11 @@ repos:
args:
- "--config=./src/setup.cfg"
- repo: https://github.com/psf/black
rev: 22.6.0
rev: 22.10.0
hooks:
- id: black
- repo: https://github.com/asottile/pyupgrade
rev: v2.37.3
rev: v3.1.0
hooks:
- id: pyupgrade
exclude: "(migrations)"

View File

@@ -30,6 +30,25 @@ RUN set -eux \
RUN set -eux \
&& ./node_modules/.bin/ng build --configuration production
FROM --platform=$BUILDPLATFORM python:3.9-slim-bullseye as pipenv-base
# This stage generates the requirements.txt file using pipenv
# This stage runs once for the native platform, as the outputs are not
# dependent on target arch
# This way, pipenv dependencies are not left in the final image
# nor can pipenv mess up the final image somehow
# Inputs: None
WORKDIR /usr/src/pipenv
COPY Pipfile* ./
RUN set -eux \
&& echo "Installing pipenv" \
&& python3 -m pip install --no-cache-dir --upgrade pipenv \
&& echo "Generating requirement.txt" \
&& pipenv requirements > requirements.txt
FROM python:3.9-slim-bullseye as main-app
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
@@ -132,6 +151,7 @@ COPY [ \
"docker/paperless_cmd.sh", \
"docker/wait-for-redis.py", \
"docker/management_script.sh", \
"docker/flower-conditional.sh", \
"docker/install_management_commands.sh", \
"/usr/src/paperless/src/docker/" \
]
@@ -151,6 +171,8 @@ RUN set -eux \
&& chmod 755 /sbin/wait-for-redis.py \
&& mv paperless_cmd.sh /usr/local/bin/paperless_cmd.sh \
&& chmod 755 /usr/local/bin/paperless_cmd.sh \
&& mv flower-conditional.sh /usr/local/bin/flower-conditional.sh \
&& chmod 755 /usr/local/bin/flower-conditional.sh \
&& echo "Installing managment commands" \
&& chmod +x install_management_commands.sh \
&& ./install_management_commands.sh
@@ -163,7 +185,7 @@ RUN --mount=type=bind,from=qpdf-builder,target=/qpdf \
--mount=type=bind,from=pikepdf-builder,target=/pikepdf \
set -eux \
&& echo "Installing qpdf" \
&& apt-get install --yes --no-install-recommends /qpdf/usr/src/qpdf/libqpdf28_*.deb \
&& apt-get install --yes --no-install-recommends /qpdf/usr/src/qpdf/libqpdf29_*.deb \
&& apt-get install --yes --no-install-recommends /qpdf/usr/src/qpdf/qpdf_*.deb \
&& echo "Installing pikepdf and dependencies" \
&& python3 -m pip install --no-cache-dir /pikepdf/usr/src/wheels/pyparsing*.whl \
@@ -180,7 +202,7 @@ WORKDIR /usr/src/paperless/src/
# Python dependencies
# Change pretty frequently
COPY Pipfile* ./
COPY --from=pipenv-base /usr/src/pipenv/requirements.txt ./
# Packages needed only for building a few quick Python
# dependencies
@@ -195,24 +217,12 @@ RUN set -eux \
&& apt-get update \
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
&& python3 -m pip install --no-cache-dir --upgrade wheel \
&& echo "Installing pipenv" \
&& python3 -m pip install --no-cache-dir --upgrade pipenv \
&& echo "Installing Python requirements" \
# pipenv tries to be too fancy and prints so much junk
&& pipenv requirements > requirements.txt \
&& python3 -m pip install --default-timeout=1000 --no-cache-dir --requirement requirements.txt \
&& rm requirements.txt \
&& echo "Cleaning up image" \
&& apt-get -y purge ${BUILD_PACKAGES} \
&& apt-get -y autoremove --purge \
&& apt-get clean --yes \
# Remove pipenv and its unique packages
&& python3 -m pip uninstall --yes \
pipenv \
distlib \
platformdirs \
virtualenv \
virtualenv-clone \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /tmp/* \
&& rm -rf /var/tmp/* \

29
Pipfile
View File

@@ -10,40 +10,42 @@ name = "piwheels"
[packages]
dateparser = "~=1.1"
django = "~=4.0"
django = "~=4.1"
django-cors-headers = "*"
django-extensions = "*"
django-filter = "~=22.1"
django-q = {editable = true, ref = "paperless-main", git = "https://github.com/paperless-ngx/django-q.git"}
djangorestframework = "~=3.13"
djangorestframework = "~=3.14"
filelock = "*"
fuzzywuzzy = {extras = ["speedup"], version = "*"}
gunicorn = "*"
imap-tools = "*"
langdetect = "*"
pathvalidate = "*"
pillow = "~=9.2"
pikepdf = "~=5.6"
pillow = "~=9.3"
pikepdf = "*"
python-gnupg = "*"
python-dotenv = "*"
python-dateutil = "*"
python-magic = "*"
psycopg2 = "*"
redis = "*"
rapidfuzz = "*"
redis = {extras = ["hiredis"], version = "*"}
scikit-learn = "~=1.1"
# Pin this until piwheels is building 1.9 (see https://www.piwheels.org/project/scipy/)
scipy = "==1.8.1"
numpy = "*"
whitenoise = "~=6.2"
watchdog = "~=2.1"
whoosh="~=2.7"
inotifyrecursive = "~=0.3"
ocrmypdf = "~=13.7"
ocrmypdf = "~=14.0"
tqdm = "*"
tika = "*"
# TODO: This will sadly also install daphne+dependencies,
# which an ASGI server we don't need. Adds about 15MB image size.
channels = "~=3.0"
channels-redis = "*"
# Locked version until https://github.com/django/channels_redis/issues/332
# is resolved
channels-redis = "==3.4.1"
uvicorn = {extras = ["standard"], version = "*"}
concurrent-log-handler = "*"
"pdfminer.six" = "*"
@@ -51,8 +53,13 @@ concurrent-log-handler = "*"
"importlib-resources" = {version = "*", markers = "python_version < '3.9'"}
zipp = {version = "*", markers = "python_version < '3.9'"}
pyzbar = "*"
pdf2image = "*"
mysqlclient = "*"
celery = {extras = ["redis"], version = "*"}
django-celery-results = "*"
setproctitle = "*"
nltk = "*"
pdf2image = "*"
flower = "*"
[dev-packages]
coveralls = "*"
@@ -64,7 +71,7 @@ pytest-django = "*"
pytest-env = "*"
pytest-sugar = "*"
pytest-xdist = "*"
sphinx = "~=5.1"
sphinx = "~=5.3"
sphinx_rtd_theme = "*"
tox = "*"
black = "*"

1609
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
[![Crowdin](https://badges.crowdin.net/paperless-ngx/localized.svg)](https://crowdin.com/project/paperless-ngx)
[![Documentation Status](https://readthedocs.org/projects/paperless-ngx/badge/?version=latest)](https://paperless-ngx.readthedocs.io/en/latest/?badge=latest)
[![Coverage Status](https://coveralls.io/repos/github/paperless-ngx/paperless-ngx/badge.svg?branch=master)](https://coveralls.io/github/paperless-ngx/paperless-ngx?branch=master)
[![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)](https://matrix.to/#/#paperless:adnidor.de)
[![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)](https://matrix.to/#/%23paperlessngx%3Amatrix.org)
<p align="center">
<img src="https://github.com/paperless-ngx/paperless-ngx/raw/main/resources/logo/web/png/Black%20logo%20-%20no%20background.png#gh-light-mode-only" width="50%" />
@@ -105,6 +105,7 @@ Paperless has been around a while now, and people are starting to build stuff on
- [Paperless App](https://github.com/bauerj/paperless_app): An Android/iOS app for Paperless-ngx. Also works with the original Paperless and Paperless-ng.
- [Paperless Share](https://github.com/qcasey/paperless_share). Share any files from your Android application with paperless. Very simple, but works with all of the mobile scanning apps out there that allow you to share scanned documents.
- [Scan to Paperless](https://github.com/sbrunner/scan-to-paperless): Scan and prepare (crop, deskew, OCR, ...) your documents for Paperless.
- [Paperless Mobile](https://github.com/astubenbord/paperless-mobile): A modern, feature rich mobile application for Paperless.
These projects also exist, but their status and compatibility with paperless-ngx is unknown.

View File

@@ -23,6 +23,8 @@ fi
# Parse what we can from Pipfile.lock
pikepdf_version=$(jq ".default.pikepdf.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g')
psycopg2_version=$(jq ".default.psycopg2.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g')
pillow_version=$(jq ".default.pillow.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g')
lxml_version=$(jq ".default.lxml.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g')
# Read this from the other config file
qpdf_version=$(jq ".qpdf.version" .build-config.json | sed 's/"//g')
jbig2enc_version=$(jq ".jbig2enc.version" .build-config.json | sed 's/"//g')
@@ -40,4 +42,6 @@ docker build --file "$1" \
--build-arg JBIG2ENC_VERSION="${jbig2enc_version}" \
--build-arg QPDF_VERSION="${qpdf_version}" \
--build-arg PIKEPDF_VERSION="${pikepdf_version}" \
--build-arg PILLOW_VERSION="${pillow_version}" \
--build-arg LXML_VERSION="${lxml_version}" \
--build-arg PSYCOPG2_VERSION="${psycopg2_version}" "${@:2}" .

View File

@@ -1,14 +0,0 @@
# This Dockerfile compiles the frontend
# Inputs: None
FROM node:16-bullseye-slim AS compile-frontend
COPY ./src /src/src
COPY ./src-ui /src/src-ui
WORKDIR /src/src-ui
RUN set -eux \
&& npm update npm -g \
&& npm ci --omit=optional
RUN set -eux \
&& ./node_modules/.bin/ng build --configuration production

View File

@@ -18,6 +18,10 @@ LABEL org.opencontainers.image.description="A intermediate image with pikepdf wh
ARG DEBIAN_FRONTEND=noninteractive
ARG PIKEPDF_VERSION
# These are not used, but will still bust the cache if one changes
# Otherwise, the main image will try to build thing (and fail)
ARG PILLOW_VERSION
ARG LXML_VERSION
ARG BUILD_PACKAGES="\
build-essential \
@@ -60,7 +64,7 @@ RUN set -eux \
&& apt-get update --quiet \
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
&& echo "Installing qpdf" \
&& dpkg --install libqpdf28_*.deb \
&& dpkg --install libqpdf29_*.deb \
&& dpkg --install libqpdf-dev_*.deb \
&& echo "Installing Python tools" \
&& python3 -m pip install --no-cache-dir --upgrade \

View File

@@ -1,7 +1,7 @@
# This Dockerfile compiles the jbig2enc library
# Inputs:
# - QPDF_VERSION - the version of qpdf to build a .deb.
# Must be preset as a deb-src
# Must be present as a deb-src in bookworm
FROM debian:bullseye-slim as main
@@ -22,27 +22,23 @@ ARG BUILD_PACKAGES="\
libjpeg62-turbo-dev \
libgnutls28-dev \
packaging-dev \
cmake \
zlib1g-dev"
WORKDIR /usr/src
# As this is an base image for a multi-stage final image
# the added size of the install is basically irrelevant
RUN set -eux \
&& echo "Installing build tools" \
&& apt-get update --quiet \
&& apt-get install --yes --quiet --no-install-recommends $BUILD_PACKAGES \
&& echo "Building qpdf" \
&& echo "Getting qpdf src" \
&& echo "deb-src http://deb.debian.org/debian/ bookworm main" > /etc/apt/sources.list.d/bookworm-src.list \
&& apt-get update \
&& mkdir qpdf \
&& cd qpdf \
&& apt-get source --yes --quiet qpdf=${QPDF_VERSION}-1/bookworm \
&& echo "Building qpdf" \
&& cd qpdf-$QPDF_VERSION \
# We don't need to build the tests (also don't run them)
&& rm -rf libtests \
&& DEBEMAIL=hello@paperless-ngx.com debchange --bpo \
&& export DEB_BUILD_OPTIONS="terse nocheck nodoc parallel=2" \
&& dpkg-buildpackage --build=binary --unsigned-source --unsigned-changes --post-clean \
&& ls -ahl ../*.deb \

View File

@@ -77,18 +77,19 @@ services:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBENGINE: mariadb
PAPERLESS_DBHOST: db
PAPERLESS_DBUSER: paperless
PAPERLESS_DBPASSWORD: paperless
PAPERLESS_DBUSER: paperless # only needed if non-default username
PAPERLESS_DBPASS: paperless # only needed if non-default password
PAPERLESS_DBPORT: 3306
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: docker.io/gotenberg/gotenberg:7.4
image: docker.io/gotenberg/gotenberg:7.6
restart: unless-stopped
environment:
CHROMIUM_DISABLE_ROUTES: 1
command:
- "gotenberg"
- "--chromium-disable-routes=true"
tika:
image: ghcr.io/paperless-ngx/tika:latest

View File

@@ -71,8 +71,8 @@ services:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBENGINE: mariadb
PAPERLESS_DBHOST: db
PAPERLESS_DBUSER: paperless
PAPERLESS_DBPASSWORD: paperless
PAPERLESS_DBUSER: paperless # only needed if non-default username
PAPERLESS_DBPASS: paperless # only needed if non-default password
PAPERLESS_DBPORT: 3306

View File

@@ -77,7 +77,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: docker.io/gotenberg/gotenberg:7.4
image: docker.io/gotenberg/gotenberg:7.6
restart: unless-stopped
command:
- "gotenberg"

View File

@@ -65,7 +65,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: docker.io/gotenberg/gotenberg:7.4
image: docker.io/gotenberg/gotenberg:7.6
restart: unless-stopped
command:
- "gotenberg"

View File

@@ -9,8 +9,8 @@ set -e
# fill in the value of "$XYZ_DB_PASSWORD" from a file, especially for Docker's
# secrets feature
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local -r var="$1"
local -r fileVar="${var}_FILE"
# Basic validation
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
@@ -35,14 +35,14 @@ file_env() {
# Source: https://github.com/sameersbn/docker-gitlab/
map_uidgid() {
USERMAP_ORIG_UID=$(id -u paperless)
USERMAP_ORIG_GID=$(id -g paperless)
USERMAP_NEW_UID=${USERMAP_UID:-$USERMAP_ORIG_UID}
USERMAP_NEW_GID=${USERMAP_GID:-${USERMAP_ORIG_GID:-$USERMAP_NEW_UID}}
if [[ ${USERMAP_NEW_UID} != "${USERMAP_ORIG_UID}" || ${USERMAP_NEW_GID} != "${USERMAP_ORIG_GID}" ]]; then
echo "Mapping UID and GID for paperless:paperless to $USERMAP_NEW_UID:$USERMAP_NEW_GID"
usermod -o -u "${USERMAP_NEW_UID}" paperless
groupmod -o -g "${USERMAP_NEW_GID}" paperless
local -r usermap_original_uid=$(id -u paperless)
local -r usermap_original_gid=$(id -g paperless)
local -r usermap_new_uid=${USERMAP_UID:-$usermap_original_uid}
local -r usermap_new_gid=${USERMAP_GID:-${usermap_original_gid:-$usermap_new_uid}}
if [[ ${usermap_new_uid} != "${usermap_original_uid}" || ${usermap_new_gid} != "${usermap_original_gid}" ]]; then
echo "Mapping UID and GID for paperless:paperless to $usermap_new_uid:$usermap_new_gid"
usermod -o -u "${usermap_new_uid}" paperless
groupmod -o -g "${usermap_new_gid}" paperless
fi
}
@@ -50,6 +50,31 @@ map_folders() {
# Export these so they can be used in docker-prepare.sh
export DATA_DIR="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}"
export MEDIA_ROOT_DIR="${PAPERLESS_MEDIA_ROOT:-/usr/src/paperless/media}"
export CONSUME_DIR="${PAPERLESS_CONSUMPTION_DIR:-/usr/src/paperless/consume}"
}
nltk_data () {
# Store the NLTK data outside the Docker container
local -r nltk_data_dir="${DATA_DIR}/nltk"
local -r truthy_things=("yes y 1 t true")
# If not set, or it looks truthy
if [[ -z "${PAPERLESS_ENABLE_NLTK}" ]] || [[ "${truthy_things[*]}" =~ ${PAPERLESS_ENABLE_NLTK,} ]]; then
# Download or update the snowball stemmer data
python3 -W ignore::RuntimeWarning -m nltk.downloader -d "${nltk_data_dir}" snowball_data
# Download or update the stopwords corpus
python3 -W ignore::RuntimeWarning -m nltk.downloader -d "${nltk_data_dir}" stopwords
# Download or update the punkt tokenizer data
python3 -W ignore::RuntimeWarning -m nltk.downloader -d "${nltk_data_dir}" punkt
else
echo "Skipping NLTK data download"
fi
}
initialize() {
@@ -75,23 +100,33 @@ initialize() {
# Check for overrides of certain folders
map_folders
local export_dir="/usr/src/paperless/export"
local -r export_dir="/usr/src/paperless/export"
for dir in "${export_dir}" "${DATA_DIR}" "${DATA_DIR}/index" "${MEDIA_ROOT_DIR}" "${MEDIA_ROOT_DIR}/documents" "${MEDIA_ROOT_DIR}/documents/originals" "${MEDIA_ROOT_DIR}/documents/thumbnails"; do
for dir in \
"${export_dir}" \
"${DATA_DIR}" "${DATA_DIR}/index" \
"${MEDIA_ROOT_DIR}" "${MEDIA_ROOT_DIR}/documents" "${MEDIA_ROOT_DIR}/documents/originals" "${MEDIA_ROOT_DIR}/documents/thumbnails" \
"${CONSUME_DIR}"; do
if [[ ! -d "${dir}" ]]; then
echo "Creating directory ${dir}"
mkdir "${dir}"
fi
done
local tmp_dir="/tmp/paperless"
local -r tmp_dir="/tmp/paperless"
echo "Creating directory ${tmp_dir}"
mkdir -p "${tmp_dir}"
nltk_data
set +e
echo "Adjusting permissions of paperless files. This may take a while."
chown -R paperless:paperless ${tmp_dir}
for dir in "${export_dir}" "${DATA_DIR}" "${MEDIA_ROOT_DIR}"; do
for dir in \
"${export_dir}" \
"${DATA_DIR}" \
"${MEDIA_ROOT_DIR}" \
"${CONSUME_DIR}"; do
find "${dir}" -not \( -user paperless -and -group paperless \) -exec chown paperless:paperless {} +
done
set -e
@@ -102,7 +137,7 @@ initialize() {
install_languages() {
echo "Installing languages..."
local langs="$1"
local -r langs="$1"
read -ra langs <<<"$langs"
# Check that it is not empty

View File

@@ -4,12 +4,12 @@ set -e
wait_for_postgres() {
local attempt_num=1
local max_attempts=5
local -r max_attempts=5
echo "Waiting for PostgreSQL to start..."
local host="${PAPERLESS_DBHOST:-localhost}"
local port="${PAPERLESS_DBPORT:-5432}"
local -r host="${PAPERLESS_DBHOST:-localhost}"
local -r port="${PAPERLESS_DBPORT:-5432}"
# Disable warning, host and port can't have spaces
# shellcheck disable=SC2086
@@ -31,11 +31,11 @@ wait_for_postgres() {
wait_for_mariadb() {
echo "Waiting for MariaDB to start..."
host="${PAPERLESS_DBHOST:=localhost}"
port="${PAPERLESS_DBPORT:=3306}"
local -r host="${PAPERLESS_DBHOST:=localhost}"
local -r port="${PAPERLESS_DBPORT:=3306}"
attempt_num=1
max_attempts=5
local attempt_num=1
local -r max_attempts=5
while ! true > /dev/tcp/$host/$port; do
@@ -73,8 +73,8 @@ migrations() {
search_index() {
local index_version=1
local index_version_file=${DATA_DIR}/.index_version
local -r index_version=1
local -r index_version_file=${DATA_DIR}/.index_version
if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then
echo "Search index out of date. Updating..."
@@ -89,6 +89,46 @@ superuser() {
fi
}
custom_container_init() {
# Mostly borrowed from the LinuxServer.io base image
# https://github.com/linuxserver/docker-baseimage-ubuntu/tree/bionic/root/etc/cont-init.d
local -r custom_script_dir="/custom-cont-init.d"
# Tamper checking.
# Don't run files which are owned by anyone except root
# Don't run files which are writeable by others
if [ -d "${custom_script_dir}" ]; then
if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 ! -user root)" ]; then
echo "**** Potential tampering with custom scripts detected ****"
echo "**** The folder '${custom_script_dir}' must be owned by root ****"
return 0
fi
if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 -perm -o+w)" ]; then
echo "**** The folder '${custom_script_dir}' or some of contents have write permissions for others, which is a security risk. ****"
echo "**** Please review the permissions and their contents to make sure they are owned by root, and can only be modified by root. ****"
return 0
fi
# Make sure custom init directory has files in it
if [ -n "$(/bin/ls -A "${custom_script_dir}" 2>/dev/null)" ]; then
echo "[custom-init] files found in ${custom_script_dir} executing"
# Loop over files in the directory
for SCRIPT in "${custom_script_dir}"/*; do
NAME="$(basename "${SCRIPT}")"
if [ -f "${SCRIPT}" ]; then
echo "[custom-init] ${NAME}: executing..."
/bin/bash "${SCRIPT}"
echo "[custom-init] ${NAME}: exited $?"
elif [ ! -f "${SCRIPT}" ]; then
echo "[custom-init] ${NAME}: is not a file"
fi
done
else
echo "[custom-init] no custom files found exiting..."
fi
fi
}
do_work() {
if [[ "${PAPERLESS_DBENGINE}" == "mariadb" ]]; then
wait_for_mariadb
@@ -104,6 +144,9 @@ do_work() {
superuser
# Leave this last thing
custom_container_init
}
do_work

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
echo "Checking if we should start flower..."
if [[ -n "${PAPERLESS_ENABLE_FLOWER}" ]]; then
celery --app paperless flower
fi

View File

@@ -12,4 +12,4 @@ if [ "$(id -u)" == "$(id -u paperless)" ]; then
)
fi
/usr/local/bin/supervisord -c /etc/supervisord.conf "${rootless_args[@]}"
exec /usr/local/bin/supervisord -c /etc/supervisord.conf "${rootless_args[@]}"

View File

@@ -10,7 +10,7 @@ user=root
[program:gunicorn]
command=gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.asgi:application
user=paperless
priority = 1
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
@@ -20,17 +20,40 @@ stderr_logfile_maxbytes=0
command=python3 manage.py document_consumer
user=paperless
stopsignal=INT
priority = 20
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:scheduler]
command=python3 manage.py qcluster
[program:celery]
command = celery --app paperless worker --loglevel INFO
user=paperless
stopasgroup = true
stopwaitsecs = 60
priority = 5
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:celery-beat]
command = celery --app paperless beat --loglevel INFO
user=paperless
stopasgroup = true
priority = 10
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:celery-flower]
command = /usr/local/bin/flower-conditional.sh
user = paperless
startsecs = 0
priority = 40
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr

View File

@@ -447,6 +447,14 @@ command:
The command takes no arguments and processes all your mail accounts and rules.
.. note::
As of October 2022 Microsoft no longer supports IMAP authentication for Exchange
servers, thus Exchange is no longer supported until a solution is implemented in
the Python IMAP library used by Paperless. See `learn.microsoft.com`_
.. _learn.microsoft.com: https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-basic-authentication-exchange-online
.. _utilities-archiver:
Creating archived documents

View File

@@ -218,7 +218,8 @@ using the identifier which it has assigned to each document. You will end up get
files like ``0000123.pdf`` in your media directory. This isn't necessarily a bad
thing, because you normally don't have to access these files manually. However, if
you wish to name your files differently, you can do that by adjusting the
``PAPERLESS_FILENAME_FORMAT`` configuration option.
``PAPERLESS_FILENAME_FORMAT`` configuration option. Paperless adds the correct
file extension e.g. ``.pdf``, ``.jpg`` automatically.
This variable allows you to configure the filename (folders are allowed) using
placeholders. For example, configuring this to
@@ -257,12 +258,18 @@ Paperless provides the following placeholders within filenames:
* ``{tag_list}``: A comma separated list of all tags assigned to the document.
* ``{title}``: The title of the document.
* ``{created}``: The full date (ISO format) the document was created.
* ``{created_year}``: Year created only.
* ``{created_year}``: Year created only, formatted as the year with century.
* ``{created_year_short}``: Year created only, formatted as the year without century, zero padded.
* ``{created_month}``: Month created only (number 01-12).
* ``{created_month_name}``: Month created name, as per locale
* ``{created_month_name_short}``: Month created abbreviated name, as per locale
* ``{created_day}``: Day created only (number 01-31).
* ``{added}``: The full date (ISO format) the document was added to paperless.
* ``{added_year}``: Year added only.
* ``{added_year_short}``: Year added only, formatted as the year without century, zero padded.
* ``{added_month}``: Month added only (number 01-12).
* ``{added_month_name}``: Month added name, as per locale
* ``{added_month_name_short}``: Month added abbreviated name, as per locale
* ``{added_day}``: Day added only (number 01-31).
@@ -363,3 +370,50 @@ For simplicity, `By Year` defines the same structure as in the previous example
If you adjust the format of an existing storage path, old documents don't get relocated automatically.
You need to run the :ref:`document renamer <utilities-renamer>` to adjust their pathes.
.. _advanced-celery-monitoring:
Celery Monitoring
#################
The monitoring tool `Flower <https://flower.readthedocs.io/en/latest/index.html>`_ can be used to view more
detailed information about the health of the celery workers used for asynchronous tasks. This includes details
on currently running, queued and completed tasks, timing and more. Flower can also be used with Prometheus, as it
exports metrics. For details on its capabilities, refer to the Flower documentation.
To configure Flower further, create a `flowerconfig.py` and place it into the `src/paperless` directory. For
a Docker installation, you can use volumes to accomplish this:
.. code:: yaml
services:
# ...
webserver:
# ...
volumes:
- /path/to/my/flowerconfig.py:/usr/src/paperless/src/paperless/flowerconfig.py:ro
Custom Container Initialization
###############################
The Docker image includes the ability to run custom user scripts during startup. This could be
utilized for installing additional tools or Python packages, for example.
To utilize this, mount a folder containing your scripts to the custom initialization directory, `/custom-cont-init.d`
and place scripts you wish to run inside. For security, the folder and its contents must be owned by `root`.
Additionally, scripts must only be writable by `root`.
Your scripts will be run directly before the webserver completes startup. Scripts will be run by the `root` user.
This is an advanced functionality with which you could break functionality or lose data.
For example, using Docker Compose:
.. code:: yaml
services:
# ...
webserver:
# ...
volumes:
- /path/to/my/scripts:/custom-cont-init.d:ro

View File

@@ -1,5 +1,218 @@
# Changelog
## paperless-ngx 1.9.2
### Bug Fixes
- Bugfix: Allow PAPERLESS_OCR_CLEAN=none [@shamoon](https://github.com/shamoon) ([#1670](https://github.com/paperless-ngx/paperless-ngx/pull/1670))
### All App Changes
- Chore: Bumps version numbers to 1.9.2 [@stumpylog](https://github.com/stumpylog) ([#1666](https://github.com/paperless-ngx/paperless-ngx/pull/1666))
## paperless-ngx 1.9.1
### Notes
- Version 1.9.1 incorrectly displays the version string as 1.9.0
### Bug Fixes
- Bugfix: Fixes missing OCR mode skip_noarchive [@stumpylog](https://github.com/stumpylog) ([#1645](https://github.com/paperless-ngx/paperless-ngx/pull/1645))
- Fix reset button padding on small screens [@shamoon](https://github.com/shamoon) ([#1646](https://github.com/paperless-ngx/paperless-ngx/pull/1646))
### Documentation
- Improve docs re [@janis-ax](https://github.com/janis-ax) ([#1625](https://github.com/paperless-ngx/paperless-ngx/pull/1625))
- [Documentation] Add v1.9.0 changelog [@github-actions](https://github.com/github-actions) ([#1639](https://github.com/paperless-ngx/paperless-ngx/pull/1639))
### All App Changes
- Bugfix: Fixes missing OCR mode skip_noarchive [@stumpylog](https://github.com/stumpylog) ([#1645](https://github.com/paperless-ngx/paperless-ngx/pull/1645))
- Fix reset button padding on small screens [@shamoon](https://github.com/shamoon) ([#1646](https://github.com/paperless-ngx/paperless-ngx/pull/1646))
## paperless-ngx 1.9.0
### Features
- Feature: Faster, less memory barcode handling [@stumpylog](https://github.com/stumpylog) ([#1594](https://github.com/paperless-ngx/paperless-ngx/pull/1594))
- Feature: Display django-q process names [@stumpylog](https://github.com/stumpylog) ([#1567](https://github.com/paperless-ngx/paperless-ngx/pull/1567))
- Feature: Add MariaDB support [@bckelly1](https://github.com/bckelly1) ([#543](https://github.com/paperless-ngx/paperless-ngx/pull/543))
- Feature: Simplify IMAP login for UTF-8 [@stumpylog](https://github.com/stumpylog) ([#1492](https://github.com/paperless-ngx/paperless-ngx/pull/1492))
- Feature: Even better re-do of OCR [@stumpylog](https://github.com/stumpylog) ([#1451](https://github.com/paperless-ngx/paperless-ngx/pull/1451))
- Feature: document comments [@tim-vogel](https://github.com/tim-vogel) ([#1375](https://github.com/paperless-ngx/paperless-ngx/pull/1375))
- Adding date suggestions to the documents details view [@Eckii24](https://github.com/Eckii24) ([#1367](https://github.com/paperless-ngx/paperless-ngx/pull/1367))
- Feature: Event driven consumer [@stumpylog](https://github.com/stumpylog) ([#1421](https://github.com/paperless-ngx/paperless-ngx/pull/1421))
- Feature: Adds storage paths to re-tagger command [@stumpylog](https://github.com/stumpylog) ([#1446](https://github.com/paperless-ngx/paperless-ngx/pull/1446))
- Feature: Preserve original filename in metadata [@GwynHannay](https://github.com/GwynHannay) ([#1440](https://github.com/paperless-ngx/paperless-ngx/pull/1440))
- Handle tags for gmail email accounts [@sisao](https://github.com/sisao) ([#1433](https://github.com/paperless-ngx/paperless-ngx/pull/1433))
- Update redis image [@tribut](https://github.com/tribut) ([#1436](https://github.com/paperless-ngx/paperless-ngx/pull/1436))
- PAPERLESS_REDIS may be set via docker secrets [@DennisGaida](https://github.com/DennisGaida) ([#1405](https://github.com/paperless-ngx/paperless-ngx/pull/1405))
### Bug Fixes
- paperless_cmd.sh: use exec to run supervisord [@lemmi](https://github.com/lemmi) ([#1617](https://github.com/paperless-ngx/paperless-ngx/pull/1617))
- Fix: Double barcode separation creates empty file [@stumpylog](https://github.com/stumpylog) ([#1596](https://github.com/paperless-ngx/paperless-ngx/pull/1596))
- Fix: Resolve issue with slow classifier [@stumpylog](https://github.com/stumpylog) ([#1576](https://github.com/paperless-ngx/paperless-ngx/pull/1576))
- Fix document comments not updating on document navigation [@shamoon](https://github.com/shamoon) ([#1566](https://github.com/paperless-ngx/paperless-ngx/pull/1566))
- Fix: Include storage paths in document exporter [@shamoon](https://github.com/shamoon) ([#1557](https://github.com/paperless-ngx/paperless-ngx/pull/1557))
- Chore: Cleanup and validate settings [@stumpylog](https://github.com/stumpylog) ([#1551](https://github.com/paperless-ngx/paperless-ngx/pull/1551))
- Bugfix: Better gunicorn settings for workers [@stumpylog](https://github.com/stumpylog) ([#1500](https://github.com/paperless-ngx/paperless-ngx/pull/1500))
- Fix actions button in tasks table [@shamoon](https://github.com/shamoon) ([#1488](https://github.com/paperless-ngx/paperless-ngx/pull/1488))
- Fix: Add missing filter rule types to SavedViewFilterRule model \& fix migrations [@shamoon](https://github.com/shamoon) ([#1463](https://github.com/paperless-ngx/paperless-ngx/pull/1463))
- Fix paperless.conf.example typo [@qcasey](https://github.com/qcasey) ([#1460](https://github.com/paperless-ngx/paperless-ngx/pull/1460))
- Bugfix: Fixes the creation of an archive file, even if noarchive was specified [@stumpylog](https://github.com/stumpylog) ([#1442](https://github.com/paperless-ngx/paperless-ngx/pull/1442))
- Fix: created_date should not be required [@shamoon](https://github.com/shamoon) ([#1412](https://github.com/paperless-ngx/paperless-ngx/pull/1412))
- Fix: dev backend testing [@stumpylog](https://github.com/stumpylog) ([#1420](https://github.com/paperless-ngx/paperless-ngx/pull/1420))
- Bugfix: Catch all exceptions during the task signals [@stumpylog](https://github.com/stumpylog) ([#1387](https://github.com/paperless-ngx/paperless-ngx/pull/1387))
- Fix: saved view page parameter [@shamoon](https://github.com/shamoon) ([#1376](https://github.com/paperless-ngx/paperless-ngx/pull/1376))
- Fix: Correct browser unsaved changes warning [@shamoon](https://github.com/shamoon) ([#1369](https://github.com/paperless-ngx/paperless-ngx/pull/1369))
- Fix: correct date pasting with other formats [@shamoon](https://github.com/shamoon) ([#1370](https://github.com/paperless-ngx/paperless-ngx/pull/1370))
- Bugfix: Allow webserver bind address to be configured [@stumpylog](https://github.com/stumpylog) ([#1358](https://github.com/paperless-ngx/paperless-ngx/pull/1358))
- Bugfix: Chain exceptions during exception handling [@stumpylog](https://github.com/stumpylog) ([#1354](https://github.com/paperless-ngx/paperless-ngx/pull/1354))
- Fix: missing tooltip translation \& filter editor wrapping [@shamoon](https://github.com/shamoon) ([#1305](https://github.com/paperless-ngx/paperless-ngx/pull/1305))
- Bugfix: Interaction between barcode and directories as tags [@stumpylog](https://github.com/stumpylog) ([#1303](https://github.com/paperless-ngx/paperless-ngx/pull/1303))
### Documentation
- [Beta] Paperless-ngx v1.9.0 Release Candidate [@stumpylog](https://github.com/stumpylog) ([#1560](https://github.com/paperless-ngx/paperless-ngx/pull/1560))
- docs/configuration: Fix binary variable defaults [@erikarvstedt](https://github.com/erikarvstedt) ([#1528](https://github.com/paperless-ngx/paperless-ngx/pull/1528))
- Info about installing on subpath [@viktor-c](https://github.com/viktor-c) ([#1350](https://github.com/paperless-ngx/paperless-ngx/pull/1350))
- Docs: move scanner \& software recs to GH wiki [@shamoon](https://github.com/shamoon) ([#1482](https://github.com/paperless-ngx/paperless-ngx/pull/1482))
- Docs: Update mobile scanner section [@tooomm](https://github.com/tooomm) ([#1467](https://github.com/paperless-ngx/paperless-ngx/pull/1467))
- Adding date suggestions to the documents details view [@Eckii24](https://github.com/Eckii24) ([#1367](https://github.com/paperless-ngx/paperless-ngx/pull/1367))
- docs: scanners: add Brother ads4700w [@ocelotsloth](https://github.com/ocelotsloth) ([#1450](https://github.com/paperless-ngx/paperless-ngx/pull/1450))
- Feature: Adds storage paths to re-tagger command [@stumpylog](https://github.com/stumpylog) ([#1446](https://github.com/paperless-ngx/paperless-ngx/pull/1446))
- Changes to Redis documentation [@Zerteax](https://github.com/Zerteax) ([#1441](https://github.com/paperless-ngx/paperless-ngx/pull/1441))
- Update scanners.rst [@glassbox-sco](https://github.com/glassbox-sco) ([#1430](https://github.com/paperless-ngx/paperless-ngx/pull/1430))
- Update scanners.rst [@derlucas](https://github.com/derlucas) ([#1415](https://github.com/paperless-ngx/paperless-ngx/pull/1415))
- Bugfix: Allow webserver bind address to be configured [@stumpylog](https://github.com/stumpylog) ([#1358](https://github.com/paperless-ngx/paperless-ngx/pull/1358))
- docs: fix small typo [@tooomm](https://github.com/tooomm) ([#1352](https://github.com/paperless-ngx/paperless-ngx/pull/1352))
- [Documentation] Add v1.8.0 changelog [@github-actions](https://github.com/github-actions) ([#1298](https://github.com/paperless-ngx/paperless-ngx/pull/1298))
### Maintenance
- [Beta] Paperless-ngx v1.9.0 Release Candidate [@stumpylog](https://github.com/stumpylog) ([#1560](https://github.com/paperless-ngx/paperless-ngx/pull/1560))
- paperless_cmd.sh: use exec to run supervisord [@lemmi](https://github.com/lemmi) ([#1617](https://github.com/paperless-ngx/paperless-ngx/pull/1617))
- Chore: Extended container image cleanup [@stumpylog](https://github.com/stumpylog) ([#1556](https://github.com/paperless-ngx/paperless-ngx/pull/1556))
- Chore: Smaller library images [@stumpylog](https://github.com/stumpylog) ([#1546](https://github.com/paperless-ngx/paperless-ngx/pull/1546))
- Bump tj-actions/changed-files from 24 to 29.0.2 [@dependabot](https://github.com/dependabot) ([#1493](https://github.com/paperless-ngx/paperless-ngx/pull/1493))
- Bugfix: Better gunicorn settings for workers [@stumpylog](https://github.com/stumpylog) ([#1500](https://github.com/paperless-ngx/paperless-ngx/pull/1500))
- [CI] Fix release drafter issues [@qcasey](https://github.com/qcasey) ([#1301](https://github.com/paperless-ngx/paperless-ngx/pull/1301))
- Fix: dev backend testing [@stumpylog](https://github.com/stumpylog) ([#1420](https://github.com/paperless-ngx/paperless-ngx/pull/1420))
- Chore: Exclude dependabot PRs from Project, set status to Needs Review [@qcasey](https://github.com/qcasey) ([#1397](https://github.com/paperless-ngx/paperless-ngx/pull/1397))
- Chore: Add to label PRs based on and title [@qcasey](https://github.com/qcasey) ([#1396](https://github.com/paperless-ngx/paperless-ngx/pull/1396))
- Chore: use pre-commit in the Ci workflow [@stumpylog](https://github.com/stumpylog) ([#1362](https://github.com/paperless-ngx/paperless-ngx/pull/1362))
- Chore: Fixes permissions for image tag cleanup [@stumpylog](https://github.com/stumpylog) ([#1315](https://github.com/paperless-ngx/paperless-ngx/pull/1315))
- Bump leonsteinhaeuser/project-beta-automations from 1.2.1 to 1.3.0 [@dependabot](https://github.com/dependabot) ([#1328](https://github.com/paperless-ngx/paperless-ngx/pull/1328))
- Bump tj-actions/changed-files from 23.1 to 24 [@dependabot](https://github.com/dependabot) ([#1329](https://github.com/paperless-ngx/paperless-ngx/pull/1329))
- Feature: Remove requirements.txt and use pipenv everywhere [@stumpylog](https://github.com/stumpylog) ([#1316](https://github.com/paperless-ngx/paperless-ngx/pull/1316))
### Dependencies
<details>
<summary>34 changes</summary>
- Bump pikepdf from 5.5.0 to 5.6.1 [@dependabot](https://github.com/dependabot) ([#1537](https://github.com/paperless-ngx/paperless-ngx/pull/1537))
- Bump black from 22.6.0 to 22.8.0 [@dependabot](https://github.com/dependabot) ([#1539](https://github.com/paperless-ngx/paperless-ngx/pull/1539))
- Bump tqdm from 4.64.0 to 4.64.1 [@dependabot](https://github.com/dependabot) ([#1540](https://github.com/paperless-ngx/paperless-ngx/pull/1540))
- Bump pytest from 7.1.2 to 7.1.3 [@dependabot](https://github.com/dependabot) ([#1538](https://github.com/paperless-ngx/paperless-ngx/pull/1538))
- Bump tj-actions/changed-files from 24 to 29.0.2 [@dependabot](https://github.com/dependabot) ([#1493](https://github.com/paperless-ngx/paperless-ngx/pull/1493))
- Bump angular packages, jest-preset-angular in src-ui [@dependabot](https://github.com/dependabot) ([#1502](https://github.com/paperless-ngx/paperless-ngx/pull/1502))
- Bump jest-environment-jsdom from 28.1.3 to 29.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1507](https://github.com/paperless-ngx/paperless-ngx/pull/1507))
- Bump [@<!---->types/node from 18.6.3 to 18.7.14 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.6.3 to 18.7.14 in /src-ui @dependabot) ([#1506](https://github.com/paperless-ngx/paperless-ngx/pull/1506))
- Bump [@<!---->angular-builders/jest from 14.0.0 to 14.0.1 in /src-ui @dependabot](https://github.com/<!---->angular-builders/jest from 14.0.0 to 14.0.1 in /src-ui @dependabot) ([#1505](https://github.com/paperless-ngx/paperless-ngx/pull/1505))
- Bump zone.js from 0.11.7 to 0.11.8 in /src-ui [@dependabot](https://github.com/dependabot) ([#1504](https://github.com/paperless-ngx/paperless-ngx/pull/1504))
- Bump ngx-color from 8.0.1 to 8.0.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#1494](https://github.com/paperless-ngx/paperless-ngx/pull/1494))
- Bump cypress from 10.3.1 to 10.7.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1496](https://github.com/paperless-ngx/paperless-ngx/pull/1496))
- Bump [@<!---->cypress/schematic from 2.0.0 to 2.1.1 in /src-ui @dependabot](https://github.com/<!---->cypress/schematic from 2.0.0 to 2.1.1 in /src-ui @dependabot) ([#1495](https://github.com/paperless-ngx/paperless-ngx/pull/1495))
- Bump [@<!---->popperjs/core from 2.11.5 to 2.11.6 in /src-ui @dependabot](https://github.com/<!---->popperjs/core from 2.11.5 to 2.11.6 in /src-ui @dependabot) ([#1498](https://github.com/paperless-ngx/paperless-ngx/pull/1498))
- Bump sphinx from 5.0.2 to 5.1.1 [@dependabot](https://github.com/dependabot) ([#1297](https://github.com/paperless-ngx/paperless-ngx/pull/1297))
- Chore: Bump Python dependencies [@stumpylog](https://github.com/stumpylog) ([#1445](https://github.com/paperless-ngx/paperless-ngx/pull/1445))
- Chore: Update Python deps [@stumpylog](https://github.com/stumpylog) ([#1391](https://github.com/paperless-ngx/paperless-ngx/pull/1391))
- Bump watchfiles from 0.15.0 to 0.16.1 [@dependabot](https://github.com/dependabot) ([#1285](https://github.com/paperless-ngx/paperless-ngx/pull/1285))
- Bump leonsteinhaeuser/project-beta-automations from 1.2.1 to 1.3.0 [@dependabot](https://github.com/dependabot) ([#1328](https://github.com/paperless-ngx/paperless-ngx/pull/1328))
- Bump tj-actions/changed-files from 23.1 to 24 [@dependabot](https://github.com/dependabot) ([#1329](https://github.com/paperless-ngx/paperless-ngx/pull/1329))
- Bump cypress from 10.3.0 to 10.3.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1342](https://github.com/paperless-ngx/paperless-ngx/pull/1342))
- Bump ngx-color from 7.3.3 to 8.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1343](https://github.com/paperless-ngx/paperless-ngx/pull/1343))
- Bump [@<!---->angular/cli from 14.0.4 to 14.1.0 in /src-ui @dependabot](https://github.com/<!---->angular/cli from 14.0.4 to 14.1.0 in /src-ui @dependabot) ([#1330](https://github.com/paperless-ngx/paperless-ngx/pull/1330))
- Bump [@<!---->types/node from 18.0.0 to 18.6.3 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.0.0 to 18.6.3 in /src-ui @dependabot) ([#1341](https://github.com/paperless-ngx/paperless-ngx/pull/1341))
- Bump jest-preset-angular from 12.1.0 to 12.2.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1340](https://github.com/paperless-ngx/paperless-ngx/pull/1340))
- Bump concurrently from 7.2.2 to 7.3.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1326](https://github.com/paperless-ngx/paperless-ngx/pull/1326))
- Bump ng2-pdf-viewer from 9.0.0 to 9.1.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1337](https://github.com/paperless-ngx/paperless-ngx/pull/1337))
- Bump jest-environment-jsdom from 28.1.2 to 28.1.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#1336](https://github.com/paperless-ngx/paperless-ngx/pull/1336))
- Bump ngx-file-drop from 13.0.0 to 14.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1331](https://github.com/paperless-ngx/paperless-ngx/pull/1331))
- Bump jest and [@<!---->types/jest in /src-ui @dependabot](https://github.com/<!---->types/jest in /src-ui @dependabot) ([#1333](https://github.com/paperless-ngx/paperless-ngx/pull/1333))
- Bump bootstrap from 5.1.3 to 5.2.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1327](https://github.com/paperless-ngx/paperless-ngx/pull/1327))
- Bump typescript from 4.6.4 to 4.7.4 in /src-ui [@dependabot](https://github.com/dependabot) ([#1324](https://github.com/paperless-ngx/paperless-ngx/pull/1324))
- Bump ts-node from 10.8.1 to 10.9.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1325](https://github.com/paperless-ngx/paperless-ngx/pull/1325))
- Bump rxjs from 7.5.5 to 7.5.6 in /src-ui [@dependabot](https://github.com/dependabot) ([#1323](https://github.com/paperless-ngx/paperless-ngx/pull/1323))
</details>
### All App Changes
- [Beta] Paperless-ngx v1.9.0 Release Candidate [@stumpylog](https://github.com/stumpylog) ([#1560](https://github.com/paperless-ngx/paperless-ngx/pull/1560))
- Feature: Faster, less memory barcode handling [@stumpylog](https://github.com/stumpylog) ([#1594](https://github.com/paperless-ngx/paperless-ngx/pull/1594))
- Fix: Consume directory permissions were not updated [@stumpylog](https://github.com/stumpylog) ([#1605](https://github.com/paperless-ngx/paperless-ngx/pull/1605))
- Fix: Double barcode separation creates empty file [@stumpylog](https://github.com/stumpylog) ([#1596](https://github.com/paperless-ngx/paperless-ngx/pull/1596))
- Fix: Parsing Tika documents fails with AttributeError [@stumpylog](https://github.com/stumpylog) ([#1591](https://github.com/paperless-ngx/paperless-ngx/pull/1591))
- Fix: Resolve issue with slow classifier [@stumpylog](https://github.com/stumpylog) ([#1576](https://github.com/paperless-ngx/paperless-ngx/pull/1576))
- Feature: Display django-q process names [@stumpylog](https://github.com/stumpylog) ([#1567](https://github.com/paperless-ngx/paperless-ngx/pull/1567))
- Fix document comments not updating on document navigation [@shamoon](https://github.com/shamoon) ([#1566](https://github.com/paperless-ngx/paperless-ngx/pull/1566))
- Feature: Add MariaDB support [@bckelly1](https://github.com/bckelly1) ([#543](https://github.com/paperless-ngx/paperless-ngx/pull/543))
- Fix: Include storage paths in document exporter [@shamoon](https://github.com/shamoon) ([#1557](https://github.com/paperless-ngx/paperless-ngx/pull/1557))
- Chore: Cleanup and validate settings [@stumpylog](https://github.com/stumpylog) ([#1551](https://github.com/paperless-ngx/paperless-ngx/pull/1551))
- Bump pikepdf from 5.5.0 to 5.6.1 [@dependabot](https://github.com/dependabot) ([#1537](https://github.com/paperless-ngx/paperless-ngx/pull/1537))
- Bump black from 22.6.0 to 22.8.0 [@dependabot](https://github.com/dependabot) ([#1539](https://github.com/paperless-ngx/paperless-ngx/pull/1539))
- Bump tqdm from 4.64.0 to 4.64.1 [@dependabot](https://github.com/dependabot) ([#1540](https://github.com/paperless-ngx/paperless-ngx/pull/1540))
- Bump pytest from 7.1.2 to 7.1.3 [@dependabot](https://github.com/dependabot) ([#1538](https://github.com/paperless-ngx/paperless-ngx/pull/1538))
- Bump angular packages, jest-preset-angular in src-ui [@dependabot](https://github.com/dependabot) ([#1502](https://github.com/paperless-ngx/paperless-ngx/pull/1502))
- Bump jest-environment-jsdom from 28.1.3 to 29.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1507](https://github.com/paperless-ngx/paperless-ngx/pull/1507))
- Bump [@<!---->types/node from 18.6.3 to 18.7.14 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.6.3 to 18.7.14 in /src-ui @dependabot) ([#1506](https://github.com/paperless-ngx/paperless-ngx/pull/1506))
- Bump [@<!---->angular-builders/jest from 14.0.0 to 14.0.1 in /src-ui @dependabot](https://github.com/<!---->angular-builders/jest from 14.0.0 to 14.0.1 in /src-ui @dependabot) ([#1505](https://github.com/paperless-ngx/paperless-ngx/pull/1505))
- Bump zone.js from 0.11.7 to 0.11.8 in /src-ui [@dependabot](https://github.com/dependabot) ([#1504](https://github.com/paperless-ngx/paperless-ngx/pull/1504))
- Bump ngx-color from 8.0.1 to 8.0.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#1494](https://github.com/paperless-ngx/paperless-ngx/pull/1494))
- Bump cypress from 10.3.1 to 10.7.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1496](https://github.com/paperless-ngx/paperless-ngx/pull/1496))
- Bump [@<!---->cypress/schematic from 2.0.0 to 2.1.1 in /src-ui @dependabot](https://github.com/<!---->cypress/schematic from 2.0.0 to 2.1.1 in /src-ui @dependabot) ([#1495](https://github.com/paperless-ngx/paperless-ngx/pull/1495))
- Bump [@<!---->popperjs/core from 2.11.5 to 2.11.6 in /src-ui @dependabot](https://github.com/<!---->popperjs/core from 2.11.5 to 2.11.6 in /src-ui @dependabot) ([#1498](https://github.com/paperless-ngx/paperless-ngx/pull/1498))
- Feature: Simplify IMAP login for UTF-8 [@stumpylog](https://github.com/stumpylog) ([#1492](https://github.com/paperless-ngx/paperless-ngx/pull/1492))
- Fix actions button in tasks table [@shamoon](https://github.com/shamoon) ([#1488](https://github.com/paperless-ngx/paperless-ngx/pull/1488))
- Fix: Add missing filter rule types to SavedViewFilterRule model \& fix migrations [@shamoon](https://github.com/shamoon) ([#1463](https://github.com/paperless-ngx/paperless-ngx/pull/1463))
- Feature: Even better re-do of OCR [@stumpylog](https://github.com/stumpylog) ([#1451](https://github.com/paperless-ngx/paperless-ngx/pull/1451))
- Feature: document comments [@tim-vogel](https://github.com/tim-vogel) ([#1375](https://github.com/paperless-ngx/paperless-ngx/pull/1375))
- Adding date suggestions to the documents details view [@Eckii24](https://github.com/Eckii24) ([#1367](https://github.com/paperless-ngx/paperless-ngx/pull/1367))
- Bump sphinx from 5.0.2 to 5.1.1 [@dependabot](https://github.com/dependabot) ([#1297](https://github.com/paperless-ngx/paperless-ngx/pull/1297))
- Feature: Event driven consumer [@stumpylog](https://github.com/stumpylog) ([#1421](https://github.com/paperless-ngx/paperless-ngx/pull/1421))
- Bugfix: Fixes the creation of an archive file, even if noarchive was specified [@stumpylog](https://github.com/stumpylog) ([#1442](https://github.com/paperless-ngx/paperless-ngx/pull/1442))
- Feature: Adds storage paths to re-tagger command [@stumpylog](https://github.com/stumpylog) ([#1446](https://github.com/paperless-ngx/paperless-ngx/pull/1446))
- Feature: Preserve original filename in metadata [@GwynHannay](https://github.com/GwynHannay) ([#1440](https://github.com/paperless-ngx/paperless-ngx/pull/1440))
- Handle tags for gmail email accounts [@sisao](https://github.com/sisao) ([#1433](https://github.com/paperless-ngx/paperless-ngx/pull/1433))
- Fix: should not be required [@shamoon](https://github.com/shamoon) ([#1412](https://github.com/paperless-ngx/paperless-ngx/pull/1412))
- Bugfix: Catch all exceptions during the task signals [@stumpylog](https://github.com/stumpylog) ([#1387](https://github.com/paperless-ngx/paperless-ngx/pull/1387))
- Fix: saved view page parameter [@shamoon](https://github.com/shamoon) ([#1376](https://github.com/paperless-ngx/paperless-ngx/pull/1376))
- Fix: Correct browser unsaved changes warning [@shamoon](https://github.com/shamoon) ([#1369](https://github.com/paperless-ngx/paperless-ngx/pull/1369))
- Fix: correct date pasting with other formats [@shamoon](https://github.com/shamoon) ([#1370](https://github.com/paperless-ngx/paperless-ngx/pull/1370))
- Chore: use pre-commit in the Ci workflow [@stumpylog](https://github.com/stumpylog) ([#1362](https://github.com/paperless-ngx/paperless-ngx/pull/1362))
- Bugfix: Chain exceptions during exception handling [@stumpylog](https://github.com/stumpylog) ([#1354](https://github.com/paperless-ngx/paperless-ngx/pull/1354))
- Bump watchfiles from 0.15.0 to 0.16.1 [@dependabot](https://github.com/dependabot) ([#1285](https://github.com/paperless-ngx/paperless-ngx/pull/1285))
- Bump cypress from 10.3.0 to 10.3.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1342](https://github.com/paperless-ngx/paperless-ngx/pull/1342))
- Bump ngx-color from 7.3.3 to 8.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1343](https://github.com/paperless-ngx/paperless-ngx/pull/1343))
- Bump [@<!---->angular/cli from 14.0.4 to 14.1.0 in /src-ui @dependabot](https://github.com/<!---->angular/cli from 14.0.4 to 14.1.0 in /src-ui @dependabot) ([#1330](https://github.com/paperless-ngx/paperless-ngx/pull/1330))
- Bump [@<!---->types/node from 18.0.0 to 18.6.3 in /src-ui @dependabot](https://github.com/<!---->types/node from 18.0.0 to 18.6.3 in /src-ui @dependabot) ([#1341](https://github.com/paperless-ngx/paperless-ngx/pull/1341))
- Bump jest-preset-angular from 12.1.0 to 12.2.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1340](https://github.com/paperless-ngx/paperless-ngx/pull/1340))
- Bump concurrently from 7.2.2 to 7.3.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1326](https://github.com/paperless-ngx/paperless-ngx/pull/1326))
- Bump ng2-pdf-viewer from 9.0.0 to 9.1.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1337](https://github.com/paperless-ngx/paperless-ngx/pull/1337))
- Bump jest-environment-jsdom from 28.1.2 to 28.1.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#1336](https://github.com/paperless-ngx/paperless-ngx/pull/1336))
- Bump ngx-file-drop from 13.0.0 to 14.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1331](https://github.com/paperless-ngx/paperless-ngx/pull/1331))
- Bump jest and [@<!---->types/jest in /src-ui @dependabot](https://github.com/<!---->types/jest in /src-ui @dependabot) ([#1333](https://github.com/paperless-ngx/paperless-ngx/pull/1333))
- Bump bootstrap from 5.1.3 to 5.2.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#1327](https://github.com/paperless-ngx/paperless-ngx/pull/1327))
- Bump typescript from 4.6.4 to 4.7.4 in /src-ui [@dependabot](https://github.com/dependabot) ([#1324](https://github.com/paperless-ngx/paperless-ngx/pull/1324))
- Bump ts-node from 10.8.1 to 10.9.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#1325](https://github.com/paperless-ngx/paperless-ngx/pull/1325))
- Bump rxjs from 7.5.5 to 7.5.6 in /src-ui [@dependabot](https://github.com/dependabot) ([#1323](https://github.com/paperless-ngx/paperless-ngx/pull/1323))
- Fix: missing tooltip translation \& filter editor wrapping [@shamoon](https://github.com/shamoon) ([#1305](https://github.com/paperless-ngx/paperless-ngx/pull/1305))
- Feature: Remove requirements.txt and use pipenv everywhere [@stumpylog](https://github.com/stumpylog) ([#1316](https://github.com/paperless-ngx/paperless-ngx/pull/1316))
- Bugfix: Interaction between barcode and directories as tags [@stumpylog](https://github.com/stumpylog) ([#1303](https://github.com/paperless-ngx/paperless-ngx/pull/1303))
## paperless-ngx 1.8.0
### Features

View File

@@ -538,7 +538,7 @@ requires are as follows:
# ...
gotenberg:
image: gotenberg/gotenberg:7.4
image: gotenberg/gotenberg:7.6
restart: unless-stopped
command:
- "gotenberg"
@@ -701,6 +701,7 @@ PAPERLESS_CONSUMER_ENABLE_BARCODES=<bool>
Defaults to false.
PAPERLESS_CONSUMER_BARCODE_TIFF_SUPPORT=<bool>
Whether TIFF image files should be scanned for barcodes.
This will automatically convert any TIFF image(s) to pdfs for later
@@ -901,6 +902,14 @@ PAPERLESS_OCR_LANGUAGES=<list>
Defaults to none, which does not install any additional languages.
PAPERLESS_ENABLE_FLOWER=<defined>
If this environment variable is defined, the Celery monitoring tool
`Flower <https://flower.readthedocs.io/en/latest/index.html>`_ will
be started by the container.
You can read more about this in the :ref:`advanced setup <advanced-celery-monitoring>`
documentation.
.. _configuration-update-checking:
@@ -908,18 +917,9 @@ Update Checking
###############
PAPERLESS_ENABLE_UPDATE_CHECK=<bool>
Enable (or disable) the automatic check for available updates. This feature is disabled
by default but if it is not explicitly set Paperless-ngx will show a message about this.
If enabled, the feature works by pinging the the Github API for the latest release e.g.
https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest
to determine whether a new version is available.
.. note::
Actual updating of the app must still be performed manually.
Note that for users of thirdy-party containers e.g. linuxserver.io this notification
may be 'ahead' of a new release from the third-party maintainers.
In either case, no tracking data is collected by the app in any way.
Defaults to none, which disables the feature.
This setting was deprecated in favor of a frontend setting after v1.9.2. A one-time
migration is performed for users who have this setting set. This setting is always
ignored if the corresponding frontend setting has been set.

View File

@@ -112,7 +112,7 @@ To do the setup you need to perform the steps from the following chapters in a c
.. code:: shell-session
python3 manage.py runserver & python3 manage.py document_consumer & python3 manage.py qcluster
python3 manage.py runserver & python3 manage.py document_consumer & celery --app paperless worker
11. Login with the superuser credentials provided in step 8 at ``http://localhost:8000`` to create a session that enables you to use the backend.
@@ -128,14 +128,14 @@ Configure the IDE to use the src/ folder as the base source folder. Configure th
launch configurations in your IDE:
* python3 manage.py runserver
* python3 manage.py qcluster
* celery --app paperless worker
* python3 manage.py document_consumer
To start them all:
.. code:: shell-session
python3 manage.py runserver & python3 manage.py document_consumer & python3 manage.py qcluster
python3 manage.py runserver & python3 manage.py document_consumer & celery --app paperless worker
Testing and code style:

View File

@@ -1 +1 @@
myst-parser==0.17.2
myst-parser==0.18.1

View File

@@ -39,7 +39,7 @@ Paperless consists of the following components:
.. _setup-task_processor:
* **The task processor:** Paperless relies on `Django Q <https://django-q.readthedocs.io/en/latest/>`_
* **The task processor:** Paperless relies on `Celery - Distributed Task Queue <https://docs.celeryq.dev/en/stable/index.html>`_
for doing most of the heavy lifting. This is a task queue that accepts tasks from
multiple sources and processes these in parallel. It also comes with a scheduler that executes
certain commands periodically.
@@ -62,13 +62,6 @@ Paperless consists of the following components:
tasks fail and inspect the errors (i.e., wrong email credentials, errors during consuming a specific
file, etc).
You may start the task processor by executing:
.. code:: shell-session
$ cd /path/to/paperless/src/
$ python3 manage.py qcluster
* A `redis <https://redis.io/>`_ message broker: This is a really lightweight service that is responsible
for getting the tasks from the webserver and the consumer to the task scheduler. These run in a different
process (maybe even on different machines!), and therefore, this is necessary.
@@ -291,7 +284,20 @@ Build the Docker image yourself
.. code:: yaml
webserver:
build: .
build:
context: .
args:
QPDF_VERSION: x.y.x
PIKEPDF_VERSION: x.y.z
PSYCOPG2_VERSION: x.y.z
JBIG2ENC_VERSION: 0.29
.. note::
You should match the build argument versions to the version for the release you have
checked out. These are pre-built images with certain, more updated software.
If you want to build these images your self, that is possible, but beyond
the scope of these steps.
4. Follow steps 3 to 8 of :ref:`setup-docker_hub`. When asked to run
``docker-compose pull`` to pull the image, do
@@ -332,7 +338,7 @@ writing. Windows is not and will never be supported.
.. code::
python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev libmagic-dev mime-support libzbar0 poppler-utils
python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev default-libmysqlclient-dev libmagic-dev mime-support libzbar0 poppler-utils
These dependencies are required for OCRmyPDF, which is used for text recognition.
@@ -361,7 +367,7 @@ writing. Windows is not and will never be supported.
You will also need ``build-essential``, ``python3-setuptools`` and ``python3-wheel``
for installing some of the python dependencies.
2. Install ``redis`` >= 5.0 and configure it to start automatically.
2. Install ``redis`` >= 6.0 and configure it to start automatically.
3. Optional. Install ``postgresql`` and configure a database, user and password for paperless. If you do not wish
to use PostgreSQL, MariaDB and SQLite are available as well.
@@ -461,8 +467,9 @@ writing. Windows is not and will never be supported.
as a starting point.
Paperless needs the ``webserver`` script to run the webserver, the
``consumer`` script to watch the input folder, and the ``scheduler``
script to run tasks such as email checking and document consumption.
``consumer`` script to watch the input folder, ``taskqueue`` for the background workers
used to handle things like document consumption and the ``scheduler`` script to run tasks such as
email checking at certain times .
The ``socket`` script enables ``gunicorn`` to run on port 80 without
root privileges. For this you need to uncomment the ``Require=paperless-webserver.socket``
@@ -513,6 +520,13 @@ writing. Windows is not and will never be supported.
to compile this by yourself, because this software has been patented until around 2017 and
binary packages are not available for most distributions.
15. Optional: If using the NLTK machine learning processing (see ``PAPERLESS_ENABLE_NLTK`` in
:ref:`configuration` for details), download the NLTK data for the Snowball Stemmer, Stopwords
and Punkt tokenizer to your ``PAPERLESS_DATA_DIR/nltk``. Refer to
the `NLTK instructions <https://www.nltk.org/data.html>`_ for details on how to
download the data.
Migrating to Paperless-ngx
##########################
@@ -634,6 +648,48 @@ Migration to paperless-ngx is then performed in a few simple steps:
10. Optionally, follow the instructions below to migrate your existing data to PostgreSQL.
Migrating from LinuxServer.io Docker Image
========================
As with any upgrades and large changes, it is highly recommended to create a backup before
starting. This assumes the image was running using Docker Compose, but the instructions
are translatable to Docker commands as well.
1. Stop and remove the paperless container
2. If using an external database, stop the container
3. Update Redis configuration
a) If ``REDIS_URL`` is already set, change it to ``PAPERLESS_REDIS`` and continue
to step 4.
b) Otherwise, in the ``docker-compose.yml`` add a new service for Redis,
following `the example compose files <https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose>`_
b) Set the environment variable ``PAPERLESS_REDIS`` so it points to the new Redis container
4. Update user mapping
a) If set, change the environment variable ``PUID`` to ``USERMAP_UID``
b) If set, change the environment variable ``PGID`` to ``USERMAP_GID``
5. Update configuration paths
a) Set the environment variable ``PAPERLESS_DATA_DIR``
to ``/config``
6. Update media paths
a) Set the environment variable ``PAPERLESS_MEDIA_ROOT``
to ``/data/media``
7. Update timezone
a) Set the environment variable ``PAPERLESS_TIME_ZONE``
to the same value as ``TZ``
8. Modify the ``image:`` to point to ``ghcr.io/paperless-ngx/paperless-ngx:latest`` or
a specific version if preferred.
9. Start the containers as before, using ``docker-compose``.
.. _setup-sqlite_to_psql:
Moving data from SQLite to PostgreSQL
@@ -767,6 +823,8 @@ configuring some options in paperless can help improve performance immensely:
OCR results.
* If using docker, consider setting ``PAPERLESS_WEBSERVER_WORKERS`` to
1. This will save some memory.
* Consider setting ``PAPERLESS_ENABLE_NLTK`` to false, to disable the more
advanced language processing, which can take more memory and processing time.
For details, refer to :ref:`configuration`.

View File

@@ -19,7 +19,7 @@ Check for the following issues:
.. code:: shell-session
$ python3 manage.py qcluster
$ celery --app paperless worker
* Look at the output of paperless and inspect it for any errors.
* Go to the admin interface, and check if there are failed tasks. If so, the
@@ -125,7 +125,7 @@ If using docker-compose, this is achieved by the following configuration change
.. code:: yaml
gotenberg:
image: gotenberg/gotenberg:7.4
image: gotenberg/gotenberg:7.6
restart: unless-stopped
command:
- "gotenberg"
@@ -317,3 +317,12 @@ Uploading or consuming multiple files at once results in many workers attempting
Consider changing to the PostgreSQL database if you will be processing many documents at once often. Otherwise,
try tweaking the ``PAPERLESS_DB_TIMEOUT`` setting to allow more time for the database to unlock. This may have
minor performance implications.
gunicorn fails to start with "is not a valid port number"
#########################################################
You are likely running using Kubernetes, which automatically creates an environment variable named `${serviceName}_PORT`.
This is the same environment variable which is used by Paperless to optionally change the port gunicorn listens on.
To fix this, set `PAPERLESS_PORT` again to your desired port, or the default of 8000.

View File

@@ -1,12 +1,12 @@
[Unit]
Description=Paperless scheduler
Description=Paperless Celery Beat
Requires=redis.service
[Service]
User=paperless
Group=paperless
WorkingDirectory=/opt/paperless/src
ExecStart=python3 manage.py qcluster
ExecStart=celery --app paperless beat --loglevel INFO
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,12 @@
[Unit]
Description=Paperless Celery Workers
Requires=redis.service
[Service]
User=paperless
Group=paperless
WorkingDirectory=/opt/paperless/src
ExecStart=celery --app paperless worker --loglevel INFO
[Install]
WantedBy=multi-user.target

View File

@@ -2,5 +2,5 @@
docker run -p 5432:5432 -e POSTGRES_PASSWORD=password -v paperless_pgdata:/var/lib/postgresql/data -d postgres:13
docker run -d -p 6379:6379 redis:latest
docker run -p 3000:3000 -d gotenberg/gotenberg:7.4
docker run -p 3000:3000 -d gotenberg/gotenberg:7.6
docker run -p 9998:9998 -d ghcr.io/paperless-ngx/tika:latest

View File

@@ -46,7 +46,7 @@ describe('settings', () => {
})
})
cy.viewport(1024, 1024)
cy.viewport(1024, 1600)
cy.visit('/settings')
cy.wait('@savedViews')
})

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

873
src-ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,48 +13,49 @@
},
"private": true,
"dependencies": {
"@angular/common": "~14.2.0",
"@angular/compiler": "~14.2.0",
"@angular/core": "~14.2.0",
"@angular/forms": "~14.2.0",
"@angular/localize": "~14.2.0",
"@angular/platform-browser": "~14.2.0",
"@angular/platform-browser-dynamic": "~14.2.0",
"@angular/router": "~14.2.0",
"@angular/common": "~14.2.8",
"@angular/compiler": "~14.2.8",
"@angular/core": "~14.2.8",
"@angular/forms": "~14.2.8",
"@angular/localize": "~14.2.8",
"@angular/platform-browser": "~14.2.8",
"@angular/platform-browser-dynamic": "~14.2.8",
"@angular/router": "~14.2.8",
"@ng-bootstrap/ng-bootstrap": "^13.0.0",
"@ng-select/ng-select": "^9.0.2",
"@ngneat/dirty-check-forms": "^3.0.2",
"@popperjs/core": "^2.11.6",
"bootstrap": "^5.2.0",
"bootstrap": "^5.2.1",
"file-saver": "^2.0.5",
"ng2-pdf-viewer": "^9.1.0",
"ngx-color": "^8.0.2",
"ng2-pdf-viewer": "^9.1.2",
"ngx-color": "^8.0.3",
"ngx-cookie-service": "^14.0.1",
"ngx-file-drop": "^14.0.1",
"rxjs": "~7.5.6",
"ngx-ui-tour-ng-bootstrap": "^11.1.0",
"rxjs": "~7.5.7",
"tslib": "^2.3.1",
"uuid": "^8.3.1",
"uuid": "^9.0.0",
"zone.js": "~0.11.8"
},
"devDependencies": {
"@angular-builders/jest": "14.0.1",
"@angular-devkit/build-angular": "~14.2.1",
"@angular/cli": "~14.2.1",
"@angular/compiler-cli": "~14.2.0",
"@angular-devkit/build-angular": "~14.2.7",
"@angular/cli": "~14.2.7",
"@angular/compiler-cli": "~14.2.8",
"@types/jest": "28.1.6",
"@types/node": "^18.7.14",
"@types/node": "^18.7.23",
"codelyzer": "^6.0.2",
"concurrently": "7.3.0",
"concurrently": "7.4.0",
"jest": "28.1.3",
"jest-environment-jsdom": "^29.0.1",
"jest-environment-jsdom": "^29.2.2",
"jest-preset-angular": "^12.2.2",
"ts-node": "~10.9.1",
"tslint": "~6.1.3",
"typescript": "~4.7.4",
"typescript": "~4.8.4",
"wait-on": "~6.0.1"
},
"optionalDependencies": {
"@cypress/schematic": "^2.1.1",
"cypress": "~10.7.0"
"cypress": "~10.9.0"
}
}

View File

@@ -15,6 +15,7 @@ import { DirtyFormGuard } from './guards/dirty-form.guard'
import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component'
import { TasksComponent } from './components/manage/tasks/tasks.component'
import { DirtyDocGuard } from './guards/dirty-doc.guard'
import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard'
const routes: Routes = [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
@@ -24,8 +25,16 @@ const routes: Routes = [
canDeactivate: [DirtyDocGuard],
children: [
{ path: 'dashboard', component: DashboardComponent },
{ path: 'documents', component: DocumentListComponent },
{ path: 'view/:id', component: DocumentListComponent },
{
path: 'documents',
component: DocumentListComponent,
canDeactivate: [DirtySavedViewGuard],
},
{
path: 'view/:id',
component: DocumentListComponent,
canDeactivate: [DirtySavedViewGuard],
},
{ path: 'documents/:id', component: DocumentDetailComponent },
{ path: 'asn/:id', component: DocumentAsnComponent },
{ path: 'tags', component: TagListComponent },

View File

@@ -11,3 +11,28 @@
</div>
</ng-template>
</ngx-file-drop>
<tour-step-template>
<ng-template #tourStep let-step="step">
<p class="tour-step-content" [innerHTML]="step?.content"></p>
<hr/>
<div class="d-flex justify-content-between align-items-center">
<span class="badge bg-light text-dark">{{ tourService.steps?.indexOf(step) + 1 }} / {{ tourService.steps?.length }}</span>
<div class="tour-step-navigation btn-toolbar" role="toolbar" aria-label="Controls">
<div class="btn-group btn-group-sm me-2" role="group" aria-label="Dismiss">
<button class="btn btn-outline-danger" (click)="tourService.end()">
{{ step?.endBtnTitle }}
</button>
</div>
<div class="btn-group btn-group-sm align-self-end" role="group" aria-label="Previous / Next">
<button *ngIf="tourService.hasPrev(step)" class="btn btn-outline-primary" (click)="tourService.prev()">
« {{ step?.prevBtnTitle }}
</button>
<button *ngIf="tourService.hasNext(step)" class="btn btn-outline-primary" (click)="tourService.next()">
{{ step?.nextBtnTitle }} »
</button>
</div>
</div>
</div>
</ng-template>
</tour-step-template>

View File

@@ -1,6 +1,6 @@
import { SettingsService } from './services/settings.service'
import { SETTINGS_KEYS } from './data/paperless-uisettings'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { Component, OnDestroy, OnInit, Renderer2 } from '@angular/core'
import { Router } from '@angular/router'
import { Subscription } from 'rxjs'
import { ConsumerStatusService } from './services/consumer-status.service'
@@ -8,6 +8,7 @@ import { ToastService } from './services/toast.service'
import { NgxFileDropEntry } from 'ngx-file-drop'
import { UploadDocumentsService } from './services/upload-documents.service'
import { TasksService } from './services/tasks.service'
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
@Component({
selector: 'app-root',
@@ -29,7 +30,9 @@ export class AppComponent implements OnInit, OnDestroy {
private toastService: ToastService,
private router: Router,
private uploadDocumentsService: UploadDocumentsService,
private tasksService: TasksService
private tasksService: TasksService,
public tourService: TourService,
private renderer: Renderer2
) {
let anyWindow = window as any
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js'
@@ -112,6 +115,87 @@ export class AppComponent implements OnInit, OnDestroy {
})
}
})
this.tourService.initialize([
{
anchorId: 'tour.dashboard',
content: $localize`The dashboard can be used to show saved views, such as an 'Inbox'. Those settings are found under Settings > Saved Views once you have created some.`,
route: '/dashboard',
enableBackdrop: true,
delayAfterNavigation: 500,
},
{
anchorId: 'tour.upload-widget',
content: $localize`Drag-and-drop documents here to start uploading or place them in the consume folder. You can also drag-and-drop documents anywhere on all other pages of the web app. Once you do, Paperless-ngx will start training its machine learning algorithms.`,
route: '/dashboard',
enableBackdrop: true,
},
{
anchorId: 'tour.documents',
content: $localize`The documents list shows all of your documents and allows for filtering as well as bulk-editing. There are three different view styles: list, small cards and large cards. A list of documents currently opened for editing is shown in the sidebar.`,
route: '/documents?sort=created&reverse=1&page=1',
delayAfterNavigation: 500,
placement: 'bottom',
enableBackdrop: true,
disableScrollToAnchor: true,
},
{
anchorId: 'tour.documents-filter-editor',
content: $localize`The filtering tools allow you to quickly find documents using various searches, dates, tags, etc.`,
route: '/documents?sort=created&reverse=1&page=1',
placement: 'bottom',
enableBackdrop: true,
},
{
anchorId: 'tour.documents-views',
content: $localize`Any combination of filters can be saved as a 'view' which can then be displayed on the dashboard and / or sidebar.`,
route: '/documents?sort=created&reverse=1&page=1',
enableBackdrop: true,
},
{
anchorId: 'tour.tags',
content: $localize`Tags, correspondents, document types and storage paths can all be managed using these pages. They can also be created from the document edit view.`,
route: '/tags',
enableBackdrop: true,
},
{
anchorId: 'tour.file-tasks',
content: $localize`File Tasks shows you documents that have been consumed, are waiting to be, or may have failed during the process.`,
route: '/tasks',
enableBackdrop: true,
},
{
anchorId: 'tour.settings',
content: $localize`Check out the settings for various tweaks to the web app or to toggle settings for saved views.`,
route: '/settings',
enableBackdrop: true,
},
{
anchorId: 'tour.admin',
content: $localize`The Admin area contains more advanced controls as well as the settings for automatic e-mail fetching.`,
enableBackdrop: true,
},
{
anchorId: 'tour.outro',
title: $localize`Thank you! 🙏`,
content:
$localize`There are <em>tons</em> more features and info we didn't cover here, but this should get you started. Check out the documentation or visit the project on GitHub to learn more or to report issues.` +
'<br/><br/>' +
$localize`Lastly, on behalf of every contributor to this community-supported project, thank you for using Paperless-ngx!`,
route: '/dashboard',
},
])
this.tourService.start$.subscribe(() => {
this.renderer.addClass(document.body, 'tour-active')
})
this.tourService.end$.subscribe(() => {
// animation time
setTimeout(() => {
this.renderer.removeClass(document.body, 'tour-active')
}, 500)
})
}
public get dragDropEnabled(): boolean {

View File

@@ -24,6 +24,7 @@ import { CorrespondentEditDialogComponent } from './components/common/edit-dialo
import { TagEditDialogComponent } from './components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
import { DocumentTypeEditDialogComponent } from './components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component'
import { TagComponent } from './components/common/tag/tag.component'
import { ClearableBadge } from './components/common/clearable-badge/clearable-badge.component'
import { PageHeaderComponent } from './components/common/page-header/page-header.component'
import { AppFrameComponent } from './components/app-frame/app-frame.component'
import { ToastsComponent } from './components/common/toasts/toasts.component'
@@ -69,6 +70,12 @@ import { ColorComponent } from './components/common/input/color/color.component'
import { DocumentAsnComponent } from './components/document-asn/document-asn.component'
import { DocumentCommentsComponent } from './components/document-comments/document-comments.component'
import { DirtyDocGuard } from './guards/dirty-doc.guard'
import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard'
import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component'
import { StoragePathEditDialogComponent } from './components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component'
import { SettingsService } from './services/settings.service'
import { TasksComponent } from './components/manage/tasks/tasks.component'
import { TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap'
import localeBe from '@angular/common/locales/be'
import localeCs from '@angular/common/locales/cs'
@@ -89,10 +96,6 @@ import localeSr from '@angular/common/locales/sr'
import localeSv from '@angular/common/locales/sv'
import localeTr from '@angular/common/locales/tr'
import localeZh from '@angular/common/locales/zh'
import { StoragePathListComponent } from './components/manage/storage-path-list/storage-path-list.component'
import { StoragePathEditDialogComponent } from './components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component'
import { SettingsService } from './services/settings.service'
import { TasksComponent } from './components/manage/tasks/tasks.component'
registerLocaleData(localeBe)
registerLocaleData(localeCs)
@@ -140,6 +143,7 @@ function initializeApp(settings: SettingsService) {
DocumentTypeEditDialogComponent,
StoragePathEditDialogComponent,
TagComponent,
ClearableBadge,
PageHeaderComponent,
AppFrameComponent,
ToastsComponent,
@@ -188,6 +192,7 @@ function initializeApp(settings: SettingsService) {
PdfViewerModule,
NgSelectModule,
ColorSliderModule,
TourNgBootstrapModule.forRoot(),
],
providers: [
{
@@ -213,6 +218,7 @@ function initializeApp(settings: SettingsService) {
{ provide: NgbDateAdapter, useClass: ISODateAdapter },
{ provide: NgbDateParserFormatter, useClass: LocalizedDateParserFormatter },
DirtyDocGuard,
DirtySavedViewGuard,
],
bootstrap: [AppComponent],
})

View File

@@ -4,11 +4,11 @@
(click)="isMenuCollapsed = !isMenuCollapsed">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand col-auto col-md-3 col-lg-2 me-0 px-3 py-3 order-sm-0" routerLink="/dashboard">
<a class="navbar-brand col-auto col-md-3 col-lg-2 me-0 px-3 py-3 order-sm-0" [ngClass]="slimSidebarEnabled ? 'slim' : 'col-auto col-md-3 col-lg-2'" routerLink="/dashboard" tourAnchor="tour.intro">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 198.43 238.91" width="1em" class="me-2" fill="currentColor">
<path d="M194.7,0C164.22,70.94,17.64,79.74,64.55,194.06c.58,1.47-10.85,17-18.47,29.9-1.76-6.45-3.81-13.48-3.52-14.07,38.11-45.14-27.26-70.65-30.78-107.58C-4.64,131.62-10.5,182.92,39,212.53c.3,0,2.64,11.14,3.81,16.71a58.55,58.55,0,0,0-2.93,6.45c-1.17,2.93,7.62,2.64,7.62,3.22.88-.29,21.7-36.93,22.28-37.23C187.67,174.72,208.48,68.6,194.7,0ZM134.61,74.75C79.5,124,70.12,160.64,71.88,178.53,53.41,134.85,107.64,86.77,134.61,74.75ZM28.2,145.11c10.55,9.67,28.14,39.28,13.19,56.57C44.91,193.77,46.08,175.89,28.2,145.11Z" transform="translate(0 0)"/>
</svg>
<ng-container i18n="app title">Paperless-ngx</ng-container>
<span class="ms-2" [class.visually-hidden]="slimSidebarEnabled" i18n="app title">Paperless-ngx</span>
</a>
<div class="search-form-container flex-grow-1 py-2 pb-3 pb-sm-2 px-3 ps-md-4 me-sm-auto order-3 order-sm-1">
<form (ngSubmit)="search()" class="form-inline flex-grow-1">
@@ -16,12 +16,17 @@
<use xlink:href="assets/bootstrap-icons.svg#search"/>
</svg>
<input class="form-control form-control-sm" type="text" placeholder="Search documents" aria-label="Search"
[formControl]="searchField" [ngbTypeahead]="searchAutoComplete" (selectItem)="itemSelected($event)" i18n-placeholder>
[formControl]="searchField" [ngbTypeahead]="searchAutoComplete" (keyup)="searchFieldKeyup($event)" (selectItem)="itemSelected($event)" i18n-placeholder>
<button *ngIf="!searchFieldEmpty" class="btn btn-link btn-sm px-0 position-absolute top-0 end-0" (click)="resetSearchField()">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x me-1" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
</svg>
</button>
</form>
</div>
<ul ngbNav class="order-sm-3">
<li ngbDropdown class="nav-item dropdown">
<button class="btn" id="userDropdown" ngbDropdownToggle>
<button class="btn border-0" id="userDropdown" ngbDropdownToggle>
<span class="small me-2 d-none d-sm-inline">
{{this.settingsService.displayName}}
</span>
@@ -51,48 +56,54 @@
<div class="container-fluid">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse" [ngbCollapse]="isMenuCollapsed">
<nav id="sidebarMenu" class="d-md-block bg-light sidebar collapse" [ngClass]="slimSidebarEnabled ? 'slim' : 'col-md-3 col-lg-2'" [class.animating]="slimSidebarAnimating" [ngbCollapse]="isMenuCollapsed">
<button class="btn btn-sm btn-dark sidebar-slim-toggler" (click)="toggleSlimSidebar()">
<svg class="sidebaricon-sm" fill="currentColor">
<use *ngIf="slimSidebarEnabled" xlink:href="assets/bootstrap-icons.svg#chevron-double-right"/>
<use *ngIf="!slimSidebarEnabled" xlink:href="assets/bootstrap-icons.svg#chevron-double-left"/>
</svg>
</button>
<div class="sidebar-sticky pt-3 d-flex flex-column justify-space-around">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link" routerLink="dashboard" routerLinkActive="active" (click)="closeMenu()">
<a class="nav-link" routerLink="dashboard" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Dashboard" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#house"/>
</svg>&nbsp;<ng-container i18n>Dashboard</ng-container>
</svg><span>&nbsp;<ng-container i18n>Dashboard</ng-container></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="documents" routerLinkActive="active" (click)="closeMenu()">
<a class="nav-link" routerLink="documents" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Documents" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#files"/>
</svg>&nbsp;<ng-container i18n>Documents</ng-container>
</svg><span>&nbsp;<ng-container i18n>Documents</ng-container></span>
</a>
</li>
</ul>
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted" *ngIf='savedViewService.loading || savedViewService.sidebarViews.length > 0'>
<ng-container i18n>Saved views</ng-container>
<span i18n>Saved views</span>
<div *ngIf="savedViewService.loading" class="spinner-border spinner-border-sm fw-normal ms-2" role="status"></div>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item w-100" *ngFor="let view of savedViewService.sidebarViews">
<a class="nav-link text-truncate" routerLink="view/{{view.id}}" routerLinkActive="active" (click)="closeMenu()">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="view/{{view.id}}" routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name" [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#funnel"/>
</svg>&nbsp;{{view.name}}
</svg><span>&nbsp;{{view.name}}</span>
</a>
</li>
</ul>
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted" *ngIf='openDocuments.length > 0'>
<ng-container i18n>Open documents</ng-container>
<span i18n>Open documents</span>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item w-100" *ngFor='let d of openDocuments'>
<a class="nav-link text-truncate" routerLink="documents/{{d.id}}" routerLinkActive="active" (click)="closeMenu()">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="documents/{{d.id}}" routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="d.title | documentTitle" [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#file-text"/>
</svg>&nbsp;{{d.title | documentTitle}}
</svg><span>&nbsp;{{d.title | documentTitle}}</span>
<span class="close" (click)="closeDocument(d); $event.preventDefault()">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-x" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
@@ -101,95 +112,96 @@
</a>
</li>
<li class="nav-item w-100" *ngIf="openDocuments.length >= 1">
<a class="nav-link text-truncate" [routerLink]="[]" (click)="closeAll()">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" [routerLink]="[]" (click)="closeAll()" ngbPopover="Close all" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>&nbsp;<ng-container i18n>Close all</ng-container>
</svg><span>&nbsp;<ng-container i18n>Close all</ng-container></span>
</a>
</li>
</ul>
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted">
<ng-container i18n>Manage</ng-container>
<span i18n>Manage</span>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" routerLink="correspondents" routerLinkActive="active" (click)="closeMenu()">
<a class="nav-link" routerLink="correspondents" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Correspondents" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#person"/>
</svg>&nbsp;<ng-container i18n>Correspondents</ng-container>
</svg><span>&nbsp;<ng-container i18n>Correspondents</ng-container></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="tags" routerLinkActive="active" (click)="closeMenu()">
<li class="nav-item" tourAnchor="tour.tags">
<a class="nav-link" routerLink="tags" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Tags" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#tags"/>
</svg>&nbsp;<ng-container i18n>Tags</ng-container>
</svg><span>&nbsp;<ng-container i18n>Tags</ng-container></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="documenttypes" routerLinkActive="active" (click)="closeMenu()">
<a class="nav-link" routerLink="documenttypes" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Document types" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#hash"/>
</svg>&nbsp;<ng-container i18n>Document types</ng-container>
</svg><span>&nbsp;<ng-container i18n>Document types</ng-container></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="storagepaths" routerLinkActive="active" (click)="closeMenu()">
<a class="nav-link" routerLink="storagepaths" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Storage paths" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#folder"/>
</svg>&nbsp;<ng-container i18n>Storage paths</ng-container>
</svg><span>&nbsp;<ng-container i18n>Storage paths</ng-container></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="tasks" routerLinkActive="active" (click)="closeMenu()">
<li class="nav-item" tourAnchor="tour.file-tasks">
<a class="nav-link" routerLink="tasks" routerLinkActive="active" (click)="closeMenu()" ngbPopover="File Tasks" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<span *ngIf="tasksService.failedFileTasks.length > 0 && slimSidebarEnabled" class="badge bg-danger position-absolute top-0 end-0">{{tasksService.failedFileTasks.length}}</span>
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#list-task"/>
</svg>&nbsp;<ng-container i18n>File Tasks<ng-container *ngIf="tasksService.failedFileTasks.length > 0"><span class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span></ng-container></ng-container>
</svg><span>&nbsp;<ng-container i18n>File Tasks<span *ngIf="tasksService.failedFileTasks.length > 0"><span class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span></span></ng-container></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="logs" routerLinkActive="active" (click)="closeMenu()">
<a class="nav-link" routerLink="logs" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Logs" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#text-left"/>
</svg>&nbsp;<ng-container i18n>Logs</ng-container>
</svg><span>&nbsp;<ng-container i18n>Logs</ng-container></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="settings" routerLinkActive="active" (click)="closeMenu()">
<li class="nav-item" tourAnchor="tour.settings">
<a class="nav-link" routerLink="settings" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Settings" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#gear"/>
</svg>&nbsp;<ng-container i18n>Settings</ng-container>
</svg><span>&nbsp;<ng-container i18n>Settings</ng-container></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="admin/">
<li class="nav-item" tourAnchor="tour.admin">
<a class="nav-link" href="admin/" ngbPopover="Admin" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#toggles"/>
</svg>&nbsp;<ng-container i18n>Admin</ng-container>
</svg><span>&nbsp;<ng-container i18n>Admin</ng-container></span>
</a>
</li>
</ul>
<h6 class="sidebar-heading px-3 mt-auto pt-4 mb-1 text-muted">
<ng-container i18n>Info</ng-container>
<span i18n>Info</span>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" target="_blank" rel="noopener noreferrer" href="https://paperless-ngx.readthedocs.io/en/latest/">
<li class="nav-item" tourAnchor="tour.outro">
<a class="nav-link" target="_blank" rel="noopener noreferrer" href="https://paperless-ngx.readthedocs.io/en/latest/" ngbPopover="Documentation" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#question-circle"/>
</svg>&nbsp;<ng-container i18n>Documentation</ng-container>
</svg><span>&nbsp;<ng-container i18n>Documentation</ng-container></span>
</a>
</li>
<li class="nav-item">
<div class="d-flex w-100 flex-wrap">
<a class="nav-link pe-2 pb-1" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx">
<a class="nav-link pe-2 pb-1" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx" ngbPopover="GitHub" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#github" />
</svg>&nbsp;<ng-container i18n>GitHub</ng-container>
</svg><span>&nbsp;<ng-container i18n>GitHub</ng-container></span>
</a>
<a class="nav-link-additional small text-muted ms-3" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/discussions/categories/feature-requests" title="Suggest an idea" i18n-title>
<a class="nav-link-additional small text-muted ms-3" [class.visually-hidden]="slimSidebarEnabled" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/discussions/categories/feature-requests" title="Suggest an idea" i18n-title>
<svg xmlns="http://www.w3.org/2000/svg" width="1.1em" height="1.1em" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#lightbulb" />
</svg>
@@ -197,17 +209,28 @@
</a>
</div>
</li>
<li class="nav-item mt-2">
<li class="nav-item mt-2" [class.visually-hidden]="slimSidebarEnabled">
<div class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap">
<div class="me-3">{{ versionString }}</div>
<div *ngIf="appRemoteVersion" class="version-check">
<div *ngIf="!settingsService.updateCheckingIsSet || appRemoteVersion" class="version-check">
<ng-template #updateAvailablePopContent>
<span class="small">Paperless-ngx {{ appRemoteVersion.version }} <ng-container i18n>is available.</ng-container><br/><ng-container i18n>Click to view.</ng-container></span>
</ng-template>
<ng-template #updateCheckingNotEnabledPopContent>
<span class="small"><ng-container i18n>Checking for updates is disabled.</ng-container><br/><ng-container i18n>Click for more information.</ng-container></span>
<p class="small mb-2">
<ng-container i18n>Paperless-ngx can automatically check for updates</ng-container>
</p>
<div class="btn-group btn-group-xs flex-fill w-100">
<button class="btn btn-outline-primary" (click)="setUpdateChecking(true)">Enable</button>
<button class="btn btn-outline-secondary" (click)="setUpdateChecking(false)">Disable</button>
</div>
<p class="small mb-0 mt-2">
<a class="small text-decoration-none fst-italic" routerLink="/settings" fragment="update-checking" i18n>
How does this work?
</a>
</p>
</ng-template>
<ng-container *ngIf="appRemoteVersion.feature_is_set; else updateCheckNotSet">
<ng-container *ngIf="settingsService.updateCheckingIsSet; else updateCheckNotSet">
<a *ngIf="appRemoteVersion.update_available" class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/releases"
[ngbPopover]="updateAvailablePopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body">
<svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16">
@@ -217,8 +240,8 @@
</a>
</ng-container>
<ng-template #updateCheckNotSet>
<a class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://paperless-ngx.readthedocs.io/en/latest/configuration.html#update-checking"
[ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body">
<a class="small text-decoration-none" routerLink="/settings" fragment="update-checking"
[ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter" container="body">
<svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#info-circle" />
</svg>
@@ -231,7 +254,7 @@
</div>
</nav>
<main role="main" class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<main role="main" class="ms-sm-auto px-md-4" [ngClass]="slimSidebarEnabled ? 'col-slim' : 'col-md-9 col-lg-10'">
<router-outlet></router-outlet>
</main>
</div>

View File

@@ -1,3 +1,6 @@
@import "node_modules/bootstrap/scss/functions";
@import "node_modules/bootstrap/scss/variables";
/*
* Sidebar
*/
@@ -14,6 +17,17 @@
width: 0.8em;
height: 0.8em;
}
// These come from the col-md-3 col-lg-2 classes for regular sidebar, needed for animation
@media (min-width: 768px) {
max-width: 25%;
}
@media (min-width: 992px) {
max-width: 16.66666667%;
}
transition: all .2s ease;
}
@media (max-width: 767.98px) {
.sidebar {
@@ -21,6 +35,90 @@
}
}
main {
transition: all .2s ease;
}
.sidebar-slim-toggler {
display: none; // hide on mobile
}
.sidebar li.nav-item span,
.sidebar .sidebar-heading span {
transition: all .1s ease;
}
@media(min-width: 768px) {
.sidebar.slim {
max-width: 50px;
li.nav-item span.badge {
display: inline-block;
margin-right: 2px;
}
}
.sidebar.slim:not(.animating) {
li.nav-item span,
.sidebar-heading span {
display: none;
}
}
.sidebar.animating {
li.nav-item span,
.sidebar-heading span {
display: unset;
position: absolute;
opacity: 0;
overflow: hidden;
}
}
.sidebar:not(.slim):not(.animating) {
li.nav-item span,
.sidebar-heading span {
position: unset;
opacity: 1;
overflow: auto;
}
}
.sidebar.slim,
.sidebar.animating {
.text-truncate {
text-overflow: unset !important;
word-wrap: break-word !important;
}
}
.sidebar.slim {
li.nav-item span.badge {
display: inline-block;
margin-right: 2px;
}
}
.col-slim {
padding-left: calc(50px + $grid-gutter-width) !important;
}
.sidebar-slim-toggler {
display: block;
position: absolute;
right: -12px;
top: 60px;
z-index: 996;
--bs-btn-padding-x: 0.35rem;
--bs-btn-padding-y: 0.125rem;
}
}
::ng-deep .popover-slim .popover-body {
--bs-popover-body-padding-x: .5rem;
--bs-popover-body-padding-y: .5rem;
}
.sidebar-sticky {
position: relative;
top: 0;
@@ -77,7 +175,7 @@
.close {
display: none;
position: absolute;
position: absolute !important;
cursor: pointer;
opacity: 1;
top: 0;
@@ -145,17 +243,18 @@
form {
position: relative;
> svg {
position: absolute;
left: 0.6rem;
top: 0.5rem;
color: rgba(255, 255, 255, 0.6);
}
}
svg {
position: absolute;
left: 0.6rem;
top: 0.5rem;
color: rgba(255, 255, 255, 0.6);
}
&:focus-within {
svg {
form > svg {
display: none;
}

View File

@@ -1,4 +1,4 @@
import { Component, HostListener } from '@angular/core'
import { Component, HostListener, OnInit } from '@angular/core'
import { FormControl } from '@angular/forms'
import { ActivatedRoute, Router } from '@angular/router'
import { from, Observable } from 'rxjs'
@@ -24,13 +24,15 @@ import {
import { SettingsService } from 'src/app/services/settings.service'
import { TasksService } from 'src/app/services/tasks.service'
import { ComponentCanDeactivate } from 'src/app/guards/dirty-doc.guard'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { ToastService } from 'src/app/services/toast.service'
@Component({
selector: 'app-app-frame',
templateUrl: './app-frame.component.html',
styleUrls: ['./app-frame.component.scss'],
})
export class AppFrameComponent implements ComponentCanDeactivate {
export class AppFrameComponent implements OnInit, ComponentCanDeactivate {
constructor(
public router: Router,
private activatedRoute: ActivatedRoute,
@@ -40,14 +42,15 @@ export class AppFrameComponent implements ComponentCanDeactivate {
private remoteVersionService: RemoteVersionService,
private list: DocumentListViewService,
public settingsService: SettingsService,
public tasksService: TasksService
) {
this.remoteVersionService
.checkForUpdates()
.subscribe((appRemoteVersion: AppRemoteVersion) => {
this.appRemoteVersion = appRemoteVersion
})
tasksService.reload()
public tasksService: TasksService,
private readonly toastService: ToastService
) {}
ngOnInit(): void {
if (this.settingsService.get(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED)) {
this.checkForUpdates()
}
this.tasksService.reload()
}
versionString = `${environment.appTitle} ${environment.version}`
@@ -55,12 +58,55 @@ export class AppFrameComponent implements ComponentCanDeactivate {
isMenuCollapsed: boolean = true
slimSidebarAnimating: boolean = false
toggleSlimSidebar(): void {
this.slimSidebarAnimating = true
this.slimSidebarEnabled = !this.slimSidebarEnabled
setTimeout(() => {
this.slimSidebarAnimating = false
}, 200) // slightly longer than css animation for slim sidebar
}
get slimSidebarEnabled(): boolean {
return this.settingsService.get(SETTINGS_KEYS.SLIM_SIDEBAR)
}
set slimSidebarEnabled(enabled: boolean) {
this.settingsService.set(SETTINGS_KEYS.SLIM_SIDEBAR, enabled)
this.settingsService
.storeSettings()
.pipe(first())
.subscribe({
error: (error) => {
this.toastService.showError(
$localize`An error occurred while saving settings.`
)
console.log(error)
},
})
}
closeMenu() {
this.isMenuCollapsed = true
}
searchField = new FormControl('')
get searchFieldEmpty(): boolean {
return this.searchField.value.trim().length == 0
}
resetSearchField() {
this.searchField.reset('')
}
searchFieldKeyup(event: KeyboardEvent) {
if (event.key == 'Escape') {
this.resetSearchField()
}
}
get openDocuments(): PaperlessDocument[] {
return this.openDocumentsService.getOpenDocuments()
}
@@ -150,4 +196,30 @@ export class AppFrameComponent implements ComponentCanDeactivate {
}
})
}
private checkForUpdates() {
this.remoteVersionService
.checkForUpdates()
.subscribe((appRemoteVersion: AppRemoteVersion) => {
this.appRemoteVersion = appRemoteVersion
})
}
setUpdateChecking(enable: boolean) {
this.settingsService.set(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED, enable)
this.settingsService
.storeSettings()
.pipe(first())
.subscribe({
error: (error) => {
this.toastService.showError(
$localize`An error occurred while saving update checking settings.`
)
console.log(error)
},
})
if (enable) {
this.checkForUpdates()
}
}
}

View File

@@ -0,0 +1,9 @@
<button *ngIf="active" class="position-absolute top-0 start-100 translate-middle badge bg-secondary border border-light rounded-pill p-1" title="Clear" i18n-title (click)="onClick($event)">
<svg *ngIf="!isNumbered && selected" width="1em" height="1em" class="check m-0 p-0 opacity-75" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<use xlink:href="assets/bootstrap-icons.svg#check-lg"/>
</svg>
<div *ngIf="isNumbered" class="number">{{number}}<span class="visually-hidden">selected</span></div>
<svg width=".9em" height="1em" class="x m-0 p-0 opacity-75" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<use xlink:href="assets/bootstrap-icons.svg#x-lg"/>
</svg>
</button>

View File

@@ -0,0 +1,28 @@
.badge {
min-width: 20px;
min-height: 20px;
}
.x {
display: none;
}
.number {
min-width: 1em;
min-height: 1em;
display: inline-block;
}
button:hover {
.check,
.number {
opacity: 0 !important;
}
.x {
display: inline-block;
position: absolute;
top: 5px;
left: calc(50% - 4px);
}
}

View File

@@ -0,0 +1,33 @@
import { Component, Input, Output, EventEmitter } from '@angular/core'
@Component({
selector: 'app-clearable-badge',
templateUrl: './clearable-badge.component.html',
styleUrls: ['./clearable-badge.component.scss'],
})
export class ClearableBadge {
constructor() {}
@Input()
number: number
@Input()
selected: boolean
@Output()
cleared: EventEmitter<boolean> = new EventEmitter()
get active(): boolean {
return this.selected || this.number > -1
}
get isNumbered(): boolean {
return this.number > -1
}
onClick(event: PointerEvent) {
this.cleared.emit(true)
event.stopImmediatePropagation()
event.preventDefault()
}
}

View File

@@ -16,4 +16,7 @@
<ngb-progressbar *ngIf="!confirmButtonEnabled" style="height: 1px;" type="dark" [max]="secondsTotal" [value]="seconds"></ngb-progressbar>
<span class="visually-hidden">{{ seconds | number: '1.0-0' }} seconds</span>
</button>
<button *ngIf="alternativeBtnCaption" type="button" class="btn" [class]="alternativeBtnClass" (click)="alternative()" [disabled]="!alternativeButtonEnabled || !buttonsEnabled">
{{alternativeBtnCaption}}
</button>
</div>

View File

@@ -13,6 +13,9 @@ export class ConfirmDialogComponent {
@Output()
public confirmClicked = new EventEmitter()
@Output()
public alternativeClicked = new EventEmitter()
@Input()
title = $localize`Confirmation`
@@ -28,14 +31,22 @@ export class ConfirmDialogComponent {
@Input()
btnCaption = $localize`Confirm`
@Input()
alternativeBtnClass = 'btn-secondary'
@Input()
alternativeBtnCaption
@Input()
buttonsEnabled = true
confirmButtonEnabled = true
alternativeButtonEnabled = true
seconds = 0
secondsTotal = 0
confirmSubject: Subject<boolean>
alternativeSubject: Subject<boolean>
delayConfirm(seconds: number) {
const refreshInterval = 0.15 // s
@@ -68,4 +79,10 @@ export class ConfirmDialogComponent {
this.confirmSubject?.next(true)
this.confirmSubject?.complete()
}
alternative() {
this.alternativeClicked.emit()
this.alternativeSubject?.next(true)
this.alternativeSubject?.complete()
}
}

View File

@@ -1,11 +1,17 @@
<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}}
<app-clearable-badge [selected]="isActive" (cleared)="reset()"></app-clearable-badge><span class="visually-hidden">selected</span>
</button>
<div class="dropdown-menu date-dropdown shadow pt-0" ngbDropdownMenu attr.aria-labelledby="dropdown{{title}}">
<div class="list-group list-group-flush">
<button *ngFor="let qf of quickFilters" class="list-group-item small list-goup list-group-item-action d-flex p-2 ps-3" role="menuitem" (click)="setDateQuickFilter(qf.id)">
{{qf.name}}
<button *ngFor="let rd of relativeDates" class="list-group-item small list-goup list-group-item-action d-flex p-2" role="menuitem" (click)="setRelativeDate(rd.date)">
<div _ngcontent-hga-c166="" class="selected-icon me-1">
<svg *ngIf="relativeDate === rd.date" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16">
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
</svg>
</div>
{{rd.name}}
</button>
<div class="list-group-item d-flex flex-column align-items-start" role="menuitem">

View File

@@ -5,3 +5,8 @@
line-height: 1;
}
}
.selected-icon {
min-width: 1em;
min-height: 1em;
}

View File

@@ -16,12 +16,15 @@ import { ISODateAdapter } from 'src/app/utils/ngb-iso-date-adapter'
export interface DateSelection {
before?: string
after?: string
relativeDateID?: number
}
const LAST_7_DAYS = 0
const LAST_MONTH = 1
const LAST_3_MONTHS = 2
const LAST_YEAR = 3
export enum RelativeDate {
LAST_7_DAYS = 0,
LAST_MONTH = 1,
LAST_3_MONTHS = 2,
LAST_YEAR = 3,
}
@Component({
selector: 'app-date-dropdown',
@@ -34,11 +37,23 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
}
quickFilters = [
{ id: LAST_7_DAYS, name: $localize`Last 7 days` },
{ id: LAST_MONTH, name: $localize`Last month` },
{ id: LAST_3_MONTHS, name: $localize`Last 3 months` },
{ id: LAST_YEAR, name: $localize`Last year` },
relativeDates = [
{
date: RelativeDate.LAST_7_DAYS,
name: $localize`Last 7 days`,
},
{
date: RelativeDate.LAST_MONTH,
name: $localize`Last month`,
},
{
date: RelativeDate.LAST_3_MONTHS,
name: $localize`Last 3 months`,
},
{
date: RelativeDate.LAST_YEAR,
name: $localize`Last year`,
},
]
datePlaceHolder: string
@@ -55,12 +70,26 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
@Output()
dateAfterChange = new EventEmitter<string>()
@Input()
relativeDate: RelativeDate
@Output()
relativeDateChange = new EventEmitter<number>()
@Input()
title: string
@Output()
datesSet = new EventEmitter<DateSelection>()
get isActive(): boolean {
return (
this.relativeDate !== null ||
this.dateAfter?.length > 0 ||
this.dateBefore?.length > 0
)
}
private datesSetDebounce$ = new Subject()
private sub: Subscription
@@ -77,37 +106,33 @@ export class DateDropdownComponent implements OnInit, OnDestroy {
}
}
setDateQuickFilter(qf: number) {
reset() {
this.dateBefore = null
let date = new Date()
switch (qf) {
case LAST_7_DAYS:
date.setDate(date.getDate() - 7)
break
this.dateAfter = null
this.relativeDate = null
this.onChange()
}
case LAST_MONTH:
date.setMonth(date.getMonth() - 1)
break
case LAST_3_MONTHS:
date.setMonth(date.getMonth() - 3)
break
case LAST_YEAR:
date.setFullYear(date.getFullYear() - 1)
break
}
this.dateAfter = formatDate(date, 'yyyy-MM-dd', 'en-us', 'UTC')
setRelativeDate(rd: RelativeDate) {
this.dateBefore = null
this.dateAfter = null
this.relativeDate = this.relativeDate == rd ? null : rd
this.onChange()
}
onChange() {
this.dateAfterChange.emit(this.dateAfter)
this.dateBeforeChange.emit(this.dateBefore)
this.datesSet.emit({ after: this.dateAfter, before: this.dateBefore })
this.dateAfterChange.emit(this.dateAfter)
this.relativeDateChange.emit(this.relativeDate)
this.datesSet.emit({
after: this.dateAfter,
before: this.dateBefore,
relativeDateID: this.relativeDate,
})
}
onChangeDebounce() {
this.relativeDate = null
this.datesSetDebounce$.next({
after: this.dateAfter,
before: this.dateBefore,

View File

@@ -2,6 +2,7 @@ import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { ToastService } from 'src/app/services/toast.service'
@@ -31,7 +32,7 @@ export class CorrespondentEditDialogComponent extends EditDialogComponent<Paperl
getForm(): FormGroup {
return new FormGroup({
name: new FormControl(''),
matching_algorithm: new FormControl(1),
matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM),
match: new FormControl(''),
is_insensitive: new FormControl(true),
})

View File

@@ -2,6 +2,7 @@ import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { ToastService } from 'src/app/services/toast.service'
@@ -31,7 +32,7 @@ export class DocumentTypeEditDialogComponent extends EditDialogComponent<Paperle
getForm(): FormGroup {
return new FormGroup({
name: new FormControl(''),
matching_algorithm: new FormControl(1),
matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM),
match: new FormControl(''),
is_insensitive: new FormControl(true),
})

View File

@@ -2,6 +2,7 @@ import { Component } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { ToastService } from 'src/app/services/toast.service'
@@ -42,7 +43,7 @@ export class StoragePathEditDialogComponent extends EditDialogComponent<Paperles
return new FormGroup({
name: new FormControl(''),
path: new FormControl(''),
matching_algorithm: new FormControl(1),
matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM),
match: new FormControl(''),
is_insensitive: new FormControl(true),
})

View File

@@ -6,6 +6,7 @@ import { PaperlessTag } from 'src/app/data/paperless-tag'
import { TagService } from 'src/app/services/rest/tag.service'
import { ToastService } from 'src/app/services/toast.service'
import { randomColor } from 'src/app/utils/color'
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
@Component({
selector: 'app-tag-edit-dialog',
@@ -34,7 +35,7 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
name: new FormControl(''),
color: new FormControl(randomColor()),
is_inbox_tag: new FormControl(false),
matching_algorithm: new FormControl(1),
matching_algorithm: new FormControl(DEFAULT_MATCHING_ALGORITHM),
match: new FormControl(''),
is_insensitive: new FormControl(true),
})

View File

@@ -5,12 +5,7 @@
</svg>
<div class="d-none d-sm-inline">&nbsp;{{title}}</div>
<ng-container *ngIf="!editing && selectionModel.selectionSize() > 0">
<div *ngIf="multiple" class="position-absolute top-0 start-100 translate-middle badge bg-secondary border border-light text-light rounded-pill">
{{selectionModel.totalCount}}<span class="visually-hidden">selected</span>
</div>
<div *ngIf="!multiple" class="position-absolute top-0 start-100 p-2 translate-middle badge bg-secondary border border-light rounded-circle">
<span class="visually-hidden">selected</span>
</div>
<app-clearable-badge [number]="multiple ? selectionModel.totalCount : undefined" [selected]="!multiple && selectionModel.selectionSize() > 0" (cleared)="reset()"></app-clearable-badge>
</ng-container>
</button>
<div class="dropdown-menu py-0 shadow" ngbDropdownMenu attr.aria-labelledby="dropdown{{title}}">

View File

@@ -17,25 +17,6 @@
}
}
.btn-group-xs {
> .btn {
padding: 0.2rem 0.25rem;
font-size: 0.675rem;
line-height: 1.2;
border-radius: 0.15rem;
}
> .btn:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
> .btn:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
.btn-group > label.disabled {
filter: brightness(0.5);

View File

@@ -384,4 +384,9 @@ export class FilterableDropdownComponent {
this.selectionModel.exclude(itemID)
}
}
reset() {
this.selectionModel.reset()
this.selectionModelChange.emit(this.selectionModel)
}
}

View File

@@ -19,17 +19,20 @@
</svg>
</app-page-header>
<div class='row'>
<div class="row">
<div class="col-lg-8">
<ng-container *ngIf="savedViewService.loading">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
<ng-container i18n>Loading...</ng-container>
</ng-container>
<app-welcome-widget *ngIf="!savedViewService.loading && savedViewService.dashboardViews.length == 0"></app-welcome-widget>
<app-welcome-widget *ngIf="settingsService.offerTour()" tourAnchor="tour.dashboard"></app-welcome-widget>
<ng-container *ngFor="let v of savedViewService.dashboardViews">
<app-saved-view-widget [savedView]="v"></app-saved-view-widget>
<ng-container *ngFor="let v of savedViewService.dashboardViews; first as isFirst">
<app-saved-view-widget *ngIf="isFirst; else noTour" [savedView]="v" tourAnchor="tour.dashboard"></app-saved-view-widget>
<ng-template #noTour>
<app-saved-view-widget [savedView]="v"></app-saved-view-widget>
</ng-template>
</ng-container>
</div>

View File

@@ -1,5 +1,4 @@
import { Component, OnInit } from '@angular/core'
import { Meta } from '@angular/platform-browser'
import { Component } from '@angular/core'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { SettingsService } from 'src/app/services/settings.service'
@@ -16,9 +15,9 @@ export class DashboardComponent {
get subtitle() {
if (this.settingsService.displayName) {
return $localize`Hello ${this.settingsService.displayName}, welcome to Paperless-ngx!`
return $localize`Hello ${this.settingsService.displayName}, welcome to Paperless-ngx`
} else {
return $localize`Welcome to Paperless-ngx!`
return $localize`Welcome to Paperless-ngx`
}
}
}

View File

@@ -8,7 +8,7 @@
</svg>
</a>
</div>
<div content>
<div content tourAnchor="tour.upload-widget">
<form>
<ngx-file-drop dropZoneLabel="Drop documents here or" browseBtnLabel="Browse files" (onFileDrop)="dropped($event)"
(onFileOver)="fileOver($event)" (onFileLeave)="fileLeave($event)" dropZoneClassName="bg-light card"

View File

@@ -1,16 +1,11 @@
<app-widget-frame title="First steps" i18n-title>
<ng-container content>
<img src="assets/save-filter.png" class="float-right">
<p i18n>Paperless is running! :)</p>
<p i18n>You can start uploading documents by dropping them in the file upload box to the right or by dropping them in the configured consumption folder and they'll start showing up in the documents list.
After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and they will appear on the dashboard instead of this message.</p>
<p i18n>Paperless offers some more features that try to make your life easier:</p>
<ul>
<li i18n>Once you've got a couple documents in paperless and added metadata to them, paperless can assign that metadata to new documents automatically.</li>
<li i18n>You can configure paperless to read your mails and add documents from attached files.</li>
</ul>
<p i18n>Consult the documentation on how to use these features. The section on basic usage also has some information on how to use paperless in general.</p>
</ng-container>
</app-widget-frame>
<ngb-alert type="primary" [dismissible]="false">
<!-- [dismissible]="isFinished(status)" (closed)="dismiss(status)" -->
<h4 class="alert-heading"><ng-container i18n>Paperless-ngx is running!</ng-container> 🎉</h4>
<p i18n>You're ready to start uploading documents! Explore the various features of this web app on your own, or start a quick tour using the button below.</p>
<p i18n>More detail on how to use and configure Paperless-ngx is always available in the <a href="https://paperless-ngx.readthedocs.io" target="_blank">documentation</a>.</p>
<hr>
<div class="d-flex align-items-end">
<p class="lead fs-6 m-0"><em i18n>Thanks for being a part of the Paperless-ngx community!</em></p>
<button class="btn btn-primary ms-auto flex-shrink-0" (click)="tourService.start()"><ng-container i18n>Start the tour</ng-container> &rarr;</button>
</div>
</ngb-alert>

View File

@@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core'
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
@Component({
selector: 'app-welcome-widget',
@@ -6,7 +7,7 @@ import { Component, OnInit } from '@angular/core'
styleUrls: ['./welcome-widget.component.scss'],
})
export class WelcomeWidgetComponent implements OnInit {
constructor() {}
constructor(public readonly tourService: TourService) {}
ngOnInit(): void {}
}

View File

@@ -6,7 +6,8 @@
Please enter a comment.
</div>
</div>
<div class="form-group mt-2 d-flex justify-content-end">
<div class="form-group mt-2 d-flex justify-content-end align-items-center">
<div *ngIf="networkActive" class="spinner-border spinner-border-sm fw-normal me-auto" role="status"></div>
<button type="button" class="btn btn-primary btn-sm" [disabled]="networkActive" (click)="addComment()" i18n>Add comment</button>
</div>
</form>

View File

@@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core'
import { Component, Input } from '@angular/core'
import { DocumentCommentsService } from 'src/app/services/rest/document-comments.service'
import { PaperlessDocumentComment } from 'src/app/data/paperless-document-comment'
import { FormControl, FormGroup } from '@angular/forms'
@@ -10,7 +10,7 @@ import { ToastService } from 'src/app/services/toast.service'
templateUrl: './document-comments.component.html',
styleUrls: ['./document-comments.component.scss'],
})
export class DocumentCommentsComponent implements OnInit {
export class DocumentCommentsComponent {
commentForm: FormGroup = new FormGroup({
newComment: new FormControl(''),
})
@@ -19,19 +19,30 @@ export class DocumentCommentsComponent implements OnInit {
comments: PaperlessDocumentComment[] = []
newCommentError: boolean = false
private _documentId: number
@Input()
documentId: number
set documentId(id: number) {
if (id != this._documentId) {
this._documentId = id
this.update()
}
}
constructor(
private commentsService: DocumentCommentsService,
private toastService: ToastService
) {}
ngOnInit(): void {
update(): void {
this.networkActive = true
this.commentsService
.getComments(this.documentId)
.getComments(this._documentId)
.pipe(first())
.subscribe((comments) => (this.comments = comments))
.subscribe((comments) => {
this.comments = comments
this.networkActive = false
})
}
addComment() {
@@ -45,7 +56,7 @@ export class DocumentCommentsComponent implements OnInit {
}
this.newCommentError = false
this.networkActive = true
this.commentsService.addComment(this.documentId, comment).subscribe({
this.commentsService.addComment(this._documentId, comment).subscribe({
next: (result) => {
this.comments = result
this.commentForm.get('newComment').reset()
@@ -61,7 +72,7 @@ export class DocumentCommentsComponent implements OnInit {
}
deleteComment(commentId: number) {
this.commentsService.deleteComment(this.documentId, commentId).subscribe({
this.commentsService.deleteComment(this._documentId, commentId).subscribe({
next: (result) => {
this.comments = result
this.networkActive = false

View File

@@ -337,7 +337,7 @@ export class DocumentDetailComponent
})
)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(({ newStoragePath, documentTypes: storagePaths }) => {
.subscribe(({ newStoragePath, storagePaths }) => {
this.storagePaths = storagePaths.results
this.documentForm.get('storage_path').setValue(newStoragePath.id)
})

View File

@@ -80,7 +80,7 @@ a {
}
.tags {
top: 0;
top: .2rem;
right: 0;
max-width: 80%;
row-gap: .2rem;

View File

@@ -60,14 +60,19 @@
</div>
<div class="btn-group ms-2 flex-fill" ngbDropdown role="group">
<button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" ngbDropdownToggle i18n>Views</button>
<button class="btn btn-sm btn-outline-primary dropdown-toggle flex-fill" tourAnchor="tour.documents-views" ngbDropdownToggle>
<ng-container i18n>Views</ng-container>
<div *ngIf="savedViewIsModified" class="position-absolute top-0 start-100 p-2 translate-middle badge bg-secondary border border-light rounded-circle">
<span class="visually-hidden">selected</span>
</div>
</button>
<div class="dropdown-menu shadow dropdown-menu-right" ngbDropdownMenu>
<ng-container *ngIf="!list.activeSavedViewId">
<button ngbDropdownItem *ngFor="let view of savedViewService.allViews" (click)="loadViewConfig(view.id)">{{view.name}}</button>
<div class="dropdown-divider" *ngIf="savedViewService.allViews.length > 0"></div>
</ng-container>
<button ngbDropdownItem (click)="saveViewConfig()" *ngIf="list.activeSavedViewId" i18n>Save "{{list.activeSavedViewTitle}}"</button>
<button ngbDropdownItem (click)="saveViewConfig()" *ngIf="list.activeSavedViewId" [disabled]="!savedViewIsModified" i18n>Save "{{list.activeSavedViewTitle}}"</button>
<button ngbDropdownItem (click)="saveViewConfigAs()" i18n>Save as...</button>
</div>
</div>
@@ -79,6 +84,7 @@
<app-bulk-editor [hidden]="!isBulkEditing"></app-bulk-editor>
</div>
<ng-template #pagination>
<div class="d-flex justify-content-between align-items-center">
<p>
@@ -96,14 +102,15 @@
</div>
</ng-template>
<ng-container *ngTemplateOutlet="pagination"></ng-container>
<div tourAnchor="tour.documents">
<ng-container *ngTemplateOutlet="pagination"></ng-container>
</div>
<ng-container *ngIf="list.error ; else documentListNoError">
<div class="alert alert-danger" role="alert"><ng-container i18n>Error while loading documents</ng-container>: {{list.error}}</div>
</ng-container>
<ng-template #documentListNoError>
<div *ngIf="displayMode == 'largeCards'">
<app-document-card-large [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" *ngFor="let d of list.documents; trackBy: trackByDocumentId" [document]="d" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickDocumentType)="clickDocumentType($event)" (clickStoragePath)="clickStoragePath($event)" (clickMoreLike)="clickMoreLike(d.id)">
</app-document-card-large>

View File

@@ -11,7 +11,7 @@ tr {
}
$paperless-card-breakpoints: (
0: 2, // xs
// 0: 2, // xs is manual for slim-sidebar
768px: 3, //md
992px: 4, //lg
1200px: 5, //xl
@@ -22,6 +22,12 @@ $paperless-card-breakpoints: (
);
.row-cols-paperless-cards {
// xs, we dont want in .col-slim block
> * {
flex: 0 0 auto;
width: calc(100% / 2);
}
@each $width, $n_cols in $paperless-card-breakpoints {
@media(min-width: $width) {
> * {
@@ -32,6 +38,17 @@ $paperless-card-breakpoints: (
}
}
::ng-deep .col-slim .row-cols-paperless-cards {
@each $width, $n_cols in $paperless-card-breakpoints {
@media(min-width: $width) {
> * {
flex: 0 0 auto;
width: calc(100% / ($n-cols + 1)) !important;
}
}
}
}
.dropdown-menu-right {
right: 0 !important;
left: auto !important;

View File

@@ -9,7 +9,11 @@ import {
import { ActivatedRoute, convertToParamMap, Router } from '@angular/router'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { filter, first, map, Subject, switchMap, takeUntil } from 'rxjs'
import { FilterRule, isFullTextFilterRule } from 'src/app/data/filter-rule'
import {
FilterRule,
filterRulesDiffer,
isFullTextFilterRule,
} from 'src/app/data/filter-rule'
import { FILTER_FULLTEXT_MORELIKE } from 'src/app/data/filter-rule-type'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
@@ -54,15 +58,36 @@ export class DocumentListComponent implements OnInit, OnDestroy {
displayMode = 'smallCards' // largeCards, smallCards, details
unmodifiedFilterRules: FilterRule[] = []
private unmodifiedSavedView: PaperlessSavedView
private unsubscribeNotifier: Subject<any> = new Subject()
get savedViewIsModified(): boolean {
if (!this.list.activeSavedViewId || !this.unmodifiedSavedView) return false
else {
return (
this.unmodifiedSavedView.sort_field !== this.list.sortField ||
this.unmodifiedSavedView.sort_reverse !== this.list.sortReverse ||
filterRulesDiffer(
this.unmodifiedSavedView.filter_rules,
this.list.filterRules
)
)
}
}
get isFiltered() {
return this.list.filterRules?.length > 0
}
getTitle() {
return this.list.activeSavedViewTitle || $localize`Documents`
let title = this.list.activeSavedViewTitle
if (title && this.savedViewIsModified) {
title += '*'
} else if (!title) {
title = $localize`Documents`
}
return title
}
getSortFields() {
@@ -122,7 +147,7 @@ export class DocumentListComponent implements OnInit, OnDestroy {
this.router.navigate(['404'])
return
}
this.unmodifiedSavedView = view
this.list.activateSavedViewWithQueryParams(
view,
convertToParamMap(this.route.snapshot.queryParams)
@@ -139,13 +164,7 @@ export class DocumentListComponent implements OnInit, OnDestroy {
.subscribe((queryParams) => {
if (queryParams.has('view')) {
// loading a saved view on /documents
this.savedViewService
.getCached(parseInt(queryParams.get('view')))
.pipe(first())
.subscribe((view) => {
this.list.activateSavedView(view)
this.list.reload()
})
this.loadViewConfig(parseInt(queryParams.get('view')))
} else {
this.list.activateSavedView(null)
this.list.loadFromQueryParams(queryParams)
@@ -171,7 +190,8 @@ export class DocumentListComponent implements OnInit, OnDestroy {
this.savedViewService
.patch(savedView)
.pipe(first())
.subscribe((result) => {
.subscribe((view) => {
this.unmodifiedSavedView = view
this.toastService.showInfo(
$localize`View "${this.list.activeSavedViewTitle}" saved successfully.`
)
@@ -180,6 +200,17 @@ export class DocumentListComponent implements OnInit, OnDestroy {
}
}
loadViewConfig(viewID: number) {
this.savedViewService
.getCached(viewID)
.pipe(first())
.subscribe((view) => {
this.unmodifiedSavedView = view
this.list.activateSavedView(view)
this.list.reload()
})
}
saveViewConfigAs() {
let modal = this.modalService.open(SaveViewConfigDialogComponent, {
backdrop: 'static',

View File

@@ -1,4 +1,4 @@
<div class="row flex-wrap">
<div class="row flex-wrap" tourAnchor="tour.documents-filter-editor">
<div class="col mb-2 mb-xxl-0">
<div class="form-inline d-flex align-items-center">
<div class="input-group input-group-sm flex-fill w-auto flex-nowrap">
@@ -11,7 +11,12 @@
<select *ngIf="textFilterTarget == 'asn'" class="form-select flex-grow-0 w-auto" [(ngModel)]="textFilterModifier" (change)="textFilterModifierChange()">
<option *ngFor="let m of textFilterModifiers" ngbDropdownItem [value]="m.id">{{m.label}}</option>
</select>
<input #textFilterInput class="form-control form-control-sm" type="text" [disabled]="textFilterModifierIsNull" [(ngModel)]="textFilter" (keyup.enter)="textFilterEnter()" [readonly]="textFilterTarget == 'fulltext-morelike'">
<button *ngIf="_textFilter" class="btn btn-link btn-sm px-0 position-absolute top-0 end-0 z-10" (click)="resetTextField()">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x me-1" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
</svg>
</button>
<input #textFilterInput class="form-control form-control-sm" type="text" [disabled]="textFilterModifierIsNull" [(ngModel)]="textFilter" (keyup)="textFilterKeyup($event)" [readonly]="textFilterTarget == 'fulltext-morelike'">
</div>
</div>
</div>
@@ -54,19 +59,21 @@
title="Created" i18n-title
(datesSet)="updateRules()"
[(dateBefore)]="dateCreatedBefore"
[(dateAfter)]="dateCreatedAfter"></app-date-dropdown>
[(dateAfter)]="dateCreatedAfter"
[(relativeDate)]="dateCreatedRelativeDate"></app-date-dropdown>
<app-date-dropdown class="mb-2 mb-xl-0"
title="Added" i18n-title
(datesSet)="updateRules()"
[(dateBefore)]="dateAddedBefore"
[(dateAfter)]="dateAddedAfter"
title="Added" i18n-title
(datesSet)="updateRules()"></app-date-dropdown>
[(relativeDate)]="dateAddedRelativeDate"></app-date-dropdown>
</div>
</div>
</div>
<div class="w-100 d-xxl-none"></div>
<div class="col col-xl-auto ps-0">
<div class="col col-xl-auto ps-xxl-0">
<button class="btn btn-link btn-sm px-0" [disabled]="!rulesModified" (click)="resetSelected()">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x me-1" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x me-1 ms-n1" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
</svg><ng-container i18n>Reset filters</ng-container>
</button>

View File

@@ -21,3 +21,7 @@
input[type="text"] {
min-width: 120px;
}
.z-10 {
z-index: 10;
}

View File

@@ -44,6 +44,7 @@ import { DocumentService } from 'src/app/services/rest/document.service'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { RelativeDate } from '../../common/date-dropdown/date-dropdown.component'
const TEXT_FILTER_TARGET_TITLE = 'title'
const TEXT_FILTER_TARGET_TITLE_CONTENT = 'title-content'
@@ -57,6 +58,27 @@ const TEXT_FILTER_MODIFIER_NOTNULL = 'not null'
const TEXT_FILTER_MODIFIER_GT = 'greater'
const TEXT_FILTER_MODIFIER_LT = 'less'
const RELATIVE_DATE_QUERY_REGEXP_CREATED = /created:\[([^\]]+)\]/g
const RELATIVE_DATE_QUERY_REGEXP_ADDED = /added:\[([^\]]+)\]/g
const RELATIVE_DATE_QUERYSTRINGS = [
{
relativeDate: RelativeDate.LAST_7_DAYS,
dateQuery: '-1 week to now',
},
{
relativeDate: RelativeDate.LAST_MONTH,
dateQuery: '-1 month to now',
},
{
relativeDate: RelativeDate.LAST_3_MONTHS,
dateQuery: '-3 month to now',
},
{
relativeDate: RelativeDate.LAST_YEAR,
dateQuery: '-1 year to now',
},
]
@Component({
selector: 'app-filter-editor',
templateUrl: './filter-editor.component.html',
@@ -197,6 +219,8 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
dateCreatedAfter: string
dateAddedBefore: string
dateAddedAfter: string
dateCreatedRelativeDate: RelativeDate
dateAddedRelativeDate: RelativeDate
_unmodifiedFilterRules: FilterRule[] = []
_filterRules: FilterRule[] = []
@@ -228,6 +252,8 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.dateAddedAfter = null
this.dateCreatedBefore = null
this.dateCreatedAfter = null
this.dateCreatedRelativeDate = null
this.dateAddedRelativeDate = null
this.textFilterModifier = TEXT_FILTER_MODIFIER_EQUALS
value.forEach((rule) => {
@@ -245,8 +271,39 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.textFilterTarget = TEXT_FILTER_TARGET_ASN
break
case FILTER_FULLTEXT_QUERY:
this._textFilter = rule.value
this.textFilterTarget = TEXT_FILTER_TARGET_FULLTEXT_QUERY
let allQueryArgs = rule.value.split(',')
let textQueryArgs = []
allQueryArgs.forEach((arg) => {
if (arg.match(RELATIVE_DATE_QUERY_REGEXP_CREATED)) {
;[...arg.matchAll(RELATIVE_DATE_QUERY_REGEXP_CREATED)].forEach(
(match) => {
if (match[1]?.length) {
this.dateCreatedRelativeDate =
RELATIVE_DATE_QUERYSTRINGS.find(
(qS) => qS.dateQuery == match[1]
)?.relativeDate
}
}
)
} else if (arg.match(RELATIVE_DATE_QUERY_REGEXP_ADDED)) {
;[...arg.matchAll(RELATIVE_DATE_QUERY_REGEXP_ADDED)].forEach(
(match) => {
if (match[1]?.length) {
this.dateAddedRelativeDate =
RELATIVE_DATE_QUERYSTRINGS.find(
(qS) => qS.dateQuery == match[1]
)?.relativeDate
}
}
)
} else {
textQueryArgs.push(arg)
}
})
if (textQueryArgs.length) {
this._textFilter = textQueryArgs.join(',')
this.textFilterTarget = TEXT_FILTER_TARGET_FULLTEXT_QUERY
}
break
case FILTER_FULLTEXT_MORELIKE:
this._moreLikeId = +rule.value
@@ -471,6 +528,89 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
value: this.dateAddedAfter,
})
}
if (
this.dateAddedRelativeDate !== null ||
this.dateCreatedRelativeDate !== null
) {
let queryArgs: Array<string> = []
let existingRule = filterRules.find(
(fr) => fr.rule_type == FILTER_FULLTEXT_QUERY
)
// if had a title / content search and added a relative date we need to carry it over...
if (
!existingRule &&
this._textFilter?.length > 0 &&
(this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT ||
this.textFilterTarget == TEXT_FILTER_TARGET_TITLE)
) {
existingRule = filterRules.find(
(fr) =>
fr.rule_type == FILTER_TITLE_CONTENT || fr.rule_type == FILTER_TITLE
)
existingRule.rule_type = FILTER_FULLTEXT_QUERY
}
let existingRuleArgs = existingRule?.value.split(',')
if (this.dateCreatedRelativeDate !== null) {
queryArgs.push(
`created:[${
RELATIVE_DATE_QUERYSTRINGS.find(
(qS) => qS.relativeDate == this.dateCreatedRelativeDate
).dateQuery
}]`
)
if (existingRule) {
queryArgs = existingRuleArgs
.filter((arg) => !arg.match(RELATIVE_DATE_QUERY_REGEXP_CREATED))
.concat(queryArgs)
}
}
if (this.dateAddedRelativeDate !== null) {
queryArgs.push(
`added:[${
RELATIVE_DATE_QUERYSTRINGS.find(
(qS) => qS.relativeDate == this.dateAddedRelativeDate
).dateQuery
}]`
)
if (existingRule) {
queryArgs = existingRuleArgs
.filter((arg) => !arg.match(RELATIVE_DATE_QUERY_REGEXP_ADDED))
.concat(queryArgs)
}
}
if (existingRule) {
existingRule.value = queryArgs.join(',')
} else {
filterRules.push({
rule_type: FILTER_FULLTEXT_QUERY,
value: queryArgs.join(','),
})
}
}
if (
this.dateCreatedRelativeDate == null &&
this.dateAddedRelativeDate == null
) {
const existingRule = filterRules.find(
(fr) => fr.rule_type == FILTER_FULLTEXT_QUERY
)
if (
existingRule?.value.match(RELATIVE_DATE_QUERY_REGEXP_CREATED) ||
existingRule?.value.match(RELATIVE_DATE_QUERY_REGEXP_ADDED)
) {
// remove any existing date query
existingRule.value = existingRule.value
.replace(RELATIVE_DATE_QUERY_REGEXP_CREATED, '')
.replace(RELATIVE_DATE_QUERY_REGEXP_ADDED, '')
if (existingRule.value.replace(',', '').trim() === '') {
// if its empty now, remove it entirely
filterRules.splice(filterRules.indexOf(existingRule), 1)
}
}
}
return filterRules
}
@@ -569,15 +709,23 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
this.updateRules()
}
textFilterEnter() {
const filterString = (
this.textFilterInput.nativeElement as HTMLInputElement
).value
if (filterString.length) {
this.updateTextFilter(filterString)
textFilterKeyup(event: KeyboardEvent) {
if (event.key == 'Enter') {
const filterString = (
this.textFilterInput.nativeElement as HTMLInputElement
).value
if (filterString.length) {
this.updateTextFilter(filterString)
}
} else if (event.key == 'Escape') {
this.resetTextField()
}
}
resetTextField() {
this.updateTextFilter('')
}
changeTextFilterTarget(target) {
if (
this.textFilterTarget == TEXT_FILTER_TARGET_FULLTEXT_MORELIKE &&

View File

@@ -1,4 +1,4 @@
::ng-deep .popover {
::ng-deep app-document-list .popover {
max-width: 40rem;
.preview {

View File

@@ -1,5 +1,5 @@
<app-page-header title="Settings" i18n-title>
<button class="btn btn-sm btn-outline-primary" (click)="tourService.start()"><ng-container i18n>Start tour</ng-container></button>
</app-page-header>
<!-- <p>items per page, documents per view type</p> -->
@@ -89,6 +89,17 @@
</div>
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<span i18n>Sidebar</span>
</div>
<div class="col">
<app-input-check i18n-title title="Use 'slim' sidebar (icons only)" formControlName="slimSidebarEnabled"></app-input-check>
</div>
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label">
<span i18n>Dark mode</span>
@@ -116,6 +127,21 @@
</div>
</div>
<h4 class="mt-4" id="update-checking" i18n>Update checking</h4>
<div class="row mb-3">
<div class="offset-md-3 col">
<p i18n>
Update checking works by pinging the the public <a href="https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest" target="_blank" rel="noopener noreferrer">Github API</a> for the latest release to determine whether a new version is available.<br/>
Actual updating of the app must still be performed manually.
</p>
<p i18n>
<em>No tracking data is collected by the app in any way.</em>
</p>
<app-input-check i18n-title title="Enable update checking" formControlName="updateCheckingEnabled" i18n-hint hint="Note that for users of thirdy-party containers e.g. linuxserver.io this notification may be 'ahead' of the current third-party release."></app-input-check>
</div>
</div>
<h4 class="mt-4" i18n>Bulk editing</h4>
<div class="row mb-3">
@@ -194,5 +220,5 @@
<div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div>
<button type="submit" class="btn btn-primary" [disabled]="!(isDirty$ | async)" i18n>Save</button>
<button type="submit" class="btn btn-primary mb-2" [disabled]="!(isDirty$ | async)" i18n>Save</button>
</form>

View File

@@ -4,7 +4,7 @@ import {
LOCALE_ID,
OnInit,
OnDestroy,
Renderer2,
AfterViewInit,
} from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
@@ -16,21 +16,35 @@ import {
} from 'src/app/services/settings.service'
import { Toast, ToastService } from 'src/app/services/toast.service'
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
import { Observable, Subscription, BehaviorSubject, first } from 'rxjs'
import {
Observable,
Subscription,
BehaviorSubject,
first,
tap,
takeUntil,
Subject,
} from 'rxjs'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { ActivatedRoute } from '@angular/router'
import { ViewportScroller } from '@angular/common'
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
@Component({
selector: 'app-settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss'],
})
export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
export class SettingsComponent
implements OnInit, AfterViewInit, OnDestroy, DirtyComponent
{
savedViewGroup = new FormGroup({})
settingsForm = new FormGroup({
bulkEditConfirmationDialogs: new FormControl(null),
bulkEditApplyOnClose: new FormControl(null),
documentListItemPerPage: new FormControl(null),
slimSidebarEnabled: new FormControl(null),
darkModeUseSystem: new FormControl(null),
darkModeEnabled: new FormControl(null),
darkModeInvertThumbs: new FormControl(null),
@@ -45,6 +59,7 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
notificationsConsumerFailed: new FormControl(null),
notificationsConsumerSuppressOnDashboard: new FormControl(null),
commentsEnabled: new FormControl(null),
updateCheckingEnabled: new FormControl(null),
})
savedViews: PaperlessSavedView[]
@@ -52,7 +67,9 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
store: BehaviorSubject<any>
storeSub: Subscription
isDirty$: Observable<boolean>
isDirty: Boolean = false
isDirty: boolean = false
unsubscribeNotifier: Subject<any> = new Subject()
savePending: boolean = false
get computedDateLocale(): string {
return (
@@ -62,105 +79,129 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
)
}
get displayLanguageIsDirty(): boolean {
return (
this.settingsForm.get('displayLanguage').value !=
this.store?.getValue()['displayLanguage']
)
}
constructor(
public savedViewService: SavedViewService,
private documentListViewService: DocumentListViewService,
private toastService: ToastService,
private settings: SettingsService,
@Inject(LOCALE_ID) public currentLocale: string
) {}
@Inject(LOCALE_ID) public currentLocale: string,
private viewportScroller: ViewportScroller,
private activatedRoute: ActivatedRoute,
public readonly tourService: TourService
) {
this.settings.settingsSaved.subscribe(() => {
if (!this.savePending) this.initialize()
})
}
ngAfterViewInit(): void {
if (this.activatedRoute.snapshot.fragment) {
this.viewportScroller.scrollToAnchor(
this.activatedRoute.snapshot.fragment
)
}
}
private getCurrentSettings() {
return {
bulkEditConfirmationDialogs: this.settings.get(
SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
),
bulkEditApplyOnClose: this.settings.get(
SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE
),
documentListItemPerPage: this.settings.get(
SETTINGS_KEYS.DOCUMENT_LIST_SIZE
),
slimSidebarEnabled: this.settings.get(SETTINGS_KEYS.SLIM_SIDEBAR),
darkModeUseSystem: this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM),
darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED),
darkModeInvertThumbs: this.settings.get(
SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED
),
themeColor: this.settings.get(SETTINGS_KEYS.THEME_COLOR),
useNativePdfViewer: this.settings.get(
SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER
),
savedViews: {},
displayLanguage: this.settings.getLanguage(),
dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE),
dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT),
notificationsConsumerNewDocument: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT
),
notificationsConsumerSuccess: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS
),
notificationsConsumerFailed: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED
),
notificationsConsumerSuppressOnDashboard: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD
),
commentsEnabled: this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED),
updateCheckingEnabled: this.settings.get(
SETTINGS_KEYS.UPDATE_CHECKING_ENABLED
),
}
}
ngOnInit() {
this.savedViewService.listAll().subscribe((r) => {
this.savedViews = r.results
let storeData = {
bulkEditConfirmationDialogs: this.settings.get(
SETTINGS_KEYS.BULK_EDIT_CONFIRMATION_DIALOGS
),
bulkEditApplyOnClose: this.settings.get(
SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE
),
documentListItemPerPage: this.settings.get(
SETTINGS_KEYS.DOCUMENT_LIST_SIZE
),
darkModeUseSystem: this.settings.get(
SETTINGS_KEYS.DARK_MODE_USE_SYSTEM
),
darkModeEnabled: this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED),
darkModeInvertThumbs: this.settings.get(
SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED
),
themeColor: this.settings.get(SETTINGS_KEYS.THEME_COLOR),
useNativePdfViewer: this.settings.get(
SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER
),
savedViews: {},
displayLanguage: this.settings.getLanguage(),
dateLocale: this.settings.get(SETTINGS_KEYS.DATE_LOCALE),
dateFormat: this.settings.get(SETTINGS_KEYS.DATE_FORMAT),
notificationsConsumerNewDocument: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_NEW_DOCUMENT
),
notificationsConsumerSuccess: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUCCESS
),
notificationsConsumerFailed: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_FAILED
),
notificationsConsumerSuppressOnDashboard: this.settings.get(
SETTINGS_KEYS.NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD
),
commentsEnabled: this.settings.get(SETTINGS_KEYS.COMMENTS_ENABLED),
this.initialize()
})
}
initialize() {
this.unsubscribeNotifier.next(true)
let storeData = this.getCurrentSettings()
for (let view of this.savedViews) {
storeData.savedViews[view.id.toString()] = {
id: view.id,
name: view.name,
show_on_dashboard: view.show_on_dashboard,
show_in_sidebar: view.show_in_sidebar,
}
this.savedViewGroup.addControl(
view.id.toString(),
new FormGroup({
id: new FormControl(null),
name: new FormControl(null),
show_on_dashboard: new FormControl(null),
show_in_sidebar: new FormControl(null),
})
)
}
for (let view of this.savedViews) {
storeData.savedViews[view.id.toString()] = {
id: view.id,
name: view.name,
show_on_dashboard: view.show_on_dashboard,
show_in_sidebar: view.show_in_sidebar,
}
this.savedViewGroup.addControl(
view.id.toString(),
new FormGroup({
id: new FormControl(null),
name: new FormControl(null),
show_on_dashboard: new FormControl(null),
show_in_sidebar: new FormControl(null),
})
)
}
this.store = new BehaviorSubject(storeData)
this.store = new BehaviorSubject(storeData)
this.storeSub = this.store.asObservable().subscribe((state) => {
this.settingsForm.patchValue(state, { emitEvent: false })
})
this.storeSub = this.store.asObservable().subscribe((state) => {
this.settingsForm.patchValue(state, { emitEvent: false })
})
// Initialize dirtyCheck
this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable())
// Initialize dirtyCheck
this.isDirty$ = dirtyCheck(this.settingsForm, this.store.asObservable())
// Record dirty in case we need to 'undo' appearance settings if not saved on close
this.isDirty$.subscribe((dirty) => {
// Record dirty in case we need to 'undo' appearance settings if not saved on close
this.isDirty$
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((dirty) => {
this.isDirty = dirty
})
// "Live" visual changes prior to save
this.settingsForm.valueChanges.subscribe(() => {
// "Live" visual changes prior to save
this.settingsForm.valueChanges
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(() => {
this.settings.updateAppearanceSettings(
this.settingsForm.get('darkModeUseSystem').value,
this.settingsForm.get('darkModeEnabled').value,
this.settingsForm.get('themeColor').value
)
})
})
}
ngOnDestroy() {
@@ -179,7 +220,14 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
}
private saveLocalSettings() {
const reloadRequired = this.displayLanguageIsDirty // just this one, for now
this.savePending = true
const reloadRequired =
this.settingsForm.value.displayLanguage !=
this.store?.getValue()['displayLanguage'] || // displayLanguage is dirty
(this.settingsForm.value.updateCheckingEnabled !=
this.store?.getValue()['updateCheckingEnabled'] &&
this.settingsForm.value.updateCheckingEnabled) // update checking was turned on
this.settings.set(
SETTINGS_KEYS.BULK_EDIT_APPLY_ON_CLOSE,
this.settingsForm.value.bulkEditApplyOnClose
@@ -192,6 +240,10 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
SETTINGS_KEYS.DOCUMENT_LIST_SIZE,
this.settingsForm.value.documentListItemPerPage
)
this.settings.set(
SETTINGS_KEYS.SLIM_SIDEBAR,
this.settingsForm.value.slimSidebarEnabled
)
this.settings.set(
SETTINGS_KEYS.DARK_MODE_USE_SYSTEM,
this.settingsForm.value.darkModeUseSystem
@@ -240,10 +292,15 @@ export class SettingsComponent implements OnInit, OnDestroy, DirtyComponent {
SETTINGS_KEYS.COMMENTS_ENABLED,
this.settingsForm.value.commentsEnabled
)
this.settings.set(
SETTINGS_KEYS.UPDATE_CHECKING_ENABLED,
this.settingsForm.value.updateCheckingEnabled
)
this.settings.setLanguage(this.settingsForm.value.displayLanguage)
this.settings
.storeSettings()
.pipe(first())
.pipe(tap(() => (this.savePending = false)))
.subscribe({
next: () => {
this.store.next(this.settingsForm.value)

View File

@@ -53,8 +53,8 @@
<label class="form-check-label" for="task{{task.id}}"></label>
</div>
</th>
<td class="overflow-auto">{{ task.name }}</td>
<td class="d-none d-lg-table-cell">{{ task.created | customDate:'short' }}</td>
<td class="overflow-auto">{{ task.task_file_name }}</td>
<td class="d-none d-lg-table-cell">{{ task.date_created | customDate:'short' }}</td>
<td class="d-none d-lg-table-cell" *ngIf="activeTab != 'started' && activeTab != 'queued'">
<div *ngIf="task.result.length > 50" class="result" (click)="expandTask(task); $event.stopPropagation();"
[ngbPopover]="resultPopover" popoverClass="shadow small mobile" triggers="mouseenter:mouseleave" container="body">
@@ -74,11 +74,18 @@
</button>
</td>
<td scope="row">
<button class="btn btn-sm btn-outline-secondary" (click)="dismissTask(task); $event.stopPropagation();">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#check"/>
</svg>&nbsp;<ng-container i18n>Dismiss</ng-container>
</button>
<div class="btn-group" role="group">
<button class="btn btn-sm btn-outline-secondary" (click)="dismissTask(task); $event.stopPropagation();">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#check"/>
</svg>&nbsp;<ng-container i18n>Dismiss</ng-container>
</button>
<button *ngIf="task.related_document" class="btn btn-sm btn-outline-primary" (click)="dismissAndGo(task); $event.stopPropagation();">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#file-text"/>
</svg>&nbsp;<ng-container i18n>Open Document</ng-container>
</button>
</div>
</td>
</tr>
<tr>

View File

@@ -1,6 +1,7 @@
import { Component, OnInit, OnDestroy } from '@angular/core'
import { Router } from '@angular/router'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { takeUntil, Subject, first } from 'rxjs'
import { Subject, first } from 'rxjs'
import { PaperlessTask } from 'src/app/data/paperless-task'
import { TasksService } from 'src/app/services/tasks.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
@@ -24,7 +25,8 @@ export class TasksComponent implements OnInit, OnDestroy {
constructor(
public tasksService: TasksService,
private modalService: NgbModal
private modalService: NgbModal,
private readonly router: Router
) {}
ngOnInit() {
@@ -64,6 +66,11 @@ export class TasksComponent implements OnInit, OnDestroy {
}
}
dismissAndGo(task: PaperlessTask) {
this.dismissTask(task)
this.router.navigate(['documents', task.related_document])
}
expandTask(task: PaperlessTask) {
this.expandedTask = this.expandedTask == task.id ? undefined : task.id
}

View File

@@ -6,6 +6,7 @@ export const MATCH_LITERAL = 3
export const MATCH_REGEX = 4
export const MATCH_FUZZY = 5
export const MATCH_AUTO = 6
export const DEFAULT_MATCHING_ALGORITHM = MATCH_AUTO
export const MATCHING_ALGORITHMS = [
{

View File

@@ -29,8 +29,6 @@ export interface PaperlessDocument extends ObjectWithId {
content?: string
file_type?: string
tags$?: Observable<PaperlessTag[]>
tags?: number[]
@@ -47,7 +45,7 @@ export interface PaperlessDocument extends ObjectWithId {
added?: Date
file_name?: string
original_file_name?: string
download_url?: string

View File

@@ -6,11 +6,10 @@ export enum PaperlessTaskType {
}
export enum PaperlessTaskStatus {
Queued = 'queued',
Started = 'started',
Complete = 'complete',
Failed = 'failed',
Unknown = 'unknown',
Pending = 'PENDING',
Started = 'STARTED',
Complete = 'SUCCESS',
Failed = 'FAILURE',
}
export interface PaperlessTask extends ObjectWithId {
@@ -22,11 +21,13 @@ export interface PaperlessTask extends ObjectWithId {
task_id: string
name: string
task_file_name: string
created: Date
date_created: Date
started?: Date
done?: Date
result: string
related_document?: number
}

View File

@@ -37,6 +37,10 @@ export const SETTINGS_KEYS = {
NOTIFICATIONS_CONSUMER_SUPPRESS_ON_DASHBOARD:
'general-settings:notifications:consumer-suppress-on-dashboard',
COMMENTS_ENABLED: 'general-settings:comments-enabled',
SLIM_SIDEBAR: 'general-settings:slim-sidebar',
UPDATE_CHECKING_ENABLED: 'general-settings:update-checking:enabled',
UPDATE_CHECKING_BACKEND_SETTING:
'general-settings:update-checking:backend-setting',
}
export const SETTINGS: PaperlessUiSetting[] = [
@@ -55,6 +59,11 @@ export const SETTINGS: PaperlessUiSetting[] = [
type: 'boolean',
default: false,
},
{
key: SETTINGS_KEYS.SLIM_SIDEBAR,
type: 'boolean',
default: false,
},
{
key: SETTINGS_KEYS.DOCUMENT_LIST_SIZE,
type: 'number',
@@ -120,4 +129,14 @@ export const SETTINGS: PaperlessUiSetting[] = [
type: 'boolean',
default: true,
},
{
key: SETTINGS_KEYS.UPDATE_CHECKING_ENABLED,
type: 'boolean',
default: false,
},
{
key: SETTINGS_KEYS.UPDATE_CHECKING_BACKEND_SETTING,
type: 'string',
default: '',
},
]

View File

@@ -0,0 +1,51 @@
import { CanDeactivate } from '@angular/router'
import { Injectable } from '@angular/core'
import { first, Observable, Subject } from 'rxjs'
import { DocumentListComponent } from '../components/document-list/document-list.component'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfirmDialogComponent } from '../components/common/confirm-dialog/confirm-dialog.component'
@Injectable()
export class DirtySavedViewGuard
implements CanDeactivate<DocumentListComponent>
{
constructor(private modalService: NgbModal) {}
canDeactivate(
component: DocumentListComponent
): boolean | Observable<boolean> {
return component.savedViewIsModified ? this.warn(component) : true
}
warn(component: DocumentListComponent) {
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Unsaved Changes`
modal.componentInstance.messageBold =
$localize`You have unsaved changes to the saved view` +
' "' +
component.getTitle()
;('".')
modal.componentInstance.message = $localize`Are you sure you want to close this saved view?`
modal.componentInstance.btnClass = 'btn-secondary'
modal.componentInstance.btnCaption = $localize`Close`
modal.componentInstance.alternativeBtnClass = 'btn-primary'
modal.componentInstance.alternativeBtnCaption = $localize`Save and close`
modal.componentInstance.alternativeClicked.pipe(first()).subscribe(() => {
modal.componentInstance.buttonsEnabled = false
component.saveViewConfig()
modal.close()
})
modal.componentInstance.confirmClicked.pipe(first()).subscribe(() => {
modal.componentInstance.buttonsEnabled = false
modal.close()
})
const subject = new Subject<boolean>()
modal.componentInstance.confirmSubject = subject
modal.componentInstance.alternativeSubject = subject
return subject
}
}

View File

@@ -171,15 +171,15 @@ export class DocumentListViewService {
this.reduceSelectionToFilter()
if (!this.router.routerState.snapshot.url.includes('/view/')) {
this.router.navigate([], {
queryParams: { view: view.id },
})
this.router.navigate(['view', view.id])
}
}
loadFromQueryParams(queryParams: ParamMap) {
const paramsEmpty: boolean = queryParams.keys.length == 0
let newState: ListViewState = this.listViewStates.get(null)
let newState: ListViewState = this.listViewStates.get(
this._activeSavedViewId
)
if (!paramsEmpty) newState = paramsToViewState(queryParams)
if (newState == undefined) newState = this.defaultListViewState() // if nothing in local storage
@@ -276,7 +276,6 @@ export class DocumentListViewService {
) {
this.activeListViewState.sortField = 'created'
}
this._activeSavedViewId = null
this.activeListViewState.filterRules = filterRules
this.reload()
this.reduceSelectionToFilter()
@@ -288,7 +287,6 @@ export class DocumentListViewService {
}
set sortField(field: string) {
this._activeSavedViewId = null
this.activeListViewState.sortField = field
this.reload()
this.saveDocumentListView()
@@ -299,7 +297,6 @@ export class DocumentListViewService {
}
set sortReverse(reverse: boolean) {
this._activeSavedViewId = null
this.activeListViewState.sortReverse = reverse
this.reload()
this.saveDocumentListView()

View File

@@ -6,7 +6,6 @@ import { environment } from 'src/environments/environment'
export interface AppRemoteVersion {
version: string
update_available: boolean
feature_is_set: boolean
}
@Injectable({

View File

@@ -1,6 +1,7 @@
import { DOCUMENT } from '@angular/common'
import { HttpClient } from '@angular/common/http'
import {
EventEmitter,
Inject,
Injectable,
LOCALE_ID,
@@ -22,6 +23,7 @@ import {
SETTINGS,
SETTINGS_KEYS,
} from '../data/paperless-uisettings'
import { SavedViewService } from './rest/saved-view.service'
import { ToastService } from './toast.service'
export interface LanguageOption {
@@ -46,6 +48,8 @@ export class SettingsService {
public displayName: string
public settingsSaved: EventEmitter<any> = new EventEmitter()
constructor(
rendererFactory: RendererFactory2,
@Inject(DOCUMENT) private document,
@@ -53,7 +57,8 @@ export class SettingsService {
private meta: Meta,
@Inject(LOCALE_ID) private localeId: string,
protected http: HttpClient,
private toastService: ToastService
private toastService: ToastService,
private savedViewService: SavedViewService
) {
this.renderer = rendererFactory.createRenderer(null, null)
}
@@ -313,13 +318,7 @@ export class SettingsService {
)
}
get(key: string): any {
let setting = SETTINGS.find((s) => s.key == key)
if (!setting) {
return null
}
private getSettingRawValue(key: string): any {
let value = null
// parse key:key:key into nested object
const keys = key.replace('general-settings:', '').split(':')
@@ -330,6 +329,17 @@ export class SettingsService {
if (index == keys.length - 1) value = settingObj[keyPart]
else settingObj = settingObj[keyPart]
})
return value
}
get(key: string): any {
let setting = SETTINGS.find((s) => s.key == key)
if (!setting) {
return null
}
let value = this.getSettingRawValue(key)
if (value != null) {
switch (setting.type) {
@@ -359,8 +369,19 @@ export class SettingsService {
})
}
private settingIsSet(key: string): boolean {
let value = this.getSettingRawValue(key)
return value != null
}
storeSettings(): Observable<any> {
return this.http.post(this.baseUrl, { settings: this.settings })
return this.http.post(this.baseUrl, { settings: this.settings }).pipe(
tap((results) => {
if (results.success) {
this.settingsSaved.emit()
}
})
)
}
maybeMigrateSettings() {
@@ -400,5 +421,38 @@ export class SettingsService {
},
})
}
if (
!this.settingIsSet(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED) &&
this.get(SETTINGS_KEYS.UPDATE_CHECKING_BACKEND_SETTING) != 'default'
) {
this.set(
SETTINGS_KEYS.UPDATE_CHECKING_ENABLED,
this.get(SETTINGS_KEYS.UPDATE_CHECKING_BACKEND_SETTING).toString() ===
'true'
)
this.storeSettings()
.pipe(first())
.subscribe({
error: (e) => {
this.toastService.showError(
'Error migrating update checking setting'
)
console.log(e)
},
})
}
}
get updateCheckingIsSet(): boolean {
return this.settingIsSet(SETTINGS_KEYS.UPDATE_CHECKING_ENABLED)
}
offerTour(): boolean {
return (
!this.savedViewService.loading &&
this.savedViewService.dashboardViews.length == 0
)
}
}

View File

@@ -1,6 +1,6 @@
import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { first, map } from 'rxjs/operators'
import { first } from 'rxjs/operators'
import {
PaperlessTask,
PaperlessTaskStatus,
@@ -27,7 +27,7 @@ export class TasksService {
}
public get queuedFileTasks(): PaperlessTask[] {
return this.fileTasks.filter((t) => t.status == PaperlessTaskStatus.Queued)
return this.fileTasks.filter((t) => t.status == PaperlessTaskStatus.Pending)
}
public get startedFileTasks(): PaperlessTask[] {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -5,7 +5,7 @@ export const environment = {
apiBaseUrl: document.baseURI + 'api/',
apiVersion: '2',
appTitle: 'Paperless-ngx',
version: '1.9.0-rc1',
version: '1.10.0-beta',
webSocketHost: window.location.host,
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
webSocketBaseUrl: base_url.pathname + 'ws/',

File diff suppressed because it is too large Load Diff

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