Compare commits

...

394 Commits

Author SHA1 Message Date
shamoon
f02e8e0dc3 Bump version to 2.4.0 2024-01-18 17:43:49 -08:00
github-actions[bot]
c1ed87a44f New Crowdin translations by GitHub Action (#5349)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-18 17:42:21 -08:00
shamoon
16169ca331 Chore: Close outdated support / general discussions (#5443) 2024-01-18 19:46:12 +00:00
shamoon
26900e0766 Fix: doc link removal before assigning value (#5451) 2024-01-18 06:58:41 -08:00
JigSaw
aa798604b3 Fix typo in bug report template (#5450) 2024-01-18 14:28:33 +00:00
shamoon
bb98fc5f65 Chore: better bootstrap icons (#5403) 2024-01-18 00:27:38 +00:00
shamoon
dc1918ad10 Fix: dont lose permissions ui if owner changed from null (#5433) 2024-01-17 17:44:04 +00:00
shamoon
ea632d0417 Fix missing frontend test imports 2024-01-16 23:01:50 -08:00
shamoon
648dc709fd Fix: tweak how auto-scrolling of logs works 2024-01-16 23:00:18 -08:00
shamoon
1a84f6a20e Fix: change auto-refresh click target 2024-01-16 22:58:41 -08:00
shamoon
96af953e6f Fix: save button layout with long button translation text 2024-01-16 21:44:41 -08:00
shamoon
6db9e292ba Enhancement: support remote user auth directly against API (DRF) (#5386) 2024-01-16 23:26:05 +00:00
shamoon
2e2362e2df Fix: outdated confirm dialog confirm 2024-01-16 15:18:26 -08:00
Trenton H
51dd95be3d Fix: Getting next ASN when no documents have an ASN (#5431)
* Fixes the next ASN logic to account for no ASNs yet being assigned

* Updates so the ASN will start at 1

* Do the same calculation without the branch
2024-01-16 23:08:37 +00:00
Trenton H
e16645b146 Feature: Add additional caching support to suggestions and metadata (#5414)
* Adds ETag and Last-Modified headers to suggestions, metadata and previews

* Slight update to the suggestions etag

* Small user message for why classifier didn't train again
2024-01-16 17:01:07 +00:00
dependabot[bot]
0068f091bb Chore(deps): Bump the small-changes group with 2 updates (#5413)
Bumps the small-changes group with 2 updates: [channels-redis](https://github.com/django/channels_redis) and [gotenberg-client](https://github.com/stumpylog/gotenberg-client).


Updates `channels-redis` from 4.1.0 to 4.2.0
- [Changelog](https://github.com/django/channels_redis/blob/main/CHANGELOG.txt)
- [Commits](https://github.com/django/channels_redis/commits)

Updates `gotenberg-client` from 0.4.1 to 0.5.0
- [Release notes](https://github.com/stumpylog/gotenberg-client/releases)
- [Changelog](https://github.com/stumpylog/gotenberg-client/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stumpylog/gotenberg-client/compare/0.4.1...0.5.0)

---
updated-dependencies:
- dependency-name: channels-redis
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: gotenberg-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-16 16:14:45 +00:00
dependabot[bot]
ad6efd2898 Chore(deps-dev): Bump the development group with 2 updates (#5412)
Bumps the development group with 2 updates: [ruff](https://github.com/astral-sh/ruff) and [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `ruff` from 0.1.11 to 0.1.13
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.11...v0.1.13)

Updates `mkdocs-material` from 9.5.3 to 9.5.4
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.3...9.5.4)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: mkdocs-material
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-16 16:02:34 +00:00
shamoon
86e380bb1c Fix signin username floating label (#5424) 2024-01-16 07:32:07 -08:00
shamoon
58aacd4814 Feature: help tooltips (#5383) 2024-01-15 15:40:36 -08:00
shamoon
ad07791bac Enhancement / QoL: show selected tasks count (#5379) 2024-01-15 22:15:30 +00:00
shamoon
783090c2cd Fix shared by me filter with multiple users / groups in postgres (#5396) 2024-01-15 22:06:59 +00:00
Trenton H
41a3c7c89b Fix: Catch new warning when loading the classifier (#5395) 2024-01-14 13:21:17 -08:00
shamoon
16cc7415c1 Fix missed migration for app_logo 2024-01-13 16:23:44 -08:00
shamoon
98c5cf89ef Fix: doc detail component fixes (#5373) 2024-01-13 21:40:22 +00:00
shamoon
53e04e66cf Enhancement: warn when outdated doc detected (#5372)
* Update modified property for target docs w bidirectional links

* Warn on doc change detected
2024-01-13 20:28:10 +00:00
shamoon
2a6e79acc8 Feature: app branding (#5357) 2024-01-13 19:57:25 +00:00
Trenton H
2da5e46386 Refactor file consumption task to allow beginnings of a plugin system (#5367) 2024-01-13 16:11:14 +00:00
Trenton H
4dbf8d7969 Reapply #5304 fix 2024-01-12 13:19:24 -08:00
shamoon
4a52fc27d4 Reset dev version string 2024-01-11 21:13:51 -08:00
shamoon
05cd34c8af Merge branch 'main' into dev 2024-01-11 21:13:44 -08:00
dependabot[bot]
8a622181fc Chore(deps-dev): Bump jinja2 from 3.1.2 to 3.1.3 (#5352)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.2 to 3.1.3.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.2...3.1.3)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 13:26:54 -08:00
github-actions[bot]
13f38bf3a1 [Documentation] Add v2.3.3 changelog (#5346)
* Changelog v2.3.3 - GHA

* Fix PR categorization, as usual

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-01-11 07:17:26 -08:00
shamoon
16acc2d6ad Merge branch 'dev' 2024-01-10 15:59:50 -08:00
github-actions[bot]
c2c9a953d3 New Crowdin translations by GitHub Action (#5314)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-10 15:58:17 -08:00
shamoon
530f4a8b28 Bump version to 2.3.3 2024-01-10 15:55:09 -08:00
shamoon
8eb1dc4f62 Merge branch 'dev' 2024-01-10 15:54:47 -08:00
shamoon
a2b87fe012 Trigger crowdin action on changes to frontend strings file 2024-01-10 15:54:02 -08:00
shamoon
3dcb973adb Enhancement: Explain behavior of unset app config boolean to user (#5345) 2024-01-10 15:34:20 -08:00
shamoon
8e8810cbaa Fix: correct OCR_MAX_IMAGE_PIXELS doc link 2024-01-10 13:22:05 -08:00
shamoon
b0aeec4c43 Fix: Coerce language app config field to None if empty 2024-01-10 13:21:51 -08:00
shamoon
1ac298f6ff Fix empty assign_title validation 2024-01-10 12:53:35 -08:00
shamoon
6d5f4e92cc Enhancement: title assignment placeholder error handling, fallback (#5282) 2024-01-10 10:18:55 -08:00
shamoon
416ad13aaf Update config page text 2024-01-09 11:37:39 -08:00
Trenton H
a7e1299194 Updates all backend, hooks and configures codespell in a slightly easier way (#5336) 2024-01-09 10:30:33 -08:00
Trenton H
a12e1fae72 Fix: Don't require the JSON user arguments field (#5320)
* Allows new user args field to be null

* Coerce empty string to None for user_args JSONField

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-01-08 13:14:36 -08:00
shamoon
f525ac0af6 Chore: add pre-commit hook for codespell (#5324) 2024-01-08 13:03:05 -08:00
luzpaz
58bf9c552b Documentation: Fix typos with automated tool (#5319)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-01-08 16:58:41 +00:00
github-actions[bot]
22d257cd1f Changelog v2.3.2 - GHA (#5310)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-01-07 23:32:11 -08:00
Trenton Holmes
f1bf1ddc54 Resets version string 2024-01-07 17:03:46 -08:00
Trenton Holmes
6015cc0e4a Bumps version to 2.3.2 2024-01-07 17:02:36 -08:00
Trenton Holmes
f9926d77d5 Merge remote-tracking branch 'origin/dev' 2024-01-07 16:47:52 -08:00
Colin Hebert
4f85dcecfc Deployment: Use the default Docker healthcheck from the Dockerfile (Part 2) (#5224)
* Set default healthcheck

* Rely on default healthcheck
2024-01-07 22:49:29 +00:00
github-actions[bot]
30c31a3d4c New Crowdin translations by GitHub Action (#5309)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-07 14:37:33 -08:00
shamoon
c64667d396 Fix: workflow assignment of customfield fails if field exists in v2.3.1 (#5302) 2024-01-07 22:27:57 +00:00
github-actions[bot]
9f6613fe05 New Crowdin translations by GitHub Action (#5268)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-07 14:19:57 -08:00
Trenton H
ea47af7034 Fixes the user arguments json field decoding (#5307) 2024-01-07 14:17:51 -08:00
shamoon
d46abeff01 Fix: empty match field cannot be saved (#5301) 2024-01-07 22:10:26 +00:00
Trenton H
2b39697ffb Fixes usages of UTC datetime instead of local datetime (#5304) 2024-01-07 13:57:40 -08:00
shamoon
4b00a72ff5 Fix: path fnmatch case note 2024-01-07 10:48:04 -08:00
shamoon
e590b2482e Update frontend strings 2024-01-07 08:46:32 -08:00
github-actions[bot]
eb7dd80410 Changelog v2.3.1 - GHA (#5283)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-01-07 08:44:12 -08:00
shamoon
86338465fb Fix: workflow edit form loses unsaved changes in v2.3.1 (#5299) 2024-01-07 08:16:58 -08:00
shamoon
a41dbdd12c Reset dev version string 2024-01-06 22:43:25 -08:00
shamoon
1e10a438cd Bump version to 2.3.1 2024-01-06 22:42:35 -08:00
shamoon
ab34ea724d Merge branch 'dev' 2024-01-06 22:42:16 -08:00
shamoon
fd8bfe1a80 Fix: edit workflow form not displaying trigger settings in v2.3.0 (#5276) 2024-01-06 17:27:49 +00:00
Trenton H
9043f45350 Adds more documentation for OCR_PAGES and prevents using 0 for actual OCR (#5275) 2024-01-06 09:06:41 -08:00
github-actions[bot]
5921e6d13e [Documentation] Add v2.3.0 changelog (#5263)
* Changelog v2.3.0 - GHA

* Re-categorize some PRs

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-01-06 09:06:23 -08:00
shamoon
ee2bfe2350 Reset dev version string 2024-01-05 22:12:29 -08:00
shamoon
0957a7ca8e Bump version to 2.3.0 2024-01-05 21:36:48 -08:00
shamoon
f4e75c7fb7 Merge branch 'dev' 2024-01-05 21:36:01 -08:00
shamoon
fae0e3b405 Update api version to v4 2024-01-05 21:35:40 -08:00
github-actions[bot]
ef335517ce New Crowdin translations by GitHub Action (#5146)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-05 21:28:32 -08:00
shamoon
e2be166e67 Update django.po 2024-01-05 21:26:30 -08:00
Trenton H
37e34d92de Replaces deprecated Django with standard library (#5262) 2024-01-05 21:20:38 -08:00
Trenton H
bd35030c59 Fix: Crash in barcode ASN reading when the file type isn't supported (#5261)
* Fixes a random crash in the barcode ASN reading so it doesn't try to access a not created temp dir

* Don't parse the barcodes twice, store the result instead
2024-01-06 05:08:24 +00:00
Trenton H
a82e3771ae Fix: Allows pre-consume scripts to modify the working path again (#5260)
* Allows pre-consume scripts to modify the working path again and generally cleans up some confusion about working copy vs original
2024-01-05 21:01:57 -08:00
shamoon
3115106dc1 Enhancement: add basic filters for listing custom fields (#5257) 2024-01-06 03:04:31 +00:00
shamoon
d623af9c41 Change: Use fnmatch for workflow path matching (#5250) 2024-01-05 19:15:14 +00:00
Thomas Falkenberg
355a434a07 Enhancement: fetch mails in bulk (#5249)
Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com>
2024-01-05 10:43:22 -08:00
Trenton H
8da2535a65 Fix: zip exports not respecting the --delete option (#5245) 2024-01-04 19:58:58 +00:00
ChrisRBe
5963dfe41b Fix: correctly format tip admonition (#5229)
format for material admonition requires a line break and another
level of indentation which was not correctly applied on this line.
2024-01-03 14:34:59 -08:00
shamoon
c6dcaa0472 Documentation: update screenshot / features list for workflows 2024-01-03 08:35:09 -08:00
shamoon
21063a5c22 Fix: workflow trigger / action ID color legibility 2024-01-03 08:33:19 -08:00
shamoon
ba2f51bed1 Fix: consistent workflow action name display 2024-01-03 00:45:22 -08:00
Bevan Kay
bbf64b7e93 Enhancement: add storage_path parameter to post_document API (#5217)
* Feature: add `storage_path` parameter to post_document API

* Complete coverage for validate_storage_path

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-01-03 08:31:56 +00:00
shamoon
3b6ce16f1c Feature: Workflows (#5121) 2024-01-03 08:19:19 +00:00
Colin Hebert
46e6be319f Set Docker default healthcheck in the Dockerfile (#5199) 2024-01-02 09:08:10 -08:00
shamoon
e6d6f21d33 Fix: fix filename format remove none when part of directory (#5210) 2024-01-02 15:17:18 +00:00
dependabot[bot]
77b9b79a9e Chore(deps): Bump the actions group with 5 updates (#5203)
* Chore(deps): Bump the actions group with 5 updates

Bumps the actions group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [actions/setup-python](https://github.com/actions/setup-python) | `4` | `5` |
| [actions/upload-artifact](https://github.com/actions/upload-artifact) | `3` | `4` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `3` | `4` |
| [github/codeql-action](https://github.com/github/codeql-action) | `2` | `3` |
| [actions/stale](https://github.com/actions/stale) | `8` | `9` |


Updates `actions/setup-python` from 4 to 5
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

Updates `actions/upload-artifact` from 3 to 4
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

Updates `actions/download-artifact` from 3 to 4
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

Updates `github/codeql-action` from 2 to 3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

Updates `actions/stale` from 8 to 9
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v8...v9)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>

* See if this fixes broken frontend coverage actions/upload & download

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-01-02 03:29:48 +00:00
dependabot[bot]
f0016ad70c Chore(deps): Bump the frontend-angular-dependencies group (#5204)
Bumps the frontend-angular-dependencies group in /src-ui with 10 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `17.0.7` | `17.0.8` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `17.0.7` | `17.0.8` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `17.0.7` | `17.0.8` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `17.0.7` | `17.0.8` |
| [@angular/localize](https://github.com/angular/angular) | `17.0.7` | `17.0.8` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `17.0.7` | `17.0.8` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `17.0.7` | `17.0.8` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `17.0.7` | `17.0.8` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `17.0.7` | `17.0.8` |
| [@angular/cli](https://github.com/angular/angular-cli) | `17.0.7` | `17.0.8` |


Updates `@angular/common` from 17.0.7 to 17.0.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/17.0.8/packages/common)

Updates `@angular/compiler` from 17.0.7 to 17.0.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/17.0.8/packages/compiler)

Updates `@angular/core` from 17.0.7 to 17.0.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/17.0.8/packages/core)

Updates `@angular/forms` from 17.0.7 to 17.0.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/17.0.8/packages/forms)

Updates `@angular/localize` from 17.0.7 to 17.0.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/compare/17.0.7...17.0.8)

Updates `@angular/platform-browser` from 17.0.7 to 17.0.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/17.0.8/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 17.0.7 to 17.0.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/17.0.8/packages/platform-browser-dynamic)

Updates `@angular/router` from 17.0.7 to 17.0.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/17.0.8/packages/router)

Updates `@angular-devkit/build-angular` from 17.0.7 to 17.0.8
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/17.0.7...17.0.8)

Updates `@angular/cli` from 17.0.7 to 17.0.8
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/17.0.7...17.0.8)

---
updated-dependencies:
- dependency-name: "@angular/common"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/compiler"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/forms"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/localize"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/platform-browser"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/platform-browser-dynamic"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/router"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-devkit/build-angular"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/cli"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-01 21:47:37 +00:00
dependabot[bot]
054468ffc2 Chore(deps-dev): Bump @types/node from 20.10.4 to 20.10.6 in /src-ui (#5207)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.10.4 to 20.10.6.
- [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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-01 21:30:31 +00:00
dependabot[bot]
607c1282e3 Chore(deps-dev): Bump the frontend-eslint-dependencies group (#5205)
Bumps the frontend-eslint-dependencies group in /src-ui with 3 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [eslint](https://github.com/eslint/eslint).


Updates `@typescript-eslint/eslint-plugin` from 6.14.0 to 6.17.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.17.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 6.14.0 to 6.17.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.17.0/packages/parser)

Updates `eslint` from 8.55.0 to 8.56.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.55.0...v8.56.0)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-eslint-dependencies
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-eslint-dependencies
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-eslint-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-01 13:22:28 -08:00
Christoph Loy
3f4f4444f7 Document invalid options in rootless containers (#5122)
Specifying `PAPERLESS_OCR_LANGUAGES` in rootless containers is not possible since the extra languages are currently installed with apt which requires root.

Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com>
2024-01-01 08:00:05 -08:00
Antoine Libert
54372b5618 Fix: improve performance of listing result IDs (#5195) 2024-01-01 07:58:43 -08:00
Johannes Schneider
670a3f6c7f fix command for Docker version check (#5196) 2023-12-31 14:21:40 -08:00
shamoon
35a4d3fb54 Disable custom field remove button if user does not have permissions (#5194) 2023-12-31 10:55:25 -08:00
shamoon
fb81612ed1 Fix overlap of focus highlight on login screen (#5193) 2023-12-31 10:01:45 -08:00
shamoon
c5d622279c Fix symmetric doc links with target value None (#5187) 2023-12-31 07:56:26 -08:00
amo13
b93f655039 Create parent dirs of data/media/consume if necessary (#5176)
* Create parent dirs of data/media/consume if necessary
* long --parents instead of short -p
2023-12-30 19:25:34 +00:00
shamoon
428ffb4729 Fix: setting empty doc link with docs to be removed (#5174) 2023-12-30 07:43:29 -08:00
Trenton H
061f33fb05 Feature: Allow setting backend configuration settings via the UI (#5126)
* Saving some start on this

* At least partially working for the tesseract parser

* Problems with migration testing need to figure out

* Work around that error

* Fixes max m_pixels

* Moving the settings to main paperless application

* Starting some consumer options

* More fixes and work

* Fixes these last tests

* Fix max_length on OcrSettings.mode field

* Fix all fields on Common & Ocr settings serializers

* Umbrellla config view

* Revert "Umbrellla config view"

This reverts commit fbaf9f4be30f89afeb509099180158a3406416a5.

* Updates to use a single configuration object for all settings

* Squashed commit of the following:

commit 8a0a49dd57
Author: shamoon <4887959+shamoon@users.noreply.github.com>
Date:   Tue Dec 19 23:02:47 2023 -0800

    Fix formatting

commit 66b2d90c50
Author: shamoon <4887959+shamoon@users.noreply.github.com>
Date:   Tue Dec 19 22:36:35 2023 -0800

    Refactor frontend data models

commit 5723bd8dd8
Author: Adam Bogdał <adam@bogdal.pl>
Date:   Wed Dec 20 01:17:43 2023 +0100

    Fix: speed up admin panel for installs with a large number of documents (#5052)

commit 9b08ce1761
Author: shamoon <4887959+shamoon@users.noreply.github.com>
Date:   Tue Dec 19 15:18:51 2023 -0800

    Update PULL_REQUEST_TEMPLATE.md

commit a6248bec2d
Author: shamoon <4887959+shamoon@users.noreply.github.com>
Date:   Tue Dec 19 15:02:05 2023 -0800

    Chore: Update Angular to v17 (#4980)

commit b1f6f52486
Author: shamoon <4887959+shamoon@users.noreply.github.com>
Date:   Tue Dec 19 13:53:56 2023 -0800

    Fix: Dont allow null custom_fields property via API (#5063)

commit 638d9970fd
Author: shamoon <4887959+shamoon@users.noreply.github.com>
Date:   Tue Dec 19 13:43:50 2023 -0800

    Enhancement: symmetric document links (#4907)

commit 5e8de4c1da
Author: shamoon <4887959+shamoon@users.noreply.github.com>
Date:   Tue Dec 19 12:45:04 2023 -0800

    Enhancement: shared icon & shared by me filter (#4859)

commit 088bad9030
Author: Trenton H <797416+stumpylog@users.noreply.github.com>
Date:   Tue Dec 19 12:04:03 2023 -0800

    Bulk updates all the backend libraries (#5061)

* Saving some work on frontend config

* Very basic but dynamically-generated config form

* Saving work on slightly less ugly frontend config

* JSON validation for user_args field

* Fully dynamic config form

* Adds in some additional validators for a nicer error message

* Cleaning up the testing and coverage more

* Reverts unintentional change

* Adds documentation about the settings and the precedence

* Couple more commenting and style fixes

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2023-12-29 15:42:56 -08:00
shamoon
da058b915b Enhancement: improve validation of custom field values (#5166)
* Support all URI schemes

* Reworks custom field value validation to check and return a 400 error code in more cases and support more URL looking items, not just some basic schemes

* Fixes a spelling error in the message

---------

Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com>
2023-12-29 14:45:29 -08:00
shamoon
cf869b1356 Fix: type casting of db values for shared by me filter (#5155) 2023-12-29 17:19:45 +00:00
shamoon
05e294fc81 Fix URL validation of empty string 2023-12-29 01:26:24 -08:00
github-actions[bot]
bd904d9e6b Changelog v2.2.1 - GHA (#5147)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-12-28 23:57:12 -08:00
shamoon
a7ac719711 Bump version to 2.2.1 2023-12-28 15:47:00 -08:00
shamoon
370f6ddb3b Merge branch 'dev' into main 2023-12-28 15:46:43 -08:00
github-actions[bot]
3861e84f89 New Crowdin translations by GitHub Action (#5133)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-12-28 15:14:10 -08:00
shamoon
76001105b8 Fix saving doc links with no value (#5144) 2023-12-28 19:57:46 +00:00
github-actions[bot]
d47cc9460b [Documentation] Add v2.2.0 changelog (#5127)
* Changelog v2.2.0 - GHA

* Fix some mis-categorized PRs

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2023-12-28 10:56:46 -08:00
shamoon
fbeb03c377 Fix: allow multiple consumption templates to assign the same custom field (#5142) 2023-12-28 10:32:33 -08:00
shamoon
2b13fa4712 Fix: dropdown with different bindlabel (#5134) 2023-12-28 08:30:52 -08:00
shamoon
98255f8e14 Reset version string 2023-12-27 23:13:56 -08:00
shamoon
eaeeb6447f Bump version to 2.2.0 2023-12-27 23:11:52 -08:00
shamoon
e6a9868e86 Merge branch 'dev' 2023-12-27 23:09:50 -08:00
shamoon
f03592d17d Update frontend strings 2023-12-27 23:09:17 -08:00
github-actions[bot]
e5db44bc2b New Crowdin translations by GitHub Action (#4998)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-12-27 23:08:42 -08:00
Trenton H
ffad42615f Fixes the case where a mail attachment has no filename to use (#5117) 2023-12-27 17:38:22 -08:00
shamoon
5576a073a5 Fix: Disable auto-login for API token requests (#5094) 2023-12-26 22:22:41 +00:00
Evgenii
151d337f6c Fix: update ASN regex to support Unicode (#5099) 2023-12-25 16:33:30 -08:00
Florian Bachmann
74e89b0ee3 Fix: ensure CSRF-Token on Index view (#5082) 2023-12-23 07:56:56 -08:00
shamoon
945fb675e9 Fix: Stop auto-refresh logs / tasks after close (#5089) 2023-12-23 07:47:18 -08:00
Diego Gsponer
7570945cf0 Documentation: organize API endpoints (#5077) 2023-12-22 09:27:07 -08:00
shamoon
d94d80a8c8 Enhancement: Add tooltip for select dropdown items (#5070) 2023-12-21 08:44:34 -08:00
shamoon
8a0a49dd57 Fix formatting 2023-12-19 23:02:47 -08:00
shamoon
66b2d90c50 Refactor frontend data models 2023-12-19 22:36:35 -08:00
Adam Bogdał
5723bd8dd8 Fix: speed up admin panel for installs with a large number of documents (#5052) 2023-12-19 16:17:43 -08:00
shamoon
9b08ce1761 Update PULL_REQUEST_TEMPLATE.md 2023-12-19 15:18:51 -08:00
shamoon
a6248bec2d Chore: Update Angular to v17 (#4980) 2023-12-19 15:02:05 -08:00
shamoon
b1f6f52486 Fix: Dont allow null custom_fields property via API (#5063) 2023-12-19 21:53:56 +00:00
shamoon
638d9970fd Enhancement: symmetric document links (#4907) 2023-12-19 13:43:50 -08:00
shamoon
5e8de4c1da Enhancement: shared icon & shared by me filter (#4859) 2023-12-19 20:45:04 +00:00
Trenton H
088bad9030 Bulk updates all the backend libraries (#5061) 2023-12-19 12:04:03 -08:00
dependabot[bot]
cfa908243b Chore(deps): Bump the django group with 3 updates (#5046)
Bumps the django group with 3 updates: [django](https://github.com/django/django), [django-cors-headers](https://github.com/adamchainz/django-cors-headers) and [django-filter](https://github.com/carltongibson/django-filter).


Updates `django` from 4.2.7 to 4.2.8
- [Commits](https://github.com/django/django/compare/4.2.7...4.2.8)

Updates `django-cors-headers` from 4.3.0 to 4.3.1
- [Changelog](https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/adamchainz/django-cors-headers/compare/4.3.0...4.3.1)

Updates `django-filter` from 23.3 to 23.5
- [Release notes](https://github.com/carltongibson/django-filter/releases)
- [Changelog](https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst)
- [Commits](https://github.com/carltongibson/django-filter/compare/23.3...23.5)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: django
- dependency-name: django-cors-headers
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: django
- dependency-name: django-filter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: django
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 15:03:54 -08:00
dependabot[bot]
cabeb00632 Chore(deps): Bump the major-versions group with 1 update (#5047)
Bumps the major-versions group with 1 update: [ocrmypdf](https://github.com/ocrmypdf/OCRmyPDF).


Updates `ocrmypdf` from 15.4.3 to 15.4.4
- [Release notes](https://github.com/ocrmypdf/OCRmyPDF/releases)
- [Changelog](https://github.com/ocrmypdf/OCRmyPDF/blob/main/docs/release_notes.rst)
- [Commits](https://github.com/ocrmypdf/OCRmyPDF/compare/v15.4.3...v15.4.4)

---
updated-dependencies:
- dependency-name: ocrmypdf
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: major-versions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 14:11:58 -08:00
dependabot[bot]
2426c01978 Chore(deps): Bump the small-changes group with 6 updates (#5048)
Bumps the small-changes group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [dateparser](https://github.com/scrapinghub/dateparser) | `1.1.8` | `1.2.0` |
| [concurrent-log-handler](https://github.com/Preston-Landers/concurrent-log-handler) | `0.9.24` | `0.9.25` |
| [mysqlclient](https://github.com/PyMySQL/mysqlclient) | `2.2.0` | `2.2.1` |
| [python-gnupg](https://github.com/vsajip/python-gnupg) | `0.5.1` | `0.5.2` |
| [python-ipware](https://github.com/un33k/python-ipware) | `2.0.0` | `2.0.1` |
| [zxing-cpp](https://github.com/zxing-cpp/zxing-cpp) | `2.1.0` | `2.2.0` |


Updates `dateparser` from 1.1.8 to 1.2.0
- [Release notes](https://github.com/scrapinghub/dateparser/releases)
- [Changelog](https://github.com/scrapinghub/dateparser/blob/master/HISTORY.rst)
- [Commits](https://github.com/scrapinghub/dateparser/compare/v1.1.8...v1.2.0)

Updates `concurrent-log-handler` from 0.9.24 to 0.9.25
- [Release notes](https://github.com/Preston-Landers/concurrent-log-handler/releases)
- [Changelog](https://github.com/Preston-Landers/concurrent-log-handler/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Preston-Landers/concurrent-log-handler/compare/0.9.24...0.9.25)

Updates `mysqlclient` from 2.2.0 to 2.2.1
- [Release notes](https://github.com/PyMySQL/mysqlclient/releases)
- [Changelog](https://github.com/PyMySQL/mysqlclient/blob/main/HISTORY.rst)
- [Commits](https://github.com/PyMySQL/mysqlclient/compare/v2.2.0...v2.2.1)

Updates `python-gnupg` from 0.5.1 to 0.5.2
- [Release notes](https://github.com/vsajip/python-gnupg/releases)
- [Changelog](https://github.com/vsajip/python-gnupg/blob/master/release)
- [Commits](https://github.com/vsajip/python-gnupg/compare/0.5.1...0.5.2)

Updates `python-ipware` from 2.0.0 to 2.0.1
- [Changelog](https://github.com/un33k/python-ipware/blob/main/CHANGELOG.md)
- [Commits](https://github.com/un33k/python-ipware/compare/v2.0.0...v2.0.1)

Updates `zxing-cpp` from 2.1.0 to 2.2.0
- [Release notes](https://github.com/zxing-cpp/zxing-cpp/releases)
- [Commits](https://github.com/zxing-cpp/zxing-cpp/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: dateparser
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: concurrent-log-handler
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: mysqlclient
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: python-gnupg
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: python-ipware
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: zxing-cpp
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-18 13:37:46 -08:00
Trenton H
80f2bee6e8 Filters out the not really useful warning from Django about async iterators (#5041) 2023-12-18 18:24:29 +00:00
Trenton H
7ec51758eb Fixes the display of the audit log configuration looks 2023-12-18 09:45:05 -08:00
shamoon
9e93ae952a Enhancement: Improved popup preview, respect embedded viewer, error handling (#4947) 2023-12-18 16:41:51 +00:00
Trenton H
829836ddf6 Updates Ghostscript to 10.02.1 and qpdf to 11.6.4 (#5040) 2023-12-18 07:33:00 -08:00
TTT7275
1197a048bc Enhancement: Add {original_filename}, {added_time} to title placeholders (#4972)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2023-12-17 21:38:24 -08:00
Trenton H
7289c4ea56 Feature: Allow deletion of documents via the fuzzy matching command (#4957)
* Adds new flag allowing deletion of one of a document pair which is over the match ratio

* Documents the new command option
2023-12-17 18:37:38 -08:00
shamoon
55dadf0b00 Fix shortcut keys prevented in date fields (#5009) 2023-12-18 01:07:09 +00:00
shamoon
341815cc03 Enhancement: document link field fixes (#5020)
* Implement more efficient getFew for document retrieval

* Filter out parent document ID & already-selected documents

* Clip very long document titles
2023-12-18 00:39:45 +00:00
shamoon
d22b27afe7 Enhancement: above and below doc detail save buttons (#5008)
* Show doc detail nav buttons above & below fields

* Fix tests for additional button nav

* Use flexbox to fix tab order but retain visual order

* Update screenshots
2023-12-18 00:11:12 +00:00
shamoon
6ee2d023d5 Fix password change detection on profile edit (#5028) 2023-12-17 13:36:17 -08:00
shamoon
5010cc6f15 Update bug-report.yml 2023-12-17 10:26:07 -08:00
github-actions[bot]
383cced158 Changelog v2.1.3 - GHA (#4995)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-12-15 18:29:49 -08:00
Trenton Holmes
a3d6967192 Resets version string 2023-12-15 17:06:06 -08:00
Trenton Holmes
c5881f75c9 Bumps version to 2.1.3 2023-12-15 17:05:41 -08:00
Trenton Holmes
c4b7429e99 Merge remote-tracking branch 'origin/dev' 2023-12-15 17:05:16 -08:00
github-actions[bot]
b1eced3612 New Crowdin translations by GitHub Action (#4967)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-12-15 17:03:38 -08:00
Adam Bogdał
9d5b07537d Reduce number of db queries (#4990) 2023-12-15 11:36:25 -08:00
Trenton H
122e4141b0 Fix: Document metadata is lost during barcode splitting (#4982)
* Fixes barcode splitting dropping metadata that might be needed for the round 2
2023-12-15 09:17:25 -08:00
Trenton H
be2de4f15d Fixes export of custom field instances during a split manifest export (#4984) 2023-12-14 19:23:39 -08:00
Trenton H
92a920021d Apply user arguments even in the case of the safe fallback to forcing OCR (#4981) 2023-12-14 11:20:47 -08:00
shamoon
72000cac36 Fix: show errors for select dropdowns (#4979) 2023-12-14 10:05:36 -08:00
Adam Bogdał
4510902677 Fix: Don't attempt to parse none objects during date searching 2023-12-14 07:39:49 -08:00
github-actions[bot]
c2b9d2fa7b [Documentation] Add v2.1.2 changelog (#4960)
* Changelog v2.1.2 - GHA

* Fix mis-categorized PR

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2023-12-12 18:15:37 -08:00
Trenton Holmes
cd38c39908 Resets to -dev version string 2023-12-12 17:42:20 -08:00
Trenton Holmes
9016a1e6df Bumps version to 2.1.2 2023-12-12 17:41:26 -08:00
github-actions[bot]
627254d5a7 New Crowdin translations by GitHub Action (#4892)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-12-12 17:29:33 -08:00
shamoon
ff31558252 Fix: Sort consumption templates by order by default (#4956) 2023-12-12 16:27:26 +00:00
Trenton H
9454978264 Updates gotenberg-client, including workaround for Gotenberg handling of non-latin filenames (#4944) 2023-12-12 15:05:33 +00:00
shamoon
e2d25a7a09 Chore: reorganize api tests (#4935)
* Move permissions-related API tests

* Move bulk-edit-related API tests

* Move bulk-download-related API tests

* Move uisettings-related API tests

* Move remoteversion-related API tests

* Move tasks API tests

* Move object-related API tests

* Move consumption-template-related API tests

* Rename pared-down documents API test file

Co-Authored-By: Trenton H <797416+stumpylog@users.noreply.github.com>
2023-12-12 04:08:51 +00:00
dependabot[bot]
85f824f032 Chore(deps-dev): Bump the small-changes group with 2 updates (#4942)
Bumps the small-changes group with 2 updates: [pre-commit](https://github.com/pre-commit/pre-commit) and [mkdocs-glightbox](https://github.com/Blueswen/mkdocs-glightbox).


Updates `pre-commit` from 3.5.0 to 3.6.0
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.5.0...v3.6.0)

Updates `mkdocs-glightbox` from 0.3.4 to 0.3.5
- [Release notes](https://github.com/Blueswen/mkdocs-glightbox/releases)
- [Changelog](https://github.com/blueswen/mkdocs-glightbox/blob/main/CHANGELOG)
- [Commits](https://github.com/Blueswen/mkdocs-glightbox/compare/v0.3.4...v0.3.5)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: mkdocs-glightbox
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: small-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-11 19:40:33 -08:00
shamoon
1a48910e6b Fix: allow text copy in pngx pdf viewer (#4938) 2023-12-12 01:06:30 +00:00
dependabot[bot]
bffd5829d0 Chore(deps-dev): Bump the development group with 1 update (#4939)
Bumps the development group with 1 update: [mkdocs-material](https://github.com/squidfunk/mkdocs-material).

- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.4.14...9.5.2)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-11 15:24:01 -08:00
Trenton H
7e12bd1bef Fix: Removes the FieldParser plugin from autocomplete searches (#4934) 2023-12-11 10:21:58 -08:00
Adam Bogdał
af0817ab74 Fix: Convert search dates to UTC in advanced search (#4891)
* Index documents using local timezone

* Add local date parser
2023-12-11 09:32:43 -08:00
Trenton H
fbf1a051a2 Use the attachment filename so downstream template matching works against it (#4931) 2023-12-11 09:08:42 -08:00
shamoon
7ecf7f704a Fix: frontend handle autocomplete failure gracefully (#4903) 2023-12-11 15:41:40 +00:00
Tom Hoover
7b7a74d821 Fix: Correct spelling of 'initialization' in install script (#4928) 2023-12-11 15:10:42 +00:00
shamoon
e4acc33519 Add -dev to version string 2023-12-07 22:20:48 -08:00
github-actions[bot]
2fd141d914 [Documentation] Add v2.1.1 changelog (#4886)
* Changelog v2.1.1 - GHA

* Fix incorrectly categorized PR

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2023-12-07 22:15:29 -08:00
shamoon
aad814d342 v2.1.1 2023-12-07 21:42:02 -08:00
github-actions[bot]
7e21aaec17 New Crowdin translations by GitHub Action (#4845)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-12-07 21:39:51 -08:00
shamoon
6d5fdfe2e2 Fix: alignment of share links archive toggle 2023-12-07 14:03:57 -08:00
shamoon
5942cd6cd2 Fix: disable toggle for share link creation without archive version, fix auto-copy in Safari (#4885)
* Fix: disable share link archive switch if archive version doesnt exist

* Fix: Add brief timeout before copy after share link creation for Safari, only show if succeeded

* Update messages.xlf
2023-12-07 13:48:33 -08:00
shamoon
5cd17e71e2 Fix: storage paths link incorrect in dashboard widget (#4878) 2023-12-07 15:23:22 +00:00
shamoon
2f2ecaa61e Fix: respect baseURI for pdfjs worker URL (#4865) 2023-12-07 07:13:00 -08:00
Trenton Holmes
aa858a35e2 Merge remote-tracking branch 'origin/main' into dev 2023-12-06 19:23:12 -08:00
Trenton Holmes
d658150d42 Sets the Git information before pushing the documentation build 2023-12-06 19:20:08 -08:00
Trenton H
c312149b35 Simplifies how the documentation site is deployed (#4858) 2023-12-06 19:06:50 -08:00
Trenton H
18a9a3df12 Allow users to configure the DEFAULT_FROM_EMAIL but default to the EMAIL_HOST_USER if not set (#4867) 2023-12-06 22:52:44 +00:00
shamoon
0cdd3581c9 Fix: dont show move icon for file tasks badge (#4860) 2023-12-06 14:26:04 -08:00
github-actions[bot]
cae79b811f [Documentation] Add v2.1.0 changelog (#4846)
* Changelog v2.1.0 - GHA
* Remove a fix from the features list

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com>
2023-12-06 07:38:14 -08:00
Trenton Holmes
6d953babcb Resets version to -dev 2023-12-05 19:05:02 -08:00
Trenton Holmes
975e5f3fd0 Sets version to v2.1.0 2023-12-05 19:03:53 -08:00
Trenton Holmes
6cfe92bed1 Merge remote-tracking branch 'origin/dev' 2023-12-05 18:37:39 -08:00
shamoon
a51c0850c8 Fix: custom fields api format not visible in docs 2023-12-05 13:48:42 -08:00
shamoon
03415456bf Fix consumption template empty field checking 2023-12-05 09:08:43 -08:00
shamoon
0309a0fae1 Fix: invalid height attr on logo svg 2023-12-05 09:08:42 -08:00
shamoon
b48910bb94 Fix: prevent dropdown open when clicking document links 2023-12-05 08:56:13 -08:00
github-actions[bot]
9a89786dd3 New Crowdin translations by GitHub Action (#4840)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2023-12-05 08:28:12 -08:00
shamoon
15a5261189 Update translation strings 2023-12-05 08:26:28 -08:00
shamoon
f616da3b85 Update settings.component.ts 2023-12-05 08:23:37 -08:00
github-actions[bot]
0e9cf016ec New Crowdin translations by GitHub Action (#4760) 2023-12-05 16:23:13 +00:00
shamoon
4481f12e32 Enhancement: implement document link custom field (#4799) 2023-12-05 08:16:56 -08:00
dependabot[bot]
66efaedcbb Bump the development group with 6 updates (#4838)
Bumps the development group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [ruff](https://github.com/astral-sh/ruff) | `0.1.5` | `0.1.7` |
| [pytest-httpx](https://github.com/Colin-b/pytest_httpx) | `0.26.0` | `0.27.0` |
| [pytest-env](https://github.com/pytest-dev/pytest-env) | `1.1.1` | `1.1.3` |
| [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) | `3.4.0` | `3.5.0` |
| [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) | `12.0` | `13.0` |
| [mkdocs-material](https://github.com/squidfunk/mkdocs-material) | `9.4.8` | `9.4.14` |


Updates `ruff` from 0.1.5 to 0.1.7
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.1.5...v0.1.7)

Updates `pytest-httpx` from 0.26.0 to 0.27.0
- [Release notes](https://github.com/Colin-b/pytest_httpx/releases)
- [Changelog](https://github.com/Colin-b/pytest_httpx/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/Colin-b/pytest_httpx/compare/v0.26.0...v0.27.0)

Updates `pytest-env` from 1.1.1 to 1.1.3
- [Release notes](https://github.com/pytest-dev/pytest-env/releases)
- [Commits](https://github.com/pytest-dev/pytest-env/compare/1.1.1...1.1.3)

Updates `pytest-xdist` from 3.4.0 to 3.5.0
- [Release notes](https://github.com/pytest-dev/pytest-xdist/releases)
- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v3.4.0...v3.5.0)

Updates `pytest-rerunfailures` from 12.0 to 13.0
- [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst)
- [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/12.0...13.0)

Updates `mkdocs-material` from 9.4.8 to 9.4.14
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.4.8...9.4.14)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: pytest-httpx
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: pytest-env
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: pytest-xdist
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: development
- dependency-name: pytest-rerunfailures
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: development
- dependency-name: mkdocs-material
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-05 15:51:37 +00:00
Trenton H
771c1fab92 Chore: Raise Gotenberg container version (#4815)
* Updates the Gotenberg version to use 7.10 and gotenberg-client to match
* Fixes a long standing bug in this test where a whole page was missing from the expected
2023-12-05 15:36:25 +00:00
shamoon
8d6e7ed477 Fix: always reset theme classes on update appearance settings 2023-12-05 00:08:03 -08:00
shamoon
5d80511b9b Fix: welcome widget text color (#4829) 2023-12-05 00:00:20 -08:00
shamoon
90f90dc9b4 Fix: export consumption templates & custom fields in exporter (#4825) 2023-12-04 21:33:15 -08:00
shamoon
a58e8498aa Merge branch 'main' into dev 2023-12-04 21:15:51 -08:00
Trenton H
ca355d5855 Adds additional warnings during an import if it might fail due to reasons (#4814) 2023-12-05 03:39:59 +00:00
shamoon
826322b610 Feature: pngx PDF viewer with updated pdfjs (#4679) 2023-12-04 17:17:40 -08:00
shamoon
80ff5677ea Fix: bulk edit object permissions should use permissions object (#4797) 2023-12-04 06:40:17 -08:00
Trenton Holmes
62c417cd51 Fixes the 0023 migration to include the new help text and verbose name 2023-12-03 19:09:02 -08:00
shamoon
f27f25aa03 Enhancement: support assigning custom fields via consumption templates (#4727) 2023-12-03 15:35:30 -08:00
shamoon
285a4b5aef Fix: empty strings for consumption template fields should be treated as None (#4762) 2023-12-03 12:57:43 -08:00
shamoon
47a2ded30d Fix: use default permissions for objects created via dropdown (#4778) 2023-12-03 00:52:48 +00:00
Trenton H
5b502b1e1a Use the original image file for the checksum, not the maybe alpha removed version (#4781) 2023-12-02 16:18:06 -08:00
shamoon
aff56077a8 Feature: update user profile (#4678) 2023-12-02 08:26:42 -08:00
Trenton H
6e371ac5ac Enhancement: Allow excluding mail attachments by name (#4691)
* Adds new filtering to exclude attachments from processing

* Frontend use include / exclude mail rule filename filters

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2023-12-02 08:26:19 -08:00
shamoon
1b69b89d2d Chore: Remove unneeded .env entry, revert crowdin action rm, reduce frequency 2023-12-02 08:24:17 -08:00
shamoon
5a20c8e512 Fix version checker GitHub api url (#4773) 2023-12-02 15:56:56 +00:00
shamoon
4ca1503beb Fix: Limit global drag-drop to events with files (#4767) 2023-12-02 07:47:57 -08:00
shamoon
567a7eb7f3 Fix title-placeholders link 2023-12-01 22:21:31 -08:00
shamoon
20f27fe32f Remove the pngx .env file for crowdin action 2023-12-01 18:06:38 -08:00
dependabot[bot]
1a50d6bb86 Bump the actions group with 2 updates (#4745)
Bumps the actions group with 2 updates: [actions/github-script](https://github.com/actions/github-script) and [stumpylog/image-cleaner-action](https://github.com/stumpylog/image-cleaner-action).


Updates `actions/github-script` from 6 to 7
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v6...v7)

Updates `stumpylog/image-cleaner-action` from 0.3.0 to 0.4.0
- [Release notes](https://github.com/stumpylog/image-cleaner-action/releases)
- [Changelog](https://github.com/stumpylog/image-cleaner-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stumpylog/image-cleaner-action/compare/v0.3.0...v0.4.0)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: stumpylog/image-cleaner-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-02 02:04:16 +00:00
shamoon
0b16c2db03 Update crowdin action triggers 2023-12-01 17:54:00 -08:00
shamoon
76ac888386 Chore: Implement crowdin GHA (#4706) 2023-12-01 17:44:33 -08:00
Paperless-ngx Bot [bot]
7cfa05d7f2 New Crowdin updates (#4729) 2023-12-01 17:44:05 -08:00
dependabot[bot]
e3496d0485 Bump the frontend-eslint-dependencies group in /src-ui with 3 updates (#4756)
Bumps the frontend-eslint-dependencies group in /src-ui with 3 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [eslint](https://github.com/eslint/eslint).


Updates `@typescript-eslint/eslint-plugin` from 6.9.1 to 6.13.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.13.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 6.9.1 to 6.13.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.13.1/packages/parser)

Updates `eslint` from 8.52.0 to 8.55.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.52.0...v8.55.0)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-eslint-dependencies
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-eslint-dependencies
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-eslint-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-02 01:07:13 +00:00
dependabot[bot]
d2c33c0074 Bump the frontend-jest-dependencies group in /src-ui with 2 updates (#4744)
Bumps the frontend-jest-dependencies group in /src-ui with 2 updates: [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) and [jest-preset-angular](https://github.com/thymikee/jest-preset-angular).


Updates `@types/jest` from 29.5.7 to 29.5.10
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Updates `jest-preset-angular` from 13.1.2 to 13.1.4
- [Release notes](https://github.com/thymikee/jest-preset-angular/releases)
- [Changelog](https://github.com/thymikee/jest-preset-angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/thymikee/jest-preset-angular/compare/v13.1.2...v13.1.4)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-jest-dependencies
- dependency-name: jest-preset-angular
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-jest-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-02 00:58:27 +00:00
dependabot[bot]
9c5caecafa Bump @playwright/test from 1.39.0 to 1.40.1 in /src-ui (#4749)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.39.0 to 1.40.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.39.0...v1.40.1)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-02 00:49:46 +00:00
dependabot[bot]
64651d5a84 Bump wait-on from 7.0.1 to 7.2.0 in /src-ui (#4747)
Bumps [wait-on](https://github.com/jeffbski/wait-on) from 7.0.1 to 7.2.0.
- [Release notes](https://github.com/jeffbski/wait-on/releases)
- [Commits](https://github.com/jeffbski/wait-on/compare/v7.0.1...v7.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-02 00:38:35 +00:00
dependabot[bot]
4493236879 Bump @types/node from 20.8.10 to 20.10.2 in /src-ui (#4748)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.8.10 to 20.10.2.
- [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-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 16:28:50 -08:00
omahs
ce643942ea Documentation: Fix typos (#4737) 2023-12-01 14:55:03 +00:00
shamoon
46d216b02f Remove project actions in favor of GH workflows 2023-11-30 23:14:59 -08:00
shamoon
133d43ae30 Enhancement: auto-refresh logs & tasks (#4680) 2023-12-01 03:08:03 +00:00
Trenton H
27155cb7e3 Fixes the image cleaner not running for the registry cache (#4732) 2023-11-30 17:12:14 -08:00
github-actions[bot]
b55c413774 Changelog v2.0.1 - GHA (#4722)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-30 08:16:17 -08:00
Trenton Holmes
69be86e16c Resets version string to 2.0.1-dev 2023-11-30 07:11:46 -08:00
Trenton Holmes
65f6b0881e Bumps version to 2.0.1 2023-11-30 07:11:00 -08:00
Trenton Holmes
c2bede40c7 Merge remote-tracking branch 'origin/dev' 2023-11-30 07:10:29 -08:00
Paperless-ngx Bot [bot]
33c2398de9 New Crowdin updates (#4695) 2023-11-30 07:08:10 -08:00
Trenton H
64cfc43891 Inreases the length to 5, allowing for commas as well as values (#4719) 2023-11-30 07:00:54 -08:00
shamoon
6575c69409 Fix: restore docs search 2023-11-29 21:56:05 -08:00
Trenton H
e3f4e0b775 Adds new setting to control color conversions (#4709) 2023-11-29 12:18:44 -08:00
github-actions[bot]
5be89bfda5 Changelog v2.0.0 - GHA (#4693)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-29 11:50:30 -08:00
Trenton H
e1b573adeb Fix: Add a warning about a low image DPI which may cause OCR to fail (#4708) 2023-11-29 11:28:27 -08:00
shamoon
0913c7aa9e Fix: share links for URLs containing 'api' incorrect in dropdown (#4701) 2023-11-29 11:10:55 -08:00
shamoon
5297626816 Update environment.prod.ts 2023-11-28 20:05:53 -08:00
shamoon
c075642d78 Add docs requirements file for lightbox 2023-11-28 20:00:04 -08:00
shamoon
253f1a44c1 Bumps the version to 2.0.0 2023-11-28 19:43:30 -08:00
shamoon
26837c8871 Merge pull request #4581 from paperless-ngx/l10n_dev
New Crowdin updates
2023-11-28 19:37:23 -08:00
shamoon
c732d31edd Merge pull request #4578 from paperless-ngx/beta
[Beta] Paperless-ngx v2.0.0 Beta
2023-11-28 19:35:42 -08:00
Trenton H
c2595f28fb Targeted update of some Python dependencies (#4688) 2023-11-28 19:10:27 -08:00
Paperless-ngx Bot [bot]
58656a63cf New translations django.po (Dutch)
[ci skip]
2023-11-28 10:26:34 -08:00
Paperless-ngx Bot [bot]
f4ce178cfa New translations django.po (Serbian (Latin))
[ci skip]
2023-11-26 12:35:01 -08:00
Paperless-ngx Bot [bot]
2c1cfc64d5 New translations messages.xlf (Serbian (Latin))
[ci skip]
2023-11-26 12:35:00 -08:00
Paperless-ngx Bot [bot]
4b84d93cee New translations messages.xlf (Vietnamese)
[ci skip]
2023-11-25 20:38:49 -08:00
Paperless-ngx Bot [bot]
0bfa347595 New translations messages.xlf (Vietnamese)
[ci skip]
2023-11-25 19:06:52 -08:00
Paperless-ngx Bot [bot]
c7a07b59fd New translations messages.xlf (Vietnamese)
[ci skip]
2023-11-25 08:29:51 -08:00
Paperless-ngx Bot [bot]
c7c737d8c9 New translations messages.xlf (Vietnamese)
[ci skip]
2023-11-25 07:24:26 -08:00
Paperless-ngx Bot [bot]
1126e668d1 New translations django.po (Slovenian)
[ci skip]
2023-11-25 06:26:03 -08:00
Paperless-ngx Bot [bot]
b71a94ebca New translations django.po (Slovenian)
[ci skip]
2023-11-25 05:20:20 -08:00
Paperless-ngx Bot [bot]
815137af19 New translations messages.xlf (Slovenian)
[ci skip]
2023-11-25 05:20:19 -08:00
Paperless-ngx Bot [bot]
3c4fbfec54 New translations messages.xlf (Slovenian)
[ci skip]
2023-11-25 04:13:08 -08:00
Paperless-ngx Bot [bot]
b3a8eb7e06 New translations messages.xlf (Bulgarian)
[ci skip]
2023-11-25 03:07:12 -08:00
shamoon
1e5c64ff73 Fix: small card borders, welcome widget colors, update screenshots 2023-11-24 08:47:35 -08:00
Josiah Outram Halstead
a320bfa425 Fix: Add missing spaces to document retagger help string (#4674) 2023-11-24 07:25:38 -08:00
Armin Gruner
1c4dfc3c6e Fix: Typo invalidates precondition for doctype, resulting in Exception (#4668)
Co-authored-by: Armin Gruner <agmuc.de>
2023-11-23 11:19:11 -08:00
shamoon
92ee4d33c3 Fix: dark mode should be consistent in dark mode vs system settings (#4669) 2023-11-23 10:18:03 -08:00
Paperless-ngx Bot [bot]
01c2f01d3f New translations messages.xlf (French)
[ci skip]
2023-11-23 03:20:50 -08:00
shamoon
b83e6a5d5b Fix dashboard saved view mobile width in v.2.0.0-beta.rc1 (#4660) 2023-11-22 12:37:17 -08:00
Paperless-ngx Bot [bot]
16281b38e0 New translations messages.xlf (Dutch)
[ci skip]
2023-11-22 11:19:50 -08:00
Paperless-ngx Bot [bot]
ca11e116b6 New translations messages.xlf (Dutch)
[ci skip]
2023-11-22 10:10:21 -08:00
shamoon
1f32e4a642 Fix: inconsistent border colors in input groups 2023-11-21 21:59:03 -08:00
Paperless-ngx Bot [bot]
4c01089de0 New translations messages.xlf (Slovenian)
[ci skip]
2023-11-21 14:05:08 -08:00
shamoon
47a416dd9b Fix: consistent bottom margin for form field labels 2023-11-21 13:53:11 -08:00
Paperless-ngx Bot [bot]
1ab1bbdd70 New translations messages.xlf (Slovenian)
[ci skip]
2023-11-21 12:14:03 -08:00
Paperless-ngx Bot [bot]
1e05cb168c New translations messages.xlf (Slovenian)
[ci skip]
2023-11-21 10:40:33 -08:00
Paperless-ngx Bot [bot]
d0383c1edf New translations messages.xlf (Turkish)
[ci skip]
2023-11-21 05:08:49 -08:00
Trenton H
c8ee35692c Documentation: Update documentation to refer only to Docker Compose v2 command (#4650)
* Replaces references to docker-compose (the v1 executable) with docker compose (the v2 plugin) as well as fixing up some referenes between the tool vs the command

* Update docs/setup.md

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>

* Replaces references to docker-compose (the v1 executable) with docker compose (the v2 plugin) as well as fixing up some referenes between the tool vs the command

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2023-11-20 20:14:33 -08:00
Paperless-ngx Bot [bot]
3e062a8021 New translations django.po (Vietnamese)
[ci skip]
2023-11-19 19:08:47 -08:00
shamoon
0eb17e7102 Fix: Miscellaneous visual fixes in v2.0.0-beta.rc1 2 (#4635)
* Fix filter editor sticky top spacing

* Fix management list long title text alignment

* Fix paperless green using incorrect lightness value
2023-11-19 16:13:38 -08:00
frozenbrain
a14796cf90 Fix: Delay consumption after MODIFY inotify events (#4626) 2023-11-19 14:18:43 -08:00
Paperless-ngx Bot [bot]
84b1c1ce6c New translations django.po (Vietnamese)
[ci skip]
2023-11-19 09:25:39 -08:00
Paperless-ngx Bot [bot]
27e0b65c2d New translations messages.xlf (Vietnamese)
[ci skip]
2023-11-19 07:53:20 -08:00
Paperless-ngx Bot [bot]
ef35576174 New translations django.po (Vietnamese)
[ci skip]
2023-11-19 07:53:19 -08:00
tooomm
804faf726b Documentation: fix typo, add to features list (#4624)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2023-11-18 07:26:36 -08:00
Trenton H
a94e5d2e47 Only delete the ContentTypes and Permissions once, not the loop (#4623) 2023-11-17 12:24:49 -08:00
Paperless-ngx Bot [bot]
54d817d656 New translations messages.xlf (French)
[ci skip]
2023-11-17 10:03:22 -08:00
shamoon
00aa6c1f6a Fix: Always retrieve sidebar views from service (#4619) 2023-11-17 09:41:00 -08:00
shamoon
ed34815393 Fix: Prevent text wrap on consumption template label (#4616) 2023-11-17 00:40:40 -08:00
Paperless-ngx Bot [bot]
ceb15716c3 New translations messages.xlf (Catalan)
[ci skip]
2023-11-17 00:11:11 -08:00
Paperless-ngx Bot [bot]
bc97f540de New translations messages.xlf (French)
[ci skip]
2023-11-17 00:11:09 -08:00
shamoon
4ae67c79e0 Merge branch 'main' into beta 2023-11-16 23:13:42 -08:00
shamoon
be857e989b Fix: increase width of labels in default perms settings (#4612) 2023-11-16 22:59:24 -08:00
Paperless-ngx Bot [bot]
232402197c New translations django.po (French)
[ci skip]
2023-11-16 15:23:24 -08:00
Paperless-ngx Bot [bot]
12cbe84166 New translations messages.xlf (French)
[ci skip]
2023-11-16 15:23:23 -08:00
shamoon
b7b0be141c Documentation: Add note that trash dir must exist (#4608) 2023-11-16 14:29:39 -08:00
Paperless-ngx Bot [bot]
8f9f4d9b71 New translations messages.xlf (German)
[ci skip]
2023-11-16 10:31:50 -08:00
Paperless-ngx Bot [bot]
b97b9b7a28 New translations django.po (French)
[ci skip]
2023-11-16 05:09:32 -08:00
Paperless-ngx Bot [bot]
cf23732da3 New translations messages.xlf (French)
[ci skip]
2023-11-16 05:09:30 -08:00
Paperless-ngx Bot [bot]
4e6b1d54e8 New translations messages.xlf (German)
[ci skip]
2023-11-15 22:45:15 -08:00
shamoon
fec8f90b78 FIx note deletion (#4602) 2023-11-15 20:25:17 -08:00
shamoon
bedd4dafc4 Fix: increase sidebar xxxl breakpoint to 2400px 2023-11-15 20:06:33 -08:00
quantenProjects
0d92648d62 Documentation: Structure backup section more clearly (#4559)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2023-11-16 00:26:52 +00:00
Trenton H
07989bc2fa Handles the case where other might have a None field as well (#4598) 2023-11-15 16:23:38 -08:00
Paperless-ngx Bot [bot]
09ff2cb9a3 New translations messages.xlf (Catalan)
[ci skip]
2023-11-15 02:22:56 -08:00
Paperless-ngx Bot [bot]
6aa2e60037 New translations messages.xlf (Serbian (Latin))
[ci skip]
2023-11-15 02:22:54 -08:00
Paperless-ngx Bot [bot]
9f3ef532b8 New translations messages.xlf (Luxembourgish)
[ci skip]
2023-11-15 02:22:53 -08:00
Paperless-ngx Bot [bot]
654621b438 New translations messages.xlf (Latvian)
[ci skip]
2023-11-15 02:22:51 -08:00
Paperless-ngx Bot [bot]
f8cfa9e02d New translations messages.xlf (Croatian)
[ci skip]
2023-11-15 02:22:49 -08:00
Paperless-ngx Bot [bot]
7476aa5897 New translations messages.xlf (Thai)
[ci skip]
2023-11-15 02:22:48 -08:00
Paperless-ngx Bot [bot]
1d00a59706 New translations messages.xlf (Indonesian)
[ci skip]
2023-11-15 02:22:46 -08:00
Paperless-ngx Bot [bot]
82d4e5e456 New translations messages.xlf (Portuguese, Brazilian)
[ci skip]
2023-11-15 02:22:45 -08:00
Paperless-ngx Bot [bot]
07b6a36aab New translations messages.xlf (Chinese Traditional)
[ci skip]
2023-11-15 02:22:43 -08:00
Paperless-ngx Bot [bot]
0ceb6cc143 New translations messages.xlf (Chinese Simplified)
[ci skip]
2023-11-15 02:22:42 -08:00
Paperless-ngx Bot [bot]
864a155ee8 New translations messages.xlf (Ukrainian)
[ci skip]
2023-11-15 02:22:40 -08:00
Paperless-ngx Bot [bot]
15e2e575ee New translations messages.xlf (Turkish)
[ci skip]
2023-11-15 02:22:38 -08:00
Paperless-ngx Bot [bot]
253c524131 New translations messages.xlf (Swedish)
[ci skip]
2023-11-15 02:22:37 -08:00
Paperless-ngx Bot [bot]
48c02b8554 New translations messages.xlf (Slovenian)
[ci skip]
2023-11-15 02:22:35 -08:00
Paperless-ngx Bot [bot]
d8fef22c9a New translations messages.xlf (Slovak)
[ci skip]
2023-11-15 02:22:33 -08:00
Paperless-ngx Bot [bot]
d82103e02d New translations messages.xlf (Russian)
[ci skip]
2023-11-15 02:22:32 -08:00
Paperless-ngx Bot [bot]
9e5b22931e New translations messages.xlf (Portuguese)
[ci skip]
2023-11-15 02:22:30 -08:00
Paperless-ngx Bot [bot]
30cd361edb New translations messages.xlf (Polish)
[ci skip]
2023-11-15 02:22:28 -08:00
Paperless-ngx Bot [bot]
48da0071bd New translations messages.xlf (Norwegian)
[ci skip]
2023-11-15 02:22:27 -08:00
Paperless-ngx Bot [bot]
32037e3645 New translations messages.xlf (Dutch)
[ci skip]
2023-11-15 02:22:12 -08:00
Paperless-ngx Bot [bot]
4279038a62 New translations messages.xlf (Korean)
[ci skip]
2023-11-15 02:22:10 -08:00
Paperless-ngx Bot [bot]
c8afe7503b New translations messages.xlf (Italian)
[ci skip]
2023-11-15 02:22:08 -08:00
Paperless-ngx Bot [bot]
4dfae98250 New translations messages.xlf (Hungarian)
[ci skip]
2023-11-15 02:22:07 -08:00
Paperless-ngx Bot [bot]
b44dca49f5 New translations messages.xlf (Hebrew)
[ci skip]
2023-11-15 02:22:05 -08:00
Paperless-ngx Bot [bot]
3d5892a841 New translations messages.xlf (Finnish)
[ci skip]
2023-11-15 02:22:03 -08:00
Paperless-ngx Bot [bot]
365e06891a New translations messages.xlf (Greek)
[ci skip]
2023-11-15 02:22:01 -08:00
Paperless-ngx Bot [bot]
0f436481b8 New translations messages.xlf (German)
[ci skip]
2023-11-15 02:21:59 -08:00
Paperless-ngx Bot [bot]
a5510be0bf New translations messages.xlf (Danish)
[ci skip]
2023-11-15 02:21:57 -08:00
Paperless-ngx Bot [bot]
45a3c95a66 New translations messages.xlf (Czech)
[ci skip]
2023-11-15 02:21:56 -08:00
Paperless-ngx Bot [bot]
94cb13ce9d New translations messages.xlf (Bulgarian)
[ci skip]
2023-11-15 02:21:54 -08:00
Paperless-ngx Bot [bot]
fecc431b10 New translations messages.xlf (Belarusian)
[ci skip]
2023-11-15 02:21:52 -08:00
Paperless-ngx Bot [bot]
c88a975bf0 New translations messages.xlf (Arabic)
[ci skip]
2023-11-15 02:21:50 -08:00
Paperless-ngx Bot [bot]
4ed26ab934 New translations messages.xlf (Afrikaans)
[ci skip]
2023-11-15 02:21:49 -08:00
Paperless-ngx Bot [bot]
76a8da20bf New translations messages.xlf (Spanish)
[ci skip]
2023-11-15 02:21:47 -08:00
Paperless-ngx Bot [bot]
9f05747681 New translations messages.xlf (French)
[ci skip]
2023-11-15 02:21:45 -08:00
Paperless-ngx Bot [bot]
f0bb9bb953 New translations messages.xlf (Romanian)
[ci skip]
2023-11-15 02:21:42 -08:00
shamoon
72c6ac09ac Merge branch 'dev' into beta 2023-11-15 00:43:11 -08:00
shamoon
07c38d6353 Fix: Add missing translation strings in v2.0.0-beta.rc1
Closes #4595
2023-11-15 00:43:00 -08:00
Paperless-ngx Bot [bot]
dd1cafbc77 New translations messages.xlf (Catalan)
[ci skip]
2023-11-15 00:30:28 -08:00
Paperless-ngx Bot [bot]
77e30d7844 New translations messages.xlf (Serbian (Latin))
[ci skip]
2023-11-15 00:30:27 -08:00
Paperless-ngx Bot [bot]
1c4f772a14 New translations messages.xlf (Luxembourgish)
[ci skip]
2023-11-15 00:30:25 -08:00
Paperless-ngx Bot [bot]
bfc3148e4f New translations messages.xlf (Latvian)
[ci skip]
2023-11-15 00:30:24 -08:00
Paperless-ngx Bot [bot]
85db76c86e New translations messages.xlf (Croatian)
[ci skip]
2023-11-15 00:30:22 -08:00
Paperless-ngx Bot [bot]
f6c0d97172 New translations messages.xlf (Thai)
[ci skip]
2023-11-15 00:30:20 -08:00
Paperless-ngx Bot [bot]
67f846513b New translations messages.xlf (Indonesian)
[ci skip]
2023-11-15 00:30:19 -08:00
Paperless-ngx Bot [bot]
ad4cf843dd New translations messages.xlf (Portuguese, Brazilian)
[ci skip]
2023-11-15 00:30:17 -08:00
Paperless-ngx Bot [bot]
af79b1504f New translations messages.xlf (Chinese Traditional)
[ci skip]
2023-11-15 00:30:15 -08:00
Paperless-ngx Bot [bot]
caff72967e New translations messages.xlf (Chinese Simplified)
[ci skip]
2023-11-15 00:30:14 -08:00
Paperless-ngx Bot [bot]
4abd21bbca New translations messages.xlf (Ukrainian)
[ci skip]
2023-11-15 00:30:12 -08:00
Paperless-ngx Bot [bot]
a2770a1bff New translations messages.xlf (Turkish)
[ci skip]
2023-11-15 00:30:11 -08:00
Paperless-ngx Bot [bot]
30e2d15321 New translations messages.xlf (Swedish)
[ci skip]
2023-11-15 00:30:09 -08:00
Paperless-ngx Bot [bot]
ba7e8b8627 New translations messages.xlf (Slovenian)
[ci skip]
2023-11-15 00:30:07 -08:00
Paperless-ngx Bot [bot]
58850e97ff New translations messages.xlf (Slovak)
[ci skip]
2023-11-15 00:30:06 -08:00
Paperless-ngx Bot [bot]
b35acabc56 New translations messages.xlf (Russian)
[ci skip]
2023-11-15 00:30:04 -08:00
Paperless-ngx Bot [bot]
ef907f1bc4 New translations messages.xlf (Portuguese)
[ci skip]
2023-11-15 00:30:03 -08:00
Paperless-ngx Bot [bot]
c25128917b New translations messages.xlf (Polish)
[ci skip]
2023-11-15 00:30:01 -08:00
Paperless-ngx Bot [bot]
9db6e64666 New translations messages.xlf (Norwegian)
[ci skip]
2023-11-15 00:29:59 -08:00
Paperless-ngx Bot [bot]
f51a29a2e1 New translations messages.xlf (Dutch)
[ci skip]
2023-11-15 00:29:57 -08:00
Paperless-ngx Bot [bot]
b90025a2d0 New translations messages.xlf (Korean)
[ci skip]
2023-11-15 00:29:56 -08:00
Paperless-ngx Bot [bot]
03b5ae02e1 New translations messages.xlf (Italian)
[ci skip]
2023-11-15 00:29:55 -08:00
Paperless-ngx Bot [bot]
70627b992a New translations messages.xlf (Hungarian)
[ci skip]
2023-11-15 00:29:53 -08:00
Paperless-ngx Bot [bot]
e830cd9baa New translations messages.xlf (Hebrew)
[ci skip]
2023-11-15 00:29:52 -08:00
Paperless-ngx Bot [bot]
05fb10390e New translations messages.xlf (Finnish)
[ci skip]
2023-11-15 00:29:50 -08:00
Paperless-ngx Bot [bot]
d2c802c9da New translations messages.xlf (Greek)
[ci skip]
2023-11-15 00:29:49 -08:00
Paperless-ngx Bot [bot]
741c165a5b New translations messages.xlf (German)
[ci skip]
2023-11-15 00:29:47 -08:00
Paperless-ngx Bot [bot]
53d3ede184 New translations messages.xlf (Danish)
[ci skip]
2023-11-15 00:29:45 -08:00
Paperless-ngx Bot [bot]
a41d2f831d New translations messages.xlf (Czech)
[ci skip]
2023-11-15 00:29:44 -08:00
Paperless-ngx Bot [bot]
e30b7d0fa4 New translations messages.xlf (Bulgarian)
[ci skip]
2023-11-15 00:29:43 -08:00
Paperless-ngx Bot [bot]
7222741863 New translations messages.xlf (Belarusian)
[ci skip]
2023-11-15 00:29:41 -08:00
Paperless-ngx Bot [bot]
49cea717af New translations messages.xlf (Arabic)
[ci skip]
2023-11-15 00:29:39 -08:00
Paperless-ngx Bot [bot]
49a7b62155 New translations messages.xlf (Afrikaans)
[ci skip]
2023-11-15 00:29:38 -08:00
Paperless-ngx Bot [bot]
3abc88985a New translations messages.xlf (Spanish)
[ci skip]
2023-11-15 00:29:36 -08:00
Paperless-ngx Bot [bot]
764b007d2c New translations messages.xlf (French)
[ci skip]
2023-11-15 00:29:35 -08:00
Paperless-ngx Bot [bot]
a5c0d3dfae New translations messages.xlf (Romanian)
[ci skip]
2023-11-15 00:29:33 -08:00
shamoon
386070923f Merge branch 'dev' into beta 2023-11-15 00:21:23 -08:00
shamoon
e79015d877 Fix: Add missing translation string in share link dropdown
Closes #4594
2023-11-15 00:20:55 -08:00
shamoon
b574396751 Chore: Update lock-threads with discussions support (#4580) 2023-11-14 19:55:14 -08:00
tooomm
91400070a7 Documentation: Updated theme dark/light image specification (#4591) 2023-11-14 19:13:44 -08:00
Paperless-ngx Bot [bot]
23d02085e8 New translations messages.xlf (Serbian (Latin))
[ci skip]
2023-11-14 12:53:55 -08:00
Paperless-ngx Bot [bot]
a9459dca89 New translations messages.xlf (Catalan)
[ci skip]
2023-11-14 10:43:26 -08:00
Paperless-ngx Bot [bot]
bd08cd1983 New translations django.po (Catalan)
[ci skip]
2023-11-14 10:43:25 -08:00
Paperless-ngx Bot [bot]
3ae91495c1 New translations django.po (French)
[ci skip]
2023-11-14 02:50:12 -08:00
Paperless-ngx Bot [bot]
7f9fa46271 New translations messages.xlf (French)
[ci skip]
2023-11-14 02:50:11 -08:00
Paperless-ngx Bot [bot]
f3b02d9922 New translations django.po (Bulgarian)
[ci skip]
2023-11-14 01:54:22 -08:00
Paperless-ngx Bot [bot]
f2be582299 New translations django.po (French)
[ci skip]
2023-11-14 01:54:21 -08:00
Paperless-ngx Bot [bot]
b2eb403f9b New translations messages.xlf (German)
[ci skip]
2023-11-14 01:54:19 -08:00
Paperless-ngx Bot [bot]
7e6a5927d7 New translations messages.xlf (Bulgarian)
[ci skip]
2023-11-14 01:54:18 -08:00
Paperless-ngx Bot [bot]
2c4a3650c4 New translations messages.xlf (French)
[ci skip]
2023-11-14 01:54:16 -08:00
Paperless-ngx Bot [bot]
5b125d4513 New translations messages.xlf (German)
[ci skip]
2023-11-13 22:39:40 -08:00
shamoon
66167aeb55 Fix: add sleep to close old discussions 2023-11-13 21:10:20 -08:00
504 changed files with 145963 additions and 82971 deletions

3
.codespellrc Normal file
View File

@@ -0,0 +1,3 @@
[codespell]
write-changes = True
ignore-words-list = criterias,afterall,valeu,ureue,equest,ure

1
.env
View File

@@ -1,2 +1 @@
COMPOSE_PROJECT_NAME=paperless
export PROMPT="(pipenv-projectname)$P$G"

View File

@@ -20,7 +20,7 @@ body:
- [The troubleshooting documentation](https://docs.paperless-ngx.com/troubleshooting/).
- [The installation instructions](https://docs.paperless-ngx.com/setup/#installation).
- [Existing issues and discussions](https://github.com/paperless-ngx/paperless-ngx/search?q=&type=issues).
- Disable any customer container initialization scripts, if using
- Disable any custom container initialization scripts, if using
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
@@ -102,3 +102,14 @@ body:
attributes:
label: Other
description: Any other relevant details.
- type: checkboxes
id: required-checks
attributes:
label: Please confirm the following
options:
- label: I believe this issue is a bug that affects all users of Paperless-ngx, not something specific to my installation.
required: true
- label: I have already searched for relevant existing issues and discussions before opening this report.
required: true
- label: I have updated the title field above with a concise description.
required: true

View File

@@ -8,7 +8,11 @@ Note: All PRs with code changes should be targeted to the `dev` branch, pure doc
Please include a summary of the change and which issue is fixed (if any) and any relevant motivation / context. List any dependencies that are required for this change. If appropriate, please include an explanation of how your proposed change can be tested. Screenshots and / or videos can also be helpful if appropriate.
-->
Fixes # (issue)
<!--
⚠️ Important: Pull requests that implement a new feature *should almost always target an existing feature request*. This is in order to balance the work of implementing and maintaining new features vs. community-interest. If that is not currently the case, please open a feature request instead of this PR to gather feedback from both users and the project maintainers.
-->
Closes #(issue or discussion)
## Type of change
@@ -17,10 +21,11 @@ What type of change does your PR introduce to Paperless-ngx?
NOTE: Please check only one box!
-->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Other (please explain):
- [ ] Bug fix: non-breaking change which fixes an issue.
- [ ] New feature: non-breaking change which adds functionality. _Please read the important note above._
- [ ] Breaking change: fix or feature that would cause existing functionality to not work as expected.
- [ ] Documentation only.
- [ ] Other. Please explain:
## Checklist:

View File

@@ -16,7 +16,7 @@ on:
env:
# This is the version of pipenv all the steps will use
# If changing this, change Dockerfile
DEFAULT_PIP_ENV_VERSION: "2023.10.24"
DEFAULT_PIP_ENV_VERSION: "2023.11.15"
# This is the default version of Python to use in most steps which aren't specific
DEFAULT_PYTHON_VERSION: "3.10"
@@ -37,7 +37,7 @@ jobs:
uses: actions/checkout@v4
-
name: Install python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
-
@@ -45,7 +45,7 @@ jobs:
uses: pre-commit/action@v3.0.0
documentation:
name: "Build Documentation"
name: "Build & Deploy Documentation"
runs-on: ubuntu-22.04
needs:
- pre-commit
@@ -56,7 +56,7 @@ jobs:
-
name: Set up Python
id: setup-python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
cache: "pipenv"
@@ -77,33 +77,22 @@ jobs:
name: Make documentation
run: |
pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs build --config-file ./mkdocs.yml
-
name: Deploy documentation
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
git config --global user.name "${{ github.actor }}"
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs gh-deploy --force --no-history
-
name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: documentation
path: site/
retention-days: 7
documentation-deploy:
name: "Deploy Documentation"
runs-on: ubuntu-22.04
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs:
- documentation
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CUSTOM_DOMAIN: docs.paperless-ngx.com
CONFIG_FILE: mkdocs.yml
EXTRA_PACKAGES: build-base
tests-backend:
name: "Backend Tests (Python ${{ matrix.python-version }})"
runs-on: ubuntu-22.04
@@ -120,12 +109,12 @@ jobs:
-
name: Start containers
run: |
docker compose --file ${GITHUB_WORKSPACE}/docker/compose/docker-compose.ci-test.yml pull --quiet
docker compose --file ${GITHUB_WORKSPACE}/docker/compose/docker-compose.ci-test.yml up --detach
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml pull --quiet
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml up --detach
-
name: Set up Python
id: setup-python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "${{ matrix.python-version }}"
cache: "pipenv"
@@ -166,7 +155,7 @@ jobs:
-
name: Upload coverage
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: backend-coverage-report
path: src/coverage.xml
@@ -176,11 +165,11 @@ jobs:
name: Stop containers
if: always()
run: |
docker compose --file ${GITHUB_WORKSPACE}/docker/compose/docker-compose.ci-test.yml logs
docker compose --file ${GITHUB_WORKSPACE}/docker/compose/docker-compose.ci-test.yml down
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down
install-frontend-depedendencies:
name: "Install Frontend Dependendencies"
name: "Install Frontend Dependencies"
runs-on: ubuntu-22.04
needs:
- pre-commit
@@ -193,7 +182,7 @@ jobs:
node-version: 20.x
cache: 'npm'
cache-dependency-path: 'src-ui/package-lock.json'
- name: Cache frontend depdendencies
- name: Cache frontend dependencies
id: cache-frontend-deps
uses: actions/cache@v3
with:
@@ -230,7 +219,7 @@ jobs:
node-version: 20.x
cache: 'npm'
cache-dependency-path: 'src-ui/package-lock.json'
- name: Cache frontend depdendencies
- name: Cache frontend dependencies
id: cache-frontend-deps
uses: actions/cache@v3
with:
@@ -249,7 +238,7 @@ jobs:
-
name: Upload Jest coverage
if: always()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: jest-coverage-report-${{ matrix.shard-index }}
path: |
@@ -264,9 +253,9 @@ jobs:
-
name: Upload Playwright test results
if: always()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: playwright-report
name: playwright-report-${{ matrix.shard-index }}
path: src-ui/playwright-report
retention-days: 7
@@ -280,10 +269,18 @@ jobs:
-
uses: actions/checkout@v4
-
name: Download frontend coverage
uses: actions/download-artifact@v3
name: Download frontend jest coverage
uses: actions/download-artifact@v4
with:
path: src-ui/coverage/
pattern: jest-coverage-report-*
-
name: Download frontend playwright coverage
uses: actions/download-artifact@v4
with:
path: src-ui/coverage/
pattern: playwright-report-*
merge-multiple: true
-
name: Upload frontend coverage to Codecov
uses: codecov/codecov-action@v3
@@ -296,7 +293,7 @@ jobs:
files: '!coverage.xml'
-
name: Download backend coverage
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: backend-coverage-report
path: src/
@@ -427,7 +424,7 @@ jobs:
docker cp frontend-extract:/usr/src/paperless/src/documents/static/frontend src/documents/static/frontend/
-
name: Upload frontend artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: frontend-compiled
path: src/documents/static/frontend/
@@ -437,6 +434,7 @@ jobs:
name: "Build Release"
needs:
- build-docker-image
- documentation
runs-on: ubuntu-22.04
steps:
-
@@ -445,7 +443,7 @@ jobs:
-
name: Set up Python
id: setup-python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
cache: "pipenv"
@@ -471,13 +469,13 @@ jobs:
sudo apt-get install -qq --no-install-recommends gettext liblept5
-
name: Download frontend artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: frontend-compiled
path: src/documents/static/frontend/
-
name: Download documentation artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: documentation
path: docs/_build/html/
@@ -543,7 +541,7 @@ jobs:
tar -cJf paperless-ngx.tar.xz paperless-ngx/
-
name: Upload release artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: release
path: dist/paperless-ngx.tar.xz
@@ -562,7 +560,7 @@ jobs:
steps:
-
name: Download release artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: release
path: ./
@@ -613,7 +611,7 @@ jobs:
ref: main
-
name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
cache: "pipenv"
@@ -642,7 +640,7 @@ jobs:
git push origin ${{ needs.publish-release.outputs.version }}-changelog
-
name: Create Pull Request
uses: actions/github-script@v6
uses: actions/github-script@v7
with:
script: |
const { repo, owner } = context.repo;

View File

@@ -19,9 +19,13 @@ concurrency:
jobs:
cleanup-images:
name: Cleanup Image Tags for paperless-ngx
name: Cleanup Image Tags for ${{ matrix.primary-name }}
if: github.repository_owner == 'paperless-ngx'
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
primary-name: ["paperless-ngx", "paperless-ngx/builder/cache/app"]
env:
# Requires a personal access token with the OAuth scope delete:packages
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
@@ -29,12 +33,12 @@ jobs:
-
name: Clean temporary images
if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/ephemeral@v0.3.0
uses: stumpylog/image-cleaner-action/ephemeral@v0.4.0
with:
token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}"
is_org: "true"
package_name: "paperless-ngx"
package_name: "${{ matrix.primary-name }}"
scheme: "branch"
repo_name: "paperless-ngx"
match_regex: "feature-"
@@ -49,18 +53,7 @@ jobs:
strategy:
fail-fast: false
matrix:
include:
- primary-name: "paperless-ngx"
- primary-name: "paperless-ngx/builder/cache/app"
# TODO: Remove the above and replace with the below
# - primary-name: "builder/qpdf"
# - primary-name: "builder/cache/qpdf"
# - primary-name: "builder/pikepdf"
# - primary-name: "builder/cache/pikepdf"
# - primary-name: "builder/jbig2enc"
# - primary-name: "builder/cache/jbig2enc"
# - primary-name: "builder/psycopg2"
# - primary-name: "builder/cache/psycopg2"
primary-name: ["paperless-ngx", "paperless-ngx/builder/cache/app"]
env:
# Requires a personal access token with the OAuth scope delete:packages
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
@@ -68,7 +61,7 @@ jobs:
-
name: Clean untagged images
if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/untagged@v0.3.0
uses: stumpylog/image-cleaner-action/untagged@v0.4.0
with:
token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}"

View File

@@ -42,7 +42,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -51,4 +51,4 @@ jobs:
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3

34
.github/workflows/crowdin.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Crowdin Action
on:
workflow_dispatch:
schedule:
- cron: '2 */12 * * *'
push:
paths: [
'src/locale/**',
'src-ui/messages.xlf',
'src-ui/src/locale/**'
]
branches: [ dev ]
jobs:
synchronize-with-crowdin:
name: Crowdin Sync
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: crowdin action
uses: crowdin/github-action@v1
with:
upload_translations: false
download_translations: true
crowdin_branch_name: 'dev'
localization_branch_name: l10n_dev
pull_request_labels: 'skip-changelog, translation'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@@ -1,10 +1,6 @@
name: Project Automations
on:
issues:
types:
- opened
- reopened
pull_request_target: #_target allows access to secrets
types:
- opened
@@ -16,25 +12,7 @@ on:
permissions:
contents: read
env:
todo: Todo
done: Done
in_progress: In Progress
jobs:
issue_opened_or_reopened:
name: issue_opened_or_reopened
runs-on: ubuntu-22.04
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@v2.2.1
with:
gh_token: ${{ secrets.GH_TOKEN }}
organization: paperless-ngx
project_id: 2
resource_node_id: ${{ github.event.issue.node_id }}
status_value: ${{ env.todo }} # Target status
pr_opened_or_reopened:
name: pr_opened_or_reopened
runs-on: ubuntu-22.04
@@ -43,14 +21,6 @@ jobs:
pull-requests: write
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@v2.2.1
with:
gh_token: ${{ secrets.GH_TOKEN }}
organization: paperless-ngx
project_id: 2
resource_node_id: ${{ github.event.pull_request.node_id }}
status_value: "Needs Review" # Target status
- name: Label PR with release-drafter
uses: release-drafter/release-drafter@v5
env:

View File

@@ -18,7 +18,7 @@ jobs:
name: 'Stale'
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
- uses: actions/stale@v9
with:
days-before-stale: 7
days-before-close: 14
@@ -33,10 +33,11 @@ jobs:
name: 'Lock Old Threads'
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v4
- uses: dessant/lock-threads@v5
with:
issue-inactive-days: '30'
pr-inactive-days: '30'
discussion-inactive-days: '30'
log-output: true
issue-comment: >
This issue has been automatically locked since there
@@ -46,13 +47,21 @@ jobs:
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new discussion or issue for related concerns.
discussion-comment: >
This discussion has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new discussion for related concerns.
close-answered-discussions:
name: 'Close Answered Discussions'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@v7
with:
script: |
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const query = `query($owner:String!, $name:String!) {
repository(owner:$owner, name:$name){
discussions(first:100, answered:true, states:[OPEN]) {
@@ -72,7 +81,7 @@ jobs:
console.log(`Found ${result.repository.discussions.nodes.length} open answered discussions`)
for (const discussion of result.repository.discussions.nodes) {
console.log(`Closing dicussion #${discussion.number} (${discussion.id})`)
console.log(`Closing discussion #${discussion.number} (${discussion.id})`)
const addCommentMutation = `mutation($discussion:ID!, $body:String!) {
addDiscussionComment(input:{discussionId:$discussion, body:$body}) {
@@ -96,4 +105,96 @@ jobs:
}
await github.graphql(closeDiscussionMutation, closeVariables)
await sleep(1000)
}
close-outdated-discussions:
name: 'Close Outdated Discussions'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const CUTOFF_DAYS = 180;
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - CUTOFF_DAYS);
const query = `query(
$owner:String!,
$name:String!,
$supportCategory:ID!,
$generalCategory:ID!,
) {
supportDiscussions: repository(owner:$owner, name:$name){
discussions(
categoryId:$supportCategory,
last:50,
answered:false,
states:[OPEN],
) {
nodes {
id,
number,
updatedAt
}
},
},
generalDiscussions: repository(owner:$owner, name:$name){
discussions(
categoryId:$generalCategory,
last:50,
states:[OPEN],
) {
nodes {
id,
number,
updatedAt
}
}
}
}`;
const variables = {
owner: context.repo.owner,
name: context.repo.repo,
supportCategory: "DIC_kwDOG1Zs184CBKWK",
generalCategory: "DIC_kwDOG1Zs184CBKWJ"
}
const result = await github.graphql(query, variables);
const combinedDiscussions = [
...result.supportDiscussions.discussions.nodes,
...result.generalDiscussions.discussions.nodes,
]
console.log(`Checking ${combinedDiscussions.length} open discussions`);
for (const discussion of combinedDiscussions) {
if (new Date(discussion.updatedAt) < cutoff) {
console.log(`Closing outdated discussion #${discussion.number} (${discussion.id}), last updated at ${discussion.updatedAt}`);
const addCommentMutation = `mutation($discussion:ID!, $body:String!) {
addDiscussionComment(input:{discussionId:$discussion, body:$body}) {
clientMutationId
}
}`;
const commentVariables = {
discussion: discussion.id,
body: 'This discussion has been automatically closed due to inactivity.',
}
await github.graphql(addCommentMutation, commentVariables);
const closeDiscussionMutation = `mutation($discussion:ID!, $reason:DiscussionCloseReason!) {
closeDiscussion(input:{discussionId:$discussion, reason:$reason}) {
clientMutationId
}
}`;
const closeVariables = {
discussion: discussion.id,
reason: "OUTDATED",
}
await github.graphql(closeDiscussionMutation, closeVariables);
await sleep(1000);
}
}

View File

@@ -11,6 +11,8 @@ repos:
- id: check-json
exclude: "tsconfig.*json"
- id: check-yaml
args:
- "--unsafe"
- id: check-toml
- id: check-executables-have-shebangs
- id: end-of-file-fixer
@@ -26,6 +28,14 @@ repos:
- svg
- id: check-case-conflict
- id: detect-private-key
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
hooks:
- id: codespell
exclude: "(^src-ui/src/locale/)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)"
exclude_types:
- pofile
- json
- repo: https://github.com/pre-commit/mirrors-prettier
rev: 'v3.1.0'
hooks:
@@ -37,11 +47,11 @@ repos:
exclude: "(^Pipfile\\.lock$)"
# Python hooks
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.1.5'
rev: 'v0.1.11'
hooks:
- id: ruff
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.11.0
rev: 23.12.1
hooks:
- id: black
# Dockerfile hooks

View File

@@ -7,7 +7,7 @@
"trailingComma": "es5",
"overrides": [
{
"files": "index.md",
"files": ["index.md", "administration.md"],
"options": {
"tabWidth": 4
}

View File

@@ -94,7 +94,7 @@ The following files need to be changed:
- src-ui/angular.json (under the _projects/paperless-ui/i18n/locales_ JSON key)
- src/paperless/settings.py (in the _LANGUAGES_ array)
- src-ui/src/app/services/settings.service.ts (inside the _getLanguageOptions_ method)
- src-ui/src/app/services/settings.service.ts (inside the _LANGUAGE_OPTIONS_ array)
- src-ui/src/app/app.module.ts (import locale from _angular/common/locales_ and call _registerLocaleData_)
Please add the language in the correct order, alphabetically by locale.

View File

@@ -12,7 +12,7 @@ COPY ./src-ui /src/src-ui
WORKDIR /src/src-ui
RUN set -eux \
&& npm update npm -g \
&& npm ci --omit=optional
&& npm ci
RUN set -eux \
&& ./node_modules/.bin/ng build --configuration production
@@ -29,7 +29,7 @@ COPY Pipfile* ./
RUN set -eux \
&& echo "Installing pipenv" \
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2023.10.24 \
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2023.11.15 \
&& echo "Generating requirement.txt" \
&& pipenv requirements > requirements.txt
@@ -39,6 +39,8 @@ RUN set -eux \
# - Don't leave anything extra in here
FROM docker.io/python:3.11-slim-bookworm as main-app
ENV PYTHONWARNINGS="ignore:::django.http.response:517"
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
LABEL org.opencontainers.image.documentation="https://docs.paperless-ngx.com/"
LABEL org.opencontainers.image.source="https://github.com/paperless-ngx/paperless-ngx"
@@ -52,8 +54,8 @@ ARG TARGETARCH
# Can be workflow provided, defaults set for manual building
ARG JBIG2ENC_VERSION=0.29
ARG QPDF_VERSION=11.6.3
ARG GS_VERSION=10.02.0
ARG QPDF_VERSION=11.6.4
ARG GS_VERSION=10.02.1
#
# Begin installation and configuration
@@ -123,13 +125,13 @@ RUN set -eux \
&& echo "Installing Ghostscript ${GS_VERSION}" \
&& curl --fail --silent --show-error --location \
--output libgs10_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \
--output ghostscript_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \
--output libgs10-common_${GS_VERSION}.dfsg-2_all.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-2_all.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
&& dpkg --install ./libgs10-common_${GS_VERSION}.dfsg-2_all.deb \
&& dpkg --install ./libgs10_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \
&& dpkg --install ./ghostscript_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \
@@ -187,7 +189,7 @@ RUN set -eux \
&& 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" \
&& echo "Installing management commands" \
&& chmod +x install_management_commands.sh \
&& ./install_management_commands.sh
@@ -268,3 +270,5 @@ ENTRYPOINT ["/sbin/docker-entrypoint.sh"]
EXPOSE 8000
CMD ["/usr/local/bin/paperless_cmd.sh"]
HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000" ]

10
Pipfile
View File

@@ -4,23 +4,23 @@ verify_ssl = true
name = "pypi"
[packages]
dateparser = "~=1.1"
dateparser = "~=1.2"
# WARNING: django does not use semver.
# Only patch versions are guaranteed to not introduce breaking changes.
django = "~=4.2.7"
django = "~=4.2.9"
django-auditlog = "*"
django-celery-results = "*"
django-compression-middleware = "*"
django-cors-headers = "*"
django-extensions = "*"
django-filter = "~=23.3"
django-filter = "~=23.5"
django-guardian = "*"
django-multiselectfield = "*"
djangorestframework = "~=3.14"
djangorestframework-guardian = "*"
drf-writable-nested = "*"
bleach = "*"
celery = "*"
celery = {extras = ["redis"], version = "*"}
channels = "~=4.0"
channels-redis = "*"
concurrent-log-handler = "*"
@@ -33,7 +33,7 @@ inotifyrecursive = "~=0.3"
langdetect = "*"
mysqlclient = "*"
nltk = "*"
ocrmypdf = "~=15.0"
ocrmypdf = "~=15.4"
pathvalidate = "*"
pdf2image = "*"
psycopg2 = "*"

2557
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,11 @@
[![demo](https://cronitor.io/badges/ve7ItY/production/W5E_B9jkelG9ZbDiNHUPQEVH3MY.svg)](https://demo.paperless-ngx.com)
<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%" />
<img src="https://github.com/paperless-ngx/paperless-ngx/raw/main/resources/logo/web/png/White%20logo%20-%20no%20background.png#gh-dark-mode-only" width="50%" />
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/paperless-ngx/paperless-ngx/blob/main/resources/logo/web/png/White%20logo%20-%20no%20background.png" width="50%">
<source media="(prefers-color-scheme: light)" srcset="https://github.com/paperless-ngx/paperless-ngx/raw/main/resources/logo/web/png/Black%20logo%20-%20no%20background.png" width="50%">
<img src="https://github.com/paperless-ngx/paperless-ngx/raw/main/resources/logo/web/png/Black%20logo%20-%20no%20background.png" width="50%">
</picture>
</p>
<!-- omit in toc -->
@@ -32,16 +35,19 @@ A demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com)
# Features
![Dashboard](https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docs/assets/screenshots/documents-smallcards.png#gh-light-mode-only)
![Dashboard](https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docs/assets/screenshots/documents-smallcards-dark.png#gh-dark-mode-only)
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docs/assets/screenshots/documents-smallcards-dark.png">
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docs/assets/screenshots/documents-smallcards.png">
<img src="https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docs/assets/screenshots/documents-smallcards.png">
</picture>
A full list of [features](https://docs.paperless-ngx.com/#features) and [screenshots](https://docs.paperless-ngx.com/#screenshots) are available in the [documentation](https://docs.paperless-ngx.com/).
# Getting started
The easiest way to deploy paperless is docker-compose. The files in the [`/docker/compose` directory](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose) are configured to pull the image from GitHub Packages.
The easiest way to deploy paperless is `docker compose`. The files in the [`/docker/compose` directory](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose) are configured to pull the image from GitHub Packages.
If you'd like to jump right in, you can configure a docker-compose environment with our install script:
If you'd like to jump right in, you can configure a `docker compose` environment with our install script:
```bash
bash -c "$(curl -L https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"

View File

@@ -1,8 +1,6 @@
commit_message: '[ci skip]'
pull_request_labels: [
"skip-changelog",
"translation"
]
project_id_env: CROWDIN_PROJECT_ID
api_token_env: CROWDIN_PERSONAL_TOKEN
preserve_hierarchy: true
files:
- source: /src/locale/en_US/LC_MESSAGES/django.po
translation: /src/locale/%locale_with_underscore%/LC_MESSAGES/django.po

View File

@@ -1,12 +1,12 @@
# docker-compose file for running paperless testing with actual gotenberg
# Docker Compose file for running paperless testing with actual gotenberg
# and Tika containers for a more end to end test of the Tika related functionality
# Can be used locally or by the CI to start the nessecary containers with the
# Can be used locally or by the CI to start the necessary containers with the
# correct networking for the tests
version: "3.7"
services:
gotenberg:
image: docker.io/gotenberg/gotenberg:7.8
image: docker.io/gotenberg/gotenberg:7.10
hostname: gotenberg
container_name: gotenberg
network_mode: host
@@ -17,6 +17,8 @@ services:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
- "--log-level=warn"
- "--log-format=text"
tika:
image: ghcr.io/paperless-ngx/tika:latest
hostname: tika

View File

@@ -1,4 +1,4 @@
# docker-compose file for running paperless from the Docker Hub.
# docker compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
@@ -10,7 +10,7 @@
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8000.
#
# In addition to that, this docker-compose file adds the following optional
# In addition to that, this Docker Compose file adds the following optional
# configurations:
#
# - Instead of SQLite (default), MariaDB is used as the database server.
@@ -23,9 +23,9 @@
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
# - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
@@ -60,11 +60,6 @@ services:
- tika
ports:
- "8000:8000"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
@@ -83,7 +78,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: docker.io/gotenberg/gotenberg:7.8
image: docker.io/gotenberg/gotenberg:7.10
restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not
# want to allow external content like tracking pixels or even javascript.

View File

@@ -1,4 +1,4 @@
# docker-compose file for running paperless from the Docker Hub.
# Docker Compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
@@ -10,7 +10,7 @@
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8000.
#
# In addition to that, this docker-compose file adds the following optional
# In addition to that, this Docker Compose file adds the following optional
# configurations:
#
# - Instead of SQLite (default), MariaDB is used as the database server.
@@ -19,9 +19,9 @@
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
# - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
@@ -54,11 +54,6 @@ services:
- broker
ports:
- "8000:8000"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
@@ -73,7 +68,6 @@ services:
PAPERLESS_DBPASS: paperless # only needed if non-default password
PAPERLESS_DBPORT: 3306
volumes:
data:
media:

View File

@@ -1,4 +1,4 @@
# docker-compose file for running paperless from the Docker Hub.
# Docker Compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
@@ -10,7 +10,7 @@
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8010.
#
# In addition to that, this docker-compose file adds the following optional
# In addition to that, this Docker Compose file adds the following optional
# configurations:
#
# - Instead of SQLite (default), PostgreSQL is used as the database server.
@@ -54,11 +54,6 @@ services:
- broker
ports:
- "8010:8000"
healthcheck:
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media

View File

@@ -1,4 +1,4 @@
# docker-compose file for running paperless from the docker container registry.
# Docker Compose file for running paperless from the docker container registry.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
@@ -10,7 +10,7 @@
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8000.
#
# In addition to that, this docker-compose file adds the following optional
# In addition to that, this Docker Compose file adds the following optional
# configurations:
#
# - Instead of SQLite (default), PostgreSQL is used as the database server.
@@ -23,9 +23,9 @@
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
# - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
@@ -58,11 +58,6 @@ services:
- tika
ports:
- "8000:8000"
healthcheck:
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
@@ -77,7 +72,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: docker.io/gotenberg/gotenberg:7.8
image: docker.io/gotenberg/gotenberg:7.10
restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not

View File

@@ -1,4 +1,4 @@
# docker-compose file for running paperless from the Docker Hub.
# Docker Compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
@@ -10,7 +10,7 @@
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8000.
#
# In addition to that, this docker-compose file adds the following optional
# In addition to that, this Docker Compose file adds the following optional
# configurations:
#
# - Instead of SQLite (default), PostgreSQL is used as the database server.
@@ -19,9 +19,9 @@
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
# - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
@@ -52,11 +52,6 @@ services:
- broker
ports:
- "8000:8000"
healthcheck:
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
@@ -67,7 +62,6 @@ services:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_DBHOST: db
volumes:
data:
media:

View File

@@ -1,4 +1,4 @@
# docker-compose file for running paperless from the docker container registry.
# Docker Compose file for running paperless from the docker container registry.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
# All compose files of paperless configure paperless in the following way:
@@ -11,7 +11,7 @@
#
# SQLite is used as the database. The SQLite file is stored in the data volume.
#
# In addition to that, this docker-compose file adds the following optional
# In addition to that, this Docker Compose file adds the following optional
# configurations:
#
# - Apache Tika and Gotenberg servers are started with paperless and paperless
@@ -23,9 +23,9 @@
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
# - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
@@ -47,11 +47,6 @@ services:
- tika
ports:
- "8000:8000"
healthcheck:
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
@@ -65,7 +60,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: docker.io/gotenberg/gotenberg:7.8
image: docker.io/gotenberg/gotenberg:7.10
restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not

View File

@@ -1,4 +1,4 @@
# docker-compose file for running paperless from the Docker Hub.
# Docker Compose file for running paperless from the Docker Hub.
# This file contains everything paperless needs to run.
# Paperless supports amd64, arm and arm64 hardware.
#
@@ -16,9 +16,9 @@
#
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder.
# - Run 'docker-compose pull'.
# - Run 'docker-compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker-compose up -d'.
# - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'.
#
# For more extensive installation and update instructions, refer to the
# documentation.
@@ -38,11 +38,6 @@ services:
- broker
ports:
- "8000:8000"
healthcheck:
test: ["CMD", "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000"]
interval: 30s
timeout: 10s
retries: 5
volumes:
- data:/usr/src/paperless/data
- media:/usr/src/paperless/media
@@ -52,7 +47,6 @@ services:
environment:
PAPERLESS_REDIS: redis://broker:6379
volumes:
data:
media:

View File

@@ -86,13 +86,13 @@ initialize() {
"${CONSUME_DIR}"; do
if [[ ! -d "${dir}" ]]; then
echo "Creating directory ${dir}"
mkdir "${dir}"
mkdir --parents "${dir}"
fi
done
local -r tmp_dir="/tmp/paperless"
echo "Creating directory ${tmp_dir}"
mkdir -p "${tmp_dir}"
mkdir --parents "${tmp_dir}"
set +e
echo "Adjusting permissions of paperless files. This may take a while."

View File

@@ -80,7 +80,7 @@ django_checks() {
search_index() {
local -r index_version=7
local -r index_version=8
local -r index_version_file=${DATA_DIR}/.index_version
if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then

View File

@@ -5,17 +5,19 @@
Multiple options exist for making backups of your paperless instance,
depending on how you installed paperless.
Before making backups, make sure that paperless is not running.
Before making a backup, it's probably best to make sure that paperless is not actively
consuming documents at that time.
Options available to any installation of paperless:
- Use the [document exporter](#exporter). The document exporter exports all your documents,
thumbnails, metadata, and database contents to a specific folder. You may import your
documents and settings into a fresh instance of paperless again or store your
documents in another DMS with this export.
- The document exporter is also able to update an already existing
export. Therefore, incremental backups with `rsync` are entirely
possible.
- Use the [document exporter](#exporter). The document exporter exports all your documents,
thumbnails, metadata, and database contents to a specific folder. You may import your
documents and settings into a fresh instance of paperless again or store your
documents in another DMS with this export.
The document exporter is also able to update an already existing
export. Therefore, incremental backups with `rsync` are entirely
possible.
!!! caution
@@ -25,31 +27,37 @@ Options available to any installation of paperless:
Options available to docker installations:
- Backup the docker volumes. These usually reside within
`/var/lib/docker/volumes` on the host and you need to be root in
order to access them.
- Backup the docker volumes. These usually reside within
`/var/lib/docker/volumes` on the host and you need to be root in
order to access them.
Paperless uses 4 volumes:
Paperless uses 4 volumes:
- `paperless_media`: This is where your documents are stored.
- `paperless_data`: This is where auxillary data is stored. This
folder also contains the SQLite database, if you use it.
- `paperless_pgdata`: Exists only if you use PostgreSQL and
contains the database.
- `paperless_dbdata`: Exists only if you use MariaDB and contains
the database.
- `paperless_media`: This is where your documents are stored.
- `paperless_data`: This is where auxiliary data is stored. This
folder also contains the SQLite database, if you use it.
- `paperless_pgdata`: Exists only if you use PostgreSQL and
contains the database.
- `paperless_dbdata`: Exists only if you use MariaDB and contains
the database.
Options available to bare-metal and non-docker installations:
- Backup the entire paperless folder. This ensures that if your
paperless instance crashes at some point or your disk fails, you can
simply copy the folder back into place and it works.
- Backup the entire paperless folder. This ensures that if your
paperless instance crashes at some point or your disk fails, you can
simply copy the folder back into place and it works.
When using PostgreSQL or MariaDB, you'll also have to backup the
database.
When using PostgreSQL or MariaDB, you'll also have to backup the
database.
### Restoring {#migrating-restoring}
If you've backed-up Paperless-ngx using the [document exporter](#exporter),
restoring can simply be done with the [document importer](#importer).
Of course, other backup strategies require restoring any volumes, folders and database
copies you created in the steps above.
## Updating Paperless {#updating}
### Docker Route {#docker-updating}
@@ -63,7 +71,7 @@ First of all, ensure that paperless is stopped.
```shell-session
$ cd /path/to/paperless
$ docker-compose down
$ docker compose down
```
After that, [make a backup](#backup).
@@ -71,22 +79,22 @@ After that, [make a backup](#backup).
1. If you pull the image from the docker hub, all you need to do is:
```shell-session
$ docker-compose pull
$ docker-compose up
$ docker compose pull
$ docker compose up
```
The docker-compose files refer to the `latest` version, which is
The Docker Compose files refer to the `latest` version, which is
always the latest stable release.
1. If you built the image yourself, do the following:
```shell-session
$ git pull
$ docker-compose build
$ docker-compose up
$ docker compose build
$ docker compose up
```
Running `docker-compose up` will also apply any new database migrations.
Running `docker compose up` will also apply any new database migrations.
If you see everything working, press CTRL+C once to gracefully stop
paperless. Then you can start paperless-ngx with `-d` to have it run in
the background.
@@ -94,7 +102,7 @@ the background.
!!! note
In version 0.9.14, the update process was changed. In 0.9.13 and
earlier, the docker-compose files specified exact versions and pull
earlier, the Docker Compose files specified exact versions and pull
won't automatically update to newer versions. In order to enable
updates as described above, either get the new `docker-compose.yml`
file from
@@ -212,11 +220,11 @@ Paperless comes with some management commands that perform various
maintenance tasks on your paperless instance. You can invoke these
commands in the following way:
With docker-compose, while paperless is running:
With Docker Compose, while paperless is running:
```shell-session
$ cd /path/to/paperless
$ docker-compose exec webserver <command> <arguments>
$ docker compose exec webserver <command> <arguments>
```
With docker, while paperless is running:
@@ -246,7 +254,7 @@ migration to another DMS.
If you use the document exporter within a cronjob to backup your data
you might use the `-T` flag behind exec to suppress "The input device
is not a TTY" errors. For example:
`docker-compose exec -T webserver document_exporter ../export`
`docker compose exec -T webserver document_exporter ../export`
```
document_exporter target [-c] [-d] [-f] [-na] [-nt] [-p] [-sm] [-z]
@@ -400,7 +408,7 @@ that don't match a document anymore get removed as well.
### Managing the Automatic matching algorithm
The _Auto_ matching algorithm requires a trained neural network to work.
This network needs to be updated whenever somethings in your data
This network needs to be updated whenever something in your data
changes. The docker image takes care of that automatically with the task
scheduler. You can manually renew the classifier by invoking the
following management command:
@@ -470,19 +478,19 @@ collection for issues.
The issues detected by the sanity checker are as follows:
- Missing original files.
- Missing archive files.
- Inaccessible original files due to improper permissions.
- Inaccessible archive files due to improper permissions.
- Corrupted original documents by comparing their checksum against
what is stored in the database.
- Corrupted archive documents by comparing their checksum against what
is stored in the database.
- Missing thumbnails.
- Inaccessible thumbnails due to improper permissions.
- Documents without any content (warning).
- Orphaned files in the media directory (warning). These are files
that are not referenced by any document in paperless.
- Missing original files.
- Missing archive files.
- Inaccessible original files due to improper permissions.
- Inaccessible archive files due to improper permissions.
- Corrupted original documents by comparing their checksum against
what is stored in the database.
- Corrupted archive documents by comparing their checksum against what
is stored in the database.
- Missing thumbnails.
- Inaccessible thumbnails due to improper permissions.
- Documents without any content (warning).
- Orphaned files in the media directory (warning). These are files
that are not referenced by any document in paperless.
```
document_sanity_checker
@@ -589,7 +597,7 @@ This tool does a fuzzy match over document content, looking for
those which look close according to a given ratio.
At this time, other metadata (such as correspondent or type) is not
take into account by the detection.
taken into account by the detection.
```
document_fuzzy_match [--ratio] [--processes N]
@@ -599,3 +607,10 @@ document_fuzzy_match [--ratio] [--processes N]
| ----------- | -------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| --ratio | No | 85.0 | a number between 0 and 100, setting how similar a document must be for it to be reported. Higher numbers mean more similarity. |
| --processes | No | 1/4 of system cores | Number of processes to use for matching. Setting 1 disables multiple processes |
| --delete | No | False | If provided, one document of a matched pair above the ratio will be deleted. |
!!! warning
If providing the `--delete` option, it is highly recommended to have a backup.
While every effort has been taken to ensure proper operation, there is always the
chance of deletion of a file you want to keep.

View File

@@ -136,6 +136,11 @@ script can access the following relevant environment variables set:
be triggered, leading to failures as two tasks work on the
same document path
!!! warning
If your script modifies `DOCUMENT_WORKING_PATH` in a non-deterministic
way, this may allow duplicate documents to be stored
A simple but common example for this would be creating a simple script
like this:
@@ -236,8 +241,8 @@ webserver:
Troubleshooting:
- Monitor the docker-compose log
`cd ~/paperless-ngx; docker-compose logs -f`
- Monitor the Docker Compose log
`cd ~/paperless-ngx; docker compose logs -f`
- Check your script's permission e.g. in case of permission error
`sudo chmod 755 post-consumption-example.sh`
- Pipe your scripts's output to a log file e.g.
@@ -510,7 +515,7 @@ existing tables) with:
## Barcodes {#barcodes}
Paperless is able to utilize barcodes for automatically preforming some tasks.
Paperless is able to utilize barcodes for automatically performing some tasks.
At this time, the library utilized for detection of barcodes supports the following types:
@@ -566,7 +571,7 @@ collating two separate scans into one document, reordering the pages as necessar
Suppose you have a double-sided document with 6 pages (3 sheets of paper). First,
put the stack into your ADF as normal, ensuring that page 1 is scanned first. Your ADF
will now scan pages 1, 3, and 5. Then you (or your the scanner, if it supports it) upload
will now scan pages 1, 3, and 5. Then you (or your scanner, if it supports it) upload
the scan into the correct sub-directory of the consume folder (`double-sided` by default;
keep in mind that Paperless will _not_ automatically create the directory for you.)
Paperless will then process the scan and move it into an internal staging area.
@@ -608,7 +613,7 @@ scan a completely new "odd numbered pages" one. The old staging file will get di
The collation feature can be used together with the [subdirs as tags](configuration.md#consume_config)
feature (but this is not a requirement). Just create a correctly named double-sided subdir
in the hierachy and upload your scans there. For example, both `double-sided/foo/bar` as
in the hierarchy and upload your scans there. For example, both `double-sided/foo/bar` as
well as `foo/bar/double-sided` will cause the collated document to be treated as if it
were uploaded into `foo/bar` and receive both `foo` and `bar` tags, but not `double-sided`.

View File

@@ -8,19 +8,22 @@ most of the available filters and ordering fields.
The API provides the following main endpoints:
- `/api/correspondents/`: Full CRUD support.
- `/api/custom_fields/`: Full CRUD support.
- `/api/documents/`: Full CRUD support, except POSTing new documents.
See below.
- `/api/correspondents/`: Full CRUD support.
- `/api/document_types/`: Full CRUD support.
- `/api/groups/`: Full CRUD support.
- `/api/logs/`: Read-Only.
- `/api/tags/`: Full CRUD support.
- `/api/tasks/`: Read-only.
- `/api/mail_accounts/`: Full CRUD support.
- `/api/mail_rules/`: Full CRUD support.
- `/api/users/`: Full CRUD support.
- `/api/groups/`: Full CRUD support.
- `/api/profile/`: GET, PATCH
- `/api/share_links/`: Full CRUD support.
- `/api/custom_fields/`: Full CRUD support.
- `/api/storage_paths/`: Full CRUD support.
- `/api/tags/`: Full CRUD support.
- `/api/tasks/`: Read-only.
- `/api/users/`: Full CRUD support.
- `/api/workflows/`: Full CRUD support.
All of these endpoints except for the logging endpoint allow you to
fetch (and edit and delete where appropriate) individual objects by
@@ -53,7 +56,7 @@ fields:
- `set_permissions`: Allows setting document permissions. Optional,
write-only. See [below](#permissions).
- `custom_fields`: Array of custom fields & values, specified as
{ field: CUSTOM_FIELD_ID, value: VALUE }
`{ field: CUSTOM_FIELD_ID, value: VALUE }`
## Downloading documents
@@ -136,7 +139,7 @@ document. Paperless only reports PDF metadata at this point.
## Authorization
The REST api provides three different forms of authentication.
The REST api provides four different forms of authentication.
1. Basic authentication
@@ -157,6 +160,10 @@ The REST api provides three different forms of authentication.
3. Token authentication
You can create (or re-create) an API token by opening the "My Profile"
link in the user dropdown found in the web UI and clicking the circular
arrow button.
Paperless also offers an endpoint to acquire authentication tokens.
POST a username and password as a form or json string to
@@ -168,7 +175,13 @@ The REST api provides three different forms of authentication.
Authorization: Token <token>
```
Tokens can be managed and revoked in the paperless admin.
Tokens can also be managed in the Django admin.
4. Remote User authentication
If already setup (see
[configuration](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER)),
you can authenticate against the API using Remote User auth.
## Searching for documents
@@ -267,6 +280,7 @@ The endpoint supports the following optional form fields:
- `correspondent`: Specify the ID of a correspondent that the consumer
should use for the document.
- `document_type`: Similar to correspondent.
- `storage_path`: Similar to correspondent.
- `tags`: Similar to correspondent. Specify this multiple times to
have multiple tags added to the document.
- `archive_serial_number`: An optional archive serial number to set.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 KiB

After

Width:  |  Height:  |  Size: 559 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View File

@@ -1,14 +1,707 @@
# Changelog
## paperless-ngx 2.3.3
### Enhancements
- Enhancement: Explain behavior of unset app config boolean to user [@shamoon](https://github.com/shamoon) ([#5345](https://github.com/paperless-ngx/paperless-ngx/pull/5345))
- Enhancement: title assignment placeholder error handling, fallback [@shamoon](https://github.com/shamoon) ([#5282](https://github.com/paperless-ngx/paperless-ngx/pull/5282))
### Bug Fixes
- Fix: Don't require the JSON user arguments field, interpret empty string as [@stumpylog](https://github.com/stumpylog) ([#5320](https://github.com/paperless-ngx/paperless-ngx/pull/5320))
### Maintenance
- Chore: Backend dependencies update [@stumpylog](https://github.com/stumpylog) ([#5336](https://github.com/paperless-ngx/paperless-ngx/pull/5336))
- Chore: add pre-commit hook for codespell [@shamoon](https://github.com/shamoon) ([#5324](https://github.com/paperless-ngx/paperless-ngx/pull/5324))
### All App Changes
<details>
<summary>5 changes</summary>
- Enhancement: Explain behavior of unset app config boolean to user [@shamoon](https://github.com/shamoon) ([#5345](https://github.com/paperless-ngx/paperless-ngx/pull/5345))
- Enhancement: title assignment placeholder error handling, fallback [@shamoon](https://github.com/shamoon) ([#5282](https://github.com/paperless-ngx/paperless-ngx/pull/5282))
- Chore: Backend dependencies update [@stumpylog](https://github.com/stumpylog) ([#5336](https://github.com/paperless-ngx/paperless-ngx/pull/5336))
- Fix: Don't require the JSON user arguments field, interpret empty string as [@stumpylog](https://github.com/stumpylog) ([#5320](https://github.com/paperless-ngx/paperless-ngx/pull/5320))
- Chore: add pre-commit hook for codespell [@shamoon](https://github.com/shamoon) ([#5324](https://github.com/paperless-ngx/paperless-ngx/pull/5324))
</details>
## paperless-ngx 2.3.2
### Bug Fixes
- Fix: triggered workflow assignment of customfield fails if field exists in v2.3.1 [@shamoon](https://github.com/shamoon) ([#5302](https://github.com/paperless-ngx/paperless-ngx/pull/5302))
- Fix: Decoding of user arguments for OCR [@stumpylog](https://github.com/stumpylog) ([#5307](https://github.com/paperless-ngx/paperless-ngx/pull/5307))
- Fix: empty workflow trigger match field cannot be saved in v.2.3.1 [@shamoon](https://github.com/shamoon) ([#5301](https://github.com/paperless-ngx/paperless-ngx/pull/5301))
- Fix: Use local time for added/updated workflow triggers [@stumpylog](https://github.com/stumpylog) ([#5304](https://github.com/paperless-ngx/paperless-ngx/pull/5304))
- Fix: workflow edit form loses unsaved changes [@shamoon](https://github.com/shamoon) ([#5299](https://github.com/paperless-ngx/paperless-ngx/pull/5299))
### All App Changes
<details>
<summary>5 changes</summary>
- Fix: triggered workflow assignment of customfield fails if field exists in v2.3.1 [@shamoon](https://github.com/shamoon) ([#5302](https://github.com/paperless-ngx/paperless-ngx/pull/5302))
- Fix: Decoding of user arguments for OCR [@stumpylog](https://github.com/stumpylog) ([#5307](https://github.com/paperless-ngx/paperless-ngx/pull/5307))
- Fix: empty workflow trigger match field cannot be saved in v.2.3.1 [@shamoon](https://github.com/shamoon) ([#5301](https://github.com/paperless-ngx/paperless-ngx/pull/5301))
- Fix: Use local time for added/updated workflow triggers [@stumpylog](https://github.com/stumpylog) ([#5304](https://github.com/paperless-ngx/paperless-ngx/pull/5304))
- Fix: workflow edit form loses unsaved changes [@shamoon](https://github.com/shamoon) ([#5299](https://github.com/paperless-ngx/paperless-ngx/pull/5299))
</details>
## paperless-ngx 2.3.1
### Bug Fixes
- Fix: edit workflow form not displaying trigger settings [@shamoon](https://github.com/shamoon) ([#5276](https://github.com/paperless-ngx/paperless-ngx/pull/5276))
- Fix: Prevent passing 0 pages to OCRMyPDF [@stumpylog](https://github.com/stumpylog) ([#5275](https://github.com/paperless-ngx/paperless-ngx/pull/5275))
### All App Changes
<details>
<summary>2 changes</summary>
- Fix: edit workflow form not displaying trigger settings [@shamoon](https://github.com/shamoon) ([#5276](https://github.com/paperless-ngx/paperless-ngx/pull/5276))
- Fix: Prevent passing 0 pages to OCRMyPDF [@stumpylog](https://github.com/stumpylog) ([#5275](https://github.com/paperless-ngx/paperless-ngx/pull/5275))
</details>
## paperless-ngx 2.3.0
### Notable Changes
- Feature: Workflows [@shamoon](https://github.com/shamoon) ([#5121](https://github.com/paperless-ngx/paperless-ngx/pull/5121))
- Feature: Allow setting backend configuration settings via the UI [@stumpylog](https://github.com/stumpylog) ([#5126](https://github.com/paperless-ngx/paperless-ngx/pull/5126))
### Features
- Feature: Workflows [@shamoon](https://github.com/shamoon) ([#5121](https://github.com/paperless-ngx/paperless-ngx/pull/5121))
- Feature: Allow setting backend configuration settings via the UI [@stumpylog](https://github.com/stumpylog) ([#5126](https://github.com/paperless-ngx/paperless-ngx/pull/5126))
- Enhancement: fetch mails in bulk [@falkenbt](https://github.com/falkenbt) ([#5249](https://github.com/paperless-ngx/paperless-ngx/pull/5249))
- Enhancement: add parameter to post_document API [@bevanjkay](https://github.com/bevanjkay) ([#5217](https://github.com/paperless-ngx/paperless-ngx/pull/5217))
### Bug Fixes
- Chore: Replaces deprecated Django alias with standard library [@stumpylog](https://github.com/stumpylog) ([#5262](https://github.com/paperless-ngx/paperless-ngx/pull/5262))
- Fix: Crash in barcode ASN reading when the file type isn't supported [@stumpylog](https://github.com/stumpylog) ([#5261](https://github.com/paperless-ngx/paperless-ngx/pull/5261))
- Fix: Allows pre-consume scripts to modify the working path again [@stumpylog](https://github.com/stumpylog) ([#5260](https://github.com/paperless-ngx/paperless-ngx/pull/5260))
- Change: Use fnmatch for more sane workflow path matching [@shamoon](https://github.com/shamoon) ([#5250](https://github.com/paperless-ngx/paperless-ngx/pull/5250))
- Fix: zip exports not respecting the --delete option [@stumpylog](https://github.com/stumpylog) ([#5245](https://github.com/paperless-ngx/paperless-ngx/pull/5245))
- Fix: correctly format tip admonition [@ChrisRBe](https://github.com/ChrisRBe) ([#5229](https://github.com/paperless-ngx/paperless-ngx/pull/5229))
- Fix: filename format remove none when part of directory [@shamoon](https://github.com/shamoon) ([#5210](https://github.com/paperless-ngx/paperless-ngx/pull/5210))
- Fix: Improve Performance for Listing and Paginating Documents [@antoinelibert](https://github.com/antoinelibert) ([#5195](https://github.com/paperless-ngx/paperless-ngx/pull/5195))
- Fix: Disable custom field remove button if user does not have permissions [@shamoon](https://github.com/shamoon) ([#5194](https://github.com/paperless-ngx/paperless-ngx/pull/5194))
- Fix: overlapping button focus highlight on login [@shamoon](https://github.com/shamoon) ([#5193](https://github.com/paperless-ngx/paperless-ngx/pull/5193))
- Fix: symmetric doc links with target doc value None [@shamoon](https://github.com/shamoon) ([#5187](https://github.com/paperless-ngx/paperless-ngx/pull/5187))
- Fix: setting empty doc link with docs to be removed [@shamoon](https://github.com/shamoon) ([#5174](https://github.com/paperless-ngx/paperless-ngx/pull/5174))
- Enhancement: improve validation of custom field values [@shamoon](https://github.com/shamoon) ([#5166](https://github.com/paperless-ngx/paperless-ngx/pull/5166))
- Fix: type casting of db values for 'shared by me' filter [@shamoon](https://github.com/shamoon) ([#5155](https://github.com/paperless-ngx/paperless-ngx/pull/5155))
### Documentation
- Fix: correctly format tip admonition [@ChrisRBe](https://github.com/ChrisRBe) ([#5229](https://github.com/paperless-ngx/paperless-ngx/pull/5229))
### Maintenance
- Chore(deps): Bump the actions group with 5 updates [@dependabot](https://github.com/dependabot) ([#5203](https://github.com/paperless-ngx/paperless-ngx/pull/5203))
### Dependencies
<details>
<summary>4 changes</summary>
- Chore(deps): Bump the actions group with 5 updates [@dependabot](https://github.com/dependabot) ([#5203](https://github.com/paperless-ngx/paperless-ngx/pull/5203))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 10 updates [@dependabot](https://github.com/dependabot) ([#5204](https://github.com/paperless-ngx/paperless-ngx/pull/5204))
- Chore(deps-dev): Bump [@<!---->types/node from 20.10.4 to 20.10.6 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.10.4 to 20.10.6 in /src-ui @dependabot) ([#5207](https://github.com/paperless-ngx/paperless-ngx/pull/5207))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#5205](https://github.com/paperless-ngx/paperless-ngx/pull/5205))
</details>
### All App Changes
<details>
<summary>21 changes</summary>
- Chore: Replaces deprecated Django alias with standard library [@stumpylog](https://github.com/stumpylog) ([#5262](https://github.com/paperless-ngx/paperless-ngx/pull/5262))
- Fix: Crash in barcode ASN reading when the file type isn't supported [@stumpylog](https://github.com/stumpylog) ([#5261](https://github.com/paperless-ngx/paperless-ngx/pull/5261))
- Fix: Allows pre-consume scripts to modify the working path again [@stumpylog](https://github.com/stumpylog) ([#5260](https://github.com/paperless-ngx/paperless-ngx/pull/5260))
- Enhancement: add basic filters for listing of custom fields [@shamoon](https://github.com/shamoon) ([#5257](https://github.com/paperless-ngx/paperless-ngx/pull/5257))
- Change: Use fnmatch for more sane workflow path matching [@shamoon](https://github.com/shamoon) ([#5250](https://github.com/paperless-ngx/paperless-ngx/pull/5250))
- Enhancement: fetch mails in bulk [@falkenbt](https://github.com/falkenbt) ([#5249](https://github.com/paperless-ngx/paperless-ngx/pull/5249))
- Fix: zip exports not respecting the --delete option [@stumpylog](https://github.com/stumpylog) ([#5245](https://github.com/paperless-ngx/paperless-ngx/pull/5245))
- Enhancement: add parameter to post_document API [@bevanjkay](https://github.com/bevanjkay) ([#5217](https://github.com/paperless-ngx/paperless-ngx/pull/5217))
- Feature: Workflows [@shamoon](https://github.com/shamoon) ([#5121](https://github.com/paperless-ngx/paperless-ngx/pull/5121))
- Fix: filename format remove none when part of directory [@shamoon](https://github.com/shamoon) ([#5210](https://github.com/paperless-ngx/paperless-ngx/pull/5210))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 10 updates [@dependabot](https://github.com/dependabot) ([#5204](https://github.com/paperless-ngx/paperless-ngx/pull/5204))
- Chore(deps-dev): Bump [@<!---->types/node from 20.10.4 to 20.10.6 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.10.4 to 20.10.6 in /src-ui @dependabot) ([#5207](https://github.com/paperless-ngx/paperless-ngx/pull/5207))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#5205](https://github.com/paperless-ngx/paperless-ngx/pull/5205))
- Fix: Improve Performance for Listing and Paginating Documents [@antoinelibert](https://github.com/antoinelibert) ([#5195](https://github.com/paperless-ngx/paperless-ngx/pull/5195))
- Fix: Disable custom field remove button if user does not have permissions [@shamoon](https://github.com/shamoon) ([#5194](https://github.com/paperless-ngx/paperless-ngx/pull/5194))
- Fix: overlapping button focus highlight on login [@shamoon](https://github.com/shamoon) ([#5193](https://github.com/paperless-ngx/paperless-ngx/pull/5193))
- Fix: symmetric doc links with target doc value None [@shamoon](https://github.com/shamoon) ([#5187](https://github.com/paperless-ngx/paperless-ngx/pull/5187))
- Fix: setting empty doc link with docs to be removed [@shamoon](https://github.com/shamoon) ([#5174](https://github.com/paperless-ngx/paperless-ngx/pull/5174))
- Feature: Allow setting backend configuration settings via the UI [@stumpylog](https://github.com/stumpylog) ([#5126](https://github.com/paperless-ngx/paperless-ngx/pull/5126))
- Enhancement: improve validation of custom field values [@shamoon](https://github.com/shamoon) ([#5166](https://github.com/paperless-ngx/paperless-ngx/pull/5166))
- Fix: type casting of db values for 'shared by me' filter [@shamoon](https://github.com/shamoon) ([#5155](https://github.com/paperless-ngx/paperless-ngx/pull/5155))
</details>
## paperless-ngx 2.2.1
### Bug Fixes
- Fix: saving doc links with no value [@shamoon](https://github.com/shamoon) ([#5144](https://github.com/paperless-ngx/paperless-ngx/pull/5144))
- Fix: allow multiple consumption templates to assign the same custom field [@shamoon](https://github.com/shamoon) ([#5142](https://github.com/paperless-ngx/paperless-ngx/pull/5142))
- Fix: some dropdowns broken in 2.2.0 [@shamoon](https://github.com/shamoon) ([#5134](https://github.com/paperless-ngx/paperless-ngx/pull/5134))
### All App Changes
<details>
<summary>3 changes</summary>
- Fix: saving doc links with no value [@shamoon](https://github.com/shamoon) ([#5144](https://github.com/paperless-ngx/paperless-ngx/pull/5144))
- Fix: allow multiple consumption templates to assign the same custom field [@shamoon](https://github.com/shamoon) ([#5142](https://github.com/paperless-ngx/paperless-ngx/pull/5142))
- Fix: some dropdowns broken in 2.2.0 [@shamoon](https://github.com/shamoon) ([#5134](https://github.com/paperless-ngx/paperless-ngx/pull/5134))
</details>
## paperless-ngx 2.2.0
### Features
- Enhancement: Add tooltip for select dropdown items [@shamoon](https://github.com/shamoon) ([#5070](https://github.com/paperless-ngx/paperless-ngx/pull/5070))
- Chore: Update Angular to v17 including new Angular control-flow [@shamoon](https://github.com/shamoon) ([#4980](https://github.com/paperless-ngx/paperless-ngx/pull/4980))
- Enhancement: symmetric document links [@shamoon](https://github.com/shamoon) ([#4907](https://github.com/paperless-ngx/paperless-ngx/pull/4907))
- Enhancement: shared icon \& shared by me filter [@shamoon](https://github.com/shamoon) ([#4859](https://github.com/paperless-ngx/paperless-ngx/pull/4859))
- Enhancement: Improved popup preview, respect embedded viewer, error handling [@shamoon](https://github.com/shamoon) ([#4947](https://github.com/paperless-ngx/paperless-ngx/pull/4947))
- Enhancement: Allow deletion of documents via the fuzzy matching command [@stumpylog](https://github.com/stumpylog) ([#4957](https://github.com/paperless-ngx/paperless-ngx/pull/4957))
- Enhancement: document link field fixes [@shamoon](https://github.com/shamoon) ([#5020](https://github.com/paperless-ngx/paperless-ngx/pull/5020))
- Enhancement: above and below doc detail save buttons [@shamoon](https://github.com/shamoon) ([#5008](https://github.com/paperless-ngx/paperless-ngx/pull/5008))
### Bug Fixes
- Fix: Case where a mail attachment has no filename to use [@stumpylog](https://github.com/stumpylog) ([#5117](https://github.com/paperless-ngx/paperless-ngx/pull/5117))
- Fix: Disable auto-login for API token requests [@shamoon](https://github.com/shamoon) ([#5094](https://github.com/paperless-ngx/paperless-ngx/pull/5094))
- Fix: update ASN regex to support Unicode [@eukub](https://github.com/eukub) ([#5099](https://github.com/paperless-ngx/paperless-ngx/pull/5099))
- Fix: ensure CSRF-Token on Index view [@baflo](https://github.com/baflo) ([#5082](https://github.com/paperless-ngx/paperless-ngx/pull/5082))
- Fix: Stop auto-refresh logs / tasks after close [@shamoon](https://github.com/shamoon) ([#5089](https://github.com/paperless-ngx/paperless-ngx/pull/5089))
- Fix: Make the admin panel accessible when using a large number of documents [@bogdal](https://github.com/bogdal) ([#5052](https://github.com/paperless-ngx/paperless-ngx/pull/5052))
- Fix: dont allow null property via API [@shamoon](https://github.com/shamoon) ([#5063](https://github.com/paperless-ngx/paperless-ngx/pull/5063))
- Fix: Updates Ghostscript to 10.02.1 for more bug fixes to it [@stumpylog](https://github.com/stumpylog) ([#5040](https://github.com/paperless-ngx/paperless-ngx/pull/5040))
- Fix: allow system keyboard shortcuts in date fields [@shamoon](https://github.com/shamoon) ([#5009](https://github.com/paperless-ngx/paperless-ngx/pull/5009))
- Fix password change detection on profile edit [@shamoon](https://github.com/shamoon) ([#5028](https://github.com/paperless-ngx/paperless-ngx/pull/5028))
### Documentation
- Documentation: organize API endpoints [@dgsponer](https://github.com/dgsponer) ([#5077](https://github.com/paperless-ngx/paperless-ngx/pull/5077))
### Maintenance
- Chore: Bulk backend update [@stumpylog](https://github.com/stumpylog) ([#5061](https://github.com/paperless-ngx/paperless-ngx/pull/5061))
### Dependencies
<details>
<summary>5 changes</summary>
- Chore: Bulk backend update [@stumpylog](https://github.com/stumpylog) ([#5061](https://github.com/paperless-ngx/paperless-ngx/pull/5061))
- Chore(deps): Bump the django group with 3 updates [@dependabot](https://github.com/dependabot) ([#5046](https://github.com/paperless-ngx/paperless-ngx/pull/5046))
- Chore(deps): Bump the major-versions group with 1 update [@dependabot](https://github.com/dependabot) ([#5047](https://github.com/paperless-ngx/paperless-ngx/pull/5047))
- Chore(deps): Bump the small-changes group with 6 updates [@dependabot](https://github.com/dependabot) ([#5048](https://github.com/paperless-ngx/paperless-ngx/pull/5048))
- Fix: Updates Ghostscript to 10.02.1 for more bug fixes to it [@stumpylog](https://github.com/stumpylog) ([#5040](https://github.com/paperless-ngx/paperless-ngx/pull/5040))
</details>
### All App Changes
<details>
<summary>20 changes</summary>
- Fix: Case where a mail attachment has no filename to use [@stumpylog](https://github.com/stumpylog) ([#5117](https://github.com/paperless-ngx/paperless-ngx/pull/5117))
- Fix: Disable auto-login for API token requests [@shamoon](https://github.com/shamoon) ([#5094](https://github.com/paperless-ngx/paperless-ngx/pull/5094))
- Fix: update ASN regex to support Unicode [@eukub](https://github.com/eukub) ([#5099](https://github.com/paperless-ngx/paperless-ngx/pull/5099))
- Fix: ensure CSRF-Token on Index view [@baflo](https://github.com/baflo) ([#5082](https://github.com/paperless-ngx/paperless-ngx/pull/5082))
- Fix: Stop auto-refresh logs / tasks after close [@shamoon](https://github.com/shamoon) ([#5089](https://github.com/paperless-ngx/paperless-ngx/pull/5089))
- Enhancement: Add tooltip for select dropdown items [@shamoon](https://github.com/shamoon) ([#5070](https://github.com/paperless-ngx/paperless-ngx/pull/5070))
- Fix: Make the admin panel accessible when using a large number of documents [@bogdal](https://github.com/bogdal) ([#5052](https://github.com/paperless-ngx/paperless-ngx/pull/5052))
- Chore: Update Angular to v17 including new Angular control-flow [@shamoon](https://github.com/shamoon) ([#4980](https://github.com/paperless-ngx/paperless-ngx/pull/4980))
- Fix: dont allow null property via API [@shamoon](https://github.com/shamoon) ([#5063](https://github.com/paperless-ngx/paperless-ngx/pull/5063))
- Enhancement: symmetric document links [@shamoon](https://github.com/shamoon) ([#4907](https://github.com/paperless-ngx/paperless-ngx/pull/4907))
- Enhancement: shared icon \& shared by me filter [@shamoon](https://github.com/shamoon) ([#4859](https://github.com/paperless-ngx/paperless-ngx/pull/4859))
- Chore(deps): Bump the django group with 3 updates [@dependabot](https://github.com/dependabot) ([#5046](https://github.com/paperless-ngx/paperless-ngx/pull/5046))
- Chore(deps): Bump the major-versions group with 1 update [@dependabot](https://github.com/dependabot) ([#5047](https://github.com/paperless-ngx/paperless-ngx/pull/5047))
- Chore(deps): Bump the small-changes group with 6 updates [@dependabot](https://github.com/dependabot) ([#5048](https://github.com/paperless-ngx/paperless-ngx/pull/5048))
- Enhancement: Improved popup preview, respect embedded viewer, error handling [@shamoon](https://github.com/shamoon) ([#4947](https://github.com/paperless-ngx/paperless-ngx/pull/4947))
- Enhancement: Add {original_filename}, {added_time} to title placeholders [@TTT7275](https://github.com/TTT7275) ([#4972](https://github.com/paperless-ngx/paperless-ngx/pull/4972))
- Feature: Allow deletion of documents via the fuzzy matching command [@stumpylog](https://github.com/stumpylog) ([#4957](https://github.com/paperless-ngx/paperless-ngx/pull/4957))
- Fix: allow system keyboard shortcuts in date fields [@shamoon](https://github.com/shamoon) ([#5009](https://github.com/paperless-ngx/paperless-ngx/pull/5009))
- Enhancement: document link field fixes [@shamoon](https://github.com/shamoon) ([#5020](https://github.com/paperless-ngx/paperless-ngx/pull/5020))
- Fix password change detection on profile edit [@shamoon](https://github.com/shamoon) ([#5028](https://github.com/paperless-ngx/paperless-ngx/pull/5028))
</details>
## paperless-ngx 2.1.3
### Bug Fixes
- Fix: Document metadata is lost during barcode splitting [@stumpylog](https://github.com/stumpylog) ([#4982](https://github.com/paperless-ngx/paperless-ngx/pull/4982))
- Fix: Export of custom field instances during a split manifest export [@stumpylog](https://github.com/stumpylog) ([#4984](https://github.com/paperless-ngx/paperless-ngx/pull/4984))
- Fix: Apply user arguments even in the case of the forcing OCR [@stumpylog](https://github.com/stumpylog) ([#4981](https://github.com/paperless-ngx/paperless-ngx/pull/4981))
- Fix: support show errors for select dropdowns [@shamoon](https://github.com/shamoon) ([#4979](https://github.com/paperless-ngx/paperless-ngx/pull/4979))
- Fix: Don't attempt to parse none objects during date searching [@bogdal](https://github.com/bogdal) ([#4977](https://github.com/paperless-ngx/paperless-ngx/pull/4977))
### All App Changes
<details>
<summary>6 changes</summary>
- Refactor: Boost performance by reducing db queries [@bogdal](https://github.com/bogdal) ([#4990](https://github.com/paperless-ngx/paperless-ngx/pull/4990))
- Fix: Document metadata is lost during barcode splitting [@stumpylog](https://github.com/stumpylog) ([#4982](https://github.com/paperless-ngx/paperless-ngx/pull/4982))
- Fix: Export of custom field instances during a split manifest export [@stumpylog](https://github.com/stumpylog) ([#4984](https://github.com/paperless-ngx/paperless-ngx/pull/4984))
- Fix: Apply user arguments even in the case of the forcing OCR [@stumpylog](https://github.com/stumpylog) ([#4981](https://github.com/paperless-ngx/paperless-ngx/pull/4981))
- Fix: support show errors for select dropdowns [@shamoon](https://github.com/shamoon) ([#4979](https://github.com/paperless-ngx/paperless-ngx/pull/4979))
- Fix: Don't attempt to parse none objects during date searching [@bogdal](https://github.com/bogdal) ([#4977](https://github.com/paperless-ngx/paperless-ngx/pull/4977))
</details>
## paperless-ngx 2.1.2
### Bug Fixes
- Fix: sort consumption templates by order by default [@shamoon](https://github.com/shamoon) ([#4956](https://github.com/paperless-ngx/paperless-ngx/pull/4956))
- Fix: Updates gotenberg-client, including workaround for Gotenberg non-latin handling [@stumpylog](https://github.com/stumpylog) ([#4944](https://github.com/paperless-ngx/paperless-ngx/pull/4944))
- Fix: allow text copy in pngx pdf viewer [@shamoon](https://github.com/shamoon) ([#4938](https://github.com/paperless-ngx/paperless-ngx/pull/4938))
- Fix: Don't allow autocomplete searches to fail on schema field matches [@stumpylog](https://github.com/stumpylog) ([#4934](https://github.com/paperless-ngx/paperless-ngx/pull/4934))
- Fix: Convert search dates to UTC in advanced search [@bogdal](https://github.com/bogdal) ([#4891](https://github.com/paperless-ngx/paperless-ngx/pull/4891))
- Fix: Use the attachment filename so downstream template matching works [@stumpylog](https://github.com/stumpylog) ([#4931](https://github.com/paperless-ngx/paperless-ngx/pull/4931))
- Fix: frontend handle autocomplete failure gracefully [@shamoon](https://github.com/shamoon) ([#4903](https://github.com/paperless-ngx/paperless-ngx/pull/4903))
### Dependencies
- Chore(deps-dev): Bump the small-changes group with 2 updates [@dependabot](https://github.com/dependabot) ([#4942](https://github.com/paperless-ngx/paperless-ngx/pull/4942))
- Chore(deps-dev): Bump the development group with 1 update [@dependabot](https://github.com/dependabot) ([#4939](https://github.com/paperless-ngx/paperless-ngx/pull/4939))
### All App Changes
<details>
<summary>9 changes</summary>
- Fix: sort consumption templates by order by default [@shamoon](https://github.com/shamoon) ([#4956](https://github.com/paperless-ngx/paperless-ngx/pull/4956))
- Chore: reorganize api tests [@shamoon](https://github.com/shamoon) ([#4935](https://github.com/paperless-ngx/paperless-ngx/pull/4935))
- Chore(deps-dev): Bump the small-changes group with 2 updates [@dependabot](https://github.com/dependabot) ([#4942](https://github.com/paperless-ngx/paperless-ngx/pull/4942))
- Fix: allow text copy in pngx pdf viewer [@shamoon](https://github.com/shamoon) ([#4938](https://github.com/paperless-ngx/paperless-ngx/pull/4938))
- Chore(deps-dev): Bump the development group with 1 update [@dependabot](https://github.com/dependabot) ([#4939](https://github.com/paperless-ngx/paperless-ngx/pull/4939))
- Fix: Don't allow autocomplete searches to fail on schema field matches [@stumpylog](https://github.com/stumpylog) ([#4934](https://github.com/paperless-ngx/paperless-ngx/pull/4934))
- Fix: Convert search dates to UTC in advanced search [@bogdal](https://github.com/bogdal) ([#4891](https://github.com/paperless-ngx/paperless-ngx/pull/4891))
- Fix: Use the attachment filename so downstream template matching works [@stumpylog](https://github.com/stumpylog) ([#4931](https://github.com/paperless-ngx/paperless-ngx/pull/4931))
- Fix: frontend handle autocomplete failure gracefully [@shamoon](https://github.com/shamoon) ([#4903](https://github.com/paperless-ngx/paperless-ngx/pull/4903))
</details>
## paperless-ngx 2.1.1
### Bug Fixes
- Fix: disable toggle for share link creation without archive version, fix auto-copy in Safari [@shamoon](https://github.com/shamoon) ([#4885](https://github.com/paperless-ngx/paperless-ngx/pull/4885))
- Fix: storage paths link incorrect in dashboard widget [@shamoon](https://github.com/shamoon) ([#4878](https://github.com/paperless-ngx/paperless-ngx/pull/4878))
- Fix: respect baseURI for pdfjs worker URL [@shamoon](https://github.com/shamoon) ([#4865](https://github.com/paperless-ngx/paperless-ngx/pull/4865))
- Fix: Allow users to configure the From email for password reset [@stumpylog](https://github.com/stumpylog) ([#4867](https://github.com/paperless-ngx/paperless-ngx/pull/4867))
- Fix: dont show move icon for file tasks badge [@shamoon](https://github.com/shamoon) ([#4860](https://github.com/paperless-ngx/paperless-ngx/pull/4860))
### Maintenance
- Chore: Simplifies how the documentation site is deployed [@stumpylog](https://github.com/stumpylog) ([#4858](https://github.com/paperless-ngx/paperless-ngx/pull/4858))
### All App Changes
<details>
<summary>5 changes</summary>
- Fix: disable toggle for share link creation without archive version, fix auto-copy in Safari [@shamoon](https://github.com/shamoon) ([#4885](https://github.com/paperless-ngx/paperless-ngx/pull/4885))
- Fix: storage paths link incorrect in dashboard widget [@shamoon](https://github.com/shamoon) ([#4878](https://github.com/paperless-ngx/paperless-ngx/pull/4878))
- Fix: respect baseURI for pdfjs worker URL [@shamoon](https://github.com/shamoon) ([#4865](https://github.com/paperless-ngx/paperless-ngx/pull/4865))
- Fix: Allow users to configure the From email for password reset [@stumpylog](https://github.com/stumpylog) ([#4867](https://github.com/paperless-ngx/paperless-ngx/pull/4867))
- Fix: dont show move icon for file tasks badge [@shamoon](https://github.com/shamoon) ([#4860](https://github.com/paperless-ngx/paperless-ngx/pull/4860))
</details>
## paperless-ngx 2.1.0
### Features
- Enhancement: implement document link custom field [@shamoon](https://github.com/shamoon) ([#4799](https://github.com/paperless-ngx/paperless-ngx/pull/4799))
- Feature: Adds additional warnings during an import if it might fail [@stumpylog](https://github.com/stumpylog) ([#4814](https://github.com/paperless-ngx/paperless-ngx/pull/4814))
- Feature: pngx PDF viewer with updated pdfjs [@shamoon](https://github.com/shamoon) ([#4679](https://github.com/paperless-ngx/paperless-ngx/pull/4679))
- Enhancement: support automatically assigning custom fields via consumption templates [@shamoon](https://github.com/shamoon) ([#4727](https://github.com/paperless-ngx/paperless-ngx/pull/4727))
- Feature: update user profile [@shamoon](https://github.com/shamoon) ([#4678](https://github.com/paperless-ngx/paperless-ngx/pull/4678))
- Enhancement: Allow excluding mail attachments by name [@stumpylog](https://github.com/stumpylog) ([#4691](https://github.com/paperless-ngx/paperless-ngx/pull/4691))
- Enhancement: auto-refresh logs \& tasks [@shamoon](https://github.com/shamoon) ([#4680](https://github.com/paperless-ngx/paperless-ngx/pull/4680))
### Bug Fixes
- Fix: welcome widget text color [@shamoon](https://github.com/shamoon) ([#4829](https://github.com/paperless-ngx/paperless-ngx/pull/4829))
- Fix: export consumption templates \& custom fields in exporter [@shamoon](https://github.com/shamoon) ([#4825](https://github.com/paperless-ngx/paperless-ngx/pull/4825))
- Fix: bulk edit object permissions should use permissions object [@shamoon](https://github.com/shamoon) ([#4797](https://github.com/paperless-ngx/paperless-ngx/pull/4797))
- Fix: empty string for consumption template field should be interpreted as [@shamoon](https://github.com/shamoon) ([#4762](https://github.com/paperless-ngx/paperless-ngx/pull/4762))
- Fix: use default permissions for objects created via dropdown [@shamoon](https://github.com/shamoon) ([#4778](https://github.com/paperless-ngx/paperless-ngx/pull/4778))
- Fix: Alpha layer removal could allow duplicates [@stumpylog](https://github.com/stumpylog) ([#4781](https://github.com/paperless-ngx/paperless-ngx/pull/4781))
- Fix: update checker broke in v2.0.0 [@shamoon](https://github.com/shamoon) ([#4773](https://github.com/paperless-ngx/paperless-ngx/pull/4773))
- Fix: only show global drag-drop when files included [@shamoon](https://github.com/shamoon) ([#4767](https://github.com/paperless-ngx/paperless-ngx/pull/4767))
### Documentation
- Enhancement: implement document link custom field [@shamoon](https://github.com/shamoon) ([#4799](https://github.com/paperless-ngx/paperless-ngx/pull/4799))
- Fix: export consumption templates \& custom fields in exporter [@shamoon](https://github.com/shamoon) ([#4825](https://github.com/paperless-ngx/paperless-ngx/pull/4825))
- Documentation: Fix typos [@omahs](https://github.com/omahs) ([#4737](https://github.com/paperless-ngx/paperless-ngx/pull/4737))
### Maintenance
- Bump the actions group with 2 updates [@dependabot](https://github.com/dependabot) ([#4745](https://github.com/paperless-ngx/paperless-ngx/pull/4745))
### Dependencies
<details>
<summary>7 changes</summary>
- Bump the development group with 6 updates [@dependabot](https://github.com/dependabot) ([#4838](https://github.com/paperless-ngx/paperless-ngx/pull/4838))
- Bump the actions group with 2 updates [@dependabot](https://github.com/dependabot) ([#4745](https://github.com/paperless-ngx/paperless-ngx/pull/4745))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4756](https://github.com/paperless-ngx/paperless-ngx/pull/4756))
- Bump the frontend-jest-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#4744](https://github.com/paperless-ngx/paperless-ngx/pull/4744))
- Bump [@<!---->playwright/test from 1.39.0 to 1.40.1 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.39.0 to 1.40.1 in /src-ui @dependabot) ([#4749](https://github.com/paperless-ngx/paperless-ngx/pull/4749))
- Bump wait-on from 7.0.1 to 7.2.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#4747](https://github.com/paperless-ngx/paperless-ngx/pull/4747))
- Bump [@<!---->types/node from 20.8.10 to 20.10.2 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.8.10 to 20.10.2 in /src-ui @dependabot) ([#4748](https://github.com/paperless-ngx/paperless-ngx/pull/4748))
</details>
### All App Changes
<details>
<summary>20 changes</summary>
- Enhancement: implement document link custom field [@shamoon](https://github.com/shamoon) ([#4799](https://github.com/paperless-ngx/paperless-ngx/pull/4799))
- Bump the development group with 6 updates [@dependabot](https://github.com/dependabot) ([#4838](https://github.com/paperless-ngx/paperless-ngx/pull/4838))
- Fix: welcome widget text color [@shamoon](https://github.com/shamoon) ([#4829](https://github.com/paperless-ngx/paperless-ngx/pull/4829))
- Fix: export consumption templates \& custom fields in exporter [@shamoon](https://github.com/shamoon) ([#4825](https://github.com/paperless-ngx/paperless-ngx/pull/4825))
- Feature: Adds additional warnings during an import if it might fail [@stumpylog](https://github.com/stumpylog) ([#4814](https://github.com/paperless-ngx/paperless-ngx/pull/4814))
- Feature: pngx PDF viewer with updated pdfjs [@shamoon](https://github.com/shamoon) ([#4679](https://github.com/paperless-ngx/paperless-ngx/pull/4679))
- Fix: bulk edit object permissions should use permissions object [@shamoon](https://github.com/shamoon) ([#4797](https://github.com/paperless-ngx/paperless-ngx/pull/4797))
- Enhancement: support automatically assigning custom fields via consumption templates [@shamoon](https://github.com/shamoon) ([#4727](https://github.com/paperless-ngx/paperless-ngx/pull/4727))
- Fix: empty string for consumption template field should be interpreted as [@shamoon](https://github.com/shamoon) ([#4762](https://github.com/paperless-ngx/paperless-ngx/pull/4762))
- Fix: use default permissions for objects created via dropdown [@shamoon](https://github.com/shamoon) ([#4778](https://github.com/paperless-ngx/paperless-ngx/pull/4778))
- Fix: Alpha layer removal could allow duplicates [@stumpylog](https://github.com/stumpylog) ([#4781](https://github.com/paperless-ngx/paperless-ngx/pull/4781))
- Feature: update user profile [@shamoon](https://github.com/shamoon) ([#4678](https://github.com/paperless-ngx/paperless-ngx/pull/4678))
- Fix: update checker broke in v2.0.0 [@shamoon](https://github.com/shamoon) ([#4773](https://github.com/paperless-ngx/paperless-ngx/pull/4773))
- Fix: only show global drag-drop when files included [@shamoon](https://github.com/shamoon) ([#4767](https://github.com/paperless-ngx/paperless-ngx/pull/4767))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4756](https://github.com/paperless-ngx/paperless-ngx/pull/4756))
- Bump the frontend-jest-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#4744](https://github.com/paperless-ngx/paperless-ngx/pull/4744))
- Bump [@<!---->playwright/test from 1.39.0 to 1.40.1 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.39.0 to 1.40.1 in /src-ui @dependabot) ([#4749](https://github.com/paperless-ngx/paperless-ngx/pull/4749))
- Bump wait-on from 7.0.1 to 7.2.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#4747](https://github.com/paperless-ngx/paperless-ngx/pull/4747))
- Bump [@<!---->types/node from 20.8.10 to 20.10.2 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.8.10 to 20.10.2 in /src-ui @dependabot) ([#4748](https://github.com/paperless-ngx/paperless-ngx/pull/4748))
- Enhancement: auto-refresh logs \& tasks [@shamoon](https://github.com/shamoon) ([#4680](https://github.com/paperless-ngx/paperless-ngx/pull/4680))
</details>
## paperless-ngx 2.0.1
### Please Note
Exports generated in Paperless-ngx v2.0.02.0.1 will **not** contain consumption templates or custom fields, we recommend users upgrade to at least v2.1.
### Bug Fixes
- Fix: Increase field the length for consumption template source [@stumpylog](https://github.com/stumpylog) ([#4719](https://github.com/paperless-ngx/paperless-ngx/pull/4719))
- Fix: Set RGB color conversion strategy for PDF outputs [@stumpylog](https://github.com/stumpylog) ([#4709](https://github.com/paperless-ngx/paperless-ngx/pull/4709))
- Fix: Add a warning about a low image DPI which may cause OCR to fail [@stumpylog](https://github.com/stumpylog) ([#4708](https://github.com/paperless-ngx/paperless-ngx/pull/4708))
- Fix: share links for URLs containing 'api' incorrect in dropdown [@shamoon](https://github.com/shamoon) ([#4701](https://github.com/paperless-ngx/paperless-ngx/pull/4701))
### All App Changes
<details>
<summary>4 changes</summary>
- Fix: Increase field the length for consumption template source [@stumpylog](https://github.com/stumpylog) ([#4719](https://github.com/paperless-ngx/paperless-ngx/pull/4719))
- Fix: Set RGB color conversion strategy for PDF outputs [@stumpylog](https://github.com/stumpylog) ([#4709](https://github.com/paperless-ngx/paperless-ngx/pull/4709))
- Fix: Add a warning about a low image DPI which may cause OCR to fail [@stumpylog](https://github.com/stumpylog) ([#4708](https://github.com/paperless-ngx/paperless-ngx/pull/4708))
- Fix: share links for URLs containing 'api' incorrect in dropdown [@shamoon](https://github.com/shamoon) ([#4701](https://github.com/paperless-ngx/paperless-ngx/pull/4701))
</details>
## paperless-ngx 2.0.0
### Please Note
Exports generated in Paperless-ngx v2.0.02.0.1 will **not** contain consumption templates or custom fields, we recommend users upgrade to at least v2.1.
### Breaking Changes
- Breaking: Rename the environment variable for self-signed email certificates [@stumpylog](https://github.com/stumpylog) ([#4346](https://github.com/paperless-ngx/paperless-ngx/pull/4346))
- Breaking: Drop support for Python 3.8 [@stumpylog](https://github.com/stumpylog) ([#4156](https://github.com/paperless-ngx/paperless-ngx/pull/4156))
- Breaking: Remove ARMv7 building of the Docker image [@stumpylog](https://github.com/stumpylog) ([#3973](https://github.com/paperless-ngx/paperless-ngx/pull/3973))
### Notable Changes
- Feature: consumption templates [@shamoon](https://github.com/shamoon) ([#4196](https://github.com/paperless-ngx/paperless-ngx/pull/4196))
- Feature: Share links [@shamoon](https://github.com/shamoon) ([#3996](https://github.com/paperless-ngx/paperless-ngx/pull/3996))
- Enhancement: Updates the underlying image to use Python 3.11 [@stumpylog](https://github.com/stumpylog) ([#4150](https://github.com/paperless-ngx/paperless-ngx/pull/4150))
### Features
- Feature: compact notifications [@shamoon](https://github.com/shamoon) ([#4545](https://github.com/paperless-ngx/paperless-ngx/pull/4545))
- Chore: Backend bulk updates [@stumpylog](https://github.com/stumpylog) ([#4509](https://github.com/paperless-ngx/paperless-ngx/pull/4509))
- Feature: Hungarian translation [@shamoon](https://github.com/shamoon) ([#4552](https://github.com/paperless-ngx/paperless-ngx/pull/4552))
- Chore: API support for id args for documents \& objects [@shamoon](https://github.com/shamoon) ([#4519](https://github.com/paperless-ngx/paperless-ngx/pull/4519))
- Feature: Add Bulgarian translation [@shamoon](https://github.com/shamoon) ([#4470](https://github.com/paperless-ngx/paperless-ngx/pull/4470))
- Feature: Audit Trail [@nanokatz](https://github.com/nanokatz) ([#4425](https://github.com/paperless-ngx/paperless-ngx/pull/4425))
- Feature: Add ahead of time compression of the static files for x86_64 [@stumpylog](https://github.com/stumpylog) ([#4390](https://github.com/paperless-ngx/paperless-ngx/pull/4390))
- Feature: sort sidebar views [@shamoon](https://github.com/shamoon) ([#4381](https://github.com/paperless-ngx/paperless-ngx/pull/4381))
- Feature: Switches to a new client to handle communication with Gotenberg [@stumpylog](https://github.com/stumpylog) ([#4391](https://github.com/paperless-ngx/paperless-ngx/pull/4391))
- barcode logic: strip non-numeric characters from detected ASN string [@queaker](https://github.com/queaker) ([#4379](https://github.com/paperless-ngx/paperless-ngx/pull/4379))
- Feature: Include more updated base tools in Docker image [@stumpylog](https://github.com/stumpylog) ([#4319](https://github.com/paperless-ngx/paperless-ngx/pull/4319))
- CI: speed-up frontend tests on ci [@shamoon](https://github.com/shamoon) ([#4316](https://github.com/paperless-ngx/paperless-ngx/pull/4316))
- Feature: password reset [@shamoon](https://github.com/shamoon) ([#4289](https://github.com/paperless-ngx/paperless-ngx/pull/4289))
- Enhancement: dashboard improvements, drag-n-drop reorder dashboard views [@shamoon](https://github.com/shamoon) ([#4252](https://github.com/paperless-ngx/paperless-ngx/pull/4252))
- Feature: Updates Django to 4.2.5 [@stumpylog](https://github.com/stumpylog) ([#4278](https://github.com/paperless-ngx/paperless-ngx/pull/4278))
- Enhancement: settings reorganization \& improvements, separate admin section [@shamoon](https://github.com/shamoon) ([#4251](https://github.com/paperless-ngx/paperless-ngx/pull/4251))
- Feature: consumption templates [@shamoon](https://github.com/shamoon) ([#4196](https://github.com/paperless-ngx/paperless-ngx/pull/4196))
- Enhancement: support default permissions for object creation via frontend [@shamoon](https://github.com/shamoon) ([#4233](https://github.com/paperless-ngx/paperless-ngx/pull/4233))
- Fix: Set permissions before declaring volumes for rootless [@stumpylog](https://github.com/stumpylog) ([#4225](https://github.com/paperless-ngx/paperless-ngx/pull/4225))
- Enhancement: bulk edit object permissions [@shamoon](https://github.com/shamoon) ([#4176](https://github.com/paperless-ngx/paperless-ngx/pull/4176))
- Enhancement: Allow the user the specify the export zip file name [@stumpylog](https://github.com/stumpylog) ([#4189](https://github.com/paperless-ngx/paperless-ngx/pull/4189))
- Feature: Share links [@shamoon](https://github.com/shamoon) ([#3996](https://github.com/paperless-ngx/paperless-ngx/pull/3996))
- Chore: update docker image and ci to node 20 [@shamoon](https://github.com/shamoon) ([#4184](https://github.com/paperless-ngx/paperless-ngx/pull/4184))
- Fix: Trim unneeded libraries from Docker image [@stumpylog](https://github.com/stumpylog) ([#4183](https://github.com/paperless-ngx/paperless-ngx/pull/4183))
- Feature: New management command for fuzzy matching document content [@stumpylog](https://github.com/stumpylog) ([#4160](https://github.com/paperless-ngx/paperless-ngx/pull/4160))
- Enhancement: Updates the underlying image to use Python 3.11 [@stumpylog](https://github.com/stumpylog) ([#4150](https://github.com/paperless-ngx/paperless-ngx/pull/4150))
- Enhancement: frontend better handle slow backend requests [@shamoon](https://github.com/shamoon) ([#4055](https://github.com/paperless-ngx/paperless-ngx/pull/4055))
- Chore: update docker image \& ci testing node to v18 [@shamoon](https://github.com/shamoon) ([#4149](https://github.com/paperless-ngx/paperless-ngx/pull/4149))
- Enhancement: Improved error notifications [@shamoon](https://github.com/shamoon) ([#4062](https://github.com/paperless-ngx/paperless-ngx/pull/4062))
- Feature: Official support for Python 3.11 [@stumpylog](https://github.com/stumpylog) ([#4146](https://github.com/paperless-ngx/paperless-ngx/pull/4146))
- Enhancement: Add Afrikaans, Greek \& Norwegian languages [@shamoon](https://github.com/shamoon) ([#4088](https://github.com/paperless-ngx/paperless-ngx/pull/4088))
- Enhancement: add task id to pre/post consume script as env [@andreheuer](https://github.com/andreheuer) ([#4037](https://github.com/paperless-ngx/paperless-ngx/pull/4037))
- Enhancement: update bootstrap to v5.3.1 for backend static pages [@shamoon](https://github.com/shamoon) ([#4060](https://github.com/paperless-ngx/paperless-ngx/pull/4060))
### Bug Fixes
- Fix: Add missing spaces to help string in [@joouha](https://github.com/joouha) ([#4674](https://github.com/paperless-ngx/paperless-ngx/pull/4674))
- Fix: Typo invalidates precondition for doctype, resulting in Exception [@ArminGruner](https://github.com/ArminGruner) ([#4668](https://github.com/paperless-ngx/paperless-ngx/pull/4668))
- Fix: Miscellaneous visual fixes in v2.0.0-beta.rc1 2 [@shamoon](https://github.com/shamoon) ([#4635](https://github.com/paperless-ngx/paperless-ngx/pull/4635))
- Fix: Delay consumption after MODIFY inotify events [@frozenbrain](https://github.com/frozenbrain) ([#4626](https://github.com/paperless-ngx/paperless-ngx/pull/4626))
- Documentation: Add note that trash dir must exist [@shamoon](https://github.com/shamoon) ([#4608](https://github.com/paperless-ngx/paperless-ngx/pull/4608))
- Fix: Miscellaneous v2.0 visual fixes [@shamoon](https://github.com/shamoon) ([#4576](https://github.com/paperless-ngx/paperless-ngx/pull/4576))
- Fix: Force UTF-8 for exporter manifests and don't allow escaping [@stumpylog](https://github.com/stumpylog) ([#4574](https://github.com/paperless-ngx/paperless-ngx/pull/4574))
- Fix: plain text preview overflows [@shamoon](https://github.com/shamoon) ([#4555](https://github.com/paperless-ngx/paperless-ngx/pull/4555))
- Fix: add permissions for custom fields with migration [@shamoon](https://github.com/shamoon) ([#4513](https://github.com/paperless-ngx/paperless-ngx/pull/4513))
- Fix: visually hidden text breaks delete button wrap [@shamoon](https://github.com/shamoon) ([#4462](https://github.com/paperless-ngx/paperless-ngx/pull/4462))
- Fix: API statistics document_file_type_counts return type [@shamoon](https://github.com/shamoon) ([#4464](https://github.com/paperless-ngx/paperless-ngx/pull/4464))
- Fix: Always return a list for audit log check [@shamoon](https://github.com/shamoon) ([#4463](https://github.com/paperless-ngx/paperless-ngx/pull/4463))
- Fix: Only create a Correspondent if the email matches rule filters [@stumpylog](https://github.com/stumpylog) ([#4431](https://github.com/paperless-ngx/paperless-ngx/pull/4431))
- Fix: Combination of consume template with recursive tagging [@stumpylog](https://github.com/stumpylog) ([#4442](https://github.com/paperless-ngx/paperless-ngx/pull/4442))
- Fix: replace drag drop \& clipboard deps with angular cdk [@shamoon](https://github.com/shamoon) ([#4362](https://github.com/paperless-ngx/paperless-ngx/pull/4362))
- Fix: update document modified time on note creation / deletion [@shamoon](https://github.com/shamoon) ([#4374](https://github.com/paperless-ngx/paperless-ngx/pull/4374))
- Fix: Updates to latest imap_tools which includes fix for the meta charset in HTML content [@stumpylog](https://github.com/stumpylog) ([#4355](https://github.com/paperless-ngx/paperless-ngx/pull/4355))
- Fix: Missing creation of a folder in Docker image [@stumpylog](https://github.com/stumpylog) ([#4347](https://github.com/paperless-ngx/paperless-ngx/pull/4347))
- Fix: Retry Tika parsing when Tika returns HTTP 500 [@stumpylog](https://github.com/stumpylog) ([#4334](https://github.com/paperless-ngx/paperless-ngx/pull/4334))
- Fix: get highest ASN regardless of user [@shamoon](https://github.com/shamoon) ([#4326](https://github.com/paperless-ngx/paperless-ngx/pull/4326))
- Fix: Generate secret key with C locale and increase allowed characters [@stumpylog](https://github.com/stumpylog) ([#4277](https://github.com/paperless-ngx/paperless-ngx/pull/4277))
- Fix: long notes cause visual overflow [@shamoon](https://github.com/shamoon) ([#4287](https://github.com/paperless-ngx/paperless-ngx/pull/4287))
- Fix: Ensures all old connections are closed in certain long lived places [@stumpylog](https://github.com/stumpylog) ([#4265](https://github.com/paperless-ngx/paperless-ngx/pull/4265))
- CI: fix playwright browser version mismatch failures [@shamoon](https://github.com/shamoon) ([#4239](https://github.com/paperless-ngx/paperless-ngx/pull/4239))
- Fix: Set a non-zero polling internal when inotify cannot import [@stumpylog](https://github.com/stumpylog) ([#4230](https://github.com/paperless-ngx/paperless-ngx/pull/4230))
- Fix: Set permissions before declaring volumes for rootless [@stumpylog](https://github.com/stumpylog) ([#4225](https://github.com/paperless-ngx/paperless-ngx/pull/4225))
- Documentation: Fix fuzzy matching details [@stumpylog](https://github.com/stumpylog) ([#4207](https://github.com/paperless-ngx/paperless-ngx/pull/4207))
- Fix: application of theme color vars at root [@shamoon](https://github.com/shamoon) ([#4193](https://github.com/paperless-ngx/paperless-ngx/pull/4193))
- Fix: Trim unneeded libraries from Docker image [@stumpylog](https://github.com/stumpylog) ([#4183](https://github.com/paperless-ngx/paperless-ngx/pull/4183))
- Fix: support storage path placeholder via API [@shamoon](https://github.com/shamoon) ([#4179](https://github.com/paperless-ngx/paperless-ngx/pull/4179))
- Fix: Logs the errors during thumbnail generation [@stumpylog](https://github.com/stumpylog) ([#4171](https://github.com/paperless-ngx/paperless-ngx/pull/4171))
- Fix: remove owner details from saved_views api endpoint [@shamoon](https://github.com/shamoon) ([#4158](https://github.com/paperless-ngx/paperless-ngx/pull/4158))
- Fix: dashboard widget card borders hidden by bkgd color [@shamoon](https://github.com/shamoon) ([#4155](https://github.com/paperless-ngx/paperless-ngx/pull/4155))
- Fix: hide entire add user / group buttons if insufficient permissions [@shamoon](https://github.com/shamoon) ([#4133](https://github.com/paperless-ngx/paperless-ngx/pull/4133))
### Documentation
- Documentation: Update documentation to refer only to Docker Compose v2 command [@stumpylog](https://github.com/stumpylog) ([#4650](https://github.com/paperless-ngx/paperless-ngx/pull/4650))
- Documentation: fix typo, add to features list [@tooomm](https://github.com/tooomm) ([#4624](https://github.com/paperless-ngx/paperless-ngx/pull/4624))
- Documentation: Add note that trash dir must exist [@shamoon](https://github.com/shamoon) ([#4608](https://github.com/paperless-ngx/paperless-ngx/pull/4608))
- Documentation: Structure backup sections more clearly [@quantenProjects](https://github.com/quantenProjects) ([#4559](https://github.com/paperless-ngx/paperless-ngx/pull/4559))
- Documentation: update docs, screenshots ahead of Paperless-ngx v2.0 [@shamoon](https://github.com/shamoon) ([#4542](https://github.com/paperless-ngx/paperless-ngx/pull/4542))
- Chore: Cleanup command arguments and standardize process count handling [@stumpylog](https://github.com/stumpylog) ([#4541](https://github.com/paperless-ngx/paperless-ngx/pull/4541))
- Add section for SELinux troubleshooting [@nachtjasmin](https://github.com/nachtjasmin) ([#4528](https://github.com/paperless-ngx/paperless-ngx/pull/4528))
- Documentation: clarify document_exporter includes settings [@coaxial](https://github.com/coaxial) ([#4533](https://github.com/paperless-ngx/paperless-ngx/pull/4533))
- Change: Install script improvements [@m-GDEV](https://github.com/m-GDEV) ([#4387](https://github.com/paperless-ngx/paperless-ngx/pull/4387))
- Fix: update document modified time on note creation / deletion [@shamoon](https://github.com/shamoon) ([#4374](https://github.com/paperless-ngx/paperless-ngx/pull/4374))
- Fix: correct set owner API location in docs, additional test [@shamoon](https://github.com/shamoon) ([#4366](https://github.com/paperless-ngx/paperless-ngx/pull/4366))
- Documentation: Remove old information about building the Docker image locally [@stumpylog](https://github.com/stumpylog) ([#4354](https://github.com/paperless-ngx/paperless-ngx/pull/4354))
- Documentation enhancement: add direct links for all config vars [@shamoon](https://github.com/shamoon) ([#4237](https://github.com/paperless-ngx/paperless-ngx/pull/4237))
- Documentation: Fix fuzzy matching details [@stumpylog](https://github.com/stumpylog) ([#4207](https://github.com/paperless-ngx/paperless-ngx/pull/4207))
### Maintenance
- Chore: Backend bulk updates [@stumpylog](https://github.com/stumpylog) ([#4509](https://github.com/paperless-ngx/paperless-ngx/pull/4509))
- Bump the actions group with 1 update [@dependabot](https://github.com/dependabot) ([#4476](https://github.com/paperless-ngx/paperless-ngx/pull/4476))
- Feature: Add Bulgarian translation [@shamoon](https://github.com/shamoon) ([#4470](https://github.com/paperless-ngx/paperless-ngx/pull/4470))
- Chore: Stop duplicated action runs against internal PRs [@stumpylog](https://github.com/stumpylog) ([#4430](https://github.com/paperless-ngx/paperless-ngx/pull/4430))
- CI: separate frontend deps install [@shamoon](https://github.com/shamoon) ([#4336](https://github.com/paperless-ngx/paperless-ngx/pull/4336))
- CI: speed-up frontend tests on ci [@shamoon](https://github.com/shamoon) ([#4316](https://github.com/paperless-ngx/paperless-ngx/pull/4316))
- Fix: Generate secret key with C locale and increase allowed characters [@stumpylog](https://github.com/stumpylog) ([#4277](https://github.com/paperless-ngx/paperless-ngx/pull/4277))
- Bump leonsteinhaeuser/project-beta-automations from 2.1.0 to 2.2.1 [@dependabot](https://github.com/dependabot) ([#4281](https://github.com/paperless-ngx/paperless-ngx/pull/4281))
- Chore: Updates dependabot to group more dependencies [@stumpylog](https://github.com/stumpylog) ([#4280](https://github.com/paperless-ngx/paperless-ngx/pull/4280))
- Change: update translation string for tasks dialog [@shamoon](https://github.com/shamoon) ([#4263](https://github.com/paperless-ngx/paperless-ngx/pull/4263))
- CI: fix playwright browser version mismatch failures [@shamoon](https://github.com/shamoon) ([#4239](https://github.com/paperless-ngx/paperless-ngx/pull/4239))
- Bump docker/login-action from 2 to 3 [@dependabot](https://github.com/dependabot) ([#4221](https://github.com/paperless-ngx/paperless-ngx/pull/4221))
- Bump docker/setup-buildx-action from 2 to 3 [@dependabot](https://github.com/dependabot) ([#4220](https://github.com/paperless-ngx/paperless-ngx/pull/4220))
- Bump docker/setup-qemu-action from 2 to 3 [@dependabot](https://github.com/dependabot) ([#4211](https://github.com/paperless-ngx/paperless-ngx/pull/4211))
- Bump stumpylog/image-cleaner-action from 0.2.0 to 0.3.0 [@dependabot](https://github.com/dependabot) ([#4210](https://github.com/paperless-ngx/paperless-ngx/pull/4210))
- Bump docker/metadata-action from 4 to 5 [@dependabot](https://github.com/dependabot) ([#4209](https://github.com/paperless-ngx/paperless-ngx/pull/4209))
- Bump docker/build-push-action from 4 to 5 [@dependabot](https://github.com/dependabot) ([#4212](https://github.com/paperless-ngx/paperless-ngx/pull/4212))
- Bump actions/checkout from 3 to 4 [@dependabot](https://github.com/dependabot) ([#4208](https://github.com/paperless-ngx/paperless-ngx/pull/4208))
- Chore: update docker image and ci to node 20 [@shamoon](https://github.com/shamoon) ([#4184](https://github.com/paperless-ngx/paperless-ngx/pull/4184))
### Dependencies
<details>
<summary>39 changes</summary>
- Chore: Bulk update of Python dependencies [@stumpylog](https://github.com/stumpylog) ([#4688](https://github.com/paperless-ngx/paperless-ngx/pull/4688))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4479](https://github.com/paperless-ngx/paperless-ngx/pull/4479))
- Bump [@<!---->playwright/test from 1.38.1 to 1.39.0 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.38.1 to 1.39.0 in /src-ui @dependabot) ([#4480](https://github.com/paperless-ngx/paperless-ngx/pull/4480))
- Bump concurrently from 8.2.1 to 8.2.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#4481](https://github.com/paperless-ngx/paperless-ngx/pull/4481))
- Bump the frontend-jest-dependencies group in /src-ui with 1 update [@dependabot](https://github.com/dependabot) ([#4478](https://github.com/paperless-ngx/paperless-ngx/pull/4478))
- Bump the frontend-angular-dependencies group in /src-ui with 14 updates [@dependabot](https://github.com/dependabot) ([#4477](https://github.com/paperless-ngx/paperless-ngx/pull/4477))
- Bump the actions group with 1 update [@dependabot](https://github.com/dependabot) ([#4476](https://github.com/paperless-ngx/paperless-ngx/pull/4476))
- Bump [@<!---->babel/traverse from 7.22.11 to 7.23.2 in /src-ui @dependabot](https://github.com/<!---->babel/traverse from 7.22.11 to 7.23.2 in /src-ui @dependabot) ([#4389](https://github.com/paperless-ngx/paperless-ngx/pull/4389))
- Fix: replace drag drop \& clipboard deps with angular cdk [@shamoon](https://github.com/shamoon) ([#4362](https://github.com/paperless-ngx/paperless-ngx/pull/4362))
- Bump postcss from 8.4.12 to 8.4.31 in /src/paperless_mail/templates [@dependabot](https://github.com/dependabot) ([#4318](https://github.com/paperless-ngx/paperless-ngx/pull/4318))
- Bump [@<!---->types/node from 20.7.0 to 20.8.0 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.7.0 to 20.8.0 in /src-ui @dependabot) ([#4303](https://github.com/paperless-ngx/paperless-ngx/pull/4303))
- Bump the frontend-angular-dependencies group in /src-ui with 8 updates [@dependabot](https://github.com/dependabot) ([#4302](https://github.com/paperless-ngx/paperless-ngx/pull/4302))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4283](https://github.com/paperless-ngx/paperless-ngx/pull/4283))
- Bump the frontend-angular-dependencies group in /src-ui with 10 updates [@dependabot](https://github.com/dependabot) ([#4282](https://github.com/paperless-ngx/paperless-ngx/pull/4282))
- Bump [@<!---->types/node from 20.6.3 to 20.7.0 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.6.3 to 20.7.0 in /src-ui @dependabot) ([#4284](https://github.com/paperless-ngx/paperless-ngx/pull/4284))
- Bump leonsteinhaeuser/project-beta-automations from 2.1.0 to 2.2.1 [@dependabot](https://github.com/dependabot) ([#4281](https://github.com/paperless-ngx/paperless-ngx/pull/4281))
- Bump zone.js from 0.13.1 to 0.13.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#4223](https://github.com/paperless-ngx/paperless-ngx/pull/4223))
- Bump [@<!---->types/node from 20.5.8 to 20.6.3 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.5.8 to 20.6.3 in /src-ui @dependabot) ([#4224](https://github.com/paperless-ngx/paperless-ngx/pull/4224))
- Bump the frontend-angular-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#4222](https://github.com/paperless-ngx/paperless-ngx/pull/4222))
- Bump docker/login-action from 2 to 3 [@dependabot](https://github.com/dependabot) ([#4221](https://github.com/paperless-ngx/paperless-ngx/pull/4221))
- Bump docker/setup-buildx-action from 2 to 3 [@dependabot](https://github.com/dependabot) ([#4220](https://github.com/paperless-ngx/paperless-ngx/pull/4220))
- Bump docker/setup-qemu-action from 2 to 3 [@dependabot](https://github.com/dependabot) ([#4211](https://github.com/paperless-ngx/paperless-ngx/pull/4211))
- Bump bootstrap from 5.3.1 to 5.3.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#4217](https://github.com/paperless-ngx/paperless-ngx/pull/4217))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4215](https://github.com/paperless-ngx/paperless-ngx/pull/4215))
- Bump the frontend-jest-dependencies group in /src-ui with 4 updates [@dependabot](https://github.com/dependabot) ([#4218](https://github.com/paperless-ngx/paperless-ngx/pull/4218))
- Bump stumpylog/image-cleaner-action from 0.2.0 to 0.3.0 [@dependabot](https://github.com/dependabot) ([#4210](https://github.com/paperless-ngx/paperless-ngx/pull/4210))
- Bump docker/metadata-action from 4 to 5 [@dependabot](https://github.com/dependabot) ([#4209](https://github.com/paperless-ngx/paperless-ngx/pull/4209))
- Bump uuid from 9.0.0 to 9.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#4216](https://github.com/paperless-ngx/paperless-ngx/pull/4216))
- Bump the frontend-angular-dependencies group in /src-ui with 16 updates [@dependabot](https://github.com/dependabot) ([#4213](https://github.com/paperless-ngx/paperless-ngx/pull/4213))
- Bump docker/build-push-action from 4 to 5 [@dependabot](https://github.com/dependabot) ([#4212](https://github.com/paperless-ngx/paperless-ngx/pull/4212))
- Bump actions/checkout from 3 to 4 [@dependabot](https://github.com/dependabot) ([#4208](https://github.com/paperless-ngx/paperless-ngx/pull/4208))
- Chore: update docker image \& ci testing node to v18 [@shamoon](https://github.com/shamoon) ([#4149](https://github.com/paperless-ngx/paperless-ngx/pull/4149))
- Chore: Unlock dependencies \& update them all [@stumpylog](https://github.com/stumpylog) ([#4142](https://github.com/paperless-ngx/paperless-ngx/pull/4142))
- Bump the frontend-jest-dependencies group in /src-ui with 4 updates [@dependabot](https://github.com/dependabot) ([#4112](https://github.com/paperless-ngx/paperless-ngx/pull/4112))
- Bump tslib from 2.6.1 to 2.6.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#4108](https://github.com/paperless-ngx/paperless-ngx/pull/4108))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4106](https://github.com/paperless-ngx/paperless-ngx/pull/4106))
- Bump concurrently from 8.2.0 to 8.2.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#4111](https://github.com/paperless-ngx/paperless-ngx/pull/4111))
- Bump [@<!---->types/node from 20.4.5 to 20.5.8 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.4.5 to 20.5.8 in /src-ui @dependabot) ([#4110](https://github.com/paperless-ngx/paperless-ngx/pull/4110))
- Bump the frontend-angular-dependencies group in /src-ui with 19 updates [@dependabot](https://github.com/dependabot) ([#4104](https://github.com/paperless-ngx/paperless-ngx/pull/4104))
</details>
### All App Changes
<details>
<summary>95 changes</summary>
- Fix: Add missing spaces to help string in [@joouha](https://github.com/joouha) ([#4674](https://github.com/paperless-ngx/paperless-ngx/pull/4674))
- Fix: Typo invalidates precondition for doctype, resulting in Exception [@ArminGruner](https://github.com/ArminGruner) ([#4668](https://github.com/paperless-ngx/paperless-ngx/pull/4668))
- Fix: dark mode inconsistencies in v2.0.0 beta.rc1 [@shamoon](https://github.com/shamoon) ([#4669](https://github.com/paperless-ngx/paperless-ngx/pull/4669))
- Fix: dashboard saved view mobile width in v.2.0.0 beta.rc1 [@shamoon](https://github.com/shamoon) ([#4660](https://github.com/paperless-ngx/paperless-ngx/pull/4660))
- Fix: Miscellaneous visual fixes in v2.0.0-beta.rc1 2 [@shamoon](https://github.com/shamoon) ([#4635](https://github.com/paperless-ngx/paperless-ngx/pull/4635))
- Fix: Delay consumption after MODIFY inotify events [@frozenbrain](https://github.com/frozenbrain) ([#4626](https://github.com/paperless-ngx/paperless-ngx/pull/4626))
- Fix: Import of split-manifests can fail [@stumpylog](https://github.com/stumpylog) ([#4623](https://github.com/paperless-ngx/paperless-ngx/pull/4623))
- Fix: sidebar views dont update after creation in v2.0.0-beta.rc1 [@shamoon](https://github.com/shamoon) ([#4619](https://github.com/paperless-ngx/paperless-ngx/pull/4619))
- Fix: Prevent text wrap on consumption template label [@shamoon](https://github.com/shamoon) ([#4616](https://github.com/paperless-ngx/paperless-ngx/pull/4616))
- Fix: increase width of labels in default perms settings [@shamoon](https://github.com/shamoon) ([#4612](https://github.com/paperless-ngx/paperless-ngx/pull/4612))
- Fix: note deletion fails in v2.0.0-beta.rc1 [@shamoon](https://github.com/shamoon) ([#4602](https://github.com/paperless-ngx/paperless-ngx/pull/4602))
- Fix: Handle override lists being None [@stumpylog](https://github.com/stumpylog) ([#4598](https://github.com/paperless-ngx/paperless-ngx/pull/4598))
- Fix: Miscellaneous v2.0 visual fixes [@shamoon](https://github.com/shamoon) ([#4576](https://github.com/paperless-ngx/paperless-ngx/pull/4576))
- Fix: Force UTF-8 for exporter manifests and don't allow escaping [@stumpylog](https://github.com/stumpylog) ([#4574](https://github.com/paperless-ngx/paperless-ngx/pull/4574))
- Feature: compact notifications [@shamoon](https://github.com/shamoon) ([#4545](https://github.com/paperless-ngx/paperless-ngx/pull/4545))
- Chore: Backend bulk updates [@stumpylog](https://github.com/stumpylog) ([#4509](https://github.com/paperless-ngx/paperless-ngx/pull/4509))
- Fix: plain text preview overflows [@shamoon](https://github.com/shamoon) ([#4555](https://github.com/paperless-ngx/paperless-ngx/pull/4555))
- Feature: Hungarian translation [@shamoon](https://github.com/shamoon) ([#4552](https://github.com/paperless-ngx/paperless-ngx/pull/4552))
- Chore: Cleanup command arguments and standardize process count handling [@stumpylog](https://github.com/stumpylog) ([#4541](https://github.com/paperless-ngx/paperless-ngx/pull/4541))
- Chore: API support for id args for documents \& objects [@shamoon](https://github.com/shamoon) ([#4519](https://github.com/paperless-ngx/paperless-ngx/pull/4519))
- Fix: add permissions for custom fields with migration [@shamoon](https://github.com/shamoon) ([#4513](https://github.com/paperless-ngx/paperless-ngx/pull/4513))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4479](https://github.com/paperless-ngx/paperless-ngx/pull/4479))
- Bump [@<!---->playwright/test from 1.38.1 to 1.39.0 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.38.1 to 1.39.0 in /src-ui @dependabot) ([#4480](https://github.com/paperless-ngx/paperless-ngx/pull/4480))
- Bump concurrently from 8.2.1 to 8.2.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#4481](https://github.com/paperless-ngx/paperless-ngx/pull/4481))
- Bump the frontend-jest-dependencies group in /src-ui with 1 update [@dependabot](https://github.com/dependabot) ([#4478](https://github.com/paperless-ngx/paperless-ngx/pull/4478))
- Bump the frontend-angular-dependencies group in /src-ui with 14 updates [@dependabot](https://github.com/dependabot) ([#4477](https://github.com/paperless-ngx/paperless-ngx/pull/4477))
- Fix: visually hidden text breaks delete button wrap [@shamoon](https://github.com/shamoon) ([#4462](https://github.com/paperless-ngx/paperless-ngx/pull/4462))
- Fix: API statistics document_file_type_counts return type [@shamoon](https://github.com/shamoon) ([#4464](https://github.com/paperless-ngx/paperless-ngx/pull/4464))
- Fix: Always return a list for audit log check [@shamoon](https://github.com/shamoon) ([#4463](https://github.com/paperless-ngx/paperless-ngx/pull/4463))
- Feature: Audit Trail [@nanokatz](https://github.com/nanokatz) ([#4425](https://github.com/paperless-ngx/paperless-ngx/pull/4425))
- Fix: Only create a Correspondent if the email matches rule filters [@stumpylog](https://github.com/stumpylog) ([#4431](https://github.com/paperless-ngx/paperless-ngx/pull/4431))
- Fix: Combination of consume template with recursive tagging [@stumpylog](https://github.com/stumpylog) ([#4442](https://github.com/paperless-ngx/paperless-ngx/pull/4442))
- Feature: Add ahead of time compression of the static files for x86_64 [@stumpylog](https://github.com/stumpylog) ([#4390](https://github.com/paperless-ngx/paperless-ngx/pull/4390))
- Feature: sort sidebar views [@shamoon](https://github.com/shamoon) ([#4381](https://github.com/paperless-ngx/paperless-ngx/pull/4381))
- Feature: Switches to a new client to handle communication with Gotenberg [@stumpylog](https://github.com/stumpylog) ([#4391](https://github.com/paperless-ngx/paperless-ngx/pull/4391))
- barcode logic: strip non-numeric characters from detected ASN string [@queaker](https://github.com/queaker) ([#4379](https://github.com/paperless-ngx/paperless-ngx/pull/4379))
- Bump [@<!---->babel/traverse from 7.22.11 to 7.23.2 in /src-ui @dependabot](https://github.com/<!---->babel/traverse from 7.22.11 to 7.23.2 in /src-ui @dependabot) ([#4389](https://github.com/paperless-ngx/paperless-ngx/pull/4389))
- Fix: replace drag drop \& clipboard deps with angular cdk [@shamoon](https://github.com/shamoon) ([#4362](https://github.com/paperless-ngx/paperless-ngx/pull/4362))
- Fix: update document modified time on note creation / deletion [@shamoon](https://github.com/shamoon) ([#4374](https://github.com/paperless-ngx/paperless-ngx/pull/4374))
- Fix: correct set owner API location in docs, additional test [@shamoon](https://github.com/shamoon) ([#4366](https://github.com/paperless-ngx/paperless-ngx/pull/4366))
- Fix: get highest ASN regardless of user [@shamoon](https://github.com/shamoon) ([#4326](https://github.com/paperless-ngx/paperless-ngx/pull/4326))
- Bump postcss from 8.4.12 to 8.4.31 in /src/paperless_mail/templates [@dependabot](https://github.com/dependabot) ([#4318](https://github.com/paperless-ngx/paperless-ngx/pull/4318))
- CI: speed-up frontend tests on ci [@shamoon](https://github.com/shamoon) ([#4316](https://github.com/paperless-ngx/paperless-ngx/pull/4316))
- Bump [@<!---->types/node from 20.7.0 to 20.8.0 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.7.0 to 20.8.0 in /src-ui @dependabot) ([#4303](https://github.com/paperless-ngx/paperless-ngx/pull/4303))
- Bump the frontend-angular-dependencies group in /src-ui with 8 updates [@dependabot](https://github.com/dependabot) ([#4302](https://github.com/paperless-ngx/paperless-ngx/pull/4302))
- Feature: password reset [@shamoon](https://github.com/shamoon) ([#4289](https://github.com/paperless-ngx/paperless-ngx/pull/4289))
- Enhancement: dashboard improvements, drag-n-drop reorder dashboard views [@shamoon](https://github.com/shamoon) ([#4252](https://github.com/paperless-ngx/paperless-ngx/pull/4252))
- Fix: long notes cause visual overflow [@shamoon](https://github.com/shamoon) ([#4287](https://github.com/paperless-ngx/paperless-ngx/pull/4287))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4283](https://github.com/paperless-ngx/paperless-ngx/pull/4283))
- Bump the frontend-angular-dependencies group in /src-ui with 10 updates [@dependabot](https://github.com/dependabot) ([#4282](https://github.com/paperless-ngx/paperless-ngx/pull/4282))
- Bump [@<!---->types/node from 20.6.3 to 20.7.0 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.6.3 to 20.7.0 in /src-ui @dependabot) ([#4284](https://github.com/paperless-ngx/paperless-ngx/pull/4284))
- Fix: Ensures all old connections are closed in certain long lived places [@stumpylog](https://github.com/stumpylog) ([#4265](https://github.com/paperless-ngx/paperless-ngx/pull/4265))
- Change: update translation string for tasks dialog [@shamoon](https://github.com/shamoon) ([#4263](https://github.com/paperless-ngx/paperless-ngx/pull/4263))
- Enhancement: settings reorganization \& improvements, separate admin section [@shamoon](https://github.com/shamoon) ([#4251](https://github.com/paperless-ngx/paperless-ngx/pull/4251))
- Chore: Standardizes the imports across all the files and modules [@stumpylog](https://github.com/stumpylog) ([#4248](https://github.com/paperless-ngx/paperless-ngx/pull/4248))
- Feature: consumption templates [@shamoon](https://github.com/shamoon) ([#4196](https://github.com/paperless-ngx/paperless-ngx/pull/4196))
- Enhancement: support default permissions for object creation via frontend [@shamoon](https://github.com/shamoon) ([#4233](https://github.com/paperless-ngx/paperless-ngx/pull/4233))
- Fix: Set a non-zero polling internal when inotify cannot import [@stumpylog](https://github.com/stumpylog) ([#4230](https://github.com/paperless-ngx/paperless-ngx/pull/4230))
- Bump zone.js from 0.13.1 to 0.13.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#4223](https://github.com/paperless-ngx/paperless-ngx/pull/4223))
- Bump [@<!---->types/node from 20.5.8 to 20.6.3 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.5.8 to 20.6.3 in /src-ui @dependabot) ([#4224](https://github.com/paperless-ngx/paperless-ngx/pull/4224))
- Bump the frontend-angular-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#4222](https://github.com/paperless-ngx/paperless-ngx/pull/4222))
- Bump bootstrap from 5.3.1 to 5.3.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#4217](https://github.com/paperless-ngx/paperless-ngx/pull/4217))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4215](https://github.com/paperless-ngx/paperless-ngx/pull/4215))
- Bump the frontend-jest-dependencies group in /src-ui with 4 updates [@dependabot](https://github.com/dependabot) ([#4218](https://github.com/paperless-ngx/paperless-ngx/pull/4218))
- Bump uuid from 9.0.0 to 9.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#4216](https://github.com/paperless-ngx/paperless-ngx/pull/4216))
- Bump the frontend-angular-dependencies group in /src-ui with 16 updates [@dependabot](https://github.com/dependabot) ([#4213](https://github.com/paperless-ngx/paperless-ngx/pull/4213))
- Enhancement: bulk edit object permissions [@shamoon](https://github.com/shamoon) ([#4176](https://github.com/paperless-ngx/paperless-ngx/pull/4176))
- Fix: completely hide upload widget if user does not have permissions [@nawramm](https://github.com/nawramm) ([#4198](https://github.com/paperless-ngx/paperless-ngx/pull/4198))
- Fix: application of theme color vars at root [@shamoon](https://github.com/shamoon) ([#4193](https://github.com/paperless-ngx/paperless-ngx/pull/4193))
- Enhancement: Allow the user the specify the export zip file name [@stumpylog](https://github.com/stumpylog) ([#4189](https://github.com/paperless-ngx/paperless-ngx/pull/4189))
- Feature: Share links [@shamoon](https://github.com/shamoon) ([#3996](https://github.com/paperless-ngx/paperless-ngx/pull/3996))
- Chore: change dark mode to use Bootstrap's color modes [@lkster](https://github.com/lkster) ([#4174](https://github.com/paperless-ngx/paperless-ngx/pull/4174))
- Fix: support storage path placeholder via API [@shamoon](https://github.com/shamoon) ([#4179](https://github.com/paperless-ngx/paperless-ngx/pull/4179))
- Fix: Logs the errors during thumbnail generation [@stumpylog](https://github.com/stumpylog) ([#4171](https://github.com/paperless-ngx/paperless-ngx/pull/4171))
- Feature: New management command for fuzzy matching document content [@stumpylog](https://github.com/stumpylog) ([#4160](https://github.com/paperless-ngx/paperless-ngx/pull/4160))
- Breaking: Drop support for Python 3.8 [@stumpylog](https://github.com/stumpylog) ([#4156](https://github.com/paperless-ngx/paperless-ngx/pull/4156))
- Fix: dashboard widget card borders hidden by bkgd color [@shamoon](https://github.com/shamoon) ([#4155](https://github.com/paperless-ngx/paperless-ngx/pull/4155))
- Enhancement: frontend better handle slow backend requests [@shamoon](https://github.com/shamoon) ([#4055](https://github.com/paperless-ngx/paperless-ngx/pull/4055))
- Chore: Extend the live service utility for handling 503 errors [@stumpylog](https://github.com/stumpylog) ([#4143](https://github.com/paperless-ngx/paperless-ngx/pull/4143))
- Chore: update docker image \& ci testing node to v18 [@shamoon](https://github.com/shamoon) ([#4149](https://github.com/paperless-ngx/paperless-ngx/pull/4149))
- Fix: hide entire add user / group buttons if insufficient permissions [@shamoon](https://github.com/shamoon) ([#4133](https://github.com/paperless-ngx/paperless-ngx/pull/4133))
- Enhancement: Improved error notifications [@shamoon](https://github.com/shamoon) ([#4062](https://github.com/paperless-ngx/paperless-ngx/pull/4062))
- Feature: Official support for Python 3.11 [@stumpylog](https://github.com/stumpylog) ([#4146](https://github.com/paperless-ngx/paperless-ngx/pull/4146))
- Chore: Unlock dependencies \& update them all [@stumpylog](https://github.com/stumpylog) ([#4142](https://github.com/paperless-ngx/paperless-ngx/pull/4142))
- Change: PWA Manifest to Standalone Display [@swoga](https://github.com/swoga) ([#4129](https://github.com/paperless-ngx/paperless-ngx/pull/4129))
- Enhancement: add --id-range for document_retagger [@kamilkosek](https://github.com/kamilkosek) ([#4080](https://github.com/paperless-ngx/paperless-ngx/pull/4080))
- Enhancement: Add Afrikaans, Greek \& Norwegian languages [@shamoon](https://github.com/shamoon) ([#4088](https://github.com/paperless-ngx/paperless-ngx/pull/4088))
- Enhancement: add task id to pre/post consume script as env [@andreheuer](https://github.com/andreheuer) ([#4037](https://github.com/paperless-ngx/paperless-ngx/pull/4037))
- Enhancement: update bootstrap to v5.3.1 for backend static pages [@shamoon](https://github.com/shamoon) ([#4060](https://github.com/paperless-ngx/paperless-ngx/pull/4060))
- Bump the frontend-jest-dependencies group in /src-ui with 4 updates [@dependabot](https://github.com/dependabot) ([#4112](https://github.com/paperless-ngx/paperless-ngx/pull/4112))
- Bump tslib from 2.6.1 to 2.6.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#4108](https://github.com/paperless-ngx/paperless-ngx/pull/4108))
- Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#4106](https://github.com/paperless-ngx/paperless-ngx/pull/4106))
- Bump concurrently from 8.2.0 to 8.2.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#4111](https://github.com/paperless-ngx/paperless-ngx/pull/4111))
- Bump [@<!---->types/node from 20.4.5 to 20.5.8 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.4.5 to 20.5.8 in /src-ui @dependabot) ([#4110](https://github.com/paperless-ngx/paperless-ngx/pull/4110))
- Bump the frontend-angular-dependencies group in /src-ui with 19 updates [@dependabot](https://github.com/dependabot) ([#4104](https://github.com/paperless-ngx/paperless-ngx/pull/4104))
</details>
## paperless-ngx 1.17.4
### Bug Fixes
- Fix: ghostscript rendering error doesnt trigger frontend failure message [@shamoon](https://github.com/shamoon) ([#4092](https://github.com/paperless-ngx/paperless-ngx/pull/4092))
- Fix: ghostscript rendering error doesn't trigger frontend failure message [@shamoon](https://github.com/shamoon) ([#4092](https://github.com/paperless-ngx/paperless-ngx/pull/4092))
### All App Changes
- Fix: ghostscript rendering error doesnt trigger frontend failure message [@shamoon](https://github.com/shamoon) ([#4092](https://github.com/paperless-ngx/paperless-ngx/pull/4092))
- Fix: ghostscript rendering error doesn't trigger frontend failure message [@shamoon](https://github.com/shamoon) ([#4092](https://github.com/paperless-ngx/paperless-ngx/pull/4092))
## paperless-ngx 1.17.3
@@ -675,7 +1368,7 @@
### Documentation
- Whitespace changes, making sure the example is correcly aligned [@denilsonsa](https://github.com/denilsonsa) ([#3089](https://github.com/paperless-ngx/paperless-ngx/pull/3089))
- Whitespace changes, making sure the example is correctly aligned [@denilsonsa](https://github.com/denilsonsa) ([#3089](https://github.com/paperless-ngx/paperless-ngx/pull/3089))
- Docs: Include additional information about barcodes [@stumpylog](https://github.com/stumpylog) ([#2889](https://github.com/paperless-ngx/paperless-ngx/pull/2889))
- Fix formatting in Setup documentation page [@igrybkov](https://github.com/igrybkov) ([#2880](https://github.com/paperless-ngx/paperless-ngx/pull/2880))
- [Documentation] Update docker-compose steps to support podman [@white-gecko](https://github.com/white-gecko) ([#2855](https://github.com/paperless-ngx/paperless-ngx/pull/2855))
@@ -730,7 +1423,7 @@
- Fix: update PaperlessTask on hard failures [@shamoon](https://github.com/shamoon) ([#3062](https://github.com/paperless-ngx/paperless-ngx/pull/3062))
- Bump typescript from 4.8.4 to 4.9.5 in /src-ui [@dependabot](https://github.com/dependabot) ([#3071](https://github.com/paperless-ngx/paperless-ngx/pull/3071))
- Bulk Bump npm packages 04.23 [@dependabot](https://github.com/dependabot) ([#3068](https://github.com/paperless-ngx/paperless-ngx/pull/3068))
- Fix: Hide UI tour steps if user doesnt have permissions [@shamoon](https://github.com/shamoon) ([#3060](https://github.com/paperless-ngx/paperless-ngx/pull/3060))
- Fix: Hide UI tour steps if user doesn't have permissions [@shamoon](https://github.com/shamoon) ([#3060](https://github.com/paperless-ngx/paperless-ngx/pull/3060))
- Fix: Hide Permissions tab if user cannot view users [@shamoon](https://github.com/shamoon) ([#3061](https://github.com/paperless-ngx/paperless-ngx/pull/3061))
- v1.14.0 delete document fixes [@shamoon](https://github.com/shamoon) ([#3020](https://github.com/paperless-ngx/paperless-ngx/pull/3020))
- Bump wait-on from 6.0.1 to 7.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#2990](https://github.com/paperless-ngx/paperless-ngx/pull/2990))
@@ -935,7 +1628,7 @@ older comments. The Docker image will automatically perform this reindex, bare m
- [Docs] Add Paperless Mobile app to docs [@astubenbord](https://github.com/astubenbord) ([#2378](https://github.com/paperless-ngx/paperless-ngx/pull/2378))
- Tiny spelling change [@veverkap](https://github.com/veverkap) ([#2369](https://github.com/paperless-ngx/paperless-ngx/pull/2369))
- Documentation: update build instructions to remove deprecated [@shamoon](https://github.com/shamoon) ([#2334](https://github.com/paperless-ngx/paperless-ngx/pull/2334))
- [Documentation] Add note that PAPERLESS_URL cant contain a path [@shamoon](https://github.com/shamoon) ([#2319](https://github.com/paperless-ngx/paperless-ngx/pull/2319))
- [Documentation] Add note that PAPERLESS_URL can't contain a path [@shamoon](https://github.com/shamoon) ([#2319](https://github.com/paperless-ngx/paperless-ngx/pull/2319))
- [Documentation] Add v1.11.3 changelog [@github-actions](https://github.com/github-actions) ([#2311](https://github.com/paperless-ngx/paperless-ngx/pull/2311))
### Maintenance
@@ -1266,7 +1959,7 @@ Versions 1.11.1 and 1.11.2 contain bug fixes from v1.11.0 that prevented use of
### All App Changes
- Add info that re-do OCR doesnt automatically refresh content [@shamoon](https://github.com/shamoon) ([#2025](https://github.com/paperless-ngx/paperless-ngx/pull/2025))
- Add info that re-do OCR doesn't automatically refresh content [@shamoon](https://github.com/shamoon) ([#2025](https://github.com/paperless-ngx/paperless-ngx/pull/2025))
- Bugfix: Fix created_date being a string [@stumpylog](https://github.com/stumpylog) ([#2023](https://github.com/paperless-ngx/paperless-ngx/pull/2023))
- Bugfix: Fixes an issue with mixed text and images when redoing OCR [@stumpylog](https://github.com/stumpylog) ([#2017](https://github.com/paperless-ngx/paperless-ngx/pull/2017))
- Bugfix: Don't allow exceptions during date parsing to fail consume [@stumpylog](https://github.com/stumpylog) ([#1998](https://github.com/paperless-ngx/paperless-ngx/pull/1998))
@@ -1677,7 +2370,7 @@ Versions 1.11.1 and 1.11.2 contain bug fixes from v1.11.0 that prevented use of
- Fix local Docker image building [\@stumpylog](https://github.com/stumpylog) ([\#849](https://github.com/paperless-ngx/paperless-ngx/pull/849))
- Fix: show errors on invalid date input [\@shamoon](https://github.com/shamoon) ([\#862](https://github.com/paperless-ngx/paperless-ngx/pull/862))
- Fix: Older dates do not display on frontend [\@shamoon](https://github.com/shamoon) ([\#852](https://github.com/paperless-ngx/paperless-ngx/pull/852))
- Fixes IMAP UTF8 Authenication [\@stumpylog](https://github.com/stumpylog) ([\#725](https://github.com/paperless-ngx/paperless-ngx/pull/725))
- Fixes IMAP UTF8 Authentication [\@stumpylog](https://github.com/stumpylog) ([\#725](https://github.com/paperless-ngx/paperless-ngx/pull/725))
- Fix password field remains visible [\@shamoon](https://github.com/shamoon) ([\#840](https://github.com/paperless-ngx/paperless-ngx/pull/840))
- Fixes Pillow build for armv7 [\@stumpylog](https://github.com/stumpylog) ([\#815](https://github.com/paperless-ngx/paperless-ngx/pull/815))
- Update frontend localization source file [\@shamoon](https://github.com/shamoon) ([\#814](https://github.com/paperless-ngx/paperless-ngx/pull/814))
@@ -1798,7 +2491,7 @@ Versions 1.11.1 and 1.11.2 contain bug fixes from v1.11.0 that prevented use of
[\@shamoon](https://github.com/shamoon) ([\#313](https://github.com/paperless-ngx/paperless-ngx/pull/313))
- Fix imap tools bug [\@stumpylog](https://github.com/stumpylog)
([\#393](https://github.com/paperless-ngx/paperless-ngx/pull/393))
- Fix filterable dropdown buttons arent translated
- Fix filterable dropdown buttons aren't translated
[\@shamoon](https://github.com/shamoon) ([\#366](https://github.com/paperless-ngx/paperless-ngx/pull/366))
- Fix 224: "Auto-detected date is day before receipt date"
[\@a17t](https://github.com/a17t) ([\#246](https://github.com/paperless-ngx/paperless-ngx/pull/246))
@@ -2634,7 +3327,7 @@ primarily.
[OCRmyPDF](https://github.com/jbarlow83/OCRmyPDF) to perform OCR
on documents. It still uses tesseract under the hood, but the
PDF parser of Paperless has changed considerably and will behave
different for some douments.
different for some documents.
- OCRmyPDF creates archived PDF/A documents with embedded text
that can be selected in the front end.
- Paperless stores archived versions of documents alongside with
@@ -2685,7 +3378,7 @@ primarily.
crash.
- Mail handling no longer exits entirely when encountering errors.
It will skip the account/rule/message on which the error
occured.
occurred.
- Assigning correspondents from mail sender names failed for very
long names. Paperless no longer assigns correspondents in these
cases.

View File

@@ -3,6 +3,11 @@
Paperless provides a wide range of customizations. Depending on how you
run paperless, these settings have to be defined in different places.
Certain configuration options may be set via the UI. This currently includes
common [OCR](#ocr) related settings and some frontend settings. If set, these will take
preference over the settings via environment variables. If not set, the environment setting
or applicable default will be utilized instead.
- If you run paperless on docker, `paperless.conf` is not used.
Rather, configure paperless by copying necessary options to
`docker-compose.env`.
@@ -181,7 +186,7 @@ configure their endpoints, and enable the feature.
Defaults to "<http://localhost:3000>".
If you run paperless on docker, you can add those services to the
docker-compose file (see the provided
Docker Compose file (see the provided
[`docker-compose.sqlite-tika.yml`](https://github.com/paperless-ngx/paperless-ngx/blob/main/docker/compose/docker-compose.sqlite-tika.yml)
file for reference).
@@ -221,6 +226,8 @@ directory.
inside docker, ensure that this path is within a permanent volume
(such as "../media/trash") so it won't get lost on upgrades.
Note that the directory must exist prior to using this setting.
Defaults to empty (i.e. really delete documents).
#### [`PAPERLESS_MEDIA_ROOT=<path>`](#PAPERLESS_MEDIA_ROOT) {#PAPERLESS_MEDIA_ROOT}
@@ -658,11 +665,13 @@ completely.
Specifying 1 here will only use the first page.
The value must be greater than or equal to 1 to be used.
When combined with `PAPERLESS_OCR_MODE=redo` or
`PAPERLESS_OCR_MODE=force`, paperless will not modify any text it
finds on excluded pages and copy it verbatim.
Defaults to 0, which disables this feature and always uses all
Defaults to unset, which disables this feature and always uses all
pages.
#### [`PAPERLESS_OCR_IMAGE_DPI=<num>`](#PAPERLESS_OCR_IMAGE_DPI) {#PAPERLESS_OCR_IMAGE_DPI}
@@ -676,7 +685,7 @@ fails, it uses this value as a fallback.
Set this to the DPI your scanner produces images at.
Default is none, which will automatically calculate image DPI so
Defaults to unset, which will automatically calculate image DPI so
that the produced PDF documents are A4 sized.
#### [`PAPERLESS_OCR_MAX_IMAGE_PIXELS=<num>`](#PAPERLESS_OCR_MAX_IMAGE_PIXELS) {#PAPERLESS_OCR_MAX_IMAGE_PIXELS}
@@ -702,6 +711,20 @@ but could result in missing text content.
this value if you are certain your documents are not malicious and
you need the text which was not OCRed
#### [`PAPERLESS_OCR_COLOR_CONVERSION_STRATEGY=<RGB>`](#PAPERLESS_OCR_COLOR_CONVERSION_STRATEGY) {#PAPERLESS_OCR_COLOR_CONVERSION_STRATEGY}
: Controls the Ghostscript color conversion strategy when creating the archive file. This setting
will only be utilized if the output is a version of PDF/A.
Valid options are CMYK, Gray, LeaveColorUnchanged, RGB or UseDeviceIndependentColor.
You can find more on the settings [here](https://ghostscript.readthedocs.io/en/latest/VectorDevices.html#color-conversion-and-management) in the Ghostscript documentation.
!!! warning
Utilizing some of the options may result in errors when creating archive
files from PDFs.
#### [`PAPERLESS_OCR_USER_ARGS=<json>`](#PAPERLESS_OCR_USER_ARGS) {#PAPERLESS_OCR_USER_ARGS}
: OCRmyPDF offers many more options. Use this parameter to specify any
@@ -717,7 +740,7 @@ they use underscores instead of dashes.
Paperless has been tested to work with the OCR options provided
above. There are many options that are incompatible with each other,
so specifying invalid options may prevent paperless from consuming
any documents.
any documents. Use with caution!
Specify arguments as a JSON dictionary. Keep note of lower case
booleans and double quoted parameter names and strings. Examples:
@@ -1138,12 +1161,13 @@ combination with PAPERLESS_CONSUMER_BARCODE_UPSCALE bigger than 1.0.
## Audit Trail
#### [`PAPERLESS_AUDIT_LOG_ENABLED=<bool>`](#PAPERLESS_AUDIT_LOG_ENABLED){#PAPERLESS_AUDIT_LOG_ENABLED}
#### [`PAPERLESS_AUDIT_LOG_ENABLED=<bool>`](#PAPERLESS_AUDIT_LOG_ENABLED) {#PAPERLESS_AUDIT_LOG_ENABLED}
: Enables an audit trail for documents, document types, correspondents, and tags. Log entries can be viewed in the Django backend only.
!!! warning
Once enabled cannot be disabled
Once enabled cannot be disabled
## Collate Double-Sided Documents {#collate}
@@ -1293,6 +1317,10 @@ specified as "chi-tra".
Defaults to none, which does not install any additional languages.
!!! warning
This option must not be used in rootless containers.
#### [`PAPERLESS_ENABLE_FLOWER=<defined>`](#PAPERLESS_ENABLE_FLOWER) {#PAPERLESS_ENABLE_FLOWER}
: If this environment variable is defined, the Celery monitoring tool
@@ -1301,7 +1329,15 @@ started by the container.
You can read more about this in the [advanced documentation](advanced_usage.md#celery-monitoring).
## Update Checking {#update-checking}
## Frontend Settings
#### [`PAPERLESS_APP_TITLE=<bool>`](#PAPERLESS_APP_TITLE) {#PAPERLESS_APP_TITLE}
: If set, overrides the default name "Paperless-ngx"
#### [`PAPERLESS_APP_LOGO=<path>`](#PAPERLESS_APP_LOGO) {#PAPERLESS_APP_LOGO}
: Path to an image file in the /media/logo directory, must include 'logo', e.g. `/logo/Atari_logo.svg`
#### [`PAPERLESS_ENABLE_UPDATE_CHECK=<bool>`](#PAPERLESS_ENABLE_UPDATE_CHECK) {#PAPERLESS_ENABLE_UPDATE_CHECK}
@@ -1329,6 +1365,10 @@ password. All of these options come from their similarly-named [Django settings]
: Defaults to ''.
#### [`PAPERLESS_EMAIL_FROM=<str>`](#PAPERLESS_EMAIL_FROM) {#PAPERLESS_EMAIL_FROM}
: Defaults to PAPERLESS_EMAIL_HOST_USER if not set.
#### [`PAPERLESS_EMAIL_HOST_PASSWORD=<str>`](#PAPERLESS_EMAIL_HOST_PASSWORD) {#PAPERLESS_EMAIL_HOST_PASSWORD}
: Defaults to ''.

View File

@@ -9,7 +9,7 @@ following way:
- `main` always represents the latest release and will only see
changes when a new release is made.
- `dev` contains the code that will be in the next release.
- `feature-X` contain bigger changes that will be in some release, but
- `feature-X` contains bigger changes that will be in some release, but
not necessarily the next one.
When making functional changes to Paperless-ngx, _always_ make your changes
@@ -277,27 +277,17 @@ Adding new languages requires adding the translated files in the
}
```
2. Add the language to the available options in
2. Add the language to the `LANGUAGE_OPTIONS` array in
`src-ui/src/app/services/settings.service.ts`:
```typescript
getLanguageOptions(): LanguageOption[] {
return [
{code: "en-us", name: $localize`English (US)`, englishName: "English (US)", dateInputFormat: "mm/dd/yyyy"},
{code: "en-gb", name: $localize`English (GB)`, englishName: "English (GB)", dateInputFormat: "dd/mm/yyyy"},
{code: "de", name: $localize`German`, englishName: "German", dateInputFormat: "dd.mm.yyyy"},
{code: "nl", name: $localize`Dutch`, englishName: "Dutch", dateInputFormat: "dd-mm-yyyy"},
{code: "fr", name: $localize`French`, englishName: "French", dateInputFormat: "dd/mm/yyyy"},
{code: "pt-br", name: $localize`Portuguese (Brazil)`, englishName: "Portuguese (Brazil)", dateInputFormat: "dd/mm/yyyy"}
// Add your new language here
]
}
```
`dateInputFormat` is a special string that defines the behavior of
the date input fields and absolutely needs to contain "dd", "mm"
and "yyyy".
```
3. Import and register the Angular data for this locale in
`src-ui/src/app/app.module.ts`:

View File

@@ -83,11 +83,11 @@ has to do much less work to serve the data.
## _How do I install paperless-ngx on Raspberry Pi?_
**A:** Docker images are available for arm64 hardware, so just
follow the [docker-compose instructions](https://docs.paperless-ngx.com/setup/#installation). Apart from more required disk
follow the [Docker Compose instructions](https://docs.paperless-ngx.com/setup/#installation). Apart from more required disk
space compared to a bare metal installation, docker comes with close to
zero overhead, even on Raspberry Pi.
If you decide to got with the bare metal route, be aware that some of
If you decide to go with the bare metal route, be aware that some of
the python requirements do not have precompiled packages for ARM /
ARM64. Installation of these will require additional development
libraries and compilation will take a long time.

View File

@@ -18,7 +18,10 @@ physical documents into a searchable online archive so you can keep, well, _less
## Features
- **Organize and index** your scanned documents with tags, correspondents, types, and more.
- Performs **OCR** on your documents, adding selectable text to image-only documents.
- _Your_ data is stored locally on _your_ server and is never transmitted or shared in any way.
- Performs **OCR** on your documents, adding searchable and selectable text, even to documents scanned with only images.
- Utilizes the open-source Tesseract engine to recognize more than 100 languages.
- Documents are saved as PDF/A format which is designed for long term storage, alongside the unaltered originals.
- Uses machine-learning to automatically add tags, correspondents and document types to your documents.
- Supports PDF documents, images, plain text files, Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents)[^1] and more.
- Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely with different configurations assigned to different documents.
@@ -39,7 +42,7 @@ physical documents into a searchable online archive so you can keep, well, _less
- Configure multiple accounts and rules for each account.
- After processing, paperless can perform actions on the messages such as marking as read, deleting and more.
- A built-in robust **multi-user permissions** system that supports 'global' permissions as well as per document or object.
- A powerful templating system that gives you more control over the consumption pipeline.
- A powerful workflow system that gives you even more control.
- **Optimized** for multi core systems: Paperless-ngx consumes multiple documents in parallel.
- The integrated sanity checker makes sure that your document archive is in good health.
@@ -154,9 +157,9 @@ Tag, correspondent, document type and storage path editing.
</div>
<div class="grid-half-right" markdown>
Consumption templates provide finer control over the document pipeline.
Workflows provide finer control over the document pipeline and trigger actions.
![image](assets/screenshots/consumption_template.png)
![image](assets/screenshots/workflow.png)
</div>
<div class="clear"></div>

View File

@@ -25,7 +25,11 @@ necessary configuration files, pull the docker image, start paperless
and create your user account. This script essentially performs all the
steps described in [Docker setup](#docker_hub) automatically.
1. Make sure that docker and docker-compose are installed.
1. Make sure that Docker and Docker Compose are installed.
!!! tip
See the Docker installation instructions at https://docs.docker.com/engine/install/
2. Download and run the installation script:
@@ -62,19 +66,19 @@ steps described in [Docker setup](#docker_hub) automatically.
For new installations, it is recommended to use PostgreSQL as the
database backend.
3. Install [Docker](https://www.docker.com/) and
[docker-compose](https://docs.docker.com/compose/install/).
3. Install [Docker](https://docs.docker.com/engine/install/) and
[Docker Compose](https://docs.docker.com/compose/install/).
!!! warning
If you want to use the included `docker-compose.*.yml` file, you
need to have at least Docker version **17.09.0** and docker-compose
version **1.17.0**. To check do: `docker-compose -v` or `docker -v`
need to have at least Docker version **17.09.0** and Docker Compose
version **v2**. To check do: `docker compose version` or `docker -v`
See the [Docker installation guide](https://docs.docker.com/engine/install/) on how to install the current
version of Docker for your operating system or Linux distribution of
choice. To get the latest version of docker-compose, follow the
[docker-compose installation guide](https://docs.docker.com/compose/install/linux/) if your package repository
choice. To get the latest version of Docker Compose, follow the
[Docker Compose installation guide](https://docs.docker.com/compose/install/linux/) if your package repository
doesn't include it.
4. Modify `docker-compose.yml` to your preferences. You may want to
@@ -92,7 +96,7 @@ steps described in [Docker setup](#docker_hub) automatically.
- /home/jonaswinkler/paperless-inbox:/usr/src/paperless/consume
```
Don't change the part after the colon or paperless wont find your
Don't change the part after the colon or paperless won't find your
documents.
You may also need to change the default port that the webserver will
@@ -117,6 +121,10 @@ steps described in [Docker setup](#docker_hub) automatically.
**Rootless**
!!! warning
It is currently not possible to run the container rootless if additional languages are specified via `PAPERLESS_OCR_LANGUAGES`.
If you want to run Paperless as a rootless container, you will need
to do the following in your `docker-compose.yml`:
@@ -165,13 +173,13 @@ steps described in [Docker setup](#docker_hub) automatically.
[`PAPERLESS_CONSUMER_POLLING`](configuration.md#PAPERLESS_CONSUMER_POLLING), which will disable inotify. See
[here](configuration.md#polling).
6. Run `docker-compose pull`. This will pull the image.
6. Run `docker compose pull`. This will pull the image.
7. To be able to login, you will need a super user. To create it,
execute the following command:
```shell-session
$ docker-compose run --rm webserver createsuperuser
$ docker compose run --rm webserver createsuperuser
```
or using docker exec from within the container:
@@ -183,7 +191,7 @@ steps described in [Docker setup](#docker_hub) automatically.
This will prompt you to set a username, an optional e-mail address
and finally a password (at least 8 characters).
8. Run `docker-compose up -d`. This will create and start the necessary containers.
8. Run `docker compose up -d`. This will create and start the necessary containers.
9. The default `docker-compose.yml` exports the webserver on your local
port
@@ -209,14 +217,14 @@ steps described in [Docker setup](#docker_hub) automatically.
root as well.
3. In the `docker-compose.yml` file, find the line that instructs
docker-compose to pull the paperless image from Docker Hub:
Docker Compose to pull the paperless image from Docker Hub:
```yaml
webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
```
and replace it with a line that instructs docker-compose to build
and replace it with a line that instructs Docker Compose to build
the image from the current working directory instead:
```yaml
@@ -226,10 +234,10 @@ steps described in [Docker setup](#docker_hub) automatically.
```
4. Follow steps 3 to 8 of [Docker Setup](#docker_hub). When asked to run
`docker-compose pull` to pull the image, do
`docker compose pull` to pull the image, do
```shell-session
$ docker-compose build
$ docker compose build
```
instead to build the image.
@@ -541,7 +549,7 @@ to
image: ghcr.io/paperless-ngx/paperless-ngx:latest
```
and then run `docker-compose up -d` which will pull the new image
and then run `docker compose up -d` which will pull the new image
recreate the container. That's it!
Users who installed with the bare-metal route should also update their
@@ -570,7 +578,7 @@ installation. The important things to keep in mind are as follows:
- The task scheduler of paperless, which is used to execute periodic
tasks such as email checking and maintenance, requires a
[redis](https://redis.io/) message broker instance. The
docker-compose route takes care of that.
Docker Compose route takes care of that.
- The layout of the folder structure for your documents and data
remains the same, so you can just plug your old docker volumes into
paperless-ngx and expect it to find everything where it should be.
@@ -581,7 +589,7 @@ Migration to paperless-ngx is then performed in a few simple steps:
```bash
$ cd /path/to/current/paperless
$ docker-compose down
$ docker compose down
```
2. Do a backup for two purposes: If something goes wrong, you still
@@ -589,7 +597,7 @@ Migration to paperless-ngx is then performed in a few simple steps:
switch back to paperless.
3. Download the latest release of paperless-ngx. You can either go with
the docker-compose files from
the Docker Compose files from
[here](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose)
or clone the repository to build the image yourself (see
[above](#docker_build)). You can
@@ -626,7 +634,7 @@ Migration to paperless-ngx is then performed in a few simple steps:
the search index:
```shell-session
$ docker-compose run --rm webserver document_index reindex
$ docker compose run --rm webserver document_index reindex
```
This will migrate your database and create the search index. After
@@ -635,7 +643,7 @@ Migration to paperless-ngx is then performed in a few simple steps:
8. Start paperless-ngx.
```bash
$ docker-compose up -d
$ docker compose up -d
```
This will run paperless in the background and automatically start it
@@ -679,7 +687,7 @@ commands as well.
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`.
9. Start the containers as before, using `docker compose`.
## Moving data from SQLite to PostgreSQL or MySQL/MariaDB {#sqlite_to_psql}
@@ -737,7 +745,7 @@ below use PostgreSQL, but are applicable to MySQL/MariaDB with the
``` shell-session
$ cd /path/to/paperless
$ docker-compose run --rm webserver /bin/bash
$ docker compose run --rm webserver /bin/bash
```
This will launch the container and initialize the PostgreSQL
@@ -790,7 +798,7 @@ Execute this:
```shell-session
$ cd /path/to/paperless
$ docker-compose run --rm webserver migrate documents 0023
$ docker compose run --rm webserver migrate documents 0023
```
Or without docker:

View File

@@ -6,7 +6,7 @@ Check for the following issues:
- Ensure that the directory you're putting your documents in is the
folder paperless is watching. With docker, this setting is performed
in the `docker-compose.yml` file. Without docker, look at the
in the `docker-compose.yml` file. Without Docker, look at the
`CONSUMPTION_DIR` setting. Don't adjust this setting if you're
using docker.
@@ -120,7 +120,7 @@ Gotenberg raises this error.
You can increase the timeout by configuring a command flag for Gotenberg
(see also [here](https://gotenberg.dev/docs/modules/api#properties)). If
using docker-compose, this is achieved by the following configuration
using Docker Compose, this is achieved by the following configuration
change in the `docker-compose.yml` file:
```yaml
@@ -138,7 +138,7 @@ command:
You might encounter errors such as:
```shell-session
The following error occured while consuming document.pdf: [Errno 13] Permission denied: '/usr/src/paperless/src/../consume/document.pdf'
The following error occurred while consuming document.pdf: [Errno 13] Permission denied: '/usr/src/paperless/src/../consume/document.pdf'
```
This happens when paperless does not have permission to delete files

View File

@@ -149,7 +149,7 @@ different means. These are as follows:
- **Flag:** Sets the 'important' flag on mails with consumed
documents. Paperless will not consume flagged mails.
- **Move to folder:** Moves consumed mails out of the way so that
paperless wont consume them again.
paperless won't consume them again.
- **Add custom Tag:** Adds a custom tag to mails with consumed
documents (the IMAP standard calls these "keywords"). Paperless
will not consume mails already tagged. Not all mail servers support
@@ -238,7 +238,7 @@ do not have an owner set.
### Default permissions
Default permissions for documents can be set using consumption templates.
Default permissions for documents can be set using workflows.
For objects created via the web UI (tags, doc types, etc.) the default is to set the current user
as owner and no extra permissions, but you explicitly set these under Settings > Permissions.
@@ -255,50 +255,92 @@ permissions can be granted to limit access to certain parts of the UI (and corre
In order to enable the password reset feature you will need to setup an SMTP backend, see
[`PAPERLESS_EMAIL_HOST`](configuration.md#PAPERLESS_EMAIL_HOST)
## Consumption templates
## Workflows
Consumption templates were introduced in v2.0 and allow for finer control over what metadata (tags, doc
types) and permissions (owner, privileges) are assigned to documents during consumption. In general,
templates are applied sequentially (by sort order) but subsequent templates will never override an
assignment from a preceding template. The same is true for mail rules, e.g. if you set the correspondent
in a mail rule any subsequent consumption templates that are applied _will not_ overwrite this. The
exception to this is assignments that can be multiple e.g. tags and permissions, which will be merged.
!!! note
Consumption templates allow you to filter by:
v2.3 added "Workflows" and existing "Consumption Templates" were converted automatically to the new more powerful format.
Workflows allow hooking into the Paperless-ngx document pipeline, for example to alter what metadata (tags, doc types) and
permissions (owner, privileges) are assigned to documents. Workflows can have multiple 'triggers' and 'actions'. Triggers
are events (with optional filtering rules) that will cause the workflow to be run and actions are the set of sequential
actions to apply.
In general, workflows and any actions they contain are applied sequentially by sort order. For "assignment" actions, subsequent
workflow actions will override previous assignments, except for assignments that accept multiple items e.g. tags, custom
fields and permissions, which will be merged.
### Workflow Triggers
Currently, there are three events that correspond to workflow trigger 'types':
1. **Consumption Started**: _before_ a document is consumed, so events can include filters by source (mail, consumption
folder or API), file path, file name, mail rule
2. **Document Added**: _after_ a document is added. At this time, file path and source information is no longer available,
but the document content has been extracted and metadata such as document type, tags, etc. have been set, so these can now
be used for filtering.
3. **Document Updated**: when a document is updated. Similar to 'added' events, triggers can include filtering by content matching,
tags, doc type, or correspondent.
The following flow diagram illustrates the three trigger types:
```mermaid
flowchart TD
consumption{"Matching
'Consumption'
trigger(s)"}
added{"Matching
'Added'
trigger(s)"}
updated{"Matching
'Updated'
trigger(s)"}
A[New Document] --> consumption
consumption --> |Yes| C[Workflow Actions Run]
consumption --> |No| D
C --> D[Document Added]
D -- Paperless-ngx 'matching' of tags, etc. --> added
added --> |Yes| F[Workflow Actions Run]
added --> |No| G
F --> G[Document Finalized]
H[Existing Document Changed] --> updated
updated --> |Yes| J[Workflow Actions Run]
updated --> |No| K
J --> K[Document Saved]
```
#### Filters {#workflow-trigger-filters}
Workflows allow you to filter by:
- Source, e.g. documents uploaded via consume folder, API (& the web UI) and mail fetch
- File name, including wildcards e.g. \*.pdf will apply to all pdfs
- File path, including wildcards. Note that enabling `PAPERLESS_CONSUMER_RECURSIVE` would allow, for
example, automatically assigning documents to different owners based on the upload directory.
- Mail rule. Choosing this option will force 'mail fetch' to be the template source.
- Mail rule. Choosing this option will force 'mail fetch' to be the workflow source.
- Content matching (`Added` and `Updated` triggers only). Filter document content using the matching settings.
- Tags (`Added` and `Updated` triggers only). Filter for documents with any of the specified tags
- Document type (`Added` and `Updated` triggers only). Filter documents with this doc type
- Correspondent (`Added` and `Updated` triggers only). Filter documents with this correspondent
!!! note
### Workflow Actions
You must include a file name filter, a path filter or a mail rule filter. Use * for either to apply
to all files.
There is currently one type of workflow action, "Assignment", which can assign:
Consumption templates can assign:
- Title, see [title placeholders](usage.md#title_placeholders) below
- Title, see [title placeholders](usage.md#title-placeholders) below
- Tags, correspondent, document types
- Document owner
- View and / or edit permissions to users or groups
- Custom fields. Note that no value for the field will be set
### Consumption template permissions
#### Title placeholders
All users who have application permissions for editing consumption templates can see the same set
of templates. In other words, templates themselves intentionally do not have an owner or permissions.
Given their potentially far-reaching capabilities, you may want to restrict access to templates.
Upon migration, existing installs will grant access to consumption templates to users who can add
documents (and superusers who can always access all parts of the app).
### Title placeholders
Consumption template titles can include placeholders, _only for items that are assigned within the template_.
This is because at the time of consumption (when the title is to be set), no automatic tags etc. have been
applied. You can use the following placeholders:
Workflow titles can include placeholders but the available options differ depending on the type of
workflow trigger. This is because at the time of consumption (when the title is to be set), no automatic tags etc. have been
applied. You can use the following placeholders with any trigger type:
- `{correspondent}`: assigned correspondent name
- `{document_type}`: assigned document type name
@@ -310,6 +352,29 @@ applied. You can use the following placeholders:
- `{added_month_name}`: added month name
- `{added_month_name_short}`: added month short name
- `{added_day}`: added day
- `{added_time}`: added time in HH:MM format
- `{original_filename}`: original file name without extension
The following placeholders are only available for "added" or "updated" triggers
- `{created}`: created datetime
- `{created_year}`: created year
- `{created_year_short}`: created year
- `{created_month}`: created month
- `{created_month_name}`: created month name
- `{created_month_name_short}`: created month short name
- `{created_day}`: created day
- `{created_time}`: created time in HH:MM format
### Workflow permissions
All users who have application permissions for editing workflows can see the same set
of workflows. In other words, workflows themselves intentionally do not have an owner or permissions.
Given their potentially far-reaching capabilities, you may want to restrict access to workflows.
Upon migration, existing installs will grant access to workflows to users who can add
documents (and superusers who can always access all parts of the app).
## Custom Fields {#custom-fields}
@@ -342,10 +407,11 @@ The following custom field types are supported:
- `Integer`: integer number e.g. 12
- `Number`: float number e.g. 12.3456
- `Monetary`: float number with exactly two decimals, e.g. 12.30
- `Document Link`: reference(s) to other document(s) displayed as links, automatically creates a symmetrical link in reverse
## Share Links
Paperless-ngx added the abiltiy to create shareable links to files in version 2.0. You can find the button for this on the document detail screen.
Paperless-ngx added the ability to create shareable links to files in version 2.0. You can find the button for this on the document detail screen.
- Share links do not require a user to login and thus link directly to a file.
- Links are unique and are of the form `{paperless-url}/share/{randomly-generated-slug}`.

View File

@@ -56,14 +56,9 @@ if ! command -v docker &> /dev/null ; then
exit 1
fi
DOCKER_COMPOSE_CMD="docker-compose"
if ! command -v ${DOCKER_COMPOSE_CMD} &> /dev/null ; then
if docker compose version &> /dev/null ; then
DOCKER_COMPOSE_CMD="docker compose"
else
echo "docker-compose executable not found. Is docker-compose installed?"
exit 1
fi
if ! command -v docker compose &> /dev/null ; then
echo "docker compose executable not found. Is docker compose installed?"
exit 1
fi
# Check if user has permissions to run Docker by trying to get the status of Docker (docker status).
@@ -382,16 +377,16 @@ if [ "$l1" -eq "$l2" ] ; then
fi
${DOCKER_COMPOSE_CMD} pull
docker compose pull
if [ "$DATABASE_BACKEND" == "postgres" ] || [ "$DATABASE_BACKEND" == "mariadb" ] ; then
echo "Starting DB first for initilzation"
${DOCKER_COMPOSE_CMD} up --detach db
echo "Starting DB first for initialization"
docker compose up --detach db
# hopefully enough time for even the slower systems
sleep 15
${DOCKER_COMPOSE_CMD} stop
docker compose stop
fi
${DOCKER_COMPOSE_CMD} run --rm -e DJANGO_SUPERUSER_PASSWORD="$PASSWORD" webserver createsuperuser --noinput --username "$USERNAME" --email "$EMAIL"
docker compose run --rm -e DJANGO_SUPERUSER_PASSWORD="$PASSWORD" webserver createsuperuser --noinput --username "$USERNAME" --email "$EMAIL"
${DOCKER_COMPOSE_CMD} up --detach
docker compose up --detach

View File

@@ -44,6 +44,11 @@ markdown_extensions:
- pymdownx.inlinehilite
- pymdownx.snippets
- footnotes
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
strict: true
nav:
- index.md
@@ -67,4 +72,5 @@ extra:
- icon: material/chat
link: https://matrix.to/#/#paperless:matrix.org
plugins:
- search
- glightbox

View File

@@ -1,7 +1,8 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
"projects/**/*",
"/src/app/components/common/pdf-viewer/**"
],
"overrides": [
{

View File

@@ -65,7 +65,7 @@
"src/assets",
"src/manifest.webmanifest",
{
"glob": "pdf.worker.min.js",
"glob": "{pdf.worker.min.js,pdf.min.js}",
"input": "node_modules/pdfjs-dist/build/",
"output": "/assets/js/"
}
@@ -75,7 +75,8 @@
],
"scripts": [],
"allowedCommonJsDependencies": [
"ng2-pdf-viewer"
"pdfjs-dist",
"pdfjs-dist/web/pdf_viewer"
],
"vendorChunk": true,
"extractLicenses": false,
@@ -109,7 +110,7 @@
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
"maximumError": "30kb"
}
]
},
@@ -124,18 +125,18 @@
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "paperless-ui:build:en-US"
"buildTarget": "paperless-ui:build:en-US"
},
"configurations": {
"production": {
"browserTarget": "paperless-ui:build:production"
"buildTarget": "paperless-ui:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "paperless-ui:build"
"buildTarget": "paperless-ui:build"
}
},
"test": {

View File

@@ -12,13 +12,9 @@ test('should activate / deactivate save button when changes are saved', async ({
await expect(page.getByTitle('Storage path', { exact: true })).toHaveText(
/\w+/
)
await expect(
page.getByRole('button', { name: 'Save', exact: true })
).toBeDisabled()
await expect(page.getByRole('button', { name: 'Save' }).nth(1)).toBeDisabled()
await page.getByTitle('Storage path').getByTitle('Clear all').click()
await expect(
page.getByRole('button', { name: 'Save', exact: true })
).toBeEnabled()
await expect(page.getByRole('button', { name: 'Save' }).nth(1)).toBeEnabled()
})
test('should warn on unsaved changes', async ({ page }) => {
@@ -27,16 +23,12 @@ test('should warn on unsaved changes', async ({ page }) => {
await expect(page.getByTitle('Correspondent', { exact: true })).toHaveText(
/\w+/
)
await expect(
page.getByRole('button', { name: 'Save', exact: true })
).toBeDisabled()
await expect(page.getByRole('button', { name: 'Save' }).nth(1)).toBeDisabled()
await page
.getByTitle('Storage path', { exact: true })
.getByTitle('Clear all')
.click()
await expect(
page.getByRole('button', { name: 'Save', exact: true })
).toBeEnabled()
await expect(page.getByRole('button', { name: 'Save' }).nth(1)).toBeEnabled()
await page.getByRole('button', { name: 'Close', exact: true }).click()
await expect(page.getByRole('dialog')).toHaveText(/unsaved changes/)
await page.getByRole('button', { name: 'Cancel' }).click()
@@ -79,7 +71,7 @@ test('should show a mobile preview', async ({ page }) => {
await page.setViewportSize({ width: 400, height: 1000 })
await expect(page.getByRole('tab', { name: 'Preview' })).toBeVisible()
await page.getByRole('tab', { name: 'Preview' }).click()
await page.waitForSelector('pdf-viewer')
await page.waitForSelector('pngx-pdf-viewer')
})
test('should show a list of notes', async ({ page }) => {

View File

@@ -131,7 +131,7 @@ test('sorting', async ({ page }) => {
await page.getByRole('button', { name: 'Notes' }).click()
await expect(page).toHaveURL(/sort=num_notes/)
await page.getByRole('button', { name: 'Sort' }).click()
await page.locator('.w-100 > label > .toolbaricon').first().click()
await page.locator('.w-100 > label > i-bs').first().click()
await expect(page).not.toHaveURL(/reverse=1/)
})

View File

@@ -7,6 +7,7 @@ module.exports = {
'abstract-name-filter-service',
'abstract-paperless-service',
],
coveragePathIgnorePatterns: ['/src/app/components/common/pdf-viewer/*'],
transformIgnorePatterns: [`<rootDir>/node_modules/(?!.*\\.mjs$|lodash-es)`],
moduleNameMapper: {
'^src/(.*)': '<rootDir>/src/$1',

File diff suppressed because it is too large Load Diff

7997
src-ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,56 +11,57 @@
},
"private": true,
"dependencies": {
"@angular/cdk": "^16.2.11",
"@angular/common": "~16.2.11",
"@angular/compiler": "~16.2.11",
"@angular/core": "~16.2.11",
"@angular/forms": "~16.2.11",
"@angular/localize": "~16.2.11",
"@angular/platform-browser": "~16.2.11",
"@angular/platform-browser-dynamic": "~16.2.11",
"@angular/router": "~16.2.11",
"@ng-bootstrap/ng-bootstrap": "^15.1.2",
"@ng-select/ng-select": "^11.2.0",
"@angular/cdk": "^17.0.4",
"@angular/common": "~17.0.8",
"@angular/compiler": "~17.0.8",
"@angular/core": "~17.0.8",
"@angular/forms": "~17.0.8",
"@angular/localize": "~17.0.8",
"@angular/platform-browser": "~17.0.8",
"@angular/platform-browser-dynamic": "~17.0.8",
"@angular/router": "~17.0.8",
"@ng-bootstrap/ng-bootstrap": "^16.0.0",
"@ng-select/ng-select": "^12.0.4",
"@ngneat/dirty-check-forms": "^3.0.3",
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.2",
"file-saver": "^2.0.5",
"mime-names": "^1.0.0",
"ng2-pdf-viewer": "^10.0.0",
"ngx-bootstrap-icons": "^1.9.3",
"ngx-color": "^9.0.0",
"ngx-cookie-service": "^16.0.1",
"ngx-cookie-service": "^17.0.1",
"ngx-file-drop": "^16.0.0",
"ngx-ui-tour-ng-bootstrap": "^13.0.6",
"ngx-ui-tour-ng-bootstrap": "^14.0.1",
"pdfjs-dist": "^3.11.174",
"rxjs": "^7.8.1",
"tslib": "^2.6.2",
"uuid": "^9.0.1",
"zone.js": "^0.13.3"
"zone.js": "^0.14.2"
},
"devDependencies": {
"@angular-builders/jest": "16.0.1",
"@angular-devkit/build-angular": "~16.2.9",
"@angular-eslint/builder": "16.2.0",
"@angular-eslint/eslint-plugin": "16.2.0",
"@angular-eslint/eslint-plugin-template": "16.2.0",
"@angular-eslint/schematics": "16.2.0",
"@angular-eslint/template-parser": "16.2.0",
"@angular/cli": "~16.2.9",
"@angular/compiler-cli": "~16.2.3",
"@playwright/test": "^1.39.0",
"@types/jest": "^29.5.7",
"@types/node": "^20.8.10",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
"@angular-builders/jest": "17.0.0",
"@angular-devkit/build-angular": "~17.0.8",
"@angular-eslint/builder": "17.1.1",
"@angular-eslint/eslint-plugin": "17.1.1",
"@angular-eslint/eslint-plugin-template": "17.1.1",
"@angular-eslint/schematics": "17.1.1",
"@angular-eslint/template-parser": "17.1.1",
"@angular/cli": "~17.0.8",
"@angular/compiler-cli": "~17.0.7",
"@playwright/test": "^1.40.1",
"@types/jest": "^29.5.10",
"@types/node": "^20.10.6",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
"concurrently": "^8.2.2",
"eslint": "^8.52.0",
"eslint": "^8.56.0",
"jest": "29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-preset-angular": "^13.1.1",
"jest-preset-angular": "^13.1.4",
"jest-websocket-mock": "^2.5.0",
"patch-package": "^8.0.0",
"ts-node": "~10.9.1",
"typescript": "^5.1.6",
"wait-on": "^7.0.1"
"typescript": "^5.2.2",
"wait-on": "^7.2.0"
}
}

View File

@@ -21,10 +21,11 @@ import {
PermissionAction,
PermissionType,
} from './services/permissions.service'
import { ConsumptionTemplatesComponent } from './components/manage/consumption-templates/consumption-templates.component'
import { WorkflowsComponent } from './components/manage/workflows/workflows.component'
import { MailComponent } from './components/manage/mail/mail.component'
import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component'
import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component'
import { ConfigComponent } from './components/admin/config/config.component'
export const routes: Routes = [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
@@ -179,6 +180,17 @@ export const routes: Routes = [
},
},
},
{
path: 'config',
component: ConfigComponent,
canActivate: [PermissionsGuard],
data: {
requiredPermission: {
action: PermissionAction.View,
type: PermissionType.Admin,
},
},
},
{
path: 'tasks',
component: TasksComponent,
@@ -202,13 +214,13 @@ export const routes: Routes = [
},
},
{
path: 'templates',
component: ConsumptionTemplatesComponent,
path: 'workflows',
component: WorkflowsComponent,
canActivate: [PermissionsGuard],
data: {
requiredPermission: {
action: PermissionAction.View,
type: PermissionType.ConsumptionTemplate,
type: PermissionType.Workflow,
},
},
},

View File

@@ -1,32 +1,36 @@
<pngx-toasts></pngx-toasts>
<pngx-file-drop>
<ng-container content>
<router-outlet></router-outlet>
</ng-container>
<ng-container content>
<router-outlet></router-outlet>
</ng-container>
</pngx-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>
<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>
</ng-template>
<div class="btn-group btn-group-sm align-self-end" role="group" aria-label="Previous / Next">
@if (tourService.hasPrev(step)) {
<button class="btn btn-outline-primary" (click)="tourService.prev()">
« {{ step?.prevBtnTitle }}
</button>
}
@if (tourService.hasNext(step)) {
<button class="btn btn-outline-primary" (click)="tourService.next()">
{{ step?.nextBtnTitle }} »
</button>
}
</div>
</div>
</div>
</ng-template>
</tour-step-template>

View File

@@ -1,5 +1,5 @@
import { SettingsService } from './services/settings.service'
import { SETTINGS_KEYS } from './data/paperless-uisettings'
import { SETTINGS_KEYS } from './data/ui-settings'
import { Component, OnDestroy, OnInit, Renderer2 } from '@angular/core'
import { Router } from '@angular/router'
import { Subscription, first } from 'rxjs'
@@ -33,8 +33,6 @@ export class AppComponent implements OnInit, OnDestroy {
private renderer: Renderer2,
private permissionsService: PermissionsService
) {
let anyWindow = window as any
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js'
this.settings.updateAppearanceSettings()
}
@@ -178,9 +176,9 @@ export class AppComponent implements OnInit, OnDestroy {
},
},
{
anchorId: 'tour.consumption-templates',
content: $localize`Consumption templates give you finer control over the document ingestion process.`,
route: '/templates',
anchorId: 'tour.workflows',
content: $localize`Workflows give you more control over the document pipeline.`,
route: '/workflows',
backdropConfig: {
offset: 0,
},

View File

@@ -51,7 +51,6 @@ import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-v
import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component'
import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component'
import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'
import { PdfViewerModule } from 'ng2-pdf-viewer'
import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'
import { YesNoPipe } from './pipes/yes-no.pipe'
import { FileSizePipe } from './pipes/file-size.pipe'
@@ -96,8 +95,8 @@ import { UsernamePipe } from './pipes/username.pipe'
import { LogoComponent } from './components/common/logo/logo.component'
import { IsNumberPipe } from './pipes/is-number.pipe'
import { ShareLinksDropdownComponent } from './components/common/share-links-dropdown/share-links-dropdown.component'
import { ConsumptionTemplatesComponent } from './components/manage/consumption-templates/consumption-templates.component'
import { ConsumptionTemplateEditDialogComponent } from './components/common/edit-dialog/consumption-template-edit-dialog/consumption-template-edit-dialog.component'
import { WorkflowsComponent } from './components/manage/workflows/workflows.component'
import { WorkflowEditDialogComponent } from './components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component'
import { MailComponent } from './components/manage/mail/mail.component'
import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component'
import { DragDropModule } from '@angular/cdk/drag-drop'
@@ -105,6 +104,181 @@ import { FileDropComponent } from './components/file-drop/file-drop.component'
import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component'
import { CustomFieldEditDialogComponent } from './components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
import { CustomFieldsDropdownComponent } from './components/common/custom-fields-dropdown/custom-fields-dropdown.component'
import { ProfileEditDialogComponent } from './components/common/profile-edit-dialog/profile-edit-dialog.component'
import { PdfViewerComponent } from './components/common/pdf-viewer/pdf-viewer.component'
import { DocumentLinkComponent } from './components/common/input/document-link/document-link.component'
import { PreviewPopupComponent } from './components/common/preview-popup/preview-popup.component'
import { SwitchComponent } from './components/common/input/switch/switch.component'
import { ConfigComponent } from './components/admin/config/config.component'
import { FileComponent } from './components/common/input/file/file.component'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import {
archive,
arrowCounterclockwise,
arrowDown,
arrowLeft,
arrowRepeat,
arrowRight,
arrowRightShort,
arrowUpRight,
asterisk,
boxArrowUp,
boxArrowUpRight,
boxes,
calendar,
calendarEvent,
caretDown,
caretUp,
chatLeftText,
check,
check2All,
checkAll,
checkLg,
chevronDoubleLeft,
chevronDoubleRight,
clipboard,
clipboardCheckFill,
clipboardFill,
dash,
diagram3,
dice5,
doorOpen,
download,
envelope,
exclamationTriangle,
eye,
fileEarmark,
fileEarmarkCheck,
fileEarmarkFill,
fileEarmarkLock,
files,
fileText,
filter,
folder,
folderFill,
funnel,
gear,
grid,
gripVertical,
hash,
hddStack,
house,
infoCircle,
link,
listTask,
listUl,
pencil,
people,
peopleFill,
person,
personCircle,
personFill,
personFillLock,
personLock,
plus,
plusCircle,
questionCircle,
search,
slashCircle,
sliders2Vertical,
sortAlphaDown,
sortAlphaUpAlt,
tagFill,
tags,
textIndentLeft,
textLeft,
threeDots,
threeDotsVertical,
trash,
uiRadios,
upcScan,
x,
xLg,
} from 'ngx-bootstrap-icons'
const icons = {
archive,
arrowCounterclockwise,
arrowDown,
arrowLeft,
arrowRepeat,
arrowRight,
arrowRightShort,
arrowUpRight,
asterisk,
boxArrowUp,
boxArrowUpRight,
boxes,
calendar,
calendarEvent,
caretDown,
caretUp,
chatLeftText,
check,
check2All,
checkAll,
checkLg,
chevronDoubleLeft,
chevronDoubleRight,
clipboard,
clipboardCheckFill,
clipboardFill,
dash,
diagram3,
dice5,
doorOpen,
download,
envelope,
exclamationTriangle,
eye,
fileEarmark,
fileEarmarkCheck,
fileEarmarkFill,
fileEarmarkLock,
files,
fileText,
filter,
folder,
folderFill,
funnel,
gear,
grid,
gripVertical,
hash,
hddStack,
house,
infoCircle,
link,
listTask,
listUl,
pencil,
people,
peopleFill,
person,
personCircle,
personFill,
personFillLock,
personLock,
plus,
plusCircle,
questionCircle,
search,
slashCircle,
sliders2Vertical,
sortAlphaDown,
sortAlphaUpAlt,
tagFill,
tags,
textIndentLeft,
textLeft,
threeDots,
threeDotsVertical,
trash,
uiRadios,
upcScan,
x,
xLg,
}
import localeAf from '@angular/common/locales/af'
import localeAr from '@angular/common/locales/ar'
@@ -248,14 +422,21 @@ function initializeApp(settings: SettingsService) {
LogoComponent,
IsNumberPipe,
ShareLinksDropdownComponent,
ConsumptionTemplatesComponent,
ConsumptionTemplateEditDialogComponent,
WorkflowsComponent,
WorkflowEditDialogComponent,
MailComponent,
UsersAndGroupsComponent,
FileDropComponent,
CustomFieldsComponent,
CustomFieldEditDialogComponent,
CustomFieldsDropdownComponent,
ProfileEditDialogComponent,
PdfViewerComponent,
DocumentLinkComponent,
PreviewPopupComponent,
SwitchComponent,
ConfigComponent,
FileComponent,
],
imports: [
BrowserModule,
@@ -265,11 +446,11 @@ function initializeApp(settings: SettingsService) {
FormsModule,
ReactiveFormsModule,
NgxFileDropModule,
PdfViewerModule,
NgSelectModule,
ColorSliderModule,
TourNgBootstrapModule,
DragDropModule,
NgxBootstrapIconsModule.pick(icons),
],
providers: [
{

View File

@@ -0,0 +1,59 @@
<pngx-page-header
title="Application Configuration"
i18n-title
info="Global app configuration options which apply to <strong>every</strong> user of this install of Paperless-ngx. Options can also be set using environment variables or the configuration file but the value here will always take precedence."
i18n-info
infoLink="configuration">
</pngx-page-header>
<form [formGroup]="configForm" (ngSubmit)="saveConfig()" class="pb-4">
<ul ngbNav #nav="ngbNav" class="nav-tabs">
@for (category of optionCategories; track category) {
<li [ngbNavItem]="category">
<a ngbNavLink i18n>{{category}}</a>
<ng-template ngbNavContent>
<div class="p-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-2">
@for (option of getCategoryOptions(category); track option.key) {
<div class="col">
<div class="card bg-light">
<div class="card-body">
<div class="card-title">
<h6>
{{option.title}}
<a class="btn btn-sm btn-link" title="Read the documentation about this setting" i18n-title [href]="getDocsUrl(option.config_key)" target="_blank" referrerpolicy="no-referrer">
<i-bs name="info-circle"></i-bs>
</a>
</h6>
</div>
<div class="mb-n3">
@switch (option.type) {
@case (ConfigOptionType.Select) { <pngx-input-select [formControlName]="option.key" [error]="errors[option.key]" [items]="option.choices" [allowNull]="true"></pngx-input-select> }
@case (ConfigOptionType.Number) { <pngx-input-number [formControlName]="option.key" [error]="errors[option.key]" [showAdd]="false"></pngx-input-number> }
@case (ConfigOptionType.Boolean) { <pngx-input-switch [formControlName]="option.key" [error]="errors[option.key]" [showUnsetNote]="true" [horizontal]="true" title="Enable" i18n-title></pngx-input-switch> }
@case (ConfigOptionType.String) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
@case (ConfigOptionType.JSON) { <pngx-input-text [formControlName]="option.key" [error]="errors[option.key]"></pngx-input-text> }
@case (ConfigOptionType.File) { <pngx-input-file [formControlName]="option.key" (upload)="uploadFile($event, option.key)" [error]="errors[option.key]"></pngx-input-file> }
}
</div>
</div>
</div>
</div>
}
</div>
</div>
</ng-template>
</li>
}
</ul>
<div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div>
<div class="btn-toolbar" role="toolbar">
<div class="btn-group me-2">
<button type="button" (click)="discardChanges()" class="btn btn-secondary" [disabled]="loading || (isDirty$ | async) === false" i18n>Discard</button>
</div>
<div class="btn-group">
<button type="submit" class="btn btn-primary" [disabled]="loading || !configForm.valid || (isDirty$ | async) === false" i18n>Save</button>
</div>
</div>
</form>

View File

@@ -0,0 +1,143 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ConfigComponent } from './config.component'
import { ConfigService } from 'src/app/services/config.service'
import { ToastService } from 'src/app/services/toast.service'
import { of, throwError } from 'rxjs'
import { OutputTypeConfig } from 'src/app/data/paperless-config'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { BrowserModule } from '@angular/platform-browser'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { TextComponent } from '../../common/input/text/text.component'
import { NumberComponent } from '../../common/input/number/number.component'
import { SwitchComponent } from '../../common/input/switch/switch.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { SelectComponent } from '../../common/input/select/select.component'
import { FileComponent } from '../../common/input/file/file.component'
import { SettingsService } from 'src/app/services/settings.service'
describe('ConfigComponent', () => {
let component: ConfigComponent
let fixture: ComponentFixture<ConfigComponent>
let configService: ConfigService
let toastService: ToastService
let settingService: SettingsService
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
ConfigComponent,
TextComponent,
SelectComponent,
NumberComponent,
SwitchComponent,
FileComponent,
PageHeaderComponent,
],
imports: [
HttpClientTestingModule,
BrowserModule,
NgbModule,
NgSelectModule,
FormsModule,
ReactiveFormsModule,
],
}).compileComponents()
configService = TestBed.inject(ConfigService)
toastService = TestBed.inject(ToastService)
settingService = TestBed.inject(SettingsService)
fixture = TestBed.createComponent(ConfigComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should load config on init, show error if necessary', () => {
const getSpy = jest.spyOn(configService, 'getConfig')
const errorSpy = jest.spyOn(toastService, 'showError')
getSpy.mockReturnValueOnce(
throwError(() => new Error('Error getting config'))
)
component.ngOnInit()
expect(getSpy).toHaveBeenCalled()
expect(errorSpy).toHaveBeenCalled()
getSpy.mockReturnValueOnce(
of({ output_type: OutputTypeConfig.PDF_A } as any)
)
component.ngOnInit()
expect(component.initialConfig).toEqual({
output_type: OutputTypeConfig.PDF_A,
})
})
it('should save config, show error if necessary', () => {
const saveSpy = jest.spyOn(configService, 'saveConfig')
const errorSpy = jest.spyOn(toastService, 'showError')
saveSpy.mockReturnValueOnce(
throwError(() => new Error('Error saving config'))
)
component.saveConfig()
expect(saveSpy).toHaveBeenCalled()
expect(errorSpy).toHaveBeenCalled()
saveSpy.mockReturnValueOnce(
of({ output_type: OutputTypeConfig.PDF_A } as any)
)
component.saveConfig()
expect(component.initialConfig).toEqual({
output_type: OutputTypeConfig.PDF_A,
})
})
it('should support discard changes', () => {
component.initialConfig = { output_type: OutputTypeConfig.PDF_A2 } as any
component.configForm.patchValue({ output_type: OutputTypeConfig.PDF_A })
component.discardChanges()
expect(component.configForm.get('output_type').value).toEqual(
OutputTypeConfig.PDF_A2
)
})
it('should support JSON validation for e.g. user_args', () => {
component.configForm.patchValue({ user_args: '{ foo bar }' })
expect(component.errors).toEqual({ user_args: 'Invalid JSON' })
component.configForm.patchValue({ user_args: '{ "foo": "bar" }' })
expect(component.errors).toEqual({ user_args: null })
})
it('should upload file, show error if necessary', () => {
const uploadSpy = jest.spyOn(configService, 'uploadFile')
const errorSpy = jest.spyOn(toastService, 'showError')
uploadSpy.mockReturnValueOnce(
throwError(() => new Error('Error uploading file'))
)
component.uploadFile(new File([], 'test.png'), 'app_logo')
expect(uploadSpy).toHaveBeenCalled()
expect(errorSpy).toHaveBeenCalled()
uploadSpy.mockReturnValueOnce(
of({ app_logo: 'https://example.com/logo/test.png' } as any)
)
component.uploadFile(new File([], 'test.png'), 'app_logo')
expect(component.initialConfig).toEqual({
app_logo: 'https://example.com/logo/test.png',
})
})
it('should refresh ui settings after save or upload', () => {
const saveSpy = jest.spyOn(configService, 'saveConfig')
const initSpy = jest.spyOn(settingService, 'initializeSettings')
saveSpy.mockReturnValueOnce(
of({ output_type: OutputTypeConfig.PDF_A } as any)
)
component.saveConfig()
expect(initSpy).toHaveBeenCalled()
const uploadSpy = jest.spyOn(configService, 'uploadFile')
uploadSpy.mockReturnValueOnce(
of({ app_logo: 'https://example.com/logo/test.png' } as any)
)
component.uploadFile(new File([], 'test.png'), 'app_logo')
expect(initSpy).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,189 @@
import { Component, OnDestroy, OnInit } from '@angular/core'
import { AbstractControl, FormControl, FormGroup } from '@angular/forms'
import {
BehaviorSubject,
Observable,
Subject,
Subscription,
first,
takeUntil,
} from 'rxjs'
import {
PaperlessConfigOptions,
ConfigCategory,
ConfigOption,
ConfigOptionType,
PaperlessConfig,
} from 'src/app/data/paperless-config'
import { ConfigService } from 'src/app/services/config.service'
import { ToastService } from 'src/app/services/toast.service'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { DirtyComponent, dirtyCheck } from '@ngneat/dirty-check-forms'
import { SettingsService } from 'src/app/services/settings.service'
@Component({
selector: 'pngx-config',
templateUrl: './config.component.html',
styleUrl: './config.component.scss',
})
export class ConfigComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy, DirtyComponent
{
public readonly ConfigOptionType = ConfigOptionType
// generated dynamically
public configForm = new FormGroup({})
public errors = {}
get optionCategories(): string[] {
return Object.values(ConfigCategory)
}
getCategoryOptions(category: string): ConfigOption[] {
return PaperlessConfigOptions.filter((o) => o.category === category)
}
public loading: boolean = false
initialConfig: PaperlessConfig
store: BehaviorSubject<any>
storeSub: Subscription
isDirty$: Observable<boolean>
private unsubscribeNotifier: Subject<any> = new Subject()
constructor(
private configService: ConfigService,
private toastService: ToastService,
private settingsService: SettingsService
) {
super()
this.configForm.addControl('id', new FormControl())
PaperlessConfigOptions.forEach((option) => {
this.configForm.addControl(option.key, new FormControl())
})
}
ngOnInit(): void {
this.loading = true
this.configService
.getConfig()
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe({
next: (config) => {
this.loading = false
this.initialize(config)
},
error: (e) => {
this.loading = false
this.toastService.showError($localize`Error retrieving config`, e)
},
})
// validate JSON inputs
PaperlessConfigOptions.filter(
(o) => o.type === ConfigOptionType.JSON
).forEach((option) => {
this.configForm
.get(option.key)
.addValidators((control: AbstractControl) => {
if (!control.value || control.value.toString().length === 0)
return null
try {
JSON.parse(control.value)
} catch (e) {
return [
{
user_args: e,
},
]
}
return null
})
this.configForm.get(option.key).statusChanges.subscribe((status) => {
this.errors[option.key] =
status === 'INVALID' ? $localize`Invalid JSON` : null
})
this.configForm.get(option.key).updateValueAndValidity()
})
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete()
}
private initialize(config: PaperlessConfig) {
if (!this.store) {
this.store = new BehaviorSubject(config)
this.store
.asObservable()
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((state) => {
this.configForm.patchValue(state, { emitEvent: false })
})
this.isDirty$ = dirtyCheck(this.configForm, this.store.asObservable())
}
this.configForm.patchValue(config)
this.initialConfig = config
}
getDocsUrl(key: string) {
return `https://docs.paperless-ngx.com/configuration/#${key}`
}
public saveConfig() {
this.loading = true
this.configService
.saveConfig(this.configForm.value as PaperlessConfig)
.pipe(takeUntil(this.unsubscribeNotifier), first())
.subscribe({
next: (config) => {
this.loading = false
this.initialize(config)
this.store.next(config)
this.settingsService.initializeSettings().subscribe()
this.toastService.showInfo($localize`Configuration updated`)
},
error: (e) => {
this.loading = false
this.toastService.showError(
$localize`An error occurred updating configuration`,
e
)
},
})
}
public discardChanges() {
this.configForm.reset(this.initialConfig)
}
public uploadFile(file: File, key: string) {
this.loading = true
this.configService
.uploadFile(file, this.configForm.value['id'], key)
.pipe(takeUntil(this.unsubscribeNotifier), first())
.subscribe({
next: (config) => {
this.loading = false
this.initialize(config)
this.store.next(config)
this.settingsService.initializeSettings().subscribe()
this.toastService.showInfo($localize`File successfully updated`)
},
error: (e) => {
this.loading = false
this.toastService.showError(
$localize`An error occurred uploading file`,
e
)
},
})
}
}

View File

@@ -1,25 +1,44 @@
<pngx-page-header title="Logs" i18n-title>
<pngx-page-header
title="Logs"
i18n-title
info="Review the log files for the application and for email checking."
i18n-info>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="autoRefreshSwitch" (click)="toggleAutoRefresh()" [attr.checked]="autoRefreshInterval">
<label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label>
</div>
</pngx-page-header>
<ul ngbNav #nav="ngbNav" [(activeId)]="activeLog" (activeIdChange)="reloadLogs()" class="nav-tabs">
<li *ngFor="let logFile of logFiles" [ngbNavItem]="logFile">
<a ngbNavLink>{{logFile}}.log</a>
</li>
<div *ngIf="isLoading && !logFiles.length" class="pb-2">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
<ng-container i18n>Loading...</ng-container>
</div>
@for (logFile of logFiles; track logFile) {
<li [ngbNavItem]="logFile">
<a ngbNavLink>
{{logFile}}.log
</a>
</li>
}
@if (isLoading || !logFiles.length) {
<div class="ps-2 d-flex align-items-center">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
@if (!logFiles.length) {
<ng-container i18n>Loading...</ng-container>
}
</div>
}
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
<div class="bg-dark p-3 text-light font-monospace log-container" #logContainer>
<div *ngIf="isLoading && logFiles.length">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
<ng-container i18n>Loading...</ng-container>
</div>
<p
class="m-0 p-0 log-entry-{{getLogLevel(log)}}"
*ngFor="let log of logs">{{log}}</p>
@if (isLoading && logFiles.length) {
<div>
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
<ng-container i18n>Loading...</ng-container>
</div>
}
@for (log of logs; track log) {
<p
class="m-0 p-0 log-entry-{{getLogLevel(log)}}"
>{{log}}</p>
}
</div>

View File

@@ -1,4 +1,9 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import { LogService } from 'src/app/services/rest/log.service'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { LogsComponent } from './logs.component'
@@ -26,6 +31,7 @@ describe('LogsComponent', () => {
let fixture: ComponentFixture<LogsComponent>
let logService: LogService
let logSpy
let reloadSpy
beforeEach(async () => {
TestBed.configureTestingModule({
@@ -42,7 +48,9 @@ describe('LogsComponent', () => {
})
fixture = TestBed.createComponent(LogsComponent)
component = fixture.componentInstance
reloadSpy = jest.spyOn(component, 'reloadLogs')
window.HTMLElement.prototype.scroll = function () {} // mock scroll
jest.useFakeTimers()
fixture.detectChanges()
})
@@ -68,4 +76,14 @@ describe('LogsComponent', () => {
component.reloadLogs()
expect(component.logs).toHaveLength(0)
})
it('should auto refresh, allow toggle', () => {
jest.advanceTimersByTime(6000)
expect(reloadSpy).toHaveBeenCalledTimes(2)
component.toggleAutoRefresh()
expect(component.autoRefreshInterval).toBeNull()
jest.advanceTimersByTime(6000)
expect(reloadSpy).toHaveBeenCalledTimes(2)
})
})

View File

@@ -2,9 +2,9 @@ import {
Component,
ElementRef,
OnInit,
AfterViewChecked,
ViewChild,
OnDestroy,
ChangeDetectorRef,
} from '@angular/core'
import { Subject, takeUntil } from 'rxjs'
import { LogService } from 'src/app/services/rest/log.service'
@@ -14,8 +14,11 @@ import { LogService } from 'src/app/services/rest/log.service'
templateUrl: './logs.component.html',
styleUrls: ['./logs.component.scss'],
})
export class LogsComponent implements OnInit, AfterViewChecked, OnDestroy {
constructor(private logService: LogService) {}
export class LogsComponent implements OnInit, OnDestroy {
constructor(
private logService: LogService,
private changedetectorRef: ChangeDetectorRef
) {}
public logs: string[] = []
@@ -27,6 +30,8 @@ export class LogsComponent implements OnInit, AfterViewChecked, OnDestroy {
public isLoading: boolean = false
public autoRefreshInterval: any
@ViewChild('logContainer') logContainer: ElementRef
ngOnInit(): void {
@@ -41,16 +46,14 @@ export class LogsComponent implements OnInit, AfterViewChecked, OnDestroy {
this.activeLog = this.logFiles[0]
this.reloadLogs()
}
this.toggleAutoRefresh()
})
}
ngAfterViewChecked() {
this.scrollToBottom()
}
ngOnDestroy(): void {
this.unsubscribeNotifier.next(true)
this.unsubscribeNotifier.complete()
clearInterval(this.autoRefreshInterval)
}
reloadLogs() {
@@ -62,6 +65,7 @@ export class LogsComponent implements OnInit, AfterViewChecked, OnDestroy {
next: (result) => {
this.logs = result
this.isLoading = false
this.scrollToBottom()
},
error: () => {
this.logs = []
@@ -85,10 +89,22 @@ export class LogsComponent implements OnInit, AfterViewChecked, OnDestroy {
}
scrollToBottom(): void {
this.changedetectorRef.detectChanges()
this.logContainer?.nativeElement.scroll({
top: this.logContainer.nativeElement.scrollHeight,
left: 0,
behavior: 'auto',
})
}
toggleAutoRefresh(): void {
if (this.autoRefreshInterval) {
clearInterval(this.autoRefreshInterval)
this.autoRefreshInterval = null
} else {
this.autoRefreshInterval = setInterval(() => {
this.reloadLogs()
}, 5000)
}
}
}

View File

@@ -1,10 +1,13 @@
<pngx-page-header title="Settings" i18n-title>
<pngx-page-header
title="Settings"
i18n-title
info="Options to customize appearance, notifications, saved views and more. Settings apply to the <strong>current user only</strong>."
i18n-info
>
<button class="btn btn-sm btn-outline-primary" (click)="tourService.start()"><ng-container i18n>Start tour</ng-container></button>
<a *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }" class="btn btn-sm btn-primary ms-3" href="admin/" target="_blank">
<ng-container i18n>Open Django Admin</ng-container>
<svg class="sidebaricon ms-1" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#arrow-up-right"/>
</svg>
<ng-container i18n>Open Django Admin</ng-container>
<i-bs name="arrow-up-right"></i-bs>
</a>
</pngx-page-header>
@@ -24,10 +27,16 @@
<div class="col">
<select class="form-select" formControlName="displayLanguage">
<option *ngFor="let lang of displayLanguageOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code && currentLocale !== 'en-US'"> - {{lang.englishName}}</span></option>
@for (lang of displayLanguageOptions; track lang) {
<option [ngValue]="lang.code">{{lang.name}}@if (lang.code && currentLocale !== 'en-US') {
<span> - {{lang.englishName}}</span>
}</option>
}
</select>
<small *ngIf="displayLanguageIsDirty" class="form-text text-primary" i18n>You need to reload the page after applying a new language.</small>
@if (displayLanguageIsDirty) {
<small class="form-text text-primary" i18n>You need to reload the page after applying a new language.</small>
}
</div>
</div>
@@ -39,7 +48,11 @@
<div class="col">
<select class="form-select" formControlName="dateLocale">
<option *ngFor="let lang of dateLocaleOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> - {{today | customDate:'shortDate':null:lang.code}}</span></option>
@for (lang of dateLocaleOptions; track lang) {
<option [ngValue]="lang.code">{{lang.name}}@if (lang.code) {
<span> - {{today | customDate:'shortDate':null:lang.code}}</span>
}</option>
}
</select>
</div>
@@ -125,9 +138,7 @@
</div>
<div class="col-2">
<button class="btn btn-link btn-sm pt-2 ps-0" [disabled]="!this.settingsForm.get('themeColor').value" (click)="clearThemeColor()">
<svg fill="currentColor" class="buttonicon-sm me-1">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg><ng-container i18n>Reset</ng-container>
<i-bs width="1em" height="1em" name="x"></i-bs><ng-container i18n>Reset</ng-container>
</button>
</div>
</div>
@@ -137,12 +148,12 @@
<div class="row mb-3">
<div class="offset-md-3 col">
<p i18n>
Update checking works by pinging 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>
Update checking works by pinging 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>
<em>No tracking data is collected by the app in any way.</em>
</p>
<pngx-input-check i18n-title title="Enable update checking" formControlName="updateCheckingEnabled"></pngx-input-check>
</div>
</div>
@@ -176,15 +187,15 @@
<div class="row mb-3">
<div class="offset-md-3 col">
<p i18n>
Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI
</p>
Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI
</p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Default Owner</span>
</div>
<div class="col-md-4">
<div class="col-md-5">
<pngx-input-select [items]="users" bindLabel="username" formControlName="defaultPermsOwner" [allowNull]="true"></pngx-input-select>
<small class="form-text text-muted text-end d-block mt-n2" i18n>Objects without an owner can be viewed and edited by all users</small>
</div>
@@ -193,9 +204,9 @@
<div class="col-md-3 col-form-label pt-0">
<span i18n>Default View Permissions</span>
</div>
<div class="col-md-4">
<div class="col-md-5">
<div class="row">
<div class="col-2">
<div class="col-3">
<span class="d-block pt-1" i18n>Users:</span>
</div>
<div class="col">
@@ -205,7 +216,7 @@
</div>
</div>
<div class="row">
<div class="col-2">
<div class="col-3">
<span class="d-block pt-1" i18n>Groups:</span>
</div>
<div class="col">
@@ -220,9 +231,9 @@
<div class="col-md-3 col-form-label pt-0">
<span i18n>Default Edit Permissions</span>
</div>
<div class="col-md-4">
<div class="col-md-5">
<div class="row">
<div class="col-2">
<div class="col-3">
<span class="d-block pt-1" i18n>Users:</span>
</div>
<div class="col">
@@ -232,7 +243,7 @@
</div>
</div>
<div class="row">
<div class="col-2">
<div class="col-3">
<span class="d-block pt-1" i18n>Groups:</span>
</div>
<div class="col">
@@ -281,12 +292,12 @@
<h4 i18n>Views</h4>
<div formGroupName="savedViews">
<div *ngFor="let view of savedViews" [formGroupName]="view.id" class="row">
@for (view of savedViews; track view) {
<div [formGroupName]="view.id" class="row">
<div class="mb-3 col">
<label class="form-label" for="name_{{view.id}}" i18n>Name</label>
<input type="text" class="form-control" formControlName="name" id="name_{{view.id}}">
</div>
<div class="mb-2 col">
<label class="form-label" for="show_on_dashboard_{{view.id}}" i18n>&nbsp;<span class="visually-hidden">Appears on</span></label>
<div class="form-check form-switch">
@@ -298,19 +309,23 @@
<label class="form-check-label" for="show_in_sidebar_{{view.id}}" i18n>Show in sidebar</label>
</div>
</div>
<div class="mb-2 col-auto">
<label class="form-label" for="name_{{view.id}}" i18n>Actions</label>
<button type="button" class="btn btn-sm btn-outline-danger form-control" (click)="deleteSavedView(view)" *pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.SavedView }" i18n>Delete</button>
</div>
</div>
}
<div *ngIf="savedViews && savedViews.length === 0" i18n>No saved views defined.</div>
@if (savedViews && savedViews.length === 0) {
<div i18n>No saved views defined.</div>
}
<div *ngIf="!savedViews">
@if (!savedViews) {
<div>
<div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div>
<div class="visually-hidden" i18n>Loading...</div>
</div>
}
</div>

View File

@@ -13,8 +13,8 @@ import {
import { NgSelectModule } from '@ng-select/ng-select'
import { of, throwError } from 'rxjs'
import { routes } from 'src/app/app-routing.module'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { SavedView } from 'src/app/data/saved-view'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
@@ -138,7 +138,7 @@ describe('SettingsComponent', () => {
of({
all: savedViews.map((v) => v.id),
count: savedViews.length,
results: (savedViews as PaperlessSavedView[]).concat([]),
results: (savedViews as SavedView[]).concat([]),
})
)
}
@@ -226,9 +226,7 @@ describe('SettingsComponent', () => {
savedViewPatchSpy.mockClear()
// succeed saved views
savedViewPatchSpy.mockReturnValueOnce(
of(savedViews as PaperlessSavedView[])
)
savedViewPatchSpy.mockReturnValueOnce(of(savedViews as SavedView[]))
component.saveSettings()
expect(toastErrorSpy).not.toHaveBeenCalled()
expect(savedViewPatchSpy).toHaveBeenCalled()
@@ -335,7 +333,7 @@ describe('SettingsComponent', () => {
const toastSpy = jest.spyOn(toastService, 'showInfo')
const deleteSpy = jest.spyOn(savedViewService, 'delete')
deleteSpy.mockReturnValue(of(true))
component.deleteSavedView(savedViews[0] as PaperlessSavedView)
component.deleteSavedView(savedViews[0] as SavedView)
expect(deleteSpy).toHaveBeenCalled()
expect(toastSpy).toHaveBeenCalledWith(
`Saved view "${savedViews[0].name}" deleted.`

View File

@@ -21,10 +21,10 @@ import {
takeUntil,
tap,
} from 'rxjs'
import { PaperlessGroup } from 'src/app/data/paperless-group'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { PaperlessUser } from 'src/app/data/paperless-user'
import { Group } from 'src/app/data/group'
import { SavedView } from 'src/app/data/saved-view'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { User } from 'src/app/data/user'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import {
PermissionsService,
@@ -48,6 +48,12 @@ enum SettingsNavIDs {
SavedViews = 4,
}
const systemLanguage = { code: '', name: $localize`Use system language` }
const systemDateFormat = {
code: '',
name: $localize`Use date format of display language`,
}
@Component({
selector: 'pngx-settings',
templateUrl: './settings.component.html',
@@ -92,7 +98,7 @@ export class SettingsComponent
savedViews: this.savedViewGroup,
})
savedViews: PaperlessSavedView[]
savedViews: SavedView[]
store: BehaviorSubject<any>
storeSub: Subscription
@@ -101,8 +107,8 @@ export class SettingsComponent
unsubscribeNotifier: Subject<any> = new Subject()
savePending: boolean = false
users: PaperlessUser[]
groups: PaperlessGroup[]
users: User[]
groups: Group[]
get computedDateLocale(): string {
return (
@@ -357,12 +363,12 @@ export class SettingsComponent
}
ngOnDestroy() {
if (this.isDirty) this.settings.updateAppearanceSettings() // in case user changed appearance but didnt save
if (this.isDirty) this.settings.updateAppearanceSettings() // in case user changed appearance but didn't save
this.storeSub && this.storeSub.unsubscribe()
this.settings.organizingSidebarSavedViews = false
}
deleteSavedView(savedView: PaperlessSavedView) {
deleteSavedView(savedView: SavedView) {
this.savedViewService.delete(savedView).subscribe(() => {
this.savedViewGroup.removeControl(savedView.id.toString())
this.savedViews.splice(this.savedViews.indexOf(savedView), 1)
@@ -416,7 +422,7 @@ export class SettingsComponent
)
this.settings.set(
SETTINGS_KEYS.THEME_COLOR,
this.settingsForm.value.themeColor.toString()
this.settingsForm.value.themeColor
)
this.settings.set(
SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER,
@@ -512,15 +518,11 @@ export class SettingsComponent
}
get displayLanguageOptions(): LanguageOption[] {
return [{ code: '', name: $localize`Use system language` }].concat(
this.settings.getLanguageOptions()
)
return [systemLanguage].concat(this.settings.getLanguageOptions())
}
get dateLocaleOptions(): LanguageOption[] {
return [
{ code: '', name: $localize`Use date format of display language` },
].concat(this.settings.getDateLocaleOptions())
return [systemDateFormat].concat(this.settings.getDateLocaleOptions())
}
get today() {
@@ -529,7 +531,7 @@ export class SettingsComponent
saveSettings() {
// only patch views that have actually changed
const changed: PaperlessSavedView[] = []
const changed: SavedView[] = []
Object.values(this.savedViewGroup.controls)
.filter((g: FormGroup) => !g.pristine)
.forEach((group: FormGroup) => {

View File

@@ -1,31 +1,27 @@
<pngx-page-header title="File Tasks" i18n-title>
<div class="btn-toolbar col col-md-auto">
<pngx-page-header
title="File Tasks"
i18n-title
info="File Tasks shows you documents that have been consumed, are waiting to be, or may have failed during the process."
i18n-info
>
<div class="btn-toolbar col col-md-auto align-items-center">
<button class="btn btn-sm btn-outline-secondary me-2" (click)="clearSelection()" [hidden]="selectedTasks.size === 0">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>&nbsp;<ng-container i18n>Clear selection</ng-container>
<i-bs name="x"></i-bs>&nbsp;<ng-container i18n>Clear selection</ng-container>
</button>
<button class="btn btn-sm btn-outline-primary me-4" (click)="dismissTasks()" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }" [disabled]="tasksService.total === 0">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#check2-all"/>
</svg>&nbsp;<ng-container i18n>{{dismissButtonText}}</ng-container>
</button>
<button class="btn btn-sm btn-outline-primary" (click)="tasksService.reload()">
<svg *ngIf="!tasksService.loading" class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#arrow-clockwise"/>
</svg>
<ng-container *ngIf="tasksService.loading">
<div class="spinner-border spinner-border-sm fw-normal" role="status"></div>
<div class="visually-hidden" i18n>Loading...</div>
</ng-container>&nbsp;<ng-container i18n>Refresh</ng-container>
<i-bs name="check2-all"></i-bs>&nbsp;{{dismissButtonText}}
</button>
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" role="switch" id="autoRefreshSwitch" (click)="toggleAutoRefresh()" [attr.checked]="autoRefreshInterval">
<label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label>
</div>
</div>
</pngx-page-header>
<ng-container *ngIf="!tasksService.completedFileTasks && tasksService.loading">
@if (!tasksService.completedFileTasks && tasksService.loading) {
<div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div>
<div class="visually-hidden" i18n>Loading...</div>
</ng-container>
}
<ng-template let-tasks="tasks" #tasksTemplate>
<table class="table table-striped align-middle border shadow-sm">
@@ -39,93 +35,118 @@
</th>
<th scope="col" i18n>Name</th>
<th scope="col" class="d-none d-lg-table-cell" i18n>Created</th>
<th scope="col" class="d-none d-lg-table-cell" *ngIf="activeTab !== 'started' && activeTab !== 'queued'" i18n>Results</th>
@if (activeTab !== 'started' && activeTab !== 'queued') {
<th scope="col" class="d-none d-lg-table-cell" i18n>Results</th>
}
<th scope="col" class="d-table-cell d-lg-none" i18n>Info</th>
<th scope="col" i18n>Actions</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let task of tasks | slice: (page-1) * pageSize : page * pageSize">
<tr (click)="toggleSelected(task, $event); $event.stopPropagation();">
<td>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="task{{task.id}}" [checked]="selectedTasks.has(task.id)" (click)="toggleSelected(task, $event); $event.stopPropagation();">
<label class="form-check-label" for="task{{task.id}}"></label>
</div>
</td>
<td class="overflow-auto name-col">{{ 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">
<span class="small d-none d-md-inline-block font-monospace text-muted">{{ task.result | slice:0:50 }}&hellip;</span>
</div>
<span *ngIf="task.result?.length <= 50" class="small d-none d-md-inline-block font-monospace text-muted">{{ task.result }}</span>
<ng-template #resultPopover>
<pre class="small mb-0">{{ task.result | slice:0:300 }}<ng-container *ngIf="task.result.length > 300">&hellip;</ng-container></pre>
<ng-container *ngIf="task.result?.length > 300"><br/><em>(<ng-container i18n>click for full output</ng-container>)</em></ng-container>
</ng-template>
</td>
<td class="d-lg-none">
<button class="btn btn-link" (click)="expandTask(task); $event.stopPropagation();">
<svg fill="currentColor" class="" 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>
</button>
</td>
<td scope="row">
<div class="btn-group" role="group">
<button class="btn btn-sm btn-outline-secondary" (click)="dismissTask(task); $event.stopPropagation();" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#check"/>
</svg>&nbsp;<ng-container i18n>Dismiss</ng-container>
@for (task of tasks | slice: (page-1) * pageSize : page * pageSize; track task) {
<tr (click)="toggleSelected(task, $event); $event.stopPropagation();">
<td>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="task{{task.id}}" [checked]="selectedTasks.has(task.id)" (click)="toggleSelected(task, $event); $event.stopPropagation();">
<label class="form-check-label" for="task{{task.id}}"></label>
</div>
</td>
<td class="overflow-auto name-col">{{ task.task_file_name }}</td>
<td class="d-none d-lg-table-cell">{{ task.date_created | customDate:'short' }}</td>
@if (activeTab !== 'started' && activeTab !== 'queued') {
<td class="d-none d-lg-table-cell">
@if (task.result?.length > 50) {
<div class="result" (click)="expandTask(task); $event.stopPropagation();"
[ngbPopover]="resultPopover" popoverClass="shadow small mobile" triggers="mouseenter:mouseleave" container="body">
<span class="small d-none d-md-inline-block font-monospace text-muted">{{ task.result | slice:0:50 }}&hellip;</span>
</div>
}
@if (task.result?.length <= 50) {
<span class="small d-none d-md-inline-block font-monospace text-muted">{{ task.result }}</span>
}
<ng-template #resultPopover>
<pre class="small mb-0">{{ task.result | slice:0:300 }}@if (task.result.length > 300) {
&hellip;
}</pre>
@if (task.result?.length > 300) {
<br/><em>(<ng-container i18n>click for full output</ng-container>)</em>
}
</ng-template>
</td>
}
<td class="d-lg-none">
<button class="btn btn-link" (click)="expandTask(task); $event.stopPropagation();">
<i-bs width="1.2em" height="1.2em" name="info-circle"></i-bs>
</button>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
<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>
</td>
<td scope="row">
<div class="btn-group" role="group">
<button class="btn btn-sm btn-outline-secondary" (click)="dismissTask(task); $event.stopPropagation();" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }">
<i-bs name="check"></i-bs>&nbsp;<ng-container i18n>Dismiss</ng-container>
</button>
</ng-container>
</div>
</td>
</tr>
<tr>
<td class="p-0" [class.border-0]="expandedTask !== task.id" colspan="5">
<pre #collapse="ngbCollapse" [ngbCollapse]="expandedTask !== task.id" class="small mb-0"><div class="small p-1 p-lg-3 ms-lg-3">{{ task.result }}</div></pre>
</td>
</tr>
</ng-container>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
@if (task.related_document) {
<button class="btn btn-sm btn-outline-primary" (click)="dismissAndGo(task); $event.stopPropagation();">
<i-bs name="file-text"></i-bs>&nbsp;<ng-container i18n>Open Document</ng-container>
</button>
}
</ng-container>
</div>
</td>
</tr>
<tr>
<td class="p-0" [class.border-0]="expandedTask !== task.id" colspan="5">
<pre #collapse="ngbCollapse" [ngbCollapse]="expandedTask !== task.id" class="small mb-0"><div class="small p-1 p-lg-3 ms-lg-3">{{ task.result }}</div></pre>
</td>
</tr>
}
</tbody>
</table>
<div class="pb-3 d-sm-flex justify-content-between align-items-center">
<div class="pb-2 pb-sm-0" i18n *ngIf="tasks.length > 0">{tasks.length, plural, =1 {One {{this.activeTabLocalized}} task} other {{{tasks.length || 0}} total {{this.activeTabLocalized}} tasks}}</div>
<ngb-pagination *ngIf="tasks.length > pageSize" [(page)]="page" [pageSize]="pageSize" [collectionSize]="tasks.length" maxSize="8" size="sm"></ngb-pagination>
@if (tasks.length > 0) {
<div class="pb-2 pb-sm-0">
<ng-container i18n>{tasks.length, plural, =1 {One {{this.activeTabLocalized}} task} other {{{tasks.length || 0}} total {{this.activeTabLocalized}} tasks}}</ng-container>
@if (selectedTasks.size > 0) {
<ng-container i18n>&nbsp;({{selectedTasks.size}} selected)</ng-container>
}
</div>
}
@if (tasks.length > pageSize) {
<ngb-pagination [(page)]="page" [pageSize]="pageSize" [collectionSize]="tasks.length" maxSize="8" size="sm"></ngb-pagination>
}
</div>
</ng-template>
<ul ngbNav #nav="ngbNav" [(activeId)]="activeTab" class="nav-tabs" (hidden)="duringTabChange($event)">
<li ngbNavItem="failed">
<a ngbNavLink i18n>Failed<span *ngIf="tasksService.failedFileTasks.length > 0" class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span></a>
<a ngbNavLink i18n>Failed@if (tasksService.failedFileTasks.length > 0) {
<span class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span>
}</a>
<ng-template ngbNavContent>
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.failedFileTasks}"></ng-container>
</ng-template>
</li>
<li ngbNavItem="completed">
<a ngbNavLink i18n>Complete<span *ngIf="tasksService.completedFileTasks.length > 0" class="badge bg-secondary ms-2">{{tasksService.completedFileTasks.length}}</span></a>
<a ngbNavLink i18n>Complete@if (tasksService.completedFileTasks.length > 0) {
<span class="badge bg-secondary ms-2">{{tasksService.completedFileTasks.length}}</span>
}</a>
<ng-template ngbNavContent>
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.completedFileTasks}"></ng-container>
</ng-template>
</li>
<li ngbNavItem="started">
<a ngbNavLink i18n>Started<span *ngIf="tasksService.startedFileTasks.length > 0" class="badge bg-secondary ms-2">{{tasksService.startedFileTasks.length}}</span></a>
<a ngbNavLink i18n>Started@if (tasksService.startedFileTasks.length > 0) {
<span class="badge bg-secondary ms-2">{{tasksService.startedFileTasks.length}}</span>
}</a>
<ng-template ngbNavContent>
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.startedFileTasks}"></ng-container>
</ng-template>
</li>
<li ngbNavItem="queued">
<a ngbNavLink i18n>Queued<span *ngIf="tasksService.queuedFileTasks.length > 0" class="badge bg-secondary ms-2">{{tasksService.queuedFileTasks.length}}</span></a>
<a ngbNavLink i18n>Queued@if (tasksService.queuedFileTasks.length > 0) {
<span class="badge bg-secondary ms-2">{{tasksService.queuedFileTasks.length}}</span>
}</a>
<ng-template ngbNavContent>
<ng-container [ngTemplateOutlet]="tasksTemplate" [ngTemplateOutletContext]="{tasks:tasksService.queuedFileTasks}"></ng-container>
</ng-template>

View File

@@ -112,6 +112,7 @@ describe('TasksComponent', () => {
let modalService: NgbModal
let router: Router
let httpTestingController: HttpTestingController
let reloadSpy
beforeEach(async () => {
TestBed.configureTestingModule({
@@ -141,11 +142,13 @@ describe('TasksComponent', () => {
}).compileComponents()
tasksService = TestBed.inject(TasksService)
reloadSpy = jest.spyOn(tasksService, 'reload')
httpTestingController = TestBed.inject(HttpTestingController)
modalService = TestBed.inject(NgbModal)
router = TestBed.inject(Router)
fixture = TestBed.createComponent(TasksComponent)
component = fixture.componentInstance
jest.useFakeTimers()
fixture.detectChanges()
httpTestingController
.expectOne(`${environment.apiBaseUrl}tasks/`)
@@ -164,7 +167,7 @@ describe('TasksComponent', () => {
`Failed${currentTasksLength}`
)
expect(
fixture.debugElement.queryAll(By.css('input[type="checkbox"]'))
fixture.debugElement.queryAll(By.css('table input[type="checkbox"]'))
).toHaveLength(currentTasksLength + 1)
currentTasksLength = tasks.filter(
@@ -245,7 +248,7 @@ describe('TasksComponent', () => {
it('should support toggle all tasks', () => {
const toggleCheck = fixture.debugElement.query(
By.css('input[type=checkbox]')
By.css('table input[type=checkbox]')
)
toggleCheck.nativeElement.dispatchEvent(new MouseEvent('click'))
fixture.detectChanges()
@@ -269,4 +272,15 @@ describe('TasksComponent', () => {
tasks[3].related_document,
])
})
it('should auto refresh, allow toggle', () => {
expect(reloadSpy).toHaveBeenCalledTimes(1)
jest.advanceTimersByTime(5000)
expect(reloadSpy).toHaveBeenCalledTimes(2)
component.toggleAutoRefresh()
expect(component.autoRefreshInterval).toBeNull()
jest.advanceTimersByTime(6000)
expect(reloadSpy).toHaveBeenCalledTimes(2)
})
})

View File

@@ -23,6 +23,8 @@ export class TasksComponent
public pageSize: number = 25
public page: number = 1
public autoRefreshInterval: any
get dismissButtonText(): string {
return this.selectedTasks.size > 0
? $localize`Dismiss selected`
@@ -39,10 +41,12 @@ export class TasksComponent
ngOnInit() {
this.tasksService.reload()
this.toggleAutoRefresh()
}
ngOnDestroy() {
this.tasksService.cancelPending()
clearInterval(this.autoRefreshInterval)
}
dismissTask(task: PaperlessTask) {
@@ -135,4 +139,15 @@ export class TasksComponent
return $localize`failed`
}
}
toggleAutoRefresh(): void {
if (this.autoRefreshInterval) {
clearInterval(this.autoRefreshInterval)
this.autoRefreshInterval = null
} else {
this.autoRefreshInterval = setInterval(() => {
this.tasksService.reload()
}, 5000)
}
}
}

View File

@@ -1,97 +1,96 @@
<pngx-page-header title="Users & Groups" i18n-title>
<pngx-page-header
title="Users & Groups"
i18n-title
info="Create, delete and edit users and groups."
i18n-info
infoLink="usage/#users-and-groups"
>
</pngx-page-header>
<ng-container *ngIf="users">
<h4 class="d-flex">
<ng-container i18n>Users</ng-container>
<button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editUser()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.User }">
<svg class="sidebaricon me-1" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#plus-circle" />
</svg>
<ng-container i18n>Add User</ng-container>
</button>
</h4>
<ul class="list-group">
<li class="list-group-item">
@if (users) {
<h4 class="d-flex">
<ng-container i18n>Users</ng-container>
<button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editUser()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.User }">
<i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add User</ng-container>
</button>
</h4>
<ul class="list-group">
<li class="list-group-item">
<div class="row">
<div class="col" i18n>Username</div>
<div class="col" i18n>Name</div>
<div class="col" i18n>Groups</div>
<div class="col" i18n>Actions</div>
</div>
</li>
@for (user of users; track user) {
<li class="list-group-item">
<div class="row">
<div class="col" i18n>Username</div>
<div class="col" i18n>Name</div>
<div class="col" i18n>Groups</div>
<div class="col" i18n>Actions</div>
</div>
</li>
<li *ngFor="let user of users" class="list-group-item">
<div class="row">
<div class="col d-flex align-items-center"><button class="btn btn-link p-0" type="button" (click)="editUser(user)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.User)">{{user.username}}</button></div>
<div class="col d-flex align-items-center">{{user.first_name}} {{user.last_name}}</div>
<div class="col d-flex align-items-center">{{user.groups?.map(getGroupName, this).join(', ')}}</div>
<div class="col">
<div class="col d-flex align-items-center"><button class="btn btn-link p-0" type="button" (click)="editUser(user)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.User)">{{user.username}}</button></div>
<div class="col d-flex align-items-center">{{user.first_name}} {{user.last_name}}</div>
<div class="col d-flex align-items-center">{{user.groups?.map(getGroupName, this).join(', ')}}</div>
<div class="col">
<div class="btn-group">
<button class="btn btn-sm btn-outline-secondary" type="button" (click)="editUser(user)" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.User }">
<svg class="buttonicon-sm" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#pencil" />
</svg>&nbsp;<ng-container i18n>Edit</ng-container>
<button class="btn btn-sm btn-outline-secondary" type="button" (click)="editUser(user)" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.User }">
<i-bs width="1em" height="1em" name="pencil"></i-bs>&nbsp;<ng-container i18n>Edit</ng-container>
</button>
<button class="btn btn-sm btn-outline-danger" type="button" (click)="deleteUser(user)" *pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.User }">
<svg class="buttonicon-sm" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#trash" />
</svg>&nbsp;<ng-container i18n>Delete</ng-container>
</button>
<i-bs width="1em" height="1em" name="trash"></i-bs>&nbsp;<ng-container i18n>Delete</ng-container>
</button>
</div>
</div>
</div>
</div>
</div>
</li>
</ul>
</ng-container>
</li>
}
</ul>
}
<ng-container *ngIf="groups">
<h4 class="mt-4 d-flex">
@if (groups) {
<h4 class="mt-4 d-flex">
<ng-container i18n>Groups</ng-container>
<button type="button" class="btn btn-sm btn-outline-primary ms-4" (click)="editGroup()" *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.Group }">
<svg class="sidebaricon me-1" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#plus-circle" />
</svg>
<ng-container i18n>Add Group</ng-container>
<i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Group</ng-container>
</button>
</h4>
<ul *ngIf="groups.length > 0" class="list-group">
<li class="list-group-item">
<div class="row">
<div class="col" i18n>Name</div>
<div class="col"></div>
<div class="col"></div>
<div class="col" i18n>Actions</div>
</div>
</li>
<li *ngFor="let group of groups" class="list-group-item">
<div class="row">
<div class="col d-flex align-items-center"><button class="btn btn-link p-0" type="button" (click)="editGroup(group)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.Group)">{{group.name}}</button></div>
<div class="col"></div>
<div class="col"></div>
<div class="col">
<div class="btn-group">
<button class="btn btn-sm btn-outline-secondary" type="button" (click)="editGroup(group)" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Group }">
<svg class="buttonicon-sm" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#pencil" />
</svg>&nbsp;<ng-container i18n>Edit</ng-container>
</button>
<button class="btn btn-sm btn-outline-danger" type="button" (click)="deleteGroup(group)" *pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.Group }">
<svg class="buttonicon-sm" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#trash" />
</svg>&nbsp;<ng-container i18n>Delete</ng-container>
</button>
</h4>
@if (groups.length > 0) {
<ul class="list-group">
<li class="list-group-item">
<div class="row">
<div class="col" i18n>Name</div>
<div class="col"></div>
<div class="col"></div>
<div class="col" i18n>Actions</div>
</div>
</div>
</div>
</li>
<li *ngIf="groups.length === 0" class="list-group-item" i18n>No groups defined</li>
</ul>
</li>
@for (group of groups; track group) {
<li class="list-group-item">
<div class="row">
<div class="col d-flex align-items-center"><button class="btn btn-link p-0" type="button" (click)="editGroup(group)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.Group)">{{group.name}}</button></div>
<div class="col"></div>
<div class="col"></div>
<div class="col">
<div class="btn-group">
<button class="btn btn-sm btn-outline-secondary" type="button" (click)="editGroup(group)" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Group }">
<i-bs width="1em" height="1em" name="pencil"></i-bs>&nbsp;<ng-container i18n>Edit</ng-container>
</button>
<button class="btn btn-sm btn-outline-danger" type="button" (click)="deleteGroup(group)" *pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.Group }">
<i-bs width="1em" height="1em" name="trash"></i-bs>&nbsp;<ng-container i18n>Delete</ng-container>
</button>
</div>
</div>
</div>
</li>
}
@if (groups.length === 0) {
<li class="list-group-item" i18n>No groups defined</li>
}
</ul>
}
}
</ng-container>
<div *ngIf="!users || !groups">
<div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div>
<div class="visually-hidden" i18n>Loading...</div>
</div>
@if (!users || !groups) {
<div>
<div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div>
<div class="visually-hidden" i18n>Loading...</div>
</div>
}

View File

@@ -41,8 +41,8 @@ import { TextComponent } from '../../common/input/text/text.component'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { SettingsComponent } from '../settings/settings.component'
import { UsersAndGroupsComponent } from './users-groups.component'
import { PaperlessUser } from 'src/app/data/paperless-user'
import { PaperlessGroup } from 'src/app/data/paperless-group'
import { User } from 'src/app/data/user'
import { Group } from 'src/app/data/group'
const users = [
{ id: 1, username: 'user1', is_superuser: false },
@@ -119,7 +119,7 @@ describe('UsersAndGroupsComponent', () => {
of({
all: users.map((a) => a.id),
count: users.length,
results: (users as PaperlessUser[]).concat([]),
results: (users as User[]).concat([]),
})
)
}
@@ -128,7 +128,7 @@ describe('UsersAndGroupsComponent', () => {
of({
all: groups.map((r) => r.id),
count: groups.length,
results: (groups as PaperlessGroup[]).concat([]),
results: (groups as Group[]).concat([]),
})
)
}

View File

@@ -1,8 +1,8 @@
import { Component, OnDestroy, OnInit } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subject, first, takeUntil } from 'rxjs'
import { PaperlessGroup } from 'src/app/data/paperless-group'
import { PaperlessUser } from 'src/app/data/paperless-user'
import { Group } from 'src/app/data/group'
import { User } from 'src/app/data/user'
import { PermissionsService } from 'src/app/services/permissions.service'
import { GroupService } from 'src/app/services/rest/group.service'
import { UserService } from 'src/app/services/rest/user.service'
@@ -23,8 +23,8 @@ export class UsersAndGroupsComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy
{
users: PaperlessUser[]
groups: PaperlessGroup[]
users: User[]
groups: Group[]
unsubscribeNotifier: Subject<any> = new Subject()
@@ -69,7 +69,7 @@ export class UsersAndGroupsComponent
this.unsubscribeNotifier.next(true)
}
editUser(user: PaperlessUser = null) {
editUser(user: User = null) {
var modal = this.modalService.open(UserEditDialogComponent, {
backdrop: 'static',
size: 'xl',
@@ -80,7 +80,7 @@ export class UsersAndGroupsComponent
modal.componentInstance.object = user
modal.componentInstance.succeeded
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((newUser: PaperlessUser) => {
.subscribe((newUser: User) => {
if (
newUser.id === this.settings.currentUser.id &&
(modal.componentInstance as UserEditDialogComponent).passwordIsSet
@@ -89,7 +89,7 @@ export class UsersAndGroupsComponent
$localize`Password has been changed, you will be logged out momentarily.`
)
setTimeout(() => {
window.location.href = `${window.location.origin}/accounts/logout/?next=/accounts/login/`
window.location.href = `${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
}, 2500)
} else {
this.toastService.showInfo(
@@ -107,7 +107,7 @@ export class UsersAndGroupsComponent
})
}
deleteUser(user: PaperlessUser) {
deleteUser(user: User) {
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
@@ -133,7 +133,7 @@ export class UsersAndGroupsComponent
})
}
editGroup(group: PaperlessGroup = null) {
editGroup(group: Group = null) {
var modal = this.modalService.open(GroupEditDialogComponent, {
backdrop: 'static',
size: 'lg',
@@ -157,7 +157,7 @@ export class UsersAndGroupsComponent
})
}
deleteGroup(group: PaperlessGroup) {
deleteGroup(group: Group) {
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})

View File

@@ -4,24 +4,38 @@
(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" [ngClass]="slimSidebarEnabled ? 'slim' : 'col-auto col-md-3 col-lg-2'" routerLink="/dashboard" tourAnchor="tour.intro">
<a class="navbar-brand d-flex col-auto col-md-3 col-lg-2 me-0 px-3 py-3 order-sm-0"
[ngClass]="{ 'slim': slimSidebarEnabled, 'd-flex col-auto col-md-3 col-lg-2' : !slimSidebarEnabled, 'py-3' : !customAppTitle?.length || slimSidebarEnabled, 'py-2': customAppTitle?.length }"
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)"/>
<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>
<span class="ms-2" [class.visually-hidden]="slimSidebarEnabled" i18n="app title">Paperless-ngx</span>
<div class="ms-2 d-inline-block" [class.visually-hidden]="slimSidebarEnabled">
@if (customAppTitle?.length) {
<div class="d-flex flex-column align-items-start">
<span class="title">{{customAppTitle}}</span>
<span class="byline text-uppercase font-monospace" i18n>by Paperless-ngx</span>
</div>
} @else {
Paperless-ngx
}
</div>
</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" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
<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"
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
<form (ngSubmit)="search()" class="form-inline flex-grow-1">
<svg width="1em" height="1em" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#search"/>
</svg>
<i-bs style="top: .25em;" width="1em" height="1em" name="search"></i-bs>
<input class="form-control form-control-sm" type="text" placeholder="Search documents" aria-label="Search"
[formControl]="searchField" [ngbTypeahead]="searchAutoComplete" (keyup)="searchFieldKeyup($event)" (selectItem)="itemSelected($event)" i18n-placeholder>
<button type="button" *ngIf="!searchFieldEmpty" class="btn btn-link btn-sm px-0 position-absolute top-0 end-0" (click)="resetSearchField()">
<svg fill="currentColor" class="buttonicon-sm me-1">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>
</button>
[formControl]="searchField" [ngbTypeahead]="searchAutoComplete" (keyup)="searchFieldKeyup($event)"
(selectItem)="itemSelected($event)" i18n-placeholder>
@if (!searchFieldEmpty) {
<button type="button" class="btn btn-link btn-sm px-0 position-absolute top-0 end-0" (click)="resetSearchField()">
<i-bs width="1em" height="1em" name="x"></i-bs>
</button>
}
</form>
</div>
<ul ngbNav class="order-sm-3">
@@ -30,30 +44,27 @@
<span class="small me-2 d-none d-sm-inline">
{{this.settingsService.displayName}}
</span>
<svg width="1.3em" height="1.3em" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#person-circle"/>
</svg>
<i-bs width="1.3em" height="1.3em" name="person-circle"></i-bs>
</button>
<div ngbDropdownMenu class="dropdown-menu-end shadow me-2" aria-labelledby="userDropdown">
<div class="d-sm-none">
<p class="small mb-0 px-3 text-muted" i18n>Logged in as {{this.settingsService.displayName}}</p>
<div class="dropdown-divider"></div>
</div>
<a ngbDropdownItem class="nav-link" routerLink="settings" (click)="closeMenu()" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.UISettings }">
<svg class="sidebaricon me-2" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#gear"/>
</svg><ng-container i18n>Settings</ng-container>
<button ngbDropdownItem class="nav-link" (click)="editProfile()">
<i-bs class="me-2" name="person"></i-bs>&nbsp;<ng-container i18n>My Profile</ng-container>
</button>
<a ngbDropdownItem class="nav-link" routerLink="settings" (click)="closeMenu()"
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.UISettings }">
<i-bs class="me-2" name="gear"></i-bs><ng-container i18n>Settings</ng-container>
</a>
<a ngbDropdownItem class="nav-link" href="accounts/logout/" (click)="onLogout()">
<svg class="sidebaricon me-2" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#door-open"/>
</svg><ng-container i18n>Logout</ng-container>
<a ngbDropdownItem class="nav-link d-flex" href="accounts/logout/" (click)="onLogout()">
<i-bs class="me-2" name="door-open"></i-bs><ng-container i18n>Logout</ng-container>
</a>
<div class="dropdown-divider"></div>
<a ngbDropdownItem class="nav-link" target="_blank" rel="noopener noreferrer" href="https://docs.paperless-ngx.com">
<svg class="sidebaricon me-2" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#question-circle"/>
</svg><ng-container i18n>Documentation</ng-container>
<div class="dropdown-divider"></div>
<a ngbDropdownItem class="nav-link" target="_blank" rel="noopener noreferrer"
href="https://docs.paperless-ngx.com">
<i-bs class="me-2" name="question-circle"></i-bs><ng-container i18n>Documentation</ng-container>
</a>
</div>
</li>
@@ -62,81 +73,92 @@
<div class="container-fluid">
<div class="row">
<nav id="sidebarMenu" class="d-md-block bg-light sidebar collapse" [ngClass]="slimSidebarEnabled ? 'slim' : 'col-md-3 col-lg-2 col-xxxl-1'" [class.animating]="slimSidebarAnimating" [ngbCollapse]="isMenuCollapsed">
<nav id="sidebarMenu" class="d-md-block bg-light sidebar collapse"
[ngClass]="slimSidebarEnabled ? 'slim' : 'col-md-3 col-lg-2 col-xxxl-1'" [class.animating]="slimSidebarAnimating"
[ngbCollapse]="isMenuCollapsed">
<button class="btn btn-sm btn-dark sidebar-slim-toggler" (click)="toggleSlimSidebar()">
<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>
@if (slimSidebarEnabled) {
<i-bs width="0.9em" height="0.9em" name="chevron-double-right"></i-bs>
} @else {
<i-bs width="0.9em" height="0.9em" name="chevron-double-left"></i-bs>
}
</button>
<div class="sidebar-sticky pt-3 d-flex flex-column justify-space-around">
<ul class="nav flex-column">
<li class="nav-item">
<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><span>&nbsp;<ng-container i18n>Dashboard</ng-container></span>
<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">
<i-bs class="me-1" name="house"></i-bs><span>&nbsp;<ng-container i18n>Dashboard</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
<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><span>&nbsp;<ng-container i18n>Documents</ng-container></span>
<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">
<i-bs class="me-1" name="files"></i-bs><span>&nbsp;<ng-container i18n>Documents</ng-container></span>
</a>
</li>
</ul>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }">
<h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted" *ngIf='savedViewService.loading || sidebarViews?.length > 0'>
<span i18n>Saved views</span>
<div *ngIf="savedViewService.loading" class="spinner-border spinner-border-sm fw-normal ms-2" role="status"></div>
</h6>
@if (savedViewService.loading || savedViewService.sidebarViews?.length > 0) {
<h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted">
<span i18n>Saved views</span>
@if (savedViewService.loading) {
<div class="spinner-border spinner-border-sm fw-normal ms-2" role="status"></div>
}
</h6>
}
<ul class="nav flex-column mb-2" cdkDropList (cdkDropListDropped)="onDrop($event)">
<li class="nav-item w-100" *ngFor="let view of sidebarViews"
cdkDrag
[cdkDragDisabled]="!settingsService.organizingSidebarSavedViews"
cdkDragPreviewContainer="parent"
cdkDragPreviewClass="navItemDrag"
(cdkDragStarted)="onDragStart($event)"
(cdkDragEnded)="onDragEnd($event)">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="view/{{view.id}}" 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><span>&nbsp;{{view.name}}</span>
</a>
<div *ngIf="settingsService.organizingSidebarSavedViews" class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
<svg class="sidebaricon text-muted" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#grip-vertical"/>
</svg>
</div>
</li>
@for (view of savedViewService.sidebarViews; track view) {
<li class="nav-item w-100" cdkDrag [cdkDragDisabled]="!settingsService.organizingSidebarSavedViews"
cdkDragPreviewContainer="parent" cdkDragPreviewClass="navItemDrag" (cdkDragStarted)="onDragStart($event)"
(cdkDragEnded)="onDragEnd($event)">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="view/{{view.id}}"
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name"
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
popoverClass="popover-slim">
<i-bs class="me-1" name="funnel"></i-bs><span>&nbsp;{{view.name}}</span>
</a>
@if (settingsService.organizingSidebarSavedViews) {
<div class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
<i-bs name="grip-vertical"></i-bs>
</div>
}
</li>
}
</ul>
</ng-container>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
<h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted" *ngIf='openDocuments.length > 0'>
<span i18n>Open documents</span>
</h6>
@if (openDocuments.length > 0) {
<h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted">
<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" [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><span>&nbsp;{{d.title | documentTitle}}</span>
<span class="close" (click)="closeDocument(d); $event.preventDefault()">
<svg fill="currentColor" class="toolbaricon">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>
</span>
</a>
</li>
<li class="nav-item w-100" *ngIf="openDocuments.length >= 1">
<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><span>&nbsp;<ng-container i18n>Close all</ng-container></span>
</a>
</li>
@for (d of openDocuments; track d) {
<li class="nav-item w-100">
<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">
<i-bs class="me-1" name="file-text"></i-bs><span>&nbsp;{{d.title | documentTitle}}</span>
<span class="close" (click)="closeDocument(d); $event.preventDefault()">
<i-bs name="x"></i-bs>
</span>
</a>
</li>
}
@if (openDocuments.length >= 1) {
<li class="nav-item w-100">
<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">
<i-bs class="me-1" name="x"></i-bs><span>&nbsp;<ng-container i18n>Close all</ng-container></span>
</a>
</li>
}
</ul>
</ng-container>
@@ -144,53 +166,59 @@
<span i18n>Manage</span>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }">
<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><span>&nbsp;<ng-container i18n>Correspondents</ng-container></span>
<li class="nav-item"
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }">
<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">
<i-bs class="me-1" name="person"></i-bs><span>&nbsp;<ng-container i18n>Correspondents</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }" 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><span>&nbsp;<ng-container i18n>Tags</ng-container></span>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }"
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">
<i-bs class="me-1" name="tags"></i-bs><span>&nbsp;<ng-container i18n>Tags</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }">
<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><span>&nbsp;<ng-container i18n>Document Types</ng-container></span>
<li class="nav-item"
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }">
<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">
<i-bs class="me-1" name="hash"></i-bs><span>&nbsp;<ng-container i18n>Document Types</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }">
<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><span>&nbsp;<ng-container i18n>Storage Paths</ng-container></span>
<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">
<i-bs class="me-1" name="folder"></i-bs><span>&nbsp;<ng-container i18n>Storage Paths</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.CustomField }">
<a class="nav-link" routerLink="customfields" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Custom Fields" 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#ui-radios"/>
</svg><span>&nbsp;<ng-container i18n>Custom Fields</ng-container></span>
<a class="nav-link" routerLink="customfields" routerLinkActive="active" (click)="closeMenu()"
ngbPopover="Custom Fields" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="me-1" name="ui-radios"></i-bs><span>&nbsp;<ng-container i18n>Custom Fields</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.ConsumptionTemplate }" tourAnchor="tour.consumption-templates">
<a class="nav-link" routerLink="templates" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Consumption templates" 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#file-earmark-ruled"/>
</svg><span>&nbsp;<ng-container i18n>Templates</ng-container></span>
<li class="nav-item"
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Workflow }"
tourAnchor="tour.workflows">
<a class="nav-link" routerLink="workflows" routerLinkActive="active" (click)="closeMenu()"
ngbPopover="Workflows" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="me-1" name="boxes"></i-bs><span>&nbsp;<ng-container i18n>Workflows</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.MailAccount }" tourAnchor="tour.mail">
<a class="nav-link" routerLink="mail" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Mail" 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#envelope"/>
</svg><span>&nbsp;<ng-container i18n>Mail</ng-container></span>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.MailAccount }"
tourAnchor="tour.mail">
<a class="nav-link" routerLink="mail" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Mail"
i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body"
triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="me-1" name="envelope"></i-bs><span>&nbsp;<ng-container i18n>Mail</ng-container></span>
</a>
</li>
</ul>
@@ -199,92 +227,116 @@
<span i18n>Administration</span>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.UISettings }" 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><span>&nbsp;<ng-container i18n>Settings</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.User }">
<a class="nav-link" routerLink="usersgroups" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Users & Groups" 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#people"/>
</svg><span>&nbsp;<ng-container i18n>Users & Groups</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.PaperlessTask }" 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><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>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.UISettings }"
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">
<i-bs class="me-1" name="gear"></i-bs><span>&nbsp;<ng-container i18n>Settings</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }">
<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><span>&nbsp;<ng-container i18n>Logs</ng-container></span>
<a class="nav-link" routerLink="config" routerLinkActive="active" (click)="closeMenu()"
ngbPopover="Configuration" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="me-1" name="sliders2-vertical"></i-bs><span>&nbsp;<ng-container i18n>Configuration</ng-container></span>
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.User }">
<a class="nav-link" routerLink="usersgroups" routerLinkActive="active" (click)="closeMenu()"
ngbPopover="Users & Groups" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="me-1" name="people"></i-bs><span>&nbsp;<ng-container i18n>Users & Groups</ng-container></span>
</a>
</li>
<li class="nav-item"
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.PaperlessTask }"
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">
<i-bs class="me-1" name="list-task"></i-bs><span>&nbsp;<ng-container i18n>File Tasks</ng-container>@if (tasksService.failedFileTasks.length > 0) {
<span><span class="badge bg-danger ms-2">{{tasksService.failedFileTasks.length}}</span></span>
}</span>
@if (tasksService.failedFileTasks.length > 0 && slimSidebarEnabled) {
<span class="badge bg-danger position-absolute top-0 end-0">{{tasksService.failedFileTasks.length}}</span>
}
</a>
</li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }">
<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">
<i-bs class="me-1" name="text-left"></i-bs><span>&nbsp;<ng-container i18n>Logs</ng-container></span>
</a>
</li>
<li class="nav-item mt-2" tourAnchor="tour.outro">
<a class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://docs.paperless-ngx.com" 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><span class="ms-1">&nbsp;<ng-container i18n>Documentation</ng-container></span>
<a class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap text-decoration-none"
target="_blank" rel="noopener noreferrer" href="https://docs.paperless-ngx.com" ngbPopover="Documentation"
i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body"
triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="d-flex" name="question-circle"></i-bs><span class="ms-1">&nbsp;<ng-container i18n>Documentation</ng-container></span>
</a>
</li>
<li class="nav-item" [class.visually-hidden]="slimSidebarEnabled">
<div class="px-3 py-0 text-muted small d-flex align-items-center flex-wrap">
<div class="me-3">
<a class="text-muted text-decoration-none" 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">
<a class="text-muted text-decoration-none" 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">
{{ versionString }}
</a>
</div>
<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>
<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?
@if (!settingsService.updateCheckingIsSet || appRemoteVersion) {
<div 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>
<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>
@if (settingsService.updateCheckingIsSet) {
@if (appRemoteVersion.update_available) {
<a 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">
<i-bs width="1.2em" height="1.2em" name="info-circle"></i-bs>
@if (appRemoteVersion?.update_available) {
<ng-container i18n>Update available</ng-container>
}
</a>
}
} @else {
<a class="small text-decoration-none" routerLink="/settings" fragment="update-checking"
[ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter"
container="body">
<i-bs width="1.2em" height="1.2em" name="info-circle"></i-bs>
</a>
</p>
</ng-template>
<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">
<use xlink:href="assets/bootstrap-icons.svg#info-circle" />
</svg>
<ng-container *ngIf="appRemoteVersion?.update_available" i18n>Update available</ng-container>
</a>
</ng-container>
<ng-template #updateCheckNotSet>
<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>
</a>
</ng-template>
</div>
}
</div>
}
</div>
</li>
</ul>
</div>
</nav>
<main role="main" class="ms-sm-auto px-md-4" [ngClass]="slimSidebarEnabled ? 'col-slim' : 'col-md-9 col-lg-10 col-xxxl-11'">
<main role="main" class="ms-sm-auto px-md-4"
[ngClass]="slimSidebarEnabled ? 'col-slim' : 'col-md-9 col-lg-10 col-xxxl-11'">
<router-outlet></router-outlet>
</main>
</div>

View File

@@ -27,7 +27,7 @@
max-width: 16.66666667%;
}
@media (min-width: 2000px) {
@media (min-width: 2400px) {
max-width: 8.33333333%;
}
@@ -152,9 +152,9 @@ main {
font-weight: bold;
}
.sidebaricon {
margin-right: 4px;
color: inherit;
i-bs {
position: relative;
top: -1px;
}
}
@@ -186,11 +186,11 @@ main {
width: 1.8rem;
height: 100%;
svg {
i-bs {
opacity: 0.5;
}
&:hover svg {
&:hover i-bs {
opacity: 1;
}
}
@@ -205,7 +205,7 @@ main {
text-decoration: underline;
}
svg {
i-bs {
margin-bottom: 2px;
}
}
@@ -217,9 +217,16 @@ main {
*/
.navbar-brand {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
font-size: 1rem;
.flex-column {
padding: 0.15rem 0;
}
.byline {
font-size: 0.5rem;
letter-spacing: 0.1rem;
}
}
@media screen and (min-width: 768px) {
@@ -241,7 +248,7 @@ main {
.navbar .dropdown-menu {
font-size: 0.875rem; // body size
a svg {
a i-bs {
opacity: 0.6;
}
}
@@ -252,7 +259,7 @@ main {
form {
position: relative;
> svg {
> i-bs {
position: absolute;
left: 0.6rem;
top: 0.5rem;
@@ -262,7 +269,7 @@ main {
&:focus-within {
form > svg {
form > i-bs {
display: none;
}
@@ -312,10 +319,10 @@ main {
}
}
.nav-item .position-absolute {
.nav-item > .position-absolute {
cursor: move;
}
::ng-deep .navItemDrag .position-absolute svg {
::ng-deep .navItemDrag .position-absolute i-bs {
display: none;
}

View File

@@ -9,13 +9,13 @@ import {
fakeAsync,
tick,
} from '@angular/core/testing'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgbModal, NgbModalModule, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { BrowserModule } from '@angular/platform-browser'
import { RouterTestingModule } from '@angular/router/testing'
import { SettingsService } from 'src/app/services/settings.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { PermissionsService } from 'src/app/services/permissions.service'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { RemoteVersionService } from 'src/app/services/rest/remote-version.service'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
@@ -31,7 +31,8 @@ import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
import { routes } from 'src/app/app-routing.module'
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { CdkDragDrop, DragDropModule } from '@angular/cdk/drag-drop'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { SavedView } from 'src/app/data/saved-view'
import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component'
const saved_views = [
{
@@ -86,6 +87,7 @@ describe('AppFrameComponent', () => {
let documentListViewService: DocumentListViewService
let router: Router
let savedViewSpy
let modalService: NgbModal
beforeEach(async () => {
TestBed.configureTestingModule({
@@ -98,6 +100,7 @@ describe('AppFrameComponent', () => {
FormsModule,
ReactiveFormsModule,
DragDropModule,
NgbModalModule,
],
providers: [
SettingsService,
@@ -120,6 +123,7 @@ describe('AppFrameComponent', () => {
ToastService,
OpenDocumentsService,
SearchService,
NgbModal,
{
provide: ActivatedRoute,
useValue: {
@@ -148,6 +152,7 @@ describe('AppFrameComponent', () => {
openDocumentsService = TestBed.inject(OpenDocumentsService)
searchService = TestBed.inject(SearchService)
documentListViewService = TestBed.inject(DocumentListViewService)
modalService = TestBed.inject(NgbModal)
router = TestBed.inject(Router)
jest
@@ -243,7 +248,7 @@ describe('AppFrameComponent', () => {
expect(toastSpy).toHaveBeenCalled()
})
it('should support collapsable menu', () => {
it('should support collapsible menu', () => {
const button: HTMLButtonElement = (
fixture.nativeElement as HTMLDivElement
).querySelector('button[data-toggle=collapse]')
@@ -293,6 +298,21 @@ describe('AppFrameComponent', () => {
expect(autocompleteSpy).toHaveBeenCalled()
}))
it('should handle autocomplete backend failure gracefully', fakeAsync(() => {
const serviceAutocompleteSpy = jest.spyOn(searchService, 'autocomplete')
serviceAutocompleteSpy.mockReturnValue(
throwError(() => new Error('autcomplete failed'))
)
// serviceAutocompleteSpy.mockReturnValue(of([' world']))
let result
component.searchAutoComplete(of('hello')).subscribe((res) => {
result = res
})
tick(250)
expect(serviceAutocompleteSpy).toHaveBeenCalled()
expect(result).toEqual([])
}))
it('should support reset search field', () => {
const resetSpy = jest.spyOn(component, 'resetSearchField')
const input = (fixture.nativeElement as HTMLDivElement).querySelector(
@@ -336,7 +356,7 @@ describe('AppFrameComponent', () => {
const toastSpy = jest.spyOn(toastService, 'showInfo')
jest.spyOn(settingsService, 'storeSettings').mockReturnValue(of(true))
component.onDrop({ previousIndex: 0, currentIndex: 1 } as CdkDragDrop<
PaperlessSavedView[]
SavedView[]
>)
expect(settingsSpy).toHaveBeenCalledWith([
saved_views[2],
@@ -359,8 +379,16 @@ describe('AppFrameComponent', () => {
.spyOn(settingsService, 'storeSettings')
.mockReturnValue(throwError(() => new Error('unable to save')))
component.onDrop({ previousIndex: 0, currentIndex: 2 } as CdkDragDrop<
PaperlessSavedView[]
SavedView[]
>)
expect(toastSpy).toHaveBeenCalled()
})
it('should support edit profile', () => {
const modalSpy = jest.spyOn(modalService, 'open')
component.editProfile()
expect(modalSpy).toHaveBeenCalledWith(ProfileEditDialogComponent, {
backdrop: 'static',
})
})
})

View File

@@ -8,8 +8,9 @@ import {
map,
switchMap,
first,
catchError,
} from 'rxjs/operators'
import { PaperlessDocument } from 'src/app/data/paperless-document'
import { Document } from 'src/app/data/document'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { SearchService } from 'src/app/services/rest/search.service'
@@ -24,7 +25,7 @@ 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 { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { ToastService } from 'src/app/services/toast.service'
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
import {
@@ -32,13 +33,15 @@ import {
PermissionsService,
PermissionType,
} from 'src/app/services/permissions.service'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { SavedView } from 'src/app/data/saved-view'
import {
CdkDragStart,
CdkDragEnd,
CdkDragDrop,
moveItemInArray,
} from '@angular/cdk/drag-drop'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component'
@Component({
selector: 'pngx-app-frame',
@@ -58,8 +61,6 @@ export class AppFrameComponent
searchField = new FormControl('')
sidebarViews: PaperlessSavedView[]
constructor(
public router: Router,
private activatedRoute: ActivatedRoute,
@@ -71,6 +72,7 @@ export class AppFrameComponent
public settingsService: SettingsService,
public tasksService: TasksService,
private readonly toastService: ToastService,
private modalService: NgbModal,
permissionsService: PermissionsService
) {
super()
@@ -90,10 +92,6 @@ export class AppFrameComponent
this.checkForUpdates()
}
this.tasksService.reload()
this.savedViewService.listAll().subscribe(() => {
this.sidebarViews = this.savedViewService.sidebarViews
})
}
toggleSlimSidebar(): void {
@@ -104,6 +102,10 @@ export class AppFrameComponent
}, 200) // slightly longer than css animation for slim sidebar
}
get customAppTitle(): string {
return this.settingsService.get(SETTINGS_KEYS.APP_TITLE)
}
get slimSidebarEnabled(): boolean {
return this.settingsService.get(SETTINGS_KEYS.SLIM_SIDEBAR)
}
@@ -127,7 +129,14 @@ export class AppFrameComponent
this.isMenuCollapsed = true
}
get openDocuments(): PaperlessDocument[] {
editProfile() {
this.modalService.open(ProfileEditDialogComponent, {
backdrop: 'static',
})
this.closeMenu()
}
get openDocuments(): Document[] {
return this.openDocumentsService.getOpenDocuments()
}
@@ -162,7 +171,13 @@ export class AppFrameComponent
}
}),
switchMap((term) =>
term.length < 2 ? from([[]]) : this.searchService.autocomplete(term)
term.length < 2
? from([[]])
: this.searchService.autocomplete(term).pipe(
catchError(() => {
return from([[]])
})
)
)
)
@@ -189,7 +204,7 @@ export class AppFrameComponent
])
}
closeDocument(d: PaperlessDocument) {
closeDocument(d: Document) {
this.openDocumentsService
.closeDocument(d)
.pipe(first())
@@ -239,10 +254,11 @@ export class AppFrameComponent
this.settingsService.globalDropzoneEnabled = true
}
onDrop(event: CdkDragDrop<PaperlessSavedView[]>) {
moveItemInArray(this.sidebarViews, event.previousIndex, event.currentIndex)
onDrop(event: CdkDragDrop<SavedView[]>) {
const sidebarViews = this.savedViewService.sidebarViews.concat([])
moveItemInArray(sidebarViews, event.previousIndex, event.currentIndex)
this.settingsService.updateSidebarViewsSort(this.sidebarViews).subscribe({
this.settingsService.updateSidebarViewsSort(sidebarViews).subscribe({
next: () => {
this.toastService.showInfo($localize`Sidebar views updated`)
},

View File

@@ -1,9 +1,11 @@
<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>
@if (active) {
<button 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)">
@if (!isNumbered && selected) {
<i-bs class="check" width="1em" height="1em" name="check-lg"></i-bs>
}
@if (isNumbered) {
<div class="number">{{number}}<span class="visually-hidden">selected</span></div>
}
<i-bs class="x" width=".9em" height="1em" name="x-lg"></i-bs>
</button>
}

View File

@@ -22,7 +22,7 @@ button:hover {
.x {
display: inline-block;
position: absolute;
top: 5px;
left: calc(50% - 4px);
top: .4em;
left: calc(50% - .4em);
}
}

View File

@@ -1,24 +1,32 @@
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{title}}</h4>
<button type="button" class="btn-close" aria-label="Close" (click)="cancel()">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{title}}</h4>
<button type="button" class="btn-close" aria-label="Close" (click)="cancel()">
</button>
</div>
<div class="modal-body">
@if (messageBold) {
<p><b>{{messageBold}}</b></p>
}
@if (message) {
<p class="mb-0" [innerHTML]="message | safeHtml"></p>
}
</div>
<div class="modal-footer">
<button type="button" class="btn" [class]="cancelBtnClass" (click)="cancel()" [disabled]="!buttonsEnabled">
<span class="d-inline-block" style="padding-bottom: 1px;">{{cancelBtnCaption}}</span>
</button>
</div>
<div class="modal-body">
<p *ngIf="messageBold"><b>{{messageBold}}</b></p>
<p class="mb-0" *ngIf="message" [innerHTML]="message | safeHtml"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" [disabled]="!buttonsEnabled" i18n>
<span class="d-inline-block" style="padding-bottom: 1px;" >Cancel</span>
</button>
<button type="button" class="btn" [class]="btnClass" (click)="confirm()" [disabled]="!confirmButtonEnabled || !buttonsEnabled">
<span>
{{btnCaption}}
<span class="visually-hidden">{{ seconds | number: '1.0-0' }} seconds</span>
</span>
<ngb-progressbar *ngIf="!confirmButtonEnabled" style="height: 1px;" type="dark" [max]="secondsTotal" [value]="seconds"></ngb-progressbar>
</button>
<button *ngIf="alternativeBtnCaption" type="button" class="btn" [class]="alternativeBtnClass" (click)="alternative()" [disabled]="!alternativeButtonEnabled || !buttonsEnabled">
{{alternativeBtnCaption}}
</button>
</div>
<button type="button" class="btn" [class]="btnClass" (click)="confirm()" [disabled]="!confirmButtonEnabled || !buttonsEnabled">
<span>
{{btnCaption}}
<span class="visually-hidden">{{ seconds | number: '1.0-0' }} seconds</span>
</span>
@if (!confirmButtonEnabled) {
<ngb-progressbar style="height: 1px;" type="dark" [max]="secondsTotal" [value]="seconds"></ngb-progressbar>
}
</button>
@if (alternativeBtnCaption) {
<button type="button" class="btn" [class]="alternativeBtnClass" (click)="alternative()" [disabled]="!alternativeButtonEnabled || !buttonsEnabled">
{{alternativeBtnCaption}}
</button>
}
</div>

View File

@@ -37,6 +37,12 @@ export class ConfirmDialogComponent {
@Input()
alternativeBtnCaption
@Input()
cancelBtnClass = 'btn-outline-secondary'
@Input()
cancelBtnCaption = $localize`Cancel`
@Input()
buttonsEnabled = true

View File

@@ -1,8 +1,6 @@
<div ngbDropdown #fieldDropdown="ngbDropdown" (openChange)="onOpenClose()">
<button class="btn btn-sm btn-outline-primary" id="customFieldsDropdown" [disabled]="disabled" ngbDropdownToggle>
<svg class="toolbaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#ui-radios" />
</svg>
<i-bs name="ui-radios"></i-bs>
<div class="d-none d-sm-inline">&nbsp;<ng-container i18n>Custom Fields</ng-container></div>
</button>
<div ngbDropdownMenu aria-labelledby="customFieldsDropdown" class="shadow custom-fields-dropdown">
@@ -20,14 +18,10 @@
</pngx-input-select>
<div class="btn-toolbar" role="toolbar">
<button class="btn btn-sm btn-outline-secondary me-auto" type="button" (click)="createField()" [disabled]="!canCreateFields">
<svg fill="currentColor" class="buttonicon-sm me-1 mb-1">
<use xlink:href="assets/bootstrap-icons.svg#asterisk"/>
</svg><ng-container i18n>Create New Field</ng-container>
<i-bs width="1em" height="1em" name="asterisk"></i-bs>&nbsp;<ng-container i18n>Create New Field</ng-container>
</button>
<button class="btn btn-sm btn-outline-primary me-1" type="button" (click)="addField(); fieldDropdown.close()" [disabled]="field === undefined">
<svg fill="currentColor" class="buttonicon me-1">
<use xlink:href="assets/bootstrap-icons.svg#plus-circle"/>
</svg><ng-container i18n>Add</ng-container>
<i-bs width="1.2em" height="1.2em" name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add</ng-container>
</button>
</div>
</li>

View File

@@ -8,10 +8,7 @@ import {
import { ToastService } from 'src/app/services/toast.service'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { of } from 'rxjs'
import {
PaperlessCustomField,
PaperlessCustomFieldDataType,
} from 'src/app/data/paperless-custom-field'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
import { SelectComponent } from '../input/select/select.component'
import { NgSelectModule } from '@ng-select/ng-select'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
@@ -24,16 +21,16 @@ import {
import { CustomFieldEditDialogComponent } from '../edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
import { By } from '@angular/platform-browser'
const fields: PaperlessCustomField[] = [
const fields: CustomField[] = [
{
id: 0,
name: 'Field 1',
data_type: PaperlessCustomFieldDataType.Integer,
data_type: CustomFieldDataType.Integer,
},
{
id: 1,
name: 'Field 2',
data_type: PaperlessCustomFieldDataType.String,
data_type: CustomFieldDataType.String,
},
]

View File

@@ -7,8 +7,8 @@ import {
} from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Subject, first, takeUntil } from 'rxjs'
import { PaperlessCustomField } from 'src/app/data/paperless-custom-field'
import { PaperlessCustomFieldInstance } from 'src/app/data/paperless-custom-field-instance'
import { CustomField } from 'src/app/data/custom-field'
import { CustomFieldInstance } from 'src/app/data/custom-field-instance'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { ToastService } from 'src/app/services/toast.service'
import { CustomFieldEditDialogComponent } from '../edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
@@ -31,16 +31,16 @@ export class CustomFieldsDropdownComponent implements OnDestroy {
disabled: boolean = false
@Input()
existingFields: PaperlessCustomFieldInstance[] = []
existingFields: CustomFieldInstance[] = []
@Output()
added: EventEmitter<PaperlessCustomField> = new EventEmitter()
added: EventEmitter<CustomField> = new EventEmitter()
@Output()
created: EventEmitter<PaperlessCustomField> = new EventEmitter()
created: EventEmitter<CustomField> = new EventEmitter()
private customFields: PaperlessCustomField[] = []
public unusedFields: PaperlessCustomField[]
private customFields: CustomField[] = []
public unusedFields: CustomField[]
public name: string
@@ -88,8 +88,8 @@ export class CustomFieldsDropdownComponent implements OnDestroy {
}
public getCustomFieldFromInstance(
instance: PaperlessCustomFieldInstance
): PaperlessCustomField {
instance: CustomFieldInstance
): CustomField {
return this.customFields.find((f) => f.id === instance.field)
}

View File

@@ -1,15 +1,16 @@
<div class="btn-group w-100" ngbDropdown role="group">
<div class="btn-group w-100" ngbDropdown role="group">
<button class="btn btn-sm" id="dropdown{{title}}" ngbDropdownToggle [ngClass]="dateBefore || dateAfter ? 'btn-primary' : 'btn-outline-primary'" [disabled]="disabled">
{{title}}
<pngx-clearable-badge [selected]="isActive" (cleared)="reset()"></pngx-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 rd of relativeDates" class="list-group-item small list-goup list-group-item-action d-flex p-2" role="menuitem" (click)="setRelativeDate(rd.id)">
@for (rd of relativeDates; track rd) {
<button class="list-group-item small list-goup list-group-item-action d-flex p-2" role="menuitem" (click)="setRelativeDate(rd.id)">
<div class="selected-icon">
<svg *ngIf="relativeDate === rd.id" fill="currentColor" class="buttonicon-sm">
<use xlink:href="assets/bootstrap-icons.svg#check"/>
</svg>
@if (relativeDate === rd.id) {
<i-bs width="1em" height="1em" name="check"></i-bs>
}
</div>
<div class="d-flex justify-content-between w-100 align-items-center ps-2">
<div class="pe-2 pe-lg-4">
@@ -22,52 +23,49 @@
</div>
</div>
</button>
<div class="list-group-item d-flex flex-column align-items-start" role="menuitem">
}
<div class="list-group-item d-flex flex-column align-items-start" role="menuitem">
<div class="mb-2 d-flex flex-row w-100 justify-content-between small">
<div i18n>After</div>
<a *ngIf="dateAfter" class="btn btn-link p-0 m-0" (click)="clearAfter()">
<svg fill="currentColor" class="buttonicon-sm">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>
<div class="mb-2 d-flex flex-row w-100 justify-content-between small">
<div i18n>After</div>
@if (dateAfter) {
<a class="btn btn-link p-0 m-0" (click)="clearAfter()">
<i-bs width="1em" height="1em" name="x"></i-bs>
<small i18n>Clear</small>
</a>
</div>
<div class="input-group input-group-sm">
<input class="form-control" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()" (keypress)="onKeyPress($event)"
maxlength="10" [(ngModel)]="dateAfter" ngbDatepicker #dateAfterPicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="dateAfterPicker.toggle()" type="button">
<svg fill="currentColor" class="buttonicon-sm">
<use xlink:href="assets/bootstrap-icons.svg#calendar"/>
</svg>
</button>
</div>
}
</div>
<div class="list-group-item d-flex flex-column align-items-start" role="menuitem">
<div class="mb-2 d-flex flex-row w-100 justify-content-between small">
<div i18n>Before</div>
<a *ngIf="dateBefore" class="btn btn-link p-0 m-0" (click)="clearBefore()">
<svg fill="currentColor" class="buttonicon-sm">
<use xlink:href="assets/bootstrap-icons.svg#x"/>
</svg>
<div class="input-group input-group-sm">
<input class="form-control" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()" (keypress)="onKeyPress($event)"
maxlength="10" [(ngModel)]="dateAfter" ngbDatepicker #dateAfterPicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="dateAfterPicker.toggle()" type="button">
<i-bs width="1em" height="1em" name="calendar"></i-bs>
</button>
</div>
</div>
<div class="list-group-item d-flex flex-column align-items-start" role="menuitem">
<div class="mb-2 d-flex flex-row w-100 justify-content-between small">
<div i18n>Before</div>
@if (dateBefore) {
<a class="btn btn-link p-0 m-0" (click)="clearBefore()">
<i-bs width="1em" height="1em" name="x"></i-bs>
<small i18n>Clear</small>
</a>
</div>
<div class="input-group input-group-sm">
<input class="form-control" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()" (keypress)="onKeyPress($event)"
maxlength="10" [(ngModel)]="dateBefore" ngbDatepicker #dateBeforePicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="dateBeforePicker.toggle()" type="button">
<svg fill="currentColor" class="buttonicon-sm">
<use xlink:href="assets/bootstrap-icons.svg#calendar"/>
</svg>
</button>
</div>
}
</div>
<div class="input-group input-group-sm">
<input class="form-control" [placeholder]="datePlaceHolder" (dateSelect)="onChangeDebounce()" (change)="onChangeDebounce()" (keypress)="onKeyPress($event)"
maxlength="10" [(ngModel)]="dateBefore" ngbDatepicker #dateBeforePicker="ngbDatepicker">
<button class="btn btn-outline-secondary" (click)="dateBeforePicker.toggle()" type="button">
<i-bs width="1em" height="1em" name="calendar"></i-bs>
</button>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,92 +0,0 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
<button type="button" [disabled]="!closeEnabled" class="btn-close" aria-label="Close" (click)="cancel()">
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-8">
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
</div>
<div class="col">
<pngx-input-number i18n-title title="Sort order" formControlName="order" [showAdd]="false" [error]="error?.order"></pngx-input-number>
</div>
</div>
<div class="row">
<div class="col-md-4">
<h5 class="border-bottom pb-2" i18n>Filters</h5>
<p class="small" i18n>Process documents that match <em>all</em> filters specified below.</p>
<pngx-input-select i18n-title title="Filter sources" [items]="sourceOptions" [multiple]="true" formControlName="sources" [error]="error?.filter_filename"></pngx-input-select>
<pngx-input-text i18n-title title="Filter filename" formControlName="filter_filename" i18n-hint hint="Apply to documents that match this filename. Wildcards such as *.pdf or *invoice* are allowed. Case insensitive." [error]="error?.filter_filename"></pngx-input-text>
<pngx-input-text i18n-title title="Filter path" formControlName="filter_path" i18n-hint hint="Apply to documents that match this path. Wildcards specified as * are allowed. Case insensitive.</a>" [error]="error?.filter_path"></pngx-input-text>
<pngx-input-select i18n-title title="Filter mail rule" [items]="mailRules" [allowNull]="true" formControlName="filter_mailrule" i18n-hint hint="Apply to documents consumed via this mail rule." [error]="error?.filter_mailrule"></pngx-input-select>
</div>
<div class="col">
<div class="row">
<div class="col">
<h5 class="border-bottom pb-2" i18n>Assignments</h5>
</div>
</div>
<div class="row">
<div class="col">
<pngx-input-text i18n-title title="Assign title" formControlName="assign_title" i18n-hint hint="Can include some placeholders, see <a target='_blank' href='https://docs.paperless-ngx.com/usage/#consumption-templates'>documentation</a>." [error]="error?.assign_title"></pngx-input-text>
<pngx-input-tags [allowCreate]="false" i18n-title title="Assign tags" formControlName="assign_tags"></pngx-input-tags>
<pngx-input-select i18n-title title="Assign document type" [items]="documentTypes" [allowNull]="true" formControlName="assign_document_type"></pngx-input-select>
<pngx-input-select i18n-title title="Assign correspondent" [items]="correspondents" [allowNull]="true" formControlName="assign_correspondent"></pngx-input-select>
<pngx-input-select i18n-title title="Assign storage path" [items]="storagePaths" [allowNull]="true" formControlName="assign_storage_path"></pngx-input-select>
</div>
<div class="col">
<pngx-input-select i18n-title title="Assign owner" [items]="users" bindLabel="username" formControlName="assign_owner" [allowNull]="true"></pngx-input-select>
<div>
<label class="form-label" i18n>Assign view permissions</label>
<div class="mb-2">
<div class="row mb-1">
<div class="col-lg-3">
<label class="form-label d-block my-2" i18n>Users:</label>
</div>
<div class="col-lg-9">
<pngx-permissions-user type="view" formControlName="assign_view_users"></pngx-permissions-user>
</div>
</div>
<div class="row">
<div class="col-lg-3">
<label class="form-label d-block my-2" i18n>Groups:</label>
</div>
<div class="col-lg-9">
<pngx-permissions-group type="view" formControlName="assign_view_groups"></pngx-permissions-group>
</div>
</div>
</div>
<label class="form-label" i18n>Assign edit permissions</label>
<div>
<div class="row mb-1">
<div class="col-lg-3">
<label class="form-label d-block my-2" i18n>Users:</label>
</div>
<div class="col-lg-9">
<pngx-permissions-user type="change" formControlName="assign_change_users"></pngx-permissions-user>
</div>
</div>
<div class="row">
<div class="col-lg-3">
<label class="form-label d-block my-2" i18n>Groups:</label>
</div>
<div class="col-lg-9">
<pngx-permissions-group type="change" formControlName="assign_change_groups"></pngx-permissions-group>
</div>
</div>
<small class="form-text text-muted text-end d-block" i18n>Edit permissions also grant viewing permissions</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<span class="text-danger" *ngIf="error?.non_field_errors"><ng-container i18n>Error</ng-container>: {{error.non_field_errors}}</span>
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>
<button type="submit" class="btn btn-primary" i18n [disabled]="networkActive">Save</button>
</div>
</form>

View File

@@ -1,125 +0,0 @@
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { of } from 'rxjs'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { MailRuleService } from 'src/app/services/rest/mail-rule.service'
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { SettingsService } from 'src/app/services/settings.service'
import { NumberComponent } from '../../input/number/number.component'
import { PermissionsGroupComponent } from '../../input/permissions/permissions-group/permissions-group.component'
import { PermissionsUserComponent } from '../../input/permissions/permissions-user/permissions-user.component'
import { SelectComponent } from '../../input/select/select.component'
import { TagsComponent } from '../../input/tags/tags.component'
import { TextComponent } from '../../input/text/text.component'
import { EditDialogMode } from '../edit-dialog.component'
import { ConsumptionTemplateEditDialogComponent } from './consumption-template-edit-dialog.component'
describe('ConsumptionTemplateEditDialogComponent', () => {
let component: ConsumptionTemplateEditDialogComponent
let settingsService: SettingsService
let fixture: ComponentFixture<ConsumptionTemplateEditDialogComponent>
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
ConsumptionTemplateEditDialogComponent,
IfPermissionsDirective,
IfOwnerDirective,
SelectComponent,
TextComponent,
NumberComponent,
TagsComponent,
PermissionsUserComponent,
PermissionsGroupComponent,
SafeHtmlPipe,
],
providers: [
NgbActiveModal,
{
provide: CorrespondentService,
useValue: {
listAll: () =>
of({
results: [
{
id: 1,
username: 'c1',
},
],
}),
},
},
{
provide: DocumentTypeService,
useValue: {
listAll: () =>
of({
results: [
{
id: 1,
username: 'dt1',
},
],
}),
},
},
{
provide: StoragePathService,
useValue: {
listAll: () =>
of({
results: [
{
id: 1,
username: 'sp1',
},
],
}),
},
},
{
provide: MailRuleService,
useValue: {
listAll: () =>
of({
results: [],
}),
},
},
],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgSelectModule,
NgbModule,
],
}).compileComponents()
fixture = TestBed.createComponent(ConsumptionTemplateEditDialogComponent)
settingsService = TestBed.inject(SettingsService)
settingsService.currentUser = { id: 99, username: 'user99' }
component = fixture.componentInstance
fixture.detectChanges()
})
it('should support create and edit modes', () => {
component.dialogMode = EditDialogMode.CREATE
const createTitleSpy = jest.spyOn(component, 'getCreateTitle')
const editTitleSpy = jest.spyOn(component, 'getEditTitle')
fixture.detectChanges()
expect(createTitleSpy).toHaveBeenCalled()
expect(editTitleSpy).not.toHaveBeenCalled()
component.dialogMode = EditDialogMode.EDIT
fixture.detectChanges()
expect(editTitleSpy).toHaveBeenCalled()
})
})

View File

@@ -1,115 +0,0 @@
import { Component } from '@angular/core'
import { FormGroup, FormControl } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import { first } from 'rxjs'
import {
DocumentSource,
PaperlessConsumptionTemplate,
} from 'src/app/data/paperless-consumption-template'
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent'
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type'
import { PaperlessStoragePath } from 'src/app/data/paperless-storage-path'
import { ConsumptionTemplateService } from 'src/app/services/rest/consumption-template.service'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
import { EditDialogComponent } from '../edit-dialog.component'
import { MailRuleService } from 'src/app/services/rest/mail-rule.service'
import { PaperlessMailRule } from 'src/app/data/paperless-mail-rule'
export const DOCUMENT_SOURCE_OPTIONS = [
{
id: DocumentSource.ConsumeFolder,
name: $localize`Consume Folder`,
},
{
id: DocumentSource.ApiUpload,
name: $localize`API Upload`,
},
{
id: DocumentSource.MailFetch,
name: $localize`Mail Fetch`,
},
]
@Component({
selector: 'pngx-consumption-template-edit-dialog',
templateUrl: './consumption-template-edit-dialog.component.html',
styleUrls: ['./consumption-template-edit-dialog.component.scss'],
})
export class ConsumptionTemplateEditDialogComponent extends EditDialogComponent<PaperlessConsumptionTemplate> {
templates: PaperlessConsumptionTemplate[]
correspondents: PaperlessCorrespondent[]
documentTypes: PaperlessDocumentType[]
storagePaths: PaperlessStoragePath[]
mailRules: PaperlessMailRule[]
constructor(
service: ConsumptionTemplateService,
activeModal: NgbActiveModal,
correspondentService: CorrespondentService,
documentTypeService: DocumentTypeService,
storagePathService: StoragePathService,
mailRuleService: MailRuleService,
userService: UserService,
settingsService: SettingsService
) {
super(service, activeModal, userService, settingsService)
correspondentService
.listAll()
.pipe(first())
.subscribe((result) => (this.correspondents = result.results))
documentTypeService
.listAll()
.pipe(first())
.subscribe((result) => (this.documentTypes = result.results))
storagePathService
.listAll()
.pipe(first())
.subscribe((result) => (this.storagePaths = result.results))
mailRuleService
.listAll()
.pipe(first())
.subscribe((result) => (this.mailRules = result.results))
}
getCreateTitle() {
return $localize`Create new consumption template`
}
getEditTitle() {
return $localize`Edit consumption template`
}
getForm(): FormGroup {
return new FormGroup({
name: new FormControl(null),
account: new FormControl(null),
filter_filename: new FormControl(null),
filter_path: new FormControl(null),
filter_mailrule: new FormControl(null),
order: new FormControl(null),
sources: new FormControl([]),
assign_title: new FormControl(null),
assign_tags: new FormControl([]),
assign_owner: new FormControl(null),
assign_document_type: new FormControl(null),
assign_correspondent: new FormControl(null),
assign_storage_path: new FormControl(null),
assign_view_users: new FormControl([]),
assign_view_groups: new FormControl([]),
assign_change_users: new FormControl([]),
assign_change_groups: new FormControl([]),
})
}
get sourceOptions() {
return DOCUMENT_SOURCE_OPTIONS
}
}

View File

@@ -8,8 +8,12 @@
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
<pngx-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
<pngx-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></pngx-input-check>
@if (patternRequired) {
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
}
@if (patternRequired) {
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></pngx-input-check>
}
<div *pngxIfOwner="object">
<pngx-permissions-form [users]="users" accordion="true" formControlName="permissions_form"></pngx-permissions-form>

View File

@@ -3,7 +3,7 @@ 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 { Correspondent } from 'src/app/data/correspondent'
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
@@ -13,7 +13,7 @@ import { SettingsService } from 'src/app/services/settings.service'
templateUrl: './correspondent-edit-dialog.component.html',
styleUrls: ['./correspondent-edit-dialog.component.scss'],
})
export class CorrespondentEditDialogComponent extends EditDialogComponent<PaperlessCorrespondent> {
export class CorrespondentEditDialogComponent extends EditDialogComponent<Correspondent> {
constructor(
service: CorrespondentService,
activeModal: NgbActiveModal,

View File

@@ -1,16 +1,18 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
<button type="button" [disabled]="!closeEnabled" class="btn-close" aria-label="Close" (click)="cancel()">
</button>
</div>
<div class="modal-body">
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
<pngx-input-select i18n-title title="Data type" [items]="getDataTypes()" formControlName="data_type"></pngx-input-select>
<small class="d-block mt-n2" *ngIf="typeFieldDisabled" i18n>Data type cannot be changed after a field is created</small>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>
<button type="submit" class="btn btn-primary" i18n [disabled]="networkActive">Save</button>
</div>
</form>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
<button type="button" [disabled]="!closeEnabled" class="btn-close" aria-label="Close" (click)="cancel()">
</button>
</div>
<div class="modal-body">
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
<pngx-input-select i18n-title title="Data type" [items]="getDataTypes()" formControlName="data_type"></pngx-input-select>
@if (typeFieldDisabled) {
<small class="d-block mt-n2" i18n>Data type cannot be changed after a field is created</small>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>
<button type="submit" class="btn btn-primary" i18n [disabled]="networkActive">Save</button>
</div>
</form>

View File

@@ -1,10 +1,7 @@
import { Component, OnInit } from '@angular/core'
import { FormGroup, FormControl } from '@angular/forms'
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
import {
DATA_TYPE_LABELS,
PaperlessCustomField,
} from 'src/app/data/paperless-custom-field'
import { DATA_TYPE_LABELS, CustomField } from 'src/app/data/custom-field'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
@@ -16,7 +13,7 @@ import { EditDialogComponent, EditDialogMode } from '../edit-dialog.component'
styleUrls: ['./custom-field-edit-dialog.component.scss'],
})
export class CustomFieldEditDialogComponent
extends EditDialogComponent<PaperlessCustomField>
extends EditDialogComponent<CustomField>
implements OnInit
{
constructor(

View File

@@ -1,25 +1,29 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
<button type="button" [disabled]="!closeEnabled" class="btn-close" aria-label="Close" (click)="cancel()">
</button>
</div>
<div class="modal-body">
<div class="col">
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
<pngx-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
<pngx-input-check *ngIf="patternRequired" i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
</div>
<div *pngxIfOwner="object">
<pngx-permissions-form [users]="users" accordion="true" formControlName="permissions_form"></pngx-permissions-form>
</div>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
<button type="button" [disabled]="!closeEnabled" class="btn-close" aria-label="Close" (click)="cancel()">
</button>
</div>
<div class="modal-body">
<div class="col">
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
@if (patternRequired) {
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
}
@if (patternRequired) {
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>
<button type="submit" class="btn btn-primary" i18n [disabled]="networkActive">Save</button>
<div *pngxIfOwner="object">
<pngx-permissions-form [users]="users" accordion="true" formControlName="permissions_form"></pngx-permissions-form>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>
<button type="submit" class="btn btn-primary" i18n [disabled]="networkActive">Save</button>
</div>
</form>

View File

@@ -3,7 +3,7 @@ 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 { DocumentType } from 'src/app/data/document-type'
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
@@ -13,7 +13,7 @@ import { SettingsService } from 'src/app/services/settings.service'
templateUrl: './document-type-edit-dialog.component.html',
styleUrls: ['./document-type-edit-dialog.component.scss'],
})
export class DocumentTypeEditDialogComponent extends EditDialogComponent<PaperlessDocumentType> {
export class DocumentTypeEditDialogComponent extends EditDialogComponent<DocumentType> {
constructor(
service: DocumentTypeService,
activeModal: NgbActiveModal,

View File

@@ -23,8 +23,8 @@ import {
MATCH_NONE,
MATCH_ALL,
} from 'src/app/data/matching-model'
import { PaperlessTag } from 'src/app/data/paperless-tag'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { Tag } from 'src/app/data/tag'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { TagService } from 'src/app/services/rest/tag.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
@@ -38,7 +38,7 @@ import { EditDialogComponent, EditDialogMode } from './edit-dialog.component'
</div>
`,
})
class TestComponent extends EditDialogComponent<PaperlessTag> {
class TestComponent extends EditDialogComponent<Tag> {
constructor(
service: TagService,
activeModal: NgbActiveModal,
@@ -131,6 +131,7 @@ describe('EditDialogComponent', () => {
})
it('should interpolate object permissions', () => {
component.getMatchingAlgorithms() // coverage
component.object = tag
component.dialogMode = EditDialogMode.EDIT
component.ngOnInit()

View File

@@ -9,12 +9,12 @@ import {
} from 'src/app/data/matching-model'
import { ObjectWithId } from 'src/app/data/object-with-id'
import { ObjectWithPermissions } from 'src/app/data/object-with-permissions'
import { PaperlessUser } from 'src/app/data/paperless-user'
import { User } from 'src/app/data/user'
import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service'
import { UserService } from 'src/app/services/rest/user.service'
import { PermissionsFormObject } from '../input/permissions/permissions-form/permissions-form.component'
import { SettingsService } from 'src/app/services/settings.service'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
export enum EditDialogMode {
CREATE = 0,
@@ -33,7 +33,7 @@ export abstract class EditDialogComponent<
private settingsService: SettingsService
) {}
users: PaperlessUser[]
users: User[]
@Input()
dialogMode: EditDialogMode = EditDialogMode.CREATE
@@ -58,8 +58,8 @@ export abstract class EditDialogComponent<
objectForm: FormGroup = this.getForm()
ngOnInit(): void {
if (this.object != null) {
if (this.object['permissions']) {
if (this.object != null && this.dialogMode !== EditDialogMode.CREATE) {
if ((this.object as ObjectWithPermissions).permissions) {
this.object['set_permissions'] = this.object['permissions']
}
@@ -69,6 +69,8 @@ export abstract class EditDialogComponent<
}
this.objectForm.patchValue(this.object)
} else {
// e.g. if name was set
this.objectForm.patchValue(this.object)
// defaults from settings
this.objectForm.patchValue({
permissions_form: {
@@ -95,7 +97,7 @@ export abstract class EditDialogComponent<
})
}
// wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
// wait to enable close button so it doesn't steal focus from input since its the first clickable element in the DOM
setTimeout(() => {
this.closeEnabled = true
})

View File

@@ -2,7 +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 { PaperlessGroup } from 'src/app/data/paperless-group'
import { Group } from 'src/app/data/group'
import { GroupService } from 'src/app/services/rest/group.service'
import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service'
@@ -12,7 +12,7 @@ import { SettingsService } from 'src/app/services/settings.service'
templateUrl: './group-edit-dialog.component.html',
styleUrls: ['./group-edit-dialog.component.scss'],
})
export class GroupEditDialogComponent extends EditDialogComponent<PaperlessGroup> {
export class GroupEditDialogComponent extends EditDialogComponent<Group> {
constructor(
service: GroupService,
activeModal: NgbActiveModal,

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