Compare commits

..

217 Commits

Author SHA1 Message Date
shamoon
ccd6ad9936 Bump version to 2.6.2 2024-03-07 20:13:29 -08:00
shamoon
979fcb0570 Merge branch 'dev' 2024-03-07 20:12:51 -08:00
github-actions[bot]
58afec98f1 New Crowdin translations by GitHub Action (#6018)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-03-07 20:12:30 -08:00
shamoon
91434a5c6f Enhancement: move and rename files when storage paths deleted, update file handling docs (#6033) 2024-03-07 20:39:55 +00:00
shamoon
37c4545444 Documentation: tweak PAPERLESS_DISABLE_REGULAR_LOGIN info 2024-03-07 09:13:01 -08:00
shamoon
7b7b257725 Fix: make document count lists for objects permissions-aware (#6019) 2024-03-07 15:15:46 +00:00
shamoon
1eff4b306f Enhancement: better detection of default currency code (#6020) 2024-03-07 15:04:32 +00:00
shamoon
700af8caa2 Fix: custom logo size in Webkit (#6036) 2024-03-06 16:08:44 -08:00
Andrew Berry
90b43e154a Documentation: add additional information about Django admin remaining accessible with PAPERLESS_DISABLE_REGULAR_LOGIN (#5993) 2024-03-06 15:46:14 +00:00
shamoon
a3892302b0 Fix: formatting, tiny large card rounded corners bug 2024-03-05 22:40:52 -08:00
shamoon
d2ee319684 Fix: remove outdated mail rule text re. consumption templates 2024-03-05 16:26:35 -08:00
github-actions[bot]
5a6923a9aa Changelog v2.6.1 - GHA (#6014)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-05 08:42:04 -08:00
shamoon
0a634883a7 Reset dev version string 2024-03-05 08:00:32 -08:00
shamoon
428f9cd761 Bump version to 2.6.1 2024-03-05 07:59:44 -08:00
shamoon
d828c1a2ff Merge branch 'dev' into main 2024-03-05 07:59:20 -08:00
github-actions[bot]
25b49db7c0 New Crowdin translations by GitHub Action (#6011)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-03-05 07:55:47 -08:00
shamoon
55a40708a6 Change: tweaks to system status (#6008) 2024-03-05 15:50:04 +00:00
github-actions[bot]
dae5bca883 Documentation: Add v2.6.0 changelog (#6006)
---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-03-05 07:21:09 -08:00
shamoon
fc74da9b82 Fix: prevent edit button click toggling list selection 2024-03-05 06:20:10 -08:00
shamoon
2b006907d5 Reset dev version string 2024-03-04 20:22:07 -08:00
shamoon
1ba1afdce5 Bump version to 2.6.0 2024-03-04 20:20:46 -08:00
shamoon
a98317c52a Fix: search field clear button padding 2024-03-04 20:13:52 -08:00
github-actions[bot]
ffddd0f323 New Crowdin translations by GitHub Action (#5896)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-03-05 03:14:30 +00:00
Trenton H
4cb2f0acef Rolls up the rest of the random updates that dependabot didn't do for some reason (#6005) 2024-03-05 03:00:18 +00:00
shamoon
98663e902f Update translation strings 2024-03-04 17:00:57 -08:00
shamoon
0f5e935214 Fix: header button wrapping issues on mobile 2024-03-04 17:00:19 -08:00
Trenton H
b9636a3def Feature: Allow user to control PIL image pixel limit (#5997) 2024-03-05 00:19:56 +00:00
shamoon
35574f3b86 Fix: prevent actions button click toggling list selection 2024-03-04 16:06:23 -08:00
shamoon
00a8f0cd6e Enhancement: show ID when editing objects (#6003) 2024-03-04 23:21:48 +00:00
Trenton H
6779042242 Feature: Allow a user to disable the pixel limit for OCR entirely (#5996) 2024-03-04 22:37:36 +00:00
dependabot[bot]
6379e7b54f Chore(deps): Bump the small-changes group with 3 updates (#6001)
Bumps the small-changes group with 3 updates: [python-dateutil](https://github.com/dateutil/dateutil), [python-ipware](https://github.com/un33k/python-ipware) and [redis](https://github.com/redis/redis-py).


Updates `python-dateutil` from 2.8.2 to 2.9.0.post0
- [Release notes](https://github.com/dateutil/dateutil/releases)
- [Changelog](https://github.com/dateutil/dateutil/blob/master/NEWS)
- [Commits](https://github.com/dateutil/dateutil/compare/2.8.2...2.9.0.post0)

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

Updates `redis` from 5.0.1 to 5.0.2
- [Release notes](https://github.com/redis/redis-py/releases)
- [Changelog](https://github.com/redis/redis-py/blob/master/CHANGES)
- [Commits](https://github.com/redis/redis-py/compare/v5.0.1...v5.0.2)

---
updated-dependencies:
- dependency-name: python-dateutil
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: python-ipware
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: redis
  dependency-type: direct:production
  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>
2024-03-04 22:27:59 +00:00
dependabot[bot]
2fa742c94b Chore(deps-dev): Bump the development group with 2 updates (#5998)
* Chore(deps-dev): Bump the development group with 2 updates

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.2.2 to 0.3.0
- [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.2.2...v0.3.0)

Updates `mkdocs-material` from 9.5.11 to 9.5.12
- [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.11...9.5.12)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-minor
  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>

* Updates ruff hook version as well

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com>
2024-03-04 22:08:13 +00:00
dependabot[bot]
aa0da2f516 Chore(deps): Bump the django group with 1 update (#6000)
Bumps the django group with 1 update: [django](https://github.com/django/django).


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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 13:15:03 -08:00
shamoon
f07441a408 Feature: workflow removal action (#5928)
---------

Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com>
2024-03-04 17:37:42 +00:00
shamoon
f6084acfc8 Feature: system status (#5743) 2024-03-04 09:26:25 -08:00
shamoon
23ceb2a5ec Fix: refactor base path settings, correct logout redirect (#5976) 2024-03-03 08:22:23 -08:00
dependabot[bot]
a698791059 Chore(deps-dev): Bump @playwright/test from 1.41.2 to 1.42.0 in /src-ui (#5964)
* Chore(deps-dev): Bump @playwright/test from 1.41.2 to 1.42.0 in /src-ui

Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.41.2 to 1.42.0.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.41.2...v1.42.0)

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

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

* Revert .har changes from playwright bug

---------

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-03-01 21:07:08 +00:00
dependabot[bot]
41c1f38ab2 Chore(deps-dev): Bump @types/node from 20.11.20 to 20.11.24 in /src-ui (#5965)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.20 to 20.11.24.
- [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-03-01 21:00:09 +00:00
dependabot[bot]
83c85dc10e Chore(deps): Bump the frontend-angular-dependencies group (#5963)
Bumps the frontend-angular-dependencies group in /src-ui with 11 updates:

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


Updates `@angular/common` from 17.2.2 to 17.2.3
- [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.2.3/packages/common)

Updates `@angular/compiler` from 17.2.2 to 17.2.3
- [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.2.3/packages/compiler)

Updates `@angular/core` from 17.2.2 to 17.2.3
- [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.2.3/packages/core)

Updates `@angular/forms` from 17.2.2 to 17.2.3
- [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.2.3/packages/forms)

Updates `@angular/localize` from 17.2.2 to 17.2.3
- [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.2.2...17.2.3)

Updates `@angular/platform-browser` from 17.2.2 to 17.2.3
- [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.2.3/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 17.2.2 to 17.2.3
- [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.2.3/packages/platform-browser-dynamic)

Updates `@angular/router` from 17.2.2 to 17.2.3
- [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.2.3/packages/router)

Updates `@angular-devkit/build-angular` from 17.2.1 to 17.2.2
- [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.2.1...17.2.2)

Updates `@angular/cli` from 17.2.1 to 17.2.2
- [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.2.1...17.2.2)

Updates `@angular/compiler-cli` from 17.2.2 to 17.2.3
- [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.2.3/packages/compiler-cli)

---
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
- dependency-name: "@angular/compiler-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-03-01 20:51:12 +00:00
shamoon
a020d807d4 Fix: always pass remove_inbox_tags from UI, dont require in API (#5962) 2024-03-01 12:00:54 -08:00
shamoon
464ee51de8 Fix: mobile sizing tweaks 2024-02-29 16:57:47 -08:00
Trenton H
c57c1d5389 Fix: Clear metadata cache when the filename(s) change (#5957)
* Update doc modified time upon move and rename

* Clear the cached metadata if the filename(s) have been changed

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-02-29 10:34:15 -08:00
shamoon
af16bb3934 Fix: include monetary, float and doc link values in search filters (#5951) 2024-02-29 08:21:56 +00:00
Trenton H
fba416e8e1 Fixes a corrupted index not being handled by whoosh.create_in (#5950) 2024-02-29 00:07:17 +00:00
Trenton H
3d8de50b5a Don't assume the /tmp folder is where the user wants to place the scratch directory (#5948) 2024-02-28 22:46:24 +00:00
Trenton H
86263a52ea Includes OCRMyPdf logging into the log file (#5947) 2024-02-28 14:39:42 -08:00
shamoon
754627681c Fix: ensure document title always limited to 128 chars (#5934) 2024-02-28 07:05:19 -08:00
shamoon
bf11dc8d1b Enhancement: better monetary field with currency code (#5858) 2024-02-27 16:26:06 +00:00
Dennis Gaida
84721b001f Change: add Thumbs.db to default ignores (#5924)
* Adding Thumbs.db to default ignores

Thanks Windows for creating these great little database files everywhere

* Updating configuration with new default
2024-02-27 07:48:50 -08:00
Felix Eckhofer
f48a20c75f Documentation: fix typo (#5922) 2024-02-27 00:25:18 -08:00
shamoon
16f4552e0e Fix: use PAPERLESS_URL if set for pw reset emails (#5902) 2024-02-26 21:41:25 +00:00
ShanSanear
86811d0733 Fix: Correcting check searching for docker compose plugin (#5904) (#5917) 2024-02-26 21:21:43 +00:00
shamoon
d2f9b5d5e5 Fix: respect global permissions for UI settings (#5919) 2024-02-26 20:19:31 +00:00
shamoon
f5e1675107 Fix: tasks badge position on mobile 2024-02-26 12:02:45 -08:00
dependabot[bot]
ae016cae4b Chore(deps-dev): Bump the frontend-eslint-dependencies group (#5918)
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.20.0 to 7.1.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/v7.1.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 6.20.0 to 7.1.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/v7.1.0/packages/parser)

Updates `eslint` from 8.56.0 to 8.57.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.56.0...v8.57.0)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: frontend-eslint-dependencies
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-major
  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-02-26 11:22:26 -08:00
dependabot[bot]
c7f6d03508 Chore(deps-dev): Bump @types/node from 20.11.16 to 20.11.20 in /src-ui (#5912)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.16 to 20.11.20.
- [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-02-26 18:55:52 +00:00
dependabot[bot]
955f2d0db9 Chore(deps): Bump zone.js from 0.14.3 to 0.14.4 in /src-ui (#5913)
Bumps [zone.js](https://github.com/angular/angular/tree/HEAD/packages/zone.js) from 0.14.3 to 0.14.4.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/packages/zone.js/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/zone.js-0.14.4/packages/zone.js)

---
updated-dependencies:
- dependency-name: zone.js
  dependency-type: direct:production
  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-02-26 18:30:58 +00:00
dependabot[bot]
ba32684df6 Chore(deps): Bump bootstrap from 5.3.2 to 5.3.3 in /src-ui (#5911)
Bumps [bootstrap](https://github.com/twbs/bootstrap) from 5.3.2 to 5.3.3.
- [Release notes](https://github.com/twbs/bootstrap/releases)
- [Commits](https://github.com/twbs/bootstrap/compare/v5.3.2...v5.3.3)

---
updated-dependencies:
- dependency-name: bootstrap
  dependency-type: direct:production
  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-02-26 18:20:03 +00:00
dependabot[bot]
2f7adf40ac Chore(deps-dev): Bump typescript from 5.2.2 to 5.3.3 in /src-ui (#5915)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.2.2 to 5.3.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v5.2.2...v5.3.3)

---
updated-dependencies:
- dependency-name: typescript
  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>
2024-02-26 18:08:50 +00:00
dependabot[bot]
a52031161b Chore(deps): Bump the frontend-angular-dependencies group (#5908)
Bumps the frontend-angular-dependencies group in /src-ui with 15 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `17.1.2` | `17.2.1` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `17.1.2` | `17.2.2` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `17.1.2` | `17.2.2` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `17.1.2` | `17.2.2` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `17.1.2` | `17.2.2` |
| [@angular/localize](https://github.com/angular/angular) | `17.1.2` | `17.2.2` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `17.1.2` | `17.2.2` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `17.1.2` | `17.2.2` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `17.1.2` | `17.2.2` |
| [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `12.0.6` | `12.0.7` |
| [ngx-cookie-service](https://github.com/stevermeister/ngx-cookie-service) | `17.0.1` | `17.1.0` |
| [@angular-builders/jest](https://github.com/just-jeb/angular-builders/tree/HEAD/packages/jest) | `17.0.0` | `17.0.2` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `17.2.0` | `17.2.1` |
| [@angular/cli](https://github.com/angular/angular-cli) | `17.1.2` | `17.2.1` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `17.1.2` | `17.2.2` |


Updates `@angular/cdk` from 17.1.2 to 17.2.1
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/17.1.2...17.2.1)

Updates `@angular/common` from 17.1.2 to 17.2.2
- [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.2.2/packages/common)

Updates `@angular/compiler` from 17.1.2 to 17.2.2
- [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.2.2/packages/compiler)

Updates `@angular/core` from 17.1.2 to 17.2.2
- [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.2.2/packages/core)

Updates `@angular/forms` from 17.1.2 to 17.2.2
- [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.2.2/packages/forms)

Updates `@angular/localize` from 17.1.2 to 17.2.2
- [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.1.2...17.2.2)

Updates `@angular/platform-browser` from 17.1.2 to 17.2.2
- [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.2.2/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 17.1.2 to 17.2.2
- [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.2.2/packages/platform-browser-dynamic)

Updates `@angular/router` from 17.1.2 to 17.2.2
- [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.2.2/packages/router)

Updates `@ng-select/ng-select` from 12.0.6 to 12.0.7
- [Release notes](https://github.com/ng-select/ng-select/releases)
- [Changelog](https://github.com/ng-select/ng-select/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ng-select/ng-select/compare/v12.0.6...v12.0.7)

Updates `ngx-cookie-service` from 17.0.1 to 17.1.0
- [Release notes](https://github.com/stevermeister/ngx-cookie-service/releases)
- [Changelog](https://github.com/stevermeister/ngx-cookie-service/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stevermeister/ngx-cookie-service/compare/v17.0.1...v17.1.0)

Updates `@angular-builders/jest` from 17.0.0 to 17.0.2
- [Release notes](https://github.com/just-jeb/angular-builders/releases)
- [Changelog](https://github.com/just-jeb/angular-builders/blob/master/packages/jest/CHANGELOG.md)
- [Commits](https://github.com/just-jeb/angular-builders/commits/@angular-builders/jest@17.0.2/packages/jest)

Updates `@angular-devkit/build-angular` from 17.2.0 to 17.2.1
- [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.2.0...17.2.1)

Updates `@angular/cli` from 17.1.2 to 17.2.1
- [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.1.2...17.2.1)

Updates `@angular/compiler-cli` from 17.1.2 to 17.2.2
- [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.2.2/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/common"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/compiler"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/forms"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/localize"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/platform-browser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/platform-browser-dynamic"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/router"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@ng-select/ng-select"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: ngx-cookie-service
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-builders/jest"
  dependency-type: direct:development
  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-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/compiler-cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  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-02-26 17:58:26 +00:00
shamoon
3e2c541b7b Fix: allow disable allauth email verification (#5895) 2024-02-26 09:33:59 -08:00
shamoon
967fc98090 Fix: Refactor accounts templates and create signup template (#5899) 2024-02-26 09:23:53 -08:00
dependabot[bot]
22e95f45bd Chore(deps): Bump the small-changes group with 4 updates (#5916)
Bumps the small-changes group with 4 updates: [scikit-learn](https://github.com/scikit-learn/scikit-learn), [tqdm](https://github.com/tqdm/tqdm), [pre-commit](https://github.com/pre-commit/pre-commit) and [daphne](https://github.com/django/daphne).


Updates `scikit-learn` from 1.4.0 to 1.4.1.post1
- [Release notes](https://github.com/scikit-learn/scikit-learn/releases)
- [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.4.0...1.4.1.post1)

Updates `tqdm` from 4.66.1 to 4.66.2
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.1...v4.66.2)

Updates `pre-commit` from 3.6.0 to 3.6.2
- [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.6.0...v3.6.2)

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

---
updated-dependencies:
- dependency-name: scikit-learn
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: tqdm
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: pre-commit
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: daphne
  dependency-type: direct:development
  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-02-26 09:11:59 -08:00
dependabot[bot]
38c777ec0f Chore(deps-dev): Bump the development group with 4 updates (#5914)
* Chore(deps-dev): Bump the development group with 4 updates

Bumps the development group with 4 updates: [ruff](https://github.com/astral-sh/ruff), [pytest](https://github.com/pytest-dev/pytest), [pytest-httpx](https://github.com/Colin-b/pytest_httpx) and [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `ruff` from 0.2.1 to 0.2.2
- [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.2.1...v0.2.2)

Updates `pytest` from 8.0.0 to 8.0.2
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.0.0...8.0.2)

Updates `pytest-httpx` from 0.29.0 to 0.30.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.29.0...v0.30.0)

Updates `mkdocs-material` from 9.5.9 to 9.5.11
- [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.9...9.5.11)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: pytest
  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: mkdocs-material
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

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

* Updates the hook versions to match installed versions

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com>
2024-02-26 16:27:45 +00:00
dependabot[bot]
ccbc97399a Chore(deps): Bump the actions group with 3 updates (#5907)
Bumps the actions group with 3 updates: [pre-commit/action](https://github.com/pre-commit/action), [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) and [stumpylog/image-cleaner-action](https://github.com/stumpylog/image-cleaner-action).


Updates `pre-commit/action` from 3.0.0 to 3.0.1
- [Release notes](https://github.com/pre-commit/action/releases)
- [Commits](https://github.com/pre-commit/action/compare/v3.0.0...v3.0.1)

Updates `release-drafter/release-drafter` from 5 to 6
- [Release notes](https://github.com/release-drafter/release-drafter/releases)
- [Commits](https://github.com/release-drafter/release-drafter/compare/v5...v6)

Updates `stumpylog/image-cleaner-action` from 0.4.0 to 0.5.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.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: pre-commit/action
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: release-drafter/release-drafter
  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>
2024-02-26 08:08:15 -08:00
Trenton H
f43013a746 Ignores uvicorn updates in dependabot (#5906) 2024-02-26 07:55:59 -08:00
shamoon
1335ab5f1b Enhancement: support disabling regular login (#5816) 2024-02-26 05:17:21 +00:00
shamoon
90b4691f16 Reset dev version string 2024-02-25 19:43:25 -08:00
github-actions[bot]
f053ee3191 Changelog v2.5.4 - GHA (#5894)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-25 19:33:07 -08:00
shamoon
86748c1e96 Merge branch 'dev' 2024-02-25 18:41:01 -08:00
github-actions[bot]
22ded7d4c3 New Crowdin translations by GitHub Action (#5805)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-02-25 18:39:05 -08:00
shamoon
ff5063849a Bump version to 2.5.4 2024-02-25 18:38:12 -08:00
shamoon
ec49284274 Merge branch 'dev' 2024-02-25 18:37:19 -08:00
shamoon
6bd5c34b54 Fix: handle title placeholder for docs without original_filename (#5828) 2024-02-25 17:45:35 -08:00
shamoon
db0a2eb1a3 Fix: bulk edit objects does not respect global permissions (#5888) 2024-02-25 16:59:59 -08:00
shamoon
4948438378 Fix: intermittent save & close warnings in v2.5.3 (#5838) 2024-02-26 00:59:29 +00:00
grembo
76064178f5 Fix: inotify read timeout not in ms (#5876)
* Fix inotify read timeout

This was off by a factor of 1000, leading to a lot more invocations
of the loop body than necessary.

* Incorporate review feedback
2024-02-26 00:46:47 +00:00
noantiq
c772bd94b0 Documentation: fix links to django-allauth (#5893) 2024-02-25 14:09:44 -08:00
shamoon
7f8f7fbb15 Fix: logo spacing tweaks 2024-02-24 11:14:27 -08:00
shamoon
388d821f45 Fix: only require DjangoModelPermissions for app config 2024-02-24 10:53:09 -08:00
shamoon
9c15623a89 Change: increase dark mode selected dropdown item bg contrast 2024-02-24 09:59:20 -08:00
shamoon
966eb00de0 Chore: miscellaneous frontend testing 2024-02-23 23:36:03 -08:00
Nepomacs
1c699278a3 Documentation: note required trailing slash for REST-API URLs (#5863)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-02-22 07:22:33 -08:00
shamoon
4c6c976f63 Chore: better explain repository maintenance (#5815) 2024-02-21 18:52:55 -08:00
shamoon
ebc9ce17b5 Fix: accidentally retained confirm button for management lists 2024-02-21 15:27:15 -08:00
dependabot[bot]
8039ce3c2b Chore(deps): Bump cryptography from 42.0.2 to 42.0.4 (#5851)
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.2 to 42.0.4.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.2...42.0.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-21 20:55:18 +00:00
shamoon
385d48f644 Fix workflow migration model imports 2024-02-21 12:32:58 -08:00
dependabot[bot]
f682fe25fc Chore(deps-dev): Bump ip from 2.0.0 to 2.0.1 in /src-ui (#5835)
Bumps [ip](https://github.com/indutny/node-ip) from 2.0.0 to 2.0.1.
- [Commits](https://github.com/indutny/node-ip/compare/v2.0.0...v2.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-20 20:24:38 +00:00
shamoon
7924bf8611 Chore: close unsupported FRs (#5804) 2024-02-18 08:01:08 -08:00
dependabot[bot]
d75b909d28 Chore(deps): Bump undici and @angular-devkit/build-angular in /src-ui (#5796)
Bumps [undici](https://github.com/nodejs/undici) to 6.6.2 and updates ancestor dependency [@angular-devkit/build-angular](https://github.com/angular/angular-cli). These dependencies need to be updated together.


Updates `undici` from 6.2.1 to 6.6.2
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.2.1...v6.6.2)

Updates `@angular-devkit/build-angular` from 17.1.2 to 17.2.0
- [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.1.2...17.2.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-type: indirect
- dependency-name: "@angular-devkit/build-angular"
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-17 06:45:41 +00:00
shamoon
47dfe85a7c Fix: allow relative date queries not in quick list (#5801) 2024-02-16 22:36:45 -08:00
shamoon
4d0e8a338f Fix: Pass rule id to consumed .eml files (#5800) 2024-02-16 22:36:33 -08:00
shamoon
cfc64d37bb Create SECURITY.md 2024-02-15 23:38:33 -08:00
github-actions[bot]
2db66280cc Documentation: Add v2.5.3 changelog (#5786)
* Changelog v2.5.3 - GHA

* Update changelog.md

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-02-15 18:20:26 -08:00
shamoon
9f045f4494 Bump version to 2.5.3 2024-02-15 17:34:22 -08:00
shamoon
4fdb28c8d6 Merge branch 'dev' 2024-02-15 17:33:54 -08:00
shamoon
f1049cf889 Fix: dont allow allauth redirects to any host (#5783)
---------

Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com>
2024-02-15 16:37:34 -08:00
Trenton H
8d664fad56 Fixes the interaction when both splitting and ASN are enabled (#5779) 2024-02-15 17:33:26 +00:00
MaciejSzczurek
f6ddcfa839 Moved ssl_mode parameter for mysql backend engine (#5771) 2024-02-15 07:59:33 -08:00
github-actions[bot]
ce59f2ad5e Changelog v2.5.2 - GHA (#5769)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-14 17:40:10 -08:00
Trenton H
0de00a4ac1 Resets develop versioning 2024-02-14 17:14:33 -08:00
Trenton H
bec72dffeb Sets the version to 2.5.2 2024-02-14 17:10:37 -08:00
Trenton H
463e95367c Merge remote-tracking branch 'origin/dev' 2024-02-14 17:08:49 -08:00
github-actions[bot]
1739de2694 New Crowdin translations by GitHub Action (#5768)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-02-14 16:53:28 -08:00
shamoon
fd8db27a88 Fix signup form html, translation string 2024-02-14 16:51:47 -08:00
github-actions[bot]
9ddf14bebe New Crowdin translations by GitHub Action (#5748)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-02-14 16:24:24 -08:00
trs
1cf8ea3aba Fix: Generated secret key may include single or double quotes (#5767) 2024-02-14 23:06:54 +00:00
shamoon
134993fce6 Fix: consumer status alerts container blocks elements (#5762) 2024-02-14 08:25:58 -08:00
shamoon
ff1955e014 Documentation: small updates to sso docs 2024-02-14 00:02:55 -08:00
shamoon
d83bbdc50b Fix: handle document notes user format api change (#5751) 2024-02-13 17:25:35 +00:00
Trenton H
907b6d1294 Handle the ASN assignment last, after the splitting (#5745) 2024-02-13 16:26:13 +00:00
github-actions[bot]
4e7bb1c8da Changelog v2.5.1 - GHA (#5744)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-13 06:07:31 -08:00
dependabot[bot]
09ab694d05 Chore(deps): Bump the major-versions group with 1 update (#5741)
Bumps the major-versions group with 1 update: [watchdog](https://github.com/gorakhargosh/watchdog).


Updates `watchdog` from 3.0.0 to 4.0.0
- [Release notes](https://github.com/gorakhargosh/watchdog/releases)
- [Changelog](https://github.com/gorakhargosh/watchdog/blob/master/changelog.rst)
- [Commits](https://github.com/gorakhargosh/watchdog/compare/v3.0.0...v4.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-13 09:30:55 +00:00
Trenton H
03ced65d5f Resets version string 2024-02-12 15:58:10 -08:00
Trenton H
01d919cf31 Bumps version to 2.5.1 2024-02-12 15:56:41 -08:00
Trenton H
8d5f331e63 Merge remote-tracking branch 'origin/dev' 2024-02-12 15:56:19 -08:00
github-actions[bot]
ed556ead6f New Crowdin translations by GitHub Action (#5714)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-02-12 23:50:25 +00:00
dependabot[bot]
e10a904f33 Chore(deps-dev): Bump the development group with 2 updates (#5737)
Bumps the development group with 2 updates: [black](https://github.com/psf/black) and [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `black` from 24.1.1 to 24.2.0
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/24.1.1...24.2.0)

Updates `mkdocs-material` from 9.5.8 to 9.5.9
- [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.8...9.5.9)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
  update-type: version-update:semver-minor
  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-02-12 13:54:31 -08:00
dependabot[bot]
f2a05b61da Chore(deps): Bump the django group with 1 update (#5739)
Bumps the django group with 1 update: [django-allauth](https://github.com/pennersr/django-allauth).


Updates `django-allauth` from 0.61.0 to 0.61.1
- [Changelog](https://github.com/pennersr/django-allauth/blob/main/ChangeLog.rst)
- [Commits](https://github.com/pennersr/django-allauth/compare/0.61.0...0.61.1)

---
updated-dependencies:
- dependency-name: django-allauth
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: django
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 21:05:34 +00:00
Trenton H
21f96f0679 Fix: Splitting on ASN barcodes even if not enabled (#5740)
* Fixes the barcodes always splitting on ASNs, even if splitting was disabled
2024-02-12 12:58:37 -08:00
shamoon
2ffabd54e5 Documentation: add barcode tagging info 2024-02-12 12:51:52 -08:00
Dennis Gaida
1197437750 Documentation: explain PAPERLESS_APPS for external authentication (#5730) 2024-02-12 07:10:14 -08:00
shamoon
d1339374d0 Fix: add missing icon 2024-02-10 22:42:02 -08:00
Trenton Holmes
5ba4b9d6b2 Corrects the spelling 2024-02-10 18:53:03 -08:00
github-actions[bot]
b386ea9426 Changelog v2.5.0 - GHA (#5715)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-10 18:46:53 -08:00
Trenton H
197174f400 Resets -dev version string 2024-02-10 11:46:51 -08:00
Trenton H
97dceba783 Actual 2.5.0 release now 2024-02-10 11:45:46 -08:00
github-actions[bot]
2ed4400827 New Crowdin translations by GitHub Action (#5590)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-02-10 19:43:30 +00:00
Trenton H
58cbcbd6ef Resets -dev version string 2024-02-10 11:15:12 -08:00
Trenton H
4aeb2e1a74 Bumps version to 2.5.0 2024-02-10 11:14:16 -08:00
Trenton H
45c5f81b34 Merge remote-tracking branch 'origin/dev' 2024-02-10 11:12:27 -08:00
Trenton H
13201dbfff Ensure all creations of directories create the parents too (#5711) 2024-02-10 11:02:40 -08:00
Trenton H
0b1523f4e5 Fix: Test metadata items for Unicode issues (#5707)
Test each key for unicode issues and reject ones which will fail inside DRF
2024-02-09 20:08:23 +00:00
shamoon
cd3b1a221e Change: show preview even if metadata fails (#5706) 2024-02-09 11:35:32 -08:00
shamoon
4855f4b8b1 Fix: only check workflow trigger source if not empty (#5701) 2024-02-09 08:57:09 -08:00
shamoon
6587470033 Update translation strings 2024-02-08 11:56:55 -08:00
shamoon
6487dab132 Enhancement: confirm buttons (#5680) 2024-02-08 18:23:06 +00:00
shamoon
b643a68fa3 Enhancement: bulk delete objects (#5688) 2024-02-08 10:13:15 -08:00
Trenton H
b60e16fe33 Chore: Backend dependencies update (#5676) 2024-02-08 09:48:24 -08:00
Moritz Pflanzer
c508be6ecd Feature: OIDC & social authentication (#5190)
---------

Co-authored-by: Moritz Pflanzer <moritz@chickadee-engineering.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-02-08 16:15:38 +00:00
silmaril42
3b2d4fe876 Documentation: add detail about consumer polling behavior (#5674) 2024-02-08 02:46:25 +00:00
Trenton H
b47f301831 Adds the Python environment flags for no byte code saving, reducing the image size slightly (#5677) 2024-02-08 02:45:59 +00:00
shamoon
a79b9de1a2 Documentation: Add storage path to workflow action assignments 2024-02-07 09:46:20 -08:00
shamoon
e98da2e72c Fix whitespace in some frontend html files 2024-02-07 07:12:24 -08:00
shamoon
718171a125 Resolve svg rem width/height attribute warning 2024-02-06 14:50:08 -08:00
shamoon
aaa130e20d Feature: allow create objects from bulk edit (#5667) 2024-02-06 15:31:07 +00:00
shamoon
4606caeaa8 Chore: Use memory cache backend in debug mode (#5666) 2024-02-06 07:16:15 -08:00
Trenton H
4813a7bc70 Chore: Adds additional rules for Ruff linter (#5660) 2024-02-05 21:46:59 +00:00
pkrahmer
fb82aa0ee1 Feature: Allow tagging by putting barcode stickers on documents (#5580) 2024-02-05 17:38:19 +00:00
LarsBel
c7e0c32226 Documentation: Include example redis URI with database index (#5653)
Co-Authored-By: shamoon <4887959+shamoon@users.noreply.github.com>
2024-02-04 15:46:43 -08:00
shamoon
607adf44f3 Documentation: Make remote-user warning clearer, maybe 2024-02-04 14:20:48 -08:00
shamoon
625780899d Fix location of bulk edit tests 2024-02-04 12:39:26 -08:00
Trenton H
25542c56b9 Feature: Cache metadata and suggestions in Redis (#5638) 2024-02-04 10:42:21 -08:00
shamoon
45e2b7f814 Fix: frontend validation of number fields fails upon save (#5646) 2024-02-03 21:22:12 +00:00
shamoon
6b34f592df Fix: Explicit validation of custom field name unique constraint (#5647) 2024-02-03 20:51:26 +00:00
shamoon
6cf732e6ec Add Japanese translation (#5641) 2024-02-03 11:37:21 -08:00
shamoon
dfd959839f Update translation strings 2024-02-02 20:16:14 -08:00
shamoon
d165e89ac3 Merge branch 'main' into dev 2024-02-02 07:44:51 -08:00
shamoon
421a87c94b Add DO demo info (#5639) 2024-02-02 07:39:06 -08:00
dependabot[bot]
b55529b913 Chore(deps-dev): Bump @playwright/test from 1.40.1 to 1.41.2 in /src-ui (#5634)
* Chore(deps-dev): Bump @playwright/test from 1.40.1 to 1.41.2 in /src-ui

Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.40.1 to 1.41.2.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.40.1...v1.41.2)

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

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

* Update tests for breaking playwright change

---------

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-02-01 22:06:10 -08:00
shamoon
c62d892969 Feature: option for auto-remove inbox tags on save (#5562) 2024-02-02 02:41:10 +00:00
dependabot[bot]
9e6aa55230 Chore(deps): Bump the frontend-angular-dependencies group (#5630)
Bumps the frontend-angular-dependencies group in /src-ui with 19 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `17.0.4` | `17.1.2` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `17.0.8` | `17.1.2` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `17.0.8` | `17.1.2` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `17.0.8` | `17.1.2` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `17.0.8` | `17.1.2` |
| [@angular/localize](https://github.com/angular/angular) | `17.0.8` | `17.1.2` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `17.0.8` | `17.1.2` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `17.0.8` | `17.1.2` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `17.0.8` | `17.1.2` |
| [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `12.0.4` | `12.0.6` |
| [ngx-ui-tour-ng-bootstrap](https://github.com/hakimio/ngx-ui-tour) | `14.0.1` | `14.0.2` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `17.0.8` | `17.1.2` |
| [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `17.1.1` | `17.2.1` |
| [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `17.1.1` | `17.2.1` |
| [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `17.1.1` | `17.2.1` |
| [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `17.1.1` | `17.2.1` |
| [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `17.1.1` | `17.2.1` |
| [@angular/cli](https://github.com/angular/angular-cli) | `17.0.8` | `17.1.2` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `17.0.8` | `17.1.2` |


Updates `@angular/cdk` from 17.0.4 to 17.1.2
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/17.0.4...17.1.2)

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

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

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

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

Updates `@angular/localize` from 17.0.8 to 17.1.2
- [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.8...17.1.2)

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

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

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

Updates `@ng-select/ng-select` from 12.0.4 to 12.0.6
- [Release notes](https://github.com/ng-select/ng-select/releases)
- [Changelog](https://github.com/ng-select/ng-select/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ng-select/ng-select/compare/v12.0.4...v12.0.6)

Updates `ngx-ui-tour-ng-bootstrap` from 14.0.1 to 14.0.2
- [Release notes](https://github.com/hakimio/ngx-ui-tour/releases)
- [Commits](https://github.com/hakimio/ngx-ui-tour/commits)

Updates `@angular-devkit/build-angular` from 17.0.8 to 17.1.2
- [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.8...17.1.2)

Updates `@angular-eslint/builder` from 17.1.1 to 17.2.1
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/builder/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/builder)

Updates `@angular-eslint/eslint-plugin` from 17.1.1 to 17.2.1
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/eslint-plugin)

Updates `@angular-eslint/eslint-plugin-template` from 17.1.1 to 17.2.1
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/eslint-plugin-template/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/eslint-plugin-template)

Updates `@angular-eslint/schematics` from 17.1.1 to 17.2.1
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/schematics/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/schematics)

Updates `@angular-eslint/template-parser` from 17.1.1 to 17.2.1
- [Release notes](https://github.com/angular-eslint/angular-eslint/releases)
- [Changelog](https://github.com/angular-eslint/angular-eslint/blob/main/packages/template-parser/CHANGELOG.md)
- [Commits](https://github.com/angular-eslint/angular-eslint/commits/v17.2.1/packages/template-parser)

Updates `@angular/cli` from 17.0.8 to 17.1.2
- [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.8...17.1.2)

Updates `@angular/compiler-cli` from 17.0.8 to 17.1.2
- [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.1.2/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/common"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/compiler"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/forms"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/localize"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/platform-browser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/platform-browser-dynamic"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/router"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@ng-select/ng-select"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: ngx-ui-tour-ng-bootstrap
  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-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/builder"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/eslint-plugin-template"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/schematics"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/template-parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/compiler-cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  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-02-01 14:49:49 -08:00
dependabot[bot]
6090305b77 Chore(deps-dev): Bump the frontend-jest-dependencies group (#5631)
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.11 to 29.5.12
- [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.4 to 14.0.0
- [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.4...v14.0.0)

---
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-major
  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>
2024-02-01 21:43:36 +00:00
dependabot[bot]
d1b516a089 Chore(deps-dev): Bump the frontend-eslint-dependencies group (#5632)
Bumps the frontend-eslint-dependencies group in /src-ui with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 6.17.0 to 6.20.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.20.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 6.17.0 to 6.20.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.20.0/packages/parser)

---
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
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-01 21:36:09 +00:00
dependabot[bot]
89aff63e52 Chore(deps): Bump zone.js from 0.14.2 to 0.14.3 in /src-ui (#5633)
Bumps [zone.js](https://github.com/angular/angular/tree/HEAD/packages/zone.js) from 0.14.2 to 0.14.3.
- [Release notes](https://github.com/angular/angular/releases)
- [Changelog](https://github.com/angular/angular/blob/main/packages/zone.js/CHANGELOG.md)
- [Commits](https://github.com/angular/angular/commits/zone.js-0.14.3/packages/zone.js)

---
updated-dependencies:
- dependency-name: zone.js
  dependency-type: direct:production
  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-02-01 21:26:21 +00:00
dependabot[bot]
11e9c4d8cc Chore(deps-dev): Bump @types/node from 20.10.6 to 20.11.16 in /src-ui (#5635)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.10.6 to 20.11.16.
- [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>
2024-02-01 21:18:29 +00:00
Henrik Gerdes
9d84e95771 feat: add env allowing paperless to run in read_only filesystem (#5596) 2024-02-01 20:42:23 +00:00
dependabot[bot]
2aced1c305 Chore(deps): Bump the actions group with 1 update (#5629)
Bumps the actions group with 1 update: [codecov/codecov-action](https://github.com/codecov/codecov-action).


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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-01 12:29:38 -08:00
shamoon
454098630b Enhancement: mergeable bulk edit permissions (#5508) 2024-02-01 19:56:57 +00:00
이토비
8f3ab2791b Documentation: fix more like parameter in API docs (#5624) 2024-02-01 11:49:34 -08:00
shamoon
61209b1057 Enhancement: re-implement remote user auth for API as opt-in (#5561) 2024-02-01 19:46:26 +00:00
Henning B
38a817e887 Enhancement: Respect PDF cropbox for thumbnail generation (#5531)
* Use the convert argument '-define "pdf:use-cropbox=true"' when creating thumbnails. That way cropboxes, if present, gets respected for thumbnail generation
2024-02-01 11:25:57 -08:00
shamoon
5e3d1b26e7 Fix: Dont attempt to retrieve objects for which user doesnt have global permissions (#5612) 2024-02-01 01:20:14 -08:00
shamoon
4996b7e5f7 Correct position of text filter clear button 2024-01-30 22:06:22 -08:00
dependabot[bot]
2c8fddb554 Chore(deps): Bump the actions group with 1 update (#5597)
Bumps the actions group with 1 update: [actions/cache](https://github.com/actions/cache).


Updates `actions/cache` from 3 to 4
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 00:10:12 +00:00
Michael Wieland
ae05011062 Documentation: Add docs about missing timezones in MySQL/MariaDB (#5583)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-01-29 19:07:40 +00:00
github-actions[bot]
50a6b7e154 Documentation: Add v2.4.3 changelog (#5584)
* Changelog v2.4.3 - GHA

* Update changelog.md

---------

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-28 15:14:43 -08:00
Trenton H
d0ce4113e0 Resets -dev version string 2024-01-28 12:51:00 -08:00
Trenton H
d55900b877 Bumps version to 2.4.3 2024-01-28 12:19:07 -08:00
github-actions[bot]
2a73ab4693 New Crowdin translations by GitHub Action (#5559)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-28 20:16:42 +00:00
Trenton H
2aea220c6d Ensure the scratch directory exists before consuming this source (#5579) 2024-01-28 09:07:04 -08:00
shamoon
b0c305e852 Merge branch 'main' into dev 2024-01-26 12:47:00 -08:00
shamoon
73a77d2a45 Remove demo info, for now (#5557) 2024-01-26 12:46:22 -08:00
shamoon
3a011e7c04 Reset dev version string 2024-01-26 09:28:04 -08:00
github-actions[bot]
d48b75d862 Changelog v2.4.2 - GHA (#5548)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-01-25 16:09:13 -08:00
shamoon
f6e26d5953 Bump version to 2.4.2 2024-01-25 14:20:26 -08:00
shamoon
7863780883 Merge branch 'dev' 2024-01-25 14:20:11 -08:00
github-actions[bot]
c2ac9a26a2 New Crowdin translations by GitHub Action (#5547)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-25 14:19:46 -08:00
github-actions[bot]
58e8f796d1 New Crowdin translations by GitHub Action (#5539)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-25 14:18:31 -08:00
shamoon
ba0f4718e5 Fix: Modify one of date regexes (#5540) 2024-01-25 21:29:22 +00:00
shamoon
6fb4daf03e Fix: tweak doc detail component behavior while awaiting metadata (#5546) 2024-01-25 13:22:03 -08:00
Sebastian Steinbeißer
f6c34494a7 Documentation: change updating instructions (#5533) 2024-01-25 13:21:36 -08:00
shamoon
1e4d284b30 Reset dev version string 2024-01-24 11:51:41 -08:00
shamoon
1f3406fd77 Fix append changelog title 2024-01-24 11:51:32 -08:00
github-actions[bot]
398faf36fc Documentation: Add v2.4.1 changelog (#5535)
* Changelog v2.4.1 - GHA

* Update changelog.md

---------

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-24 11:50:07 -08:00
shamoon
ce841d4196 Merge branch 'dev' 2024-01-24 11:24:02 -08:00
shamoon
c77f8acf41 Bump version to v2.4.1 2024-01-24 11:02:24 -08:00
github-actions[bot]
212674f9df New Crowdin translations by GitHub Action (#5488)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-24 11:01:37 -08:00
shamoon
283ced56d1 Revert "Enhancement: support remote user auth directly against API (DRF)" (#5534) 2024-01-24 11:00:44 -08:00
shamoon
530e57151d Update PULL_REQUEST_TEMPLATE.md 2024-01-24 10:31:10 -08:00
shamoon
1141c3f361 Fix: fix calendar popup header background color and disable browser autofill (#5514) 2024-01-23 19:48:41 -08:00
shamoon
49416d3372 Fix: install script fails on alpine linux due to the use of head (#5520) 2024-01-23 14:25:16 -08:00
shamoon
88ae60a4a0 Fix: enforce object permissions for app config (#5516) 2024-01-23 12:23:15 -08:00
shamoon
6651c80fb9 Remove unused workflow trigger / action perms from UI 2024-01-23 12:18:52 -08:00
shamoon
6d6650d5f6 Fix: disable clickable name edit if user does not have permissions 2024-01-22 22:12:07 -08:00
shamoon
6df252c99b Chore: Build fix- branches (#5501) 2024-01-22 16:35:45 -08:00
shamoon
5881f05dbc Change workflow permissions assignment to merge (#5496) 2024-01-22 16:34:16 -08:00
dependabot[bot]
00eba3b223 Chore(deps-dev): Bump the development group with 1 update (#5503)
Bumps the development group with 1 update: [ruff](https://github.com/astral-sh/ruff).


Updates `ruff` from 0.1.13 to 0.1.14
- [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.13...v0.1.14)

---
updated-dependencies:
- dependency-name: ruff
  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-22 13:45:25 -08:00
Uli Fahrer
f7ab8d23a7 Documentation: update celery monitoring docker usage (#5484)
* docs: update celery monitoring docker usage

* docs: add review comments

Co-Authored-By: shamoon <4887959+shamoon@users.noreply.github.com>
2024-01-22 10:11:28 -08:00
Joakim Berglund
85b596d20d Lowercase stack name in docker-compose.portainer.yml (#5491)
Portainer does not allow upper case letters in the stack name. Update documentation to adhere to this limitation.
2024-01-21 10:34:04 -08:00
github-actions[bot]
4d43f6b63d New Crowdin translations by GitHub Action (#5463)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-01-21 00:35:54 -08:00
shamoon
ea1eb551a7 Enhancement: add background to consumer statuses 2024-01-21 00:34:24 -08:00
shamoon
5842944d1e Fix: render images not converted to pdf, refactor doc detail rendering (#5475) 2024-01-20 08:26:24 -08:00
shamoon
5781a0d51f Fix frontend tests icon imports 2024-01-19 22:28:32 -08:00
shamoon
e5f48739a0 Fix: Dont parse numbers with exponent as integer (#5457) 2024-01-19 06:29:13 -08:00
shamoon
d378c861f6 Reset dev version string 2024-01-18 22:14:01 -08:00
github-actions[bot]
9466bfdb00 [Documentation] Add v2.4.0 changelog (#5453) 2024-01-18 22:11:53 -08:00
343 changed files with 75434 additions and 45242 deletions

View File

@@ -9,7 +9,7 @@ Please include a summary of the change and which issue is fixed (if any) and any
-->
<!--
⚠️ 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.
⚠️ Important: Pull requests that implement a new feature or enhancement *should almost always target an existing feature request* with evidence of community interest and discussion. This is in order to balance the work of implementing and maintaining new features / enhancements. 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)
@@ -22,7 +22,7 @@ NOTE: Please check only one box!
-->
- [ ] Bug fix: non-breaking change which fixes an issue.
- [ ] New feature: non-breaking change which adds functionality. _Please read the important note above._
- [ ] New feature / Enhancement: 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:

View File

@@ -47,6 +47,8 @@ updates:
# Add reviewers
reviewers:
- "paperless-ngx/backend"
ignore:
- dependency-name: "uvicorn"
groups:
development:
patterns:

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.11.15"
DEFAULT_PIP_ENV_VERSION: "2023.12.1"
# This is the default version of Python to use in most steps which aren't specific
DEFAULT_PYTHON_VERSION: "3.10"
@@ -42,7 +42,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
-
name: Check files
uses: pre-commit/action@v3.0.0
uses: pre-commit/action@v3.0.1
documentation:
name: "Build & Deploy Documentation"
@@ -184,7 +184,7 @@ jobs:
cache-dependency-path: 'src-ui/package-lock.json'
- name: Cache frontend dependencies
id: cache-frontend-deps
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.npm
@@ -221,7 +221,7 @@ jobs:
cache-dependency-path: 'src-ui/package-lock.json'
- name: Cache frontend dependencies
id: cache-frontend-deps
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
~/.npm
@@ -283,7 +283,7 @@ jobs:
merge-multiple: true
-
name: Upload frontend coverage to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
# not required for public repos, but intermittently fails otherwise
token: ${{ secrets.CODECOV_TOKEN }}
@@ -299,7 +299,7 @@ jobs:
path: src/
-
name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
# not required for public repos, but intermittently fails otherwise
token: ${{ secrets.CODECOV_TOKEN }}
@@ -310,7 +310,7 @@ jobs:
build-docker-image:
name: Build Docker image for ${{ github.ref_name }}
runs-on: ubuntu-22.04
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/beta' || contains(github.ref, 'beta.rc') || startsWith(github.ref, 'refs/tags/v'))
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || startsWith(github.ref, 'refs/heads/fix-') || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/beta' || contains(github.ref, 'beta.rc') || startsWith(github.ref, 'refs/tags/v'))
concurrency:
group: ${{ github.workflow }}-build-docker-image-${{ github.ref_name }}
cancel-in-progress: true
@@ -577,7 +577,7 @@ jobs:
-
name: Create Release and Changelog
id: create-release
uses: release-drafter/release-drafter@v5
uses: release-drafter/release-drafter@v6
with:
name: Paperless-ngx ${{ steps.get_version.outputs.version }}
tag: ${{ steps.get_version.outputs.version }}
@@ -645,7 +645,7 @@ jobs:
script: |
const { repo, owner } = context.repo;
const result = await github.rest.pulls.create({
title: '[Documentation] Add ${{ needs.publish-release.outputs.version }} changelog',
title: 'Documentation: Add ${{ needs.publish-release.outputs.version }} changelog',
owner,
repo,
head: '${{ needs.publish-release.outputs.version }}-changelog',

View File

@@ -33,7 +33,7 @@ jobs:
-
name: Clean temporary images
if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/ephemeral@v0.4.0
uses: stumpylog/image-cleaner-action/ephemeral@v0.5.0
with:
token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}"
@@ -41,7 +41,7 @@ jobs:
package_name: "${{ matrix.primary-name }}"
scheme: "branch"
repo_name: "paperless-ngx"
match_regex: "feature-"
match_regex: "(feature|fix)"
do_delete: "true"
cleanup-untagged-images:
@@ -61,7 +61,7 @@ jobs:
-
name: Clean untagged images
if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/untagged@v0.4.0
uses: stumpylog/image-cleaner-action/untagged@v0.5.0
with:
token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}"

View File

@@ -22,6 +22,6 @@ jobs:
if: github.event_name == 'pull_request_target' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request.user.login != 'dependabot'
steps:
- name: Label PR with release-drafter
uses: release-drafter/release-drafter@v5
uses: release-drafter/release-drafter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -28,7 +28,7 @@ jobs:
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
for your contributions. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
lock-threads:
name: 'Lock Old Threads'
runs-on: ubuntu-latest
@@ -43,14 +43,17 @@ jobs:
This issue 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.
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
pr-comment: >
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.
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
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.
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
close-answered-discussions:
name: 'Close Answered Discussions'
runs-on: ubuntu-latest
@@ -90,7 +93,7 @@ jobs:
}`;
const commentVariables = {
discussion: discussion.id,
body: 'This discussion has been automatically closed because it was marked as answered.',
body: 'This discussion has been automatically closed because it was marked as answered. Please see our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.',
}
await github.graphql(addCommentMutation, commentVariables)
@@ -180,7 +183,85 @@ jobs:
}`;
const commentVariables = {
discussion: discussion.id,
body: 'This discussion has been automatically closed due to inactivity.',
body: 'This discussion has been automatically closed due to inactivity. Please see our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.',
}
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);
}
}
close-unsupported-feature-requests:
name: 'Close Unsupported Feature Requests'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const CUTOFF_1_DAYS = 180;
const CUTOFF_1_COUNT = 5;
const CUTOFF_2_DAYS = 365;
const CUTOFF_2_COUNT = 10;
const cutoff1Date = new Date();
cutoff1Date.setDate(cutoff1Date.getDate() - CUTOFF_1_DAYS);
const cutoff2Date = new Date();
cutoff2Date.setDate(cutoff2Date.getDate() - CUTOFF_2_DAYS);
const query = `query(
$owner:String!,
$name:String!,
$featureRequestsCategory:ID!,
) {
repository(owner:$owner, name:$name){
discussions(
categoryId:$featureRequestsCategory,
last:100,
states:[OPEN],
) {
nodes {
id,
number,
updatedAt,
upvoteCount,
}
},
}
}`;
const variables = {
owner: context.repo.owner,
name: context.repo.repo,
featureRequestsCategory: "DIC_kwDOG1Zs184CBNr4"
}
const result = await github.graphql(query, variables);
for (const discussion of result.repository.discussions.nodes) {
const discussionDate = new Date(discussion.updatedAt);
if ((discussionDate < cutoff1Date && discussion.upvoteCount < CUTOFF_1_COUNT) ||
(discussionDate < cutoff2Date && discussion.upvoteCount < CUTOFF_2_COUNT)) {
console.log(`Closing discussion #${discussion.number} (${discussion.id}), last updated at ${discussion.updatedAt} with votes ${discussion.upvoteCount}`);
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 lack of community support. Please see our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.',
}
await github.graphql(addCommentMutation, commentVariables);

View File

@@ -47,11 +47,11 @@ repos:
exclude: "(^Pipfile\\.lock$)"
# Python hooks
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.1.11'
rev: 'v0.3.0'
hooks:
- id: ruff
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 23.12.1
rev: 24.2.0
hooks:
- id: black
# Dockerfile hooks

View File

@@ -1,8 +1,3 @@
# https://beta.ruff.rs/docs/settings/
# https://beta.ruff.rs/docs/rules/
extend-select = ["I", "W", "UP", "COM", "DJ", "EXE", "ISC", "ICN", "G201", "INP", "PIE", "RSE", "SIM", "TID", "PLC", "PLE", "RUF"]
# TODO PTH
ignore = ["DJ001", "SIM105", "RUF012"]
fix = true
line-length = 88
respect-gitignore = true
@@ -11,13 +6,42 @@ target-version = "py39"
output-format = "grouped"
show-fixes = true
[per-file-ignores]
# https://docs.astral.sh/ruff/settings/
# https://docs.astral.sh/ruff/rules/
[lint]
extend-select = [
"W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
"I", # https://docs.astral.sh/ruff/rules/#isort-i
"UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up
"COM", # https://docs.astral.sh/ruff/rules/#flake8-commas-com
"DJ", # https://docs.astral.sh/ruff/rules/#flake8-django-dj
"EXE", # https://docs.astral.sh/ruff/rules/#flake8-executable-exe
"ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc
"ICN", # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn
"G201", # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g
"INP", # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp
"PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie
"Q", # https://docs.astral.sh/ruff/rules/#flake8-quotes-q
"RSE", # https://docs.astral.sh/ruff/rules/#flake8-raise-rse
"T20", # https://docs.astral.sh/ruff/rules/#flake8-print-t20
"SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim
"TID", # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid
"TCH", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tch
"PLC", # https://docs.astral.sh/ruff/rules/#pylint-pl
"PLE", # https://docs.astral.sh/ruff/rules/#pylint-pl
"RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf
"FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly
]
# TODO PTH https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
ignore = ["DJ001", "SIM105", "RUF012"]
[lint.per-file-ignores]
".github/scripts/*.py" = ["E501", "INP001", "SIM117"]
"docker/wait-for-redis.py" = ["INP001"]
"docker/wait-for-redis.py" = ["INP001", "T201"]
"*/tests/*.py" = ["E501", "SIM117"]
"*/migrations/*.py" = ["E501", "SIM"]
"*/migrations/*.py" = ["E501", "SIM", "T201"]
"src/paperless_tesseract/tests/test_parser.py" = ["RUF001"]
"src/documents/models.py" = ["SIM115"]
[isort]
[lint.isort]
force-single-line = true

View File

@@ -137,3 +137,19 @@ All team members are notified when mentioned or assigned to a relevant issue or
We are not overly strict with inviting people to the organization. If you have read the [team permissions](#permissions) and think having additional access would enhance your contributions, please reach out to an [admin](#structure) of the team.
The admins occasionally invite contributors directly if we believe having them on a team will accelerate their work.
# Automatic Repository Maintenance
The Paperless-ngx team appreciates all effort and interest from the community in filing bug reports, creating feature requests, sharing ideas and helping other
community members. That said, in an effort to keep the repository organized and managebale the project uses automatic handling of certain areas:
- Issues that cannot be reproduced will be marked 'stale' after 7 days of inactivity and closed after 14 further days of inactivity.
- Issues, pull requests and discussions that are closed will be locked after 30 days of inactivity.
- Discussions with a marked answer will be automatically closed.
- Discussions in the 'General' or 'Support' categories will be closed after 180 days of inactivity.
- Feature requests that do not meet the following thresholds will be closed: 5 "up-votes" after 180 days of inactivity or 10 "up-votes" after 365 days.
In all cases, threads can be re-opened by project maintainers and, of course, users can always create a new discussion for related concerns.
Finally, remember that all information remains searchable and 'closed' feature requests can still serve as inspiration for new features.
Thank you all for your contributions.

View File

@@ -29,7 +29,7 @@ COPY Pipfile* ./
RUN set -eux \
&& echo "Installing pipenv" \
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2023.11.15 \
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2023.12.1 \
&& echo "Generating requirement.txt" \
&& pipenv requirements > requirements.txt
@@ -39,8 +39,6 @@ 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"
@@ -57,6 +55,13 @@ ARG JBIG2ENC_VERSION=0.29
ARG QPDF_VERSION=11.6.4
ARG GS_VERSION=10.02.1
# Set Python environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
# Ignore warning from Whitenoise
PYTHONWARNINGS="ignore:::django.http.response:517" \
PNGX_CONTAINERIZED=1
#
# Begin installation and configuration
# Order the steps below from least often changed to most

View File

@@ -7,7 +7,8 @@ name = "pypi"
dateparser = "~=1.2"
# WARNING: django does not use semver.
# Only patch versions are guaranteed to not introduce breaking changes.
django = "~=4.2.9"
django = "~=4.2.11"
django-allauth = "*"
django-auditlog = "*"
django-celery-results = "*"
django-compression-middleware = "*"
@@ -45,12 +46,12 @@ python-magic = "*"
pyzbar = "*"
rapidfuzz = "*"
redis = {extras = ["hiredis"], version = "*"}
scikit-learn = "~=1.3"
scikit-learn = "~=1.4"
setproctitle = "*"
tika-client = "*"
tqdm = "*"
uvicorn = {extras = ["standard"], version = "*"}
watchdog = "~=3.0"
uvicorn = {extras = ["standard"], version = "==0.25.0"}
watchdog = "~=4.0"
whitenoise = "~=6.6"
whoosh="~=2.7"
zxing-cpp = {version = "*", platform_machine = "== 'x86_64'"}

1680
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@ Paperless-ngx is a document management system that transforms your physical docu
Paperless-ngx is the official successor to the original [Paperless](https://github.com/the-paperless-project/paperless) & [Paperless-ng](https://github.com/jonaswinkler/paperless-ng) projects and is designed to distribute the responsibility of advancing and supporting the project among a team of people. [Consider joining us!](#community-support)
A demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com) using login `demo` / `demo`. _Note: demo content is reset frequently and confidential information should not be uploaded._
Thanks to the generous folks at [DigitalOcean](https://m.do.co/c/8d70b916d462), a demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com) using login `demo` / `demo`. _Note: demo content is reset frequently and confidential information should not be uploaded._
- [Features](#features)
- [Getting started](#getting-started)
@@ -33,6 +33,16 @@ A demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com)
- [Affiliated Projects](#affiliated-projects)
- [Important Note](#important-note)
<p align="right">This project is supported by:<br/>
<a href="https://m.do.co/c/8d70b916d462" style="padding-top: 4px; display: block;">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_white.svg" width="140px">
<source media="(prefers-color-scheme: light)" srcset="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_black_.svg" width="140px">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_black_.svg" width="140px">
</picture>
</a>
</p>
# Features
<picture>

9
SECURITY.md Normal file
View File

@@ -0,0 +1,9 @@
# Security Policy
## Reporting a Vulnerability
The Paperless-ngx team and community take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/paperless-ngx/paperless-ngx/security/advisories/new) tab.
The team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.

View File

@@ -18,7 +18,7 @@
# To install and update paperless with this file, do the following:
#
# - Open portainer Stacks list and click 'Add stack'
# - Paste the contents of this file and assign a name, e.g. 'Paperless'
# - Paste the contents of this file and assign a name, e.g. 'paperless'
# - Click 'Deploy the stack' and wait for it to be deployed
# - Open the list of containers, select paperless_webserver_1
# - Click 'Console' and then 'Connect' to open the command line inside the container

View File

@@ -90,13 +90,13 @@ initialize() {
fi
done
local -r tmp_dir="/tmp/paperless"
echo "Creating directory ${tmp_dir}"
local -r tmp_dir="${PAPERLESS_SCRATCH_DIR:=/tmp/paperless}"
echo "Creating directory scratch directory ${tmp_dir}"
mkdir --parents "${tmp_dir}"
set +e
echo "Adjusting permissions of paperless files. This may take a while."
chown -R paperless:paperless ${tmp_dir}
chown -R paperless:paperless "${tmp_dir}"
for dir in \
"${export_dir}" \
"${DATA_DIR}" \

View File

@@ -1,14 +1,15 @@
#!/usr/bin/env bash
SUPERVISORD_WORKING_DIR="${PAPERLESS_SUPERVISORD_WORKING_DIR:-$PWD}"
rootless_args=()
if [ "$(id -u)" == "$(id -u paperless)" ]; then
rootless_args=(
--user
paperless
--logfile
supervisord.log
"${SUPERVISORD_WORKING_DIR}/supervisord.log"
--pidfile
supervisord.pid
"${SUPERVISORD_WORKING_DIR}/supervisord.pid"
)
fi

View File

@@ -67,15 +67,15 @@ you installed paperless-ngx in the first place. The releases are
available at the [release
page](https://github.com/paperless-ngx/paperless-ngx/releases).
First of all, ensure that paperless is stopped.
First of all, make sure no active processes (like consumption) are running, then [make a backup](#backup).
After that, ensure that paperless is stopped:
```shell-session
$ cd /path/to/paperless
$ docker compose down
```
After that, [make a backup](#backup).
1. If you pull the image from the docker hub, all you need to do is:
```shell-session

View File

@@ -256,7 +256,8 @@ document. You will end up getting files like `0000123.pdf` in your media
directory. This isn't necessarily a bad thing, because you normally
don't have to access these files manually. However, if you wish to name
your files differently, you can do that by adjusting the
[`PAPERLESS_FILENAME_FORMAT`](configuration.md#PAPERLESS_FILENAME_FORMAT) configuration option. Paperless adds the
[`PAPERLESS_FILENAME_FORMAT`](configuration.md#PAPERLESS_FILENAME_FORMAT) configuration option
or using [storage paths (see below)](#storage-paths). Paperless adds the
correct file extension e.g. `.pdf`, `.jpg` automatically.
This variable allows you to configure the filename (folders are allowed)
@@ -289,6 +290,15 @@ will create a directory structure as follows:
paperless will report your files as missing and won't be able to find
them.
!!! tip
Paperless checks the filename of a document whenever it is saved. Changing (or deleting)
a [storage paths](#storage-paths) will automatically be reflected in the file system. However,
when changing `PAPERLESS_FILENAME_FORMAT` you will need to manually run the
[`document renamer`](administration.md#renamer) to move any existing documents.
#### Placeholders
Paperless provides the following placeholders within filenames:
- `{asn}`: The archive serial number of the document, or "none".
@@ -321,6 +331,12 @@ Paperless provides the following placeholders within filenames:
- `{original_name}`: Document original filename, minus the extension, if any, or "none"
- `{doc_pk}`: The paperless identifier (primary key) for the document.
!!! warning
When using file name placeholders, in particular when using `{tag_list}`,
you may run into the limits of your operating system's maximum path lengths.
In that case, files will retain the previous path instead and the issue logged.
Paperless will try to conserve the information from your database as
much as possible. However, some characters that you can use in document
titles and correspondent names (such as `: \ /` and a couple more) are
@@ -331,34 +347,12 @@ paperless will automatically append `_01`, `_02`, etc to the filename.
This happens if all the placeholders in a filename evaluate to the same
value.
!!! tip
You can affect how empty placeholders are treated by changing the
following setting to `true`.
```
PAPERLESS_FILENAME_FORMAT_REMOVE_NONE=True
```
Doing this results in all empty placeholders resolving to "" instead
of "none" as stated above. Spaces before empty placeholders are
removed as well, empty directories are omitted.
!!! tip
Paperless checks the filename of a document whenever it is saved.
Therefore, you need to update the filenames of your documents and move
them after altering this setting by invoking the
[`document renamer`](administration.md#renamer).
!!! warning
Make absolutely sure you get the spelling of the placeholders right, or
else paperless will use the default naming scheme instead.
If there are any errors in the placeholders included in `PAPERLESS_FILENAME_FORMAT`,
paperless will fallback to using the default naming scheme instead.
!!! caution
As of now, you could totally tell paperless to store your files anywhere
As of now, you could potentially tell paperless to store your files anywhere
outside the media directory by setting
```
@@ -366,28 +360,25 @@ value.
```
However, keep in mind that inside docker, if files get stored outside of
the predefined volumes, they will be lost after a restart of paperless.
the predefined volumes, they will be lost after a restart.
!!! warning
##### Empty placeholders
When file naming handling, in particular when using `{tag_list}`,
you may run into the limits of your operating system's maximum
path lengths. Files will retain the previous path instead and
the issue logged.
You can affect how empty placeholders are treated by changing the
[`PAPERLESS_FILENAME_FORMAT_REMOVE_NONE`](configuration.md#PAPERLESS_FILENAME_FORMAT_REMOVE_NONE) setting.
## Storage paths
Enabling this results in all empty placeholders resolving to "" instead of "none" as stated above. Spaces
before empty placeholders are removed as well, empty directories are omitted.
One of the best things in Paperless is that you can not only access the
documents via the web interface, but also via the file system.
### Storage paths
When a single storage layout is not sufficient for your use case,
storage paths come to the rescue. Storage paths allow you to configure
more precisely where each document is stored in the file system.
When a single storage layout is not sufficient for your use case, storage paths allow for more complex
structure to set precisely where each document is stored in the file system.
- Each storage path is a [`PAPERLESS_FILENAME_FORMAT`](configuration.md#PAPERLESS_FILENAME_FORMAT) and
follows the rules described above
- Each document is assigned a storage path using the matching
algorithms described above, but can be overwritten at any time
- Each document is assigned a storage path using the matching algorithms described above, but can be
overwritten at any time
For example, you could define the following two storage paths:
@@ -434,8 +425,10 @@ to view more detailed information about the health of the celery workers
used for asynchronous tasks. This includes details on currently running,
queued and completed tasks, timing and more. Flower can also be used
with Prometheus, as it exports metrics. For details on its capabilities,
refer to the Flower documentation.
refer to the [Flower](https://flower.readthedocs.io/en/latest/index.html)
documentation.
Flower can be enabled with the setting [PAPERLESS_ENABLE_FLOWER](configuration.md#PAPERLESS_ENABLE_FLOWER).
To configure Flower further, create a `flowerconfig.py` and
place it into the `src/paperless` directory. For a Docker
installation, you can use volumes to accomplish this:
@@ -444,6 +437,8 @@ installation, you can use volumes to accomplish this:
services:
# ...
webserver:
environment:
- PAPERLESS_ENABLE_FLOWER
ports:
- 5555:5555 # (2)!
# ...
@@ -452,7 +447,7 @@ services:
```
1. Note the `:ro` tag means the file will be mounted as read only.
2. `flower` runs by default on port 5555, but this can be configured
2. By default, Flower runs on port 5555, but this can be configured.
## Custom Container Initialization
@@ -513,6 +508,18 @@ existing tables) with:
an older system may fix issues that can arise while setting up Paperless-ngx but
`utf8mb3` can cause issues with consumption (where `utf8mb4` does not).
### Missing timezones
MySQL as well as MariaDB do not have any timezone information by default (though some
docker images such as the official MariaDB image take care of this for you) which will
cause unexpected behavior with date-based queries.
To fix this, execute one of the following commands:
MySQL: `mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql -p`
MariaDB: `mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb -u root mysql -p`
## Barcodes {#barcodes}
Paperless is able to utilize barcodes for automatically performing some tasks.
@@ -553,6 +560,14 @@ barcode is located. However, differing from the splitting, the page with the
barcode _will_ be retained. This allows application of a barcode to any page, including
one which holds data to keep in the document.
### Tag Assignment
When enabled, Paperless will parse barcodes and attempt to interpret and assign tags.
See the relevant settings [`PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE`](configuration.md#PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE)
and [`PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING`](configuration.md#PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING)
for more information.
## Automatic collation of double-sided documents {#collate}
!!! note
@@ -624,3 +639,51 @@ single-sided split marker page, the split document(s) will have an empty page at
whatever else was on the backside of the split marker page.) You can work around that by having
a split marker page that has the split barcode on _both_ sides. This way, the extra page will
get automatically removed.
## SSO and third party authentication with Paperless-ngx
Paperless-ngx has a built-in authentication system from Django but you can easily integrate an
external authentication solution using one of the following methods:
### Remote User authentication
This is a simple option that uses remote user authentication made available by certain SSO
applications. See the relevant configuration options for more information:
[PAPERLESS_ENABLE_HTTP_REMOTE_USER](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER) and
[PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME](configuration.md#PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME)
### OpenID Connect and social authentication
Version 2.5.0 of Paperless-ngx added support for integrating other authentication systems via
the [django-allauth](https://github.com/pennersr/django-allauth) package. Once set up, users
can either log in or (optionally) sign up using any third party systems you integrate. See the
relevant [configuration settings](configuration.md#PAPERLESS_SOCIALACCOUNT_PROVIDERS) and
[django-allauth docs](https://docs.allauth.org/en/latest/socialaccount/configuration.html)
for more information.
To associate an existing Paperless-ngx account with a social account, first login with your
regular credentials and then choose "My Profile" from the user dropdown in the app and you
will see options to connect social account(s). If enabled, signup options will be available
on the login page.
As an example, to set up login via Github, the following environment variables would need to be
set:
```conf
PAPERLESS_APPS="allauth.socialaccount.providers.github"
PAPERLESS_SOCIALACCOUNT_PROVIDERS='{"github": {"APPS": [{"provider_id": "github","name": "Github","client_id": "<CLIENT_ID>","secret": "<CLIENT_SECRET>"}]}}'
```
Or, to use OpenID Connect ("OIDC"), via Keycloak in this example:
```conf
PAPERLESS_APPS="allauth.socialaccount.providers.openid_connect"
PAPERLESS_SOCIALACCOUNT_PROVIDERS='
{"openid_connect": {"APPS": [{"provider_id": "keycloak","name": "Keycloak","client_id": "paperless","secret": "<CLIENT_SECRET>","settings": { "server_url": "https://<KEYCLOAK_SERVER>/realms/<REALM>/.well-known/openid-configuration"}}]}}'
```
More details about configuration option for various providers can be found in the [allauth documentation](https://docs.allauth.org/en/latest/socialaccount/providers/index.html#provider-specifics).
### Disabling Regular Login
Once external auth is set up, 'regular' login can be disabled with the [PAPERLESS_DISABLE_REGULAR_LOGIN](configuration.md#PAPERLESS_DISABLE_REGULAR_LOGIN) setting.

View File

@@ -58,6 +58,10 @@ fields:
- `custom_fields`: Array of custom fields & values, specified as
`{ field: CUSTOM_FIELD_ID, value: VALUE }`
!!! note
Note that all endpoint URLs must end with a `/`slash.
## Downloading documents
In addition to that, the document endpoint offers these additional
@@ -179,8 +183,8 @@ The REST api provides four different forms of authentication.
4. Remote User authentication
If already setup (see
[configuration](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER)),
If enabled (see
[configuration](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER_API)),
you can authenticate against the API using Remote User auth.
## Searching for documents
@@ -191,7 +195,7 @@ results:
- `/api/documents/?query=your%20search%20query`: Search for a document
using a full text query. For details on the syntax, see [Basic Usage - Searching](usage.md#basic-usage_searching).
- `/api/documents/?more_like=1234`: Search for documents similar to
- `/api/documents/?more_like_id=1234`: Search for documents similar to
the document with id 1234.
Pagination works exactly the same as it does for normal requests on this
@@ -330,6 +334,65 @@ granted). You can pass the parameter `full_perms=true` to API calls to view the
full permissions of objects in a format that mirrors the `set_permissions`
parameter above.
## Bulk Editing
The API supports various bulk-editing operations which are executed asynchronously.
### Documents
For bulk operations on documents, use the endpoint `/api/bulk_edit/` which accepts
a json payload of the format:
```json
{
"documents": [LIST_OF_DOCUMENT_IDS],
"method": METHOD, // see below
"parameters": args // see below
}
```
The following methods are supported:
- `set_correspondent`
- Requires `parameters`: `{ "correspondent": CORRESPONDENT_ID }`
- `set_document_type`
- Requires `parameters`: `{ "document_type": DOCUMENT_TYPE_ID }`
- `set_storage_path`
- Requires `parameters`: `{ "storage_path": STORAGE_PATH_ID }`
- `add_tag`
- Requires `parameters`: `{ "tag": TAG_ID }`
- `remove_tag`
- Requires `parameters`: `{ "tag": TAG_ID }`
- `modify_tags`
- Requires `parameters`: `{ "add_tags": [LIST_OF_TAG_IDS] }` and / or `{ "remove_tags": [LIST_OF_TAG_IDS] }`
- `delete`
- No `parameters` required
- `redo_ocr`
- No `parameters` required
- `set_permissions`
- Requires `parameters`:
- `"permissions": PERMISSIONS_OBJ` (see format [above](#permissions)) and / or
- `"owner": OWNER_ID or null`
- `"merge": true or false` (defaults to false)
- The `merge` flag determines if the supplied permissions will overwrite all existing permissions (including
removing them) or be merged with existing permissions.
### Objects
Bulk editing for objects (tags, document types etc.) currently supports set permissions or delete
operations, using the endpoint: `/api/bulk_edit_objects/`, which requires a json payload of the format:
```json
{
"objects": [LIST_OF_OBJECT_IDS],
"object_type": "tags", "correspondents", "document_types" or "storage_paths",
"operation": "set_permissions" or "delete",
"owner": OWNER_ID, // optional
"permissions": { "view": { "users": [] ... }, "change": { ... } }, // (see 'set_permissions' format above)
"merge": true / false // defaults to false, see above
}
```
## API Versioning
The REST API is versioned since Paperless-ngx 1.3.0.
@@ -386,3 +449,13 @@ Initial API version.
color to use for a specific tag, which is either black or white
depending on the brightness of `Tag.color`.
- Removed field `Tag.colour`.
#### Version 3
- Permissions endpoints have been added.
- The format of the `/api/ui_settings/` has changed.
#### Version 4
- Consumption templates were refactored to workflows and API endpoints
changed as such.

View File

@@ -1,5 +1,410 @@
# Changelog
## paperless-ngx 2.6.1
### All App Changes
- Change: tweaks to system status [@shamoon](https://github.com/shamoon) ([#6008](https://github.com/paperless-ngx/paperless-ngx/pull/6008))
## paperless-ngx 2.6.0
### Features
- Feature: Allow user to control PIL image pixel limit [@stumpylog](https://github.com/stumpylog) ([#5997](https://github.com/paperless-ngx/paperless-ngx/pull/5997))
- Feature: Allow a user to disable the pixel limit for OCR entirely [@stumpylog](https://github.com/stumpylog) ([#5996](https://github.com/paperless-ngx/paperless-ngx/pull/5996))
- Feature: workflow removal action [@shamoon](https://github.com/shamoon) ([#5928](https://github.com/paperless-ngx/paperless-ngx/pull/5928))
- Feature: system status [@shamoon](https://github.com/shamoon) ([#5743](https://github.com/paperless-ngx/paperless-ngx/pull/5743))
- Enhancement: better monetary field with currency code [@shamoon](https://github.com/shamoon) ([#5858](https://github.com/paperless-ngx/paperless-ngx/pull/5858))
- Enhancement: support disabling regular login [@shamoon](https://github.com/shamoon) ([#5816](https://github.com/paperless-ngx/paperless-ngx/pull/5816))
### Bug Fixes
- Fix: refactor base path settings, correct logout redirect [@shamoon](https://github.com/shamoon) ([#5976](https://github.com/paperless-ngx/paperless-ngx/pull/5976))
- Fix: always pass from UI, dont require in API [@shamoon](https://github.com/shamoon) ([#5962](https://github.com/paperless-ngx/paperless-ngx/pull/5962))
- Fix: Clear metadata cache when the filename(s) change [@stumpylog](https://github.com/stumpylog) ([#5957](https://github.com/paperless-ngx/paperless-ngx/pull/5957))
- Fix: include monetary, float and doc link values in search filters [@shamoon](https://github.com/shamoon) ([#5951](https://github.com/paperless-ngx/paperless-ngx/pull/5951))
- Fix: Better handling of a corrupted index [@stumpylog](https://github.com/stumpylog) ([#5950](https://github.com/paperless-ngx/paperless-ngx/pull/5950))
- Fix: Don't assume the location of scratch directory in Docker [@stumpylog](https://github.com/stumpylog) ([#5948](https://github.com/paperless-ngx/paperless-ngx/pull/5948))
- Fix: ensure document title always limited to 128 chars [@shamoon](https://github.com/shamoon) ([#5934](https://github.com/paperless-ngx/paperless-ngx/pull/5934))
- Fix: use for password reset emails, if set [@shamoon](https://github.com/shamoon) ([#5902](https://github.com/paperless-ngx/paperless-ngx/pull/5902))
- Fix: Correct docker compose check in install script [@ShanSanear](https://github.com/ShanSanear) ([#5917](https://github.com/paperless-ngx/paperless-ngx/pull/5917))
- Fix: respect global permissions for UI settings [@shamoon](https://github.com/shamoon) ([#5919](https://github.com/paperless-ngx/paperless-ngx/pull/5919))
- Fix: allow disable email verification during signup [@shamoon](https://github.com/shamoon) ([#5895](https://github.com/paperless-ngx/paperless-ngx/pull/5895))
- Fix: refactor accounts templates and create signup template [@shamoon](https://github.com/shamoon) ([#5899](https://github.com/paperless-ngx/paperless-ngx/pull/5899))
### Maintenance
- Chore(deps): Bump the actions group with 3 updates [@dependabot](https://github.com/dependabot) ([#5907](https://github.com/paperless-ngx/paperless-ngx/pull/5907))
- Chore: Ignores uvicorn updates in dependabot [@stumpylog](https://github.com/stumpylog) ([#5906](https://github.com/paperless-ngx/paperless-ngx/pull/5906))
### Dependencies
<details>
<summary>15 changes</summary>
- Chore(deps): Bump the small-changes group with 3 updates [@dependabot](https://github.com/dependabot) ([#6001](https://github.com/paperless-ngx/paperless-ngx/pull/6001))
- Chore(deps-dev): Bump the development group with 2 updates [@dependabot](https://github.com/dependabot) ([#5998](https://github.com/paperless-ngx/paperless-ngx/pull/5998))
- Chore(deps): Bump the django group with 1 update [@dependabot](https://github.com/dependabot) ([#6000](https://github.com/paperless-ngx/paperless-ngx/pull/6000))
- Chore(deps-dev): Bump [@<!---->playwright/test from 1.41.2 to 1.42.0 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.41.2 to 1.42.0 in /src-ui @dependabot) ([#5964](https://github.com/paperless-ngx/paperless-ngx/pull/5964))
- Chore(deps-dev): Bump [@<!---->types/node from 20.11.20 to 20.11.24 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.11.20 to 20.11.24 in /src-ui @dependabot) ([#5965](https://github.com/paperless-ngx/paperless-ngx/pull/5965))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 11 updates [@dependabot](https://github.com/dependabot) ([#5963](https://github.com/paperless-ngx/paperless-ngx/pull/5963))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#5918](https://github.com/paperless-ngx/paperless-ngx/pull/5918))
- Chore(deps-dev): Bump [@<!---->types/node from 20.11.16 to 20.11.20 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.11.16 to 20.11.20 in /src-ui @dependabot) ([#5912](https://github.com/paperless-ngx/paperless-ngx/pull/5912))
- Chore(deps): Bump zone.js from 0.14.3 to 0.14.4 in /src-ui [@dependabot](https://github.com/dependabot) ([#5913](https://github.com/paperless-ngx/paperless-ngx/pull/5913))
- Chore(deps): Bump bootstrap from 5.3.2 to 5.3.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#5911](https://github.com/paperless-ngx/paperless-ngx/pull/5911))
- Chore(deps-dev): Bump typescript from 5.2.2 to 5.3.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#5915](https://github.com/paperless-ngx/paperless-ngx/pull/5915))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 15 updates [@dependabot](https://github.com/dependabot) ([#5908](https://github.com/paperless-ngx/paperless-ngx/pull/5908))
- Chore(deps): Bump the small-changes group with 4 updates [@dependabot](https://github.com/dependabot) ([#5916](https://github.com/paperless-ngx/paperless-ngx/pull/5916))
- Chore(deps-dev): Bump the development group with 4 updates [@dependabot](https://github.com/dependabot) ([#5914](https://github.com/paperless-ngx/paperless-ngx/pull/5914))
- Chore(deps): Bump the actions group with 3 updates [@dependabot](https://github.com/dependabot) ([#5907](https://github.com/paperless-ngx/paperless-ngx/pull/5907))
</details>
### All App Changes
<details>
<summary>33 changes</summary>
- Feature: Allow user to control PIL image pixel limit [@stumpylog](https://github.com/stumpylog) ([#5997](https://github.com/paperless-ngx/paperless-ngx/pull/5997))
- Enhancement: show ID when editing objects [@shamoon](https://github.com/shamoon) ([#6003](https://github.com/paperless-ngx/paperless-ngx/pull/6003))
- Feature: Allow a user to disable the pixel limit for OCR entirely [@stumpylog](https://github.com/stumpylog) ([#5996](https://github.com/paperless-ngx/paperless-ngx/pull/5996))
- Chore(deps): Bump the small-changes group with 3 updates [@dependabot](https://github.com/dependabot) ([#6001](https://github.com/paperless-ngx/paperless-ngx/pull/6001))
- Chore(deps-dev): Bump the development group with 2 updates [@dependabot](https://github.com/dependabot) ([#5998](https://github.com/paperless-ngx/paperless-ngx/pull/5998))
- Chore(deps): Bump the django group with 1 update [@dependabot](https://github.com/dependabot) ([#6000](https://github.com/paperless-ngx/paperless-ngx/pull/6000))
- Feature: workflow removal action [@shamoon](https://github.com/shamoon) ([#5928](https://github.com/paperless-ngx/paperless-ngx/pull/5928))
- Feature: system status [@shamoon](https://github.com/shamoon) ([#5743](https://github.com/paperless-ngx/paperless-ngx/pull/5743))
- Fix: refactor base path settings, correct logout redirect [@shamoon](https://github.com/shamoon) ([#5976](https://github.com/paperless-ngx/paperless-ngx/pull/5976))
- Chore(deps-dev): Bump [@<!---->playwright/test from 1.41.2 to 1.42.0 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.41.2 to 1.42.0 in /src-ui @dependabot) ([#5964](https://github.com/paperless-ngx/paperless-ngx/pull/5964))
- Chore(deps-dev): Bump [@<!---->types/node from 20.11.20 to 20.11.24 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.11.20 to 20.11.24 in /src-ui @dependabot) ([#5965](https://github.com/paperless-ngx/paperless-ngx/pull/5965))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 11 updates [@dependabot](https://github.com/dependabot) ([#5963](https://github.com/paperless-ngx/paperless-ngx/pull/5963))
- Fix: always pass from UI, dont require in API [@shamoon](https://github.com/shamoon) ([#5962](https://github.com/paperless-ngx/paperless-ngx/pull/5962))
- Fix: Clear metadata cache when the filename(s) change [@stumpylog](https://github.com/stumpylog) ([#5957](https://github.com/paperless-ngx/paperless-ngx/pull/5957))
- Fix: include monetary, float and doc link values in search filters [@shamoon](https://github.com/shamoon) ([#5951](https://github.com/paperless-ngx/paperless-ngx/pull/5951))
- Fix: Better handling of a corrupted index [@stumpylog](https://github.com/stumpylog) ([#5950](https://github.com/paperless-ngx/paperless-ngx/pull/5950))
- Chore: Includes OCRMyPdf logging into the log file [@stumpylog](https://github.com/stumpylog) ([#5947](https://github.com/paperless-ngx/paperless-ngx/pull/5947))
- Fix: ensure document title always limited to 128 chars [@shamoon](https://github.com/shamoon) ([#5934](https://github.com/paperless-ngx/paperless-ngx/pull/5934))
- Enhancement: better monetary field with currency code [@shamoon](https://github.com/shamoon) ([#5858](https://github.com/paperless-ngx/paperless-ngx/pull/5858))
- Change: add Thumbs.db to default ignores [@DennisGaida](https://github.com/DennisGaida) ([#5924](https://github.com/paperless-ngx/paperless-ngx/pull/5924))
- Fix: use for password reset emails, if set [@shamoon](https://github.com/shamoon) ([#5902](https://github.com/paperless-ngx/paperless-ngx/pull/5902))
- Fix: respect global permissions for UI settings [@shamoon](https://github.com/shamoon) ([#5919](https://github.com/paperless-ngx/paperless-ngx/pull/5919))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates [@dependabot](https://github.com/dependabot) ([#5918](https://github.com/paperless-ngx/paperless-ngx/pull/5918))
- Chore(deps-dev): Bump [@<!---->types/node from 20.11.16 to 20.11.20 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.11.16 to 20.11.20 in /src-ui @dependabot) ([#5912](https://github.com/paperless-ngx/paperless-ngx/pull/5912))
- Chore(deps): Bump zone.js from 0.14.3 to 0.14.4 in /src-ui [@dependabot](https://github.com/dependabot) ([#5913](https://github.com/paperless-ngx/paperless-ngx/pull/5913))
- Chore(deps): Bump bootstrap from 5.3.2 to 5.3.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#5911](https://github.com/paperless-ngx/paperless-ngx/pull/5911))
- Chore(deps-dev): Bump typescript from 5.2.2 to 5.3.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#5915](https://github.com/paperless-ngx/paperless-ngx/pull/5915))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 15 updates [@dependabot](https://github.com/dependabot) ([#5908](https://github.com/paperless-ngx/paperless-ngx/pull/5908))
- Fix: allow disable email verification during signup [@shamoon](https://github.com/shamoon) ([#5895](https://github.com/paperless-ngx/paperless-ngx/pull/5895))
- Fix: refactor accounts templates and create signup template [@shamoon](https://github.com/shamoon) ([#5899](https://github.com/paperless-ngx/paperless-ngx/pull/5899))
- Chore(deps): Bump the small-changes group with 4 updates [@dependabot](https://github.com/dependabot) ([#5916](https://github.com/paperless-ngx/paperless-ngx/pull/5916))
- Chore(deps-dev): Bump the development group with 4 updates [@dependabot](https://github.com/dependabot) ([#5914](https://github.com/paperless-ngx/paperless-ngx/pull/5914))
- Enhancement: support disabling regular login [@shamoon](https://github.com/shamoon) ([#5816](https://github.com/paperless-ngx/paperless-ngx/pull/5816))
</details>
## paperless-ngx 2.5.4
### Bug Fixes
- Fix: handle title placeholder for docs without original_filename [@shamoon](https://github.com/shamoon) ([#5828](https://github.com/paperless-ngx/paperless-ngx/pull/5828))
- Fix: bulk edit objects does not respect global permissions [@shamoon](https://github.com/shamoon) ([#5888](https://github.com/paperless-ngx/paperless-ngx/pull/5888))
- Fix: intermittent save \& close warnings [@shamoon](https://github.com/shamoon) ([#5838](https://github.com/paperless-ngx/paperless-ngx/pull/5838))
- Fix: inotify read timeout not in ms [@grembo](https://github.com/grembo) ([#5876](https://github.com/paperless-ngx/paperless-ngx/pull/5876))
- Fix: allow relative date queries not in quick list [@shamoon](https://github.com/shamoon) ([#5801](https://github.com/paperless-ngx/paperless-ngx/pull/5801))
- Fix: pass rule id to consumed .eml files [@shamoon](https://github.com/shamoon) ([#5800](https://github.com/paperless-ngx/paperless-ngx/pull/5800))
### Dependencies
- Chore(deps): Bump cryptography from 42.0.2 to 42.0.4 [@dependabot](https://github.com/dependabot) ([#5851](https://github.com/paperless-ngx/paperless-ngx/pull/5851))
- Chore(deps-dev): Bump ip from 2.0.0 to 2.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#5835](https://github.com/paperless-ngx/paperless-ngx/pull/5835))
- Chore(deps): Bump undici and [@<!---->angular-devkit/build-angular in /src-ui @dependabot](https://github.com/<!---->angular-devkit/build-angular in /src-ui @dependabot) ([#5796](https://github.com/paperless-ngx/paperless-ngx/pull/5796))
### All App Changes
<details>
<summary>8 changes</summary>
- Fix: handle title placeholder for docs without original_filename [@shamoon](https://github.com/shamoon) ([#5828](https://github.com/paperless-ngx/paperless-ngx/pull/5828))
- Fix: bulk edit objects does not respect global permissions [@shamoon](https://github.com/shamoon) ([#5888](https://github.com/paperless-ngx/paperless-ngx/pull/5888))
- Fix: intermittent save \& close warnings [@shamoon](https://github.com/shamoon) ([#5838](https://github.com/paperless-ngx/paperless-ngx/pull/5838))
- Fix: inotify read timeout not in ms [@grembo](https://github.com/grembo) ([#5876](https://github.com/paperless-ngx/paperless-ngx/pull/5876))
- Chore(deps-dev): Bump ip from 2.0.0 to 2.0.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#5835](https://github.com/paperless-ngx/paperless-ngx/pull/5835))
- Chore(deps): Bump undici and [@<!---->angular-devkit/build-angular in /src-ui @dependabot](https://github.com/<!---->angular-devkit/build-angular in /src-ui @dependabot) ([#5796](https://github.com/paperless-ngx/paperless-ngx/pull/5796))
- Fix: allow relative date queries not in quick list [@shamoon](https://github.com/shamoon) ([#5801](https://github.com/paperless-ngx/paperless-ngx/pull/5801))
- Fix: pass rule id to consumed .eml files [@shamoon](https://github.com/shamoon) ([#5800](https://github.com/paperless-ngx/paperless-ngx/pull/5800))
</details>
## paperless-ngx 2.5.3
### Bug Fixes
- Fix: dont allow allauth redirects to any host [@shamoon](https://github.com/shamoon) ([#5783](https://github.com/paperless-ngx/paperless-ngx/pull/5783))
- Fix: Interaction when both splitting and ASN are enabled [@stumpylog](https://github.com/stumpylog) ([#5779](https://github.com/paperless-ngx/paperless-ngx/pull/5779))
- Fix: moved ssl_mode parameter for mysql backend engine [@MaciejSzczurek](https://github.com/MaciejSzczurek) ([#5771](https://github.com/paperless-ngx/paperless-ngx/pull/5771))
### All App Changes
<details>
<summary>3 changes</summary>
- Fix: dont allow allauth redirects to any host [@shamoon](https://github.com/shamoon) ([#5783](https://github.com/paperless-ngx/paperless-ngx/pull/5783))
- Fix: Interaction when both splitting and ASN are enabled [@stumpylog](https://github.com/stumpylog) ([#5779](https://github.com/paperless-ngx/paperless-ngx/pull/5779))
- Fix: moved ssl_mode parameter for mysql backend engine [@MaciejSzczurek](https://github.com/MaciejSzczurek) ([#5771](https://github.com/paperless-ngx/paperless-ngx/pull/5771))
</details>
## paperless-ngx 2.5.2
### Bug Fixes
- Fix: Generated secret key may include single or double quotes [@schmidtnz](https://github.com/schmidtnz) ([#5767](https://github.com/paperless-ngx/paperless-ngx/pull/5767))
- Fix: consumer status alerts container blocks elements [@shamoon](https://github.com/shamoon) ([#5762](https://github.com/paperless-ngx/paperless-ngx/pull/5762))
- Fix: handle document notes user format api change [@shamoon](https://github.com/shamoon) ([#5751](https://github.com/paperless-ngx/paperless-ngx/pull/5751))
- Fix: Assign ASN from barcode only after any splitting [@stumpylog](https://github.com/stumpylog) ([#5745](https://github.com/paperless-ngx/paperless-ngx/pull/5745))
### Dependencies
- Chore(deps): Bump the major-versions group with 1 update [@dependabot](https://github.com/dependabot) ([#5741](https://github.com/paperless-ngx/paperless-ngx/pull/5741))
### All App Changes
<details>
<summary>4 changes</summary>
- Fix: consumer status alerts container blocks elements [@shamoon](https://github.com/shamoon) ([#5762](https://github.com/paperless-ngx/paperless-ngx/pull/5762))
- Fix: handle document notes user format api change [@shamoon](https://github.com/shamoon) ([#5751](https://github.com/paperless-ngx/paperless-ngx/pull/5751))
- Fix: Assign ASN from barcode only after any splitting [@stumpylog](https://github.com/stumpylog) ([#5745](https://github.com/paperless-ngx/paperless-ngx/pull/5745))
- Chore(deps): Bump the major-versions group with 1 update [@dependabot](https://github.com/dependabot) ([#5741](https://github.com/paperless-ngx/paperless-ngx/pull/5741))
</details>
## paperless-ngx 2.5.1
### Bug Fixes
- Fix: Splitting on ASN barcodes even if not enabled [@stumpylog](https://github.com/stumpylog) ([#5740](https://github.com/paperless-ngx/paperless-ngx/pull/5740))
### Dependencies
- Chore(deps-dev): Bump the development group with 2 updates [@dependabot](https://github.com/dependabot) ([#5737](https://github.com/paperless-ngx/paperless-ngx/pull/5737))
- Chore(deps): Bump the django group with 1 update [@dependabot](https://github.com/dependabot) ([#5739](https://github.com/paperless-ngx/paperless-ngx/pull/5739))
### All App Changes
<details>
<summary>3 changes</summary>
- Chore(deps-dev): Bump the development group with 2 updates [@dependabot](https://github.com/dependabot) ([#5737](https://github.com/paperless-ngx/paperless-ngx/pull/5737))
- Chore(deps): Bump the django group with 1 update [@dependabot](https://github.com/dependabot) ([#5739](https://github.com/paperless-ngx/paperless-ngx/pull/5739))
- Fix: Splitting on ASN barcodes even if not enabled [@stumpylog](https://github.com/stumpylog) ([#5740](https://github.com/paperless-ngx/paperless-ngx/pull/5740))
</details>
## paperless-ngx 2.5.0
### Breaking Changes
- Enhancement: bulk delete objects [@shamoon](https://github.com/shamoon) ([#5688](https://github.com/paperless-ngx/paperless-ngx/pull/5688))
### Notable Changes
- Feature: OIDC \& social authentication [@mpflanzer](https://github.com/mpflanzer) ([#5190](https://github.com/paperless-ngx/paperless-ngx/pull/5190))
### Features
- Enhancement: confirm buttons [@shamoon](https://github.com/shamoon) ([#5680](https://github.com/paperless-ngx/paperless-ngx/pull/5680))
- Enhancement: bulk delete objects [@shamoon](https://github.com/shamoon) ([#5688](https://github.com/paperless-ngx/paperless-ngx/pull/5688))
- Feature: allow create objects from bulk edit [@shamoon](https://github.com/shamoon) ([#5667](https://github.com/paperless-ngx/paperless-ngx/pull/5667))
- Feature: Allow tagging by putting barcodes on documents [@pkrahmer](https://github.com/pkrahmer) ([#5580](https://github.com/paperless-ngx/paperless-ngx/pull/5580))
- Feature: Cache metadata and suggestions in Redis [@stumpylog](https://github.com/stumpylog) ([#5638](https://github.com/paperless-ngx/paperless-ngx/pull/5638))
- Feature: Japanese translation [@shamoon](https://github.com/shamoon) ([#5641](https://github.com/paperless-ngx/paperless-ngx/pull/5641))
- Feature: option for auto-remove inbox tags on save [@shamoon](https://github.com/shamoon) ([#5562](https://github.com/paperless-ngx/paperless-ngx/pull/5562))
- Enhancement: allow paperless to run in read-only filesystem [@hegerdes](https://github.com/hegerdes) ([#5596](https://github.com/paperless-ngx/paperless-ngx/pull/5596))
- Enhancement: mergeable bulk edit permissions [@shamoon](https://github.com/shamoon) ([#5508](https://github.com/paperless-ngx/paperless-ngx/pull/5508))
- Enhancement: re-implement remote user auth for unsafe API requests as opt-in [@shamoon](https://github.com/shamoon) ([#5561](https://github.com/paperless-ngx/paperless-ngx/pull/5561))
- Enhancement: Respect PDF cropbox for thumbnail generation [@henningBunk](https://github.com/henningBunk) ([#5531](https://github.com/paperless-ngx/paperless-ngx/pull/5531))
### Bug Fixes
- Fix: Test metadata items for Unicode issues [@stumpylog](https://github.com/stumpylog) ([#5707](https://github.com/paperless-ngx/paperless-ngx/pull/5707))
- Change: try to show preview even if metadata fails [@shamoon](https://github.com/shamoon) ([#5706](https://github.com/paperless-ngx/paperless-ngx/pull/5706))
- Fix: only check workflow trigger source if not empty [@shamoon](https://github.com/shamoon) ([#5701](https://github.com/paperless-ngx/paperless-ngx/pull/5701))
- Fix: frontend validation of number fields fails upon save [@shamoon](https://github.com/shamoon) ([#5646](https://github.com/paperless-ngx/paperless-ngx/pull/5646))
- Fix: Explicit validation of custom field name unique constraint [@shamoon](https://github.com/shamoon) ([#5647](https://github.com/paperless-ngx/paperless-ngx/pull/5647))
- Fix: Don't attempt to retrieve object types user doesn't have permissions to [@shamoon](https://github.com/shamoon) ([#5612](https://github.com/paperless-ngx/paperless-ngx/pull/5612))
### Documentation
- Documentation: add detail about consumer polling behavior [@silmaril42](https://github.com/silmaril42) ([#5674](https://github.com/paperless-ngx/paperless-ngx/pull/5674))
- Paperless-ngx Demo: new and improved [@shamoon](https://github.com/shamoon) ([#5639](https://github.com/paperless-ngx/paperless-ngx/pull/5639))
- Documentation: Add docs about missing timezones in MySQL/MariaDB [@Programie](https://github.com/Programie) ([#5583](https://github.com/paperless-ngx/paperless-ngx/pull/5583))
### Maintenance
- Chore(deps): Bump the actions group with 1 update [@dependabot](https://github.com/dependabot) ([#5629](https://github.com/paperless-ngx/paperless-ngx/pull/5629))
- Chore(deps): Bump the actions group with 1 update [@dependabot](https://github.com/dependabot) ([#5597](https://github.com/paperless-ngx/paperless-ngx/pull/5597))
### Dependencies
<details>
<summary>9 changes</summary>
- Chore: Backend dependencies update [@stumpylog](https://github.com/stumpylog) ([#5676](https://github.com/paperless-ngx/paperless-ngx/pull/5676))
- Chore(deps-dev): Bump [@<!---->playwright/test from 1.40.1 to 1.41.2 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.40.1 to 1.41.2 in /src-ui @dependabot) ([#5634](https://github.com/paperless-ngx/paperless-ngx/pull/5634))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 19 updates [@dependabot](https://github.com/dependabot) ([#5630](https://github.com/paperless-ngx/paperless-ngx/pull/5630))
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#5631](https://github.com/paperless-ngx/paperless-ngx/pull/5631))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#5632](https://github.com/paperless-ngx/paperless-ngx/pull/5632))
- Chore(deps): Bump zone.js from 0.14.2 to 0.14.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#5633](https://github.com/paperless-ngx/paperless-ngx/pull/5633))
- Chore(deps-dev): Bump [@<!---->types/node from 20.10.6 to 20.11.16 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.10.6 to 20.11.16 in /src-ui @dependabot) ([#5635](https://github.com/paperless-ngx/paperless-ngx/pull/5635))
- Chore(deps): Bump the actions group with 1 update [@dependabot](https://github.com/dependabot) ([#5629](https://github.com/paperless-ngx/paperless-ngx/pull/5629))
- Chore(deps): Bump the actions group with 1 update [@dependabot](https://github.com/dependabot) ([#5597](https://github.com/paperless-ngx/paperless-ngx/pull/5597))
</details>
### All App Changes
<details>
<summary>28 changes</summary>
- Chore: Ensure all creations of directories create the parents too [@stumpylog](https://github.com/stumpylog) ([#5711](https://github.com/paperless-ngx/paperless-ngx/pull/5711))
- Fix: Test metadata items for Unicode issues [@stumpylog](https://github.com/stumpylog) ([#5707](https://github.com/paperless-ngx/paperless-ngx/pull/5707))
- Change: try to show preview even if metadata fails [@shamoon](https://github.com/shamoon) ([#5706](https://github.com/paperless-ngx/paperless-ngx/pull/5706))
- Fix: only check workflow trigger source if not empty [@shamoon](https://github.com/shamoon) ([#5701](https://github.com/paperless-ngx/paperless-ngx/pull/5701))
- Enhancement: confirm buttons [@shamoon](https://github.com/shamoon) ([#5680](https://github.com/paperless-ngx/paperless-ngx/pull/5680))
- Enhancement: bulk delete objects [@shamoon](https://github.com/shamoon) ([#5688](https://github.com/paperless-ngx/paperless-ngx/pull/5688))
- Chore: Backend dependencies update [@stumpylog](https://github.com/stumpylog) ([#5676](https://github.com/paperless-ngx/paperless-ngx/pull/5676))
- Feature: OIDC \& social authentication [@mpflanzer](https://github.com/mpflanzer) ([#5190](https://github.com/paperless-ngx/paperless-ngx/pull/5190))
- Chore: Don't write Python bytecode in the Docker image [@stumpylog](https://github.com/stumpylog) ([#5677](https://github.com/paperless-ngx/paperless-ngx/pull/5677))
- Feature: allow create objects from bulk edit [@shamoon](https://github.com/shamoon) ([#5667](https://github.com/paperless-ngx/paperless-ngx/pull/5667))
- Chore: Use memory cache backend in debug mode [@shamoon](https://github.com/shamoon) ([#5666](https://github.com/paperless-ngx/paperless-ngx/pull/5666))
- Chore: Adds additional rules for Ruff linter [@stumpylog](https://github.com/stumpylog) ([#5660](https://github.com/paperless-ngx/paperless-ngx/pull/5660))
- Feature: Allow tagging by putting barcodes on documents [@pkrahmer](https://github.com/pkrahmer) ([#5580](https://github.com/paperless-ngx/paperless-ngx/pull/5580))
- Feature: Cache metadata and suggestions in Redis [@stumpylog](https://github.com/stumpylog) ([#5638](https://github.com/paperless-ngx/paperless-ngx/pull/5638))
- Fix: frontend validation of number fields fails upon save [@shamoon](https://github.com/shamoon) ([#5646](https://github.com/paperless-ngx/paperless-ngx/pull/5646))
- Fix: Explicit validation of custom field name unique constraint [@shamoon](https://github.com/shamoon) ([#5647](https://github.com/paperless-ngx/paperless-ngx/pull/5647))
- Feature: Japanese translation [@shamoon](https://github.com/shamoon) ([#5641](https://github.com/paperless-ngx/paperless-ngx/pull/5641))
- Chore(deps-dev): Bump [@<!---->playwright/test from 1.40.1 to 1.41.2 in /src-ui @dependabot](https://github.com/<!---->playwright/test from 1.40.1 to 1.41.2 in /src-ui @dependabot) ([#5634](https://github.com/paperless-ngx/paperless-ngx/pull/5634))
- Feature: option for auto-remove inbox tags on save [@shamoon](https://github.com/shamoon) ([#5562](https://github.com/paperless-ngx/paperless-ngx/pull/5562))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 19 updates [@dependabot](https://github.com/dependabot) ([#5630](https://github.com/paperless-ngx/paperless-ngx/pull/5630))
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#5631](https://github.com/paperless-ngx/paperless-ngx/pull/5631))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#5632](https://github.com/paperless-ngx/paperless-ngx/pull/5632))
- Chore(deps): Bump zone.js from 0.14.2 to 0.14.3 in /src-ui [@dependabot](https://github.com/dependabot) ([#5633](https://github.com/paperless-ngx/paperless-ngx/pull/5633))
- Chore(deps-dev): Bump [@<!---->types/node from 20.10.6 to 20.11.16 in /src-ui @dependabot](https://github.com/<!---->types/node from 20.10.6 to 20.11.16 in /src-ui @dependabot) ([#5635](https://github.com/paperless-ngx/paperless-ngx/pull/5635))
- Enhancement: mergeable bulk edit permissions [@shamoon](https://github.com/shamoon) ([#5508](https://github.com/paperless-ngx/paperless-ngx/pull/5508))
- Enhancement: re-implement remote user auth for unsafe API requests as opt-in [@shamoon](https://github.com/shamoon) ([#5561](https://github.com/paperless-ngx/paperless-ngx/pull/5561))
- Enhancement: Respect PDF cropbox for thumbnail generation [@henningBunk](https://github.com/henningBunk) ([#5531](https://github.com/paperless-ngx/paperless-ngx/pull/5531))
- Fix: Don't attempt to retrieve object types user doesn't have permissions to [@shamoon](https://github.com/shamoon) ([#5612](https://github.com/paperless-ngx/paperless-ngx/pull/5612))
</details>
## paperless-ngx 2.4.3
### Bug Fixes
- Fix: Ensure the scratch directory exists before consuming via the folder [@stumpylog](https://github.com/stumpylog) ([#5579](https://github.com/paperless-ngx/paperless-ngx/pull/5579))
### All App Changes
- Fix: Ensure the scratch directory exists before consuming via the folder [@stumpylog](https://github.com/stumpylog) ([#5579](https://github.com/paperless-ngx/paperless-ngx/pull/5579))
## paperless-ngx 2.4.2
### Bug Fixes
- Fix: improve one of the date matching regexes [@shamoon](https://github.com/shamoon) ([#5540](https://github.com/paperless-ngx/paperless-ngx/pull/5540))
- Fix: tweak doc detail component behavior while awaiting metadata [@shamoon](https://github.com/shamoon) ([#5546](https://github.com/paperless-ngx/paperless-ngx/pull/5546))
### All App Changes
<details>
<summary>2 changes</summary>
- Fix: improve one of the date matching regexes [@shamoon](https://github.com/shamoon) ([#5540](https://github.com/paperless-ngx/paperless-ngx/pull/5540))
- Fix: tweak doc detail component behavior while awaiting metadata [@shamoon](https://github.com/shamoon) ([#5546](https://github.com/paperless-ngx/paperless-ngx/pull/5546))
</details>
## paperless-ngx 2.4.1
### Breaking Changes
- Change: merge workflow permissions assignments instead of overwrite [@shamoon](https://github.com/shamoon) ([#5496](https://github.com/paperless-ngx/paperless-ngx/pull/5496))
### Bug Fixes
- Fix: Minor frontend things in 2.4.0 [@shamoon](https://github.com/shamoon) ([#5514](https://github.com/paperless-ngx/paperless-ngx/pull/5514))
- Fix: install script fails on alpine linux [@shamoon](https://github.com/shamoon) ([#5520](https://github.com/paperless-ngx/paperless-ngx/pull/5520))
- Fix: enforce permissions for app config [@shamoon](https://github.com/shamoon) ([#5516](https://github.com/paperless-ngx/paperless-ngx/pull/5516))
- Fix: render images not converted to pdf, refactor doc detail rendering [@shamoon](https://github.com/shamoon) ([#5475](https://github.com/paperless-ngx/paperless-ngx/pull/5475))
- Fix: Dont parse numbers with exponent as integer [@shamoon](https://github.com/shamoon) ([#5457](https://github.com/paperless-ngx/paperless-ngx/pull/5457))
### Maintenance
- Chore: Build fix- branches [@shamoon](https://github.com/shamoon) ([#5501](https://github.com/paperless-ngx/paperless-ngx/pull/5501))
### Dependencies
- Chore(deps-dev): Bump the development group with 1 update [@dependabot](https://github.com/dependabot) ([#5503](https://github.com/paperless-ngx/paperless-ngx/pull/5503))
### All App Changes
<details>
<summary>7 changes</summary>
- Revert "Enhancement: support remote user auth directly against API (DRF)" @shamoon ([#5534](https://github.com/paperless-ngx/paperless-ngx/pull/5534))
- Fix: Minor frontend things in 2.4.0 [@shamoon](https://github.com/shamoon) ([#5514](https://github.com/paperless-ngx/paperless-ngx/pull/5514))
- Fix: enforce permissions for app config [@shamoon](https://github.com/shamoon) ([#5516](https://github.com/paperless-ngx/paperless-ngx/pull/5516))
- Change: merge workflow permissions assignments instead of overwrite [@shamoon](https://github.com/shamoon) ([#5496](https://github.com/paperless-ngx/paperless-ngx/pull/5496))
- Chore(deps-dev): Bump the development group with 1 update [@dependabot](https://github.com/dependabot) ([#5503](https://github.com/paperless-ngx/paperless-ngx/pull/5503))
- Fix: render images not converted to pdf, refactor doc detail rendering [@shamoon](https://github.com/shamoon) ([#5475](https://github.com/paperless-ngx/paperless-ngx/pull/5475))
- Fix: Dont parse numbers with exponent as integer [@shamoon](https://github.com/shamoon) ([#5457](https://github.com/paperless-ngx/paperless-ngx/pull/5457))
</details>
## paperless-ngx 2.4.0
### Features
- Enhancement: support remote user auth directly against API (DRF) [@shamoon](https://github.com/shamoon) ([#5386](https://github.com/paperless-ngx/paperless-ngx/pull/5386))
- Feature: Add additional caching support to suggestions and metadata [@stumpylog](https://github.com/stumpylog) ([#5414](https://github.com/paperless-ngx/paperless-ngx/pull/5414))
- Feature: help tooltips [@shamoon](https://github.com/shamoon) ([#5383](https://github.com/paperless-ngx/paperless-ngx/pull/5383))
- Enhancement: warn when outdated doc detected [@shamoon](https://github.com/shamoon) ([#5372](https://github.com/paperless-ngx/paperless-ngx/pull/5372))
- Feature: app branding [@shamoon](https://github.com/shamoon) ([#5357](https://github.com/paperless-ngx/paperless-ngx/pull/5357))
### Bug Fixes
- Fix: doc link removal when has never been assigned [@shamoon](https://github.com/shamoon) ([#5451](https://github.com/paperless-ngx/paperless-ngx/pull/5451))
- Fix: dont lose permissions ui if owner changed from [@shamoon](https://github.com/shamoon) ([#5433](https://github.com/paperless-ngx/paperless-ngx/pull/5433))
- Fix: Getting next ASN when no documents have an ASN [@stumpylog](https://github.com/stumpylog) ([#5431](https://github.com/paperless-ngx/paperless-ngx/pull/5431))
- Fix: signin username floating label [@shamoon](https://github.com/shamoon) ([#5424](https://github.com/paperless-ngx/paperless-ngx/pull/5424))
- Fix: shared by me filter with multiple users / groups in postgres [@shamoon](https://github.com/shamoon) ([#5396](https://github.com/paperless-ngx/paperless-ngx/pull/5396))
- Fix: Catch new warning when loading the classifier [@stumpylog](https://github.com/stumpylog) ([#5395](https://github.com/paperless-ngx/paperless-ngx/pull/5395))
- Fix: doc detail component fixes [@shamoon](https://github.com/shamoon) ([#5373](https://github.com/paperless-ngx/paperless-ngx/pull/5373))
### Maintenance
- Chore: better bootstrap icons [@shamoon](https://github.com/shamoon) ([#5403](https://github.com/paperless-ngx/paperless-ngx/pull/5403))
- Chore: Close outdated support / general discussions [@shamoon](https://github.com/shamoon) ([#5443](https://github.com/paperless-ngx/paperless-ngx/pull/5443))
### Dependencies
- Chore(deps): Bump the small-changes group with 2 updates [@dependabot](https://github.com/dependabot) ([#5413](https://github.com/paperless-ngx/paperless-ngx/pull/5413))
- Chore(deps-dev): Bump the development group with 2 updates [@dependabot](https://github.com/dependabot) ([#5412](https://github.com/paperless-ngx/paperless-ngx/pull/5412))
- Chore(deps-dev): Bump jinja2 from 3.1.2 to 3.1.3 [@dependabot](https://github.com/dependabot) ([#5352](https://github.com/paperless-ngx/paperless-ngx/pull/5352))
### All App Changes
<details>
<summary>16 changes</summary>
- Fix: doc link removal when has never been assigned [@shamoon](https://github.com/shamoon) ([#5451](https://github.com/paperless-ngx/paperless-ngx/pull/5451))
- Chore: better bootstrap icons [@shamoon](https://github.com/shamoon) ([#5403](https://github.com/paperless-ngx/paperless-ngx/pull/5403))
- Fix: dont lose permissions ui if owner changed from [@shamoon](https://github.com/shamoon) ([#5433](https://github.com/paperless-ngx/paperless-ngx/pull/5433))
- Enhancement: support remote user auth directly against API (DRF) [@shamoon](https://github.com/shamoon) ([#5386](https://github.com/paperless-ngx/paperless-ngx/pull/5386))
- Fix: Getting next ASN when no documents have an ASN [@stumpylog](https://github.com/stumpylog) ([#5431](https://github.com/paperless-ngx/paperless-ngx/pull/5431))
- Feature: Add additional caching support to suggestions and metadata [@stumpylog](https://github.com/stumpylog) ([#5414](https://github.com/paperless-ngx/paperless-ngx/pull/5414))
- Chore(deps): Bump the small-changes group with 2 updates [@dependabot](https://github.com/dependabot) ([#5413](https://github.com/paperless-ngx/paperless-ngx/pull/5413))
- Chore(deps-dev): Bump the development group with 2 updates [@dependabot](https://github.com/dependabot) ([#5412](https://github.com/paperless-ngx/paperless-ngx/pull/5412))
- Fix: signin username floating label [@shamoon](https://github.com/shamoon) ([#5424](https://github.com/paperless-ngx/paperless-ngx/pull/5424))
- Feature: help tooltips [@shamoon](https://github.com/shamoon) ([#5383](https://github.com/paperless-ngx/paperless-ngx/pull/5383))
- Enhancement / QoL: show selected tasks count [@shamoon](https://github.com/shamoon) ([#5379](https://github.com/paperless-ngx/paperless-ngx/pull/5379))
- Fix: shared by me filter with multiple users / groups in postgres [@shamoon](https://github.com/shamoon) ([#5396](https://github.com/paperless-ngx/paperless-ngx/pull/5396))
- Fix: doc detail component fixes [@shamoon](https://github.com/shamoon) ([#5373](https://github.com/paperless-ngx/paperless-ngx/pull/5373))
- Enhancement: warn when outdated doc detected [@shamoon](https://github.com/shamoon) ([#5372](https://github.com/paperless-ngx/paperless-ngx/pull/5372))
- Feature: app branding [@shamoon](https://github.com/shamoon) ([#5357](https://github.com/paperless-ngx/paperless-ngx/pull/5357))
- Chore: Initial refactor of consume task [@stumpylog](https://github.com/stumpylog) ([#5367](https://github.com/paperless-ngx/paperless-ngx/pull/5367))
</details>
## paperless-ngx 2.3.3
### Enhancements

View File

@@ -34,6 +34,8 @@ matcher.
`redis://<username>:<password>@<host>:<port>`
- With the requirepass option PAPERLESS_REDIS =
`redis://:<password>@<host>:<port>`
- To include the redis database index PAPERLESS_REDIS =
`redis://<username>:<password>@<host>:<port>/<DBIndex>`
[More information on securing your Redis
Instance](https://redis.io/docs/getting-started/#securing-redis).
@@ -262,7 +264,7 @@ directory. See [File name handling](advanced_usage.md#file-name-handling) for de
: Tells paperless to replace placeholders in
`PAPERLESS_FILENAME_FORMAT` that would resolve to
'none' to be omitted from the resulting filename. This also holds
true for directory names. See [File name handling](advanced_usage.md#file-name-handling) for
true for directory names. See [File name handling](advanced_usage.md#empty-placeholders) for
details.
Defaults to `false` which disables this feature.
@@ -452,19 +454,32 @@ applications.
This will allow authentication by simply adding a
`Remote-User: <username>` header to a request. Use with care! You
especially *must: ensure that any such header is not passed from
your proxy server to paperless.
especially *must* ensure that any such header is not passed from
external requests to your reverse-proxy to paperless (that would
effectively bypass all authentication).
If you're exposing paperless to the internet directly, do not use
this.
If you're exposing paperless to the internet directly (i.e.
without a reverse proxy), do not use this.
Also see the warning [in the official documentation](https://docs.djangoproject.com/en/4.1/howto/auth-remote-user/#configuration).
Defaults to "false" which disables this feature.
#### [`PAPERLESS_ENABLE_HTTP_REMOTE_USER_API=<bool>`](#PAPERLESS_ENABLE_HTTP_REMOTE_USER_API) {#PAPERLESS_ENABLE_HTTP_REMOTE_USER_API}
: Allows authentication via HTTP_REMOTE_USER directly against the API
!!! warning
See the warning above about securing your installation when using remote user header authentication. This setting is separate from
`PAPERLESS_ENABLE_HTTP_REMOTE_USER` to avoid introducing a security vulnerability to existing reverse proxy setups. As above,
ensure that your reverse proxy does not simply pass the `Remote-User` header from the internet to paperless.
Defaults to "false" which disables this feature.
#### [`PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME=<str>`](#PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME) {#PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME}
: If "PAPERLESS_ENABLE_HTTP_REMOTE_USER" is enabled, this
: If "PAPERLESS_ENABLE_HTTP_REMOTE_USER" or `PAPERLESS_ENABLE_HTTP_REMOTE_USER_API` are enabled, this
property allows to customize the name of the HTTP header from which
the authenticated username is extracted. Values are in terms of
[HttpRequest.META](https://docs.djangoproject.com/en/4.1/ref/request-response/#django.http.HttpRequest.META).
@@ -521,6 +536,59 @@ This is for use with self-signed certificates against local IMAP servers.
Settings this value has security implications for the security of your email.
Understand what it does and be sure you need to before setting.
#### [`PAPERLESS_SOCIALACCOUNT_PROVIDERS=<json>`](#PAPERLESS_SOCIALACCOUNT_PROVIDERS) {#PAPERLESS_SOCIALACCOUNT_PROVIDERS}
: This variable is used to setup login and signup via social account providers which are compatible with django-allauth.
See the corresponding [django-allauth documentation](https://docs.allauth.org/en/latest/socialaccount/providers/index.html)
for a list of provider configurations. You will also need to include the relevant Django 'application' inside the
[PAPERLESS_APPS](#PAPERLESS_APPS) setting to activate that specific authentication provider (e.g. `allauth.socialaccount.providers.openid_connect` for the [OIDC Connect provider](https://docs.allauth.org/en/latest/socialaccount/providers/openid_connect.html)).
Defaults to None, which does not enable any third party authentication systems.
#### [`PAPERLESS_SOCIAL_AUTO_SIGNUP=<bool>`](#PAPERLESS_SOCIAL_AUTO_SIGNUP) {#PAPERLESS_SOCIAL_AUTO_SIGNUP}
: Attempt to signup the user using retrieved email, username etc from the third party authentication
system. See the corresponding
[django-allauth documentation](https://docs.allauth.org/en/latest/socialaccount/configuration.html)
Defaults to False
#### [`PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS=<bool>`](#PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS) {#PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS}
: Allow users to signup for a new Paperless-ngx account using any setup third party authentication systems.
Defaults to True
#### [`PAPERLESS_ACCOUNT_ALLOW_SIGNUPS=<bool>`](#PAPERLESS_ACCOUNT_ALLOW_SIGNUPS) {#PAPERLESS_ACCOUNT_ALLOW_SIGNUPS}
: Allow users to signup for a new Paperless-ngx account.
Defaults to False
#### [`PAPERLESS_ACCOUNT_DEFAULT_HTTP_PROTOCOL=<string>`](#PAPERLESS_ACCOUNT_DEFAULT_HTTP_PROTOCOL) {#PAPERLESS_ACCOUNT_DEFAULT_HTTP_PROTOCOL}
: The protocol used when generating URLs, e.g. login callback URLs. See the corresponding
[django-allauth documentation](https://docs.allauth.org/en/latest/account/configuration.html)
Defaults to 'https'
#### [`PAPERLESS_ACCOUNT_EMAIL_VERIFICATION=<string>`](#PAPERLESS_ACCOUNT_EMAIL_VERIFICATION) {#PAPERLESS_ACCOUNT_EMAIL_VERIFICATION}
: Determines whether email addresses are verified during signup (as performed by Django allauth). See the relevant
[paperless settings](#PAPERLESS_EMAIL_HOST) and [the allauth docs](https://docs.allauth.org/en/latest/account/configuration.html)
Defaults to 'optional'
!!! note
If you do not have a working email server set up you should set this to 'none'.
#### [`PAPERLESS_DISABLE_REGULAR_LOGIN=<bool>`](#PAPERLESS_DISABLE_REGULAR_LOGIN) {#PAPERLESS_DISABLE_REGULAR_LOGIN}
: Disables the regular frontend username / password login, i.e. once you have setup SSO. Note that this setting does not disable the Django admin login. To prevent logins directly to Django, consider blocking `/admin/` in your [web server or reverse proxy configuration](https://github.com/paperless-ngx/paperless-ngx/wiki/Using-a-Reverse-Proxy-with-Paperless-ngx).
Defaults to False
## OCR settings {#ocr}
Paperless uses [OCRmyPDF](https://ocrmypdf.readthedocs.io/en/latest/)
@@ -698,6 +766,8 @@ but could result in missing text content.
If unset, will default to the value determined by
[Pillow](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.MAX_IMAGE_PIXELS).
Setting this value to 0 will entirely disable the limit. See the below warning.
!!! note
Increasing this limit could cause Paperless to consume additional
@@ -707,7 +777,7 @@ but could result in missing text content.
!!! warning
The limit is intended to prevent malicious files from consuming
system resources and causing crashes and other errors. Only increase
system resources and causing crashes and other errors. Only change
this value if you are certain your documents are not malicious and
you need the text which was not OCRed
@@ -891,6 +961,28 @@ documents.
Default is none, which disables the temporary directory.
#### [`PAPERLESS_APPS=<string>`](#PAPERLESS_APPS) {#PAPERLESS_APPS}
: A comma-separated list of Django apps to be included in Django's
[`INSTALLED_APPS`](https://docs.djangoproject.com/en/5.0/ref/applications/). This setting should
be used with caution!
Defaults to None, which does not add any additional apps.
#### [`PAPERLESS_MAX_IMAGE_PIXELS=<number>`](#PAPERLESS_MAX_IMAGE_PIXELS) {#PAPERLESS_MAX_IMAGE_PIXELS}
: Configures the maximum size of an image PIL will allow to load without warning or error.
: If unset, will default to the value determined by
[Pillow](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.MAX_IMAGE_PIXELS).
Defaults to None, which does change the limit
!!! warning
This limit is designed to prevent denial of service from malicious files.
It should only be raised or disabled in certain circumstances and with great care.
## Document Consumption {#consume_config}
#### [`PAPERLESS_CONSUMER_DELETE_DUPLICATES=<bool>`](#PAPERLESS_CONSUMER_DELETE_DUPLICATES) {#PAPERLESS_CONSUMER_DELETE_DUPLICATES}
@@ -938,7 +1030,7 @@ or hidden folders some tools use to store data.
`._foo.pdf` and `._bar/foo.pdf`
Defaults to
`[".DS_STORE/*", "._*", ".stfolder/*", ".stversions/*", ".localized/*", "desktop.ini", "@eaDir/*"]`.
`[".DS_Store", ".DS_STORE", "._*", ".stfolder/*", ".stversions/*", ".localized/*", "desktop.ini", "@eaDir/*", "Thumbs.db"]`.
#### [`PAPERLESS_CONSUMER_BARCODE_SCANNER=<string>`](#PAPERLESS_CONSUMER_BARCODE_SCANNER) {#PAPERLESS_CONSUMER_BARCODE_SCANNER}
@@ -1049,8 +1141,10 @@ system changes with `inotify`.
#### [`PAPERLESS_CONSUMER_POLLING_RETRY_COUNT=<num>`](#PAPERLESS_CONSUMER_POLLING_RETRY_COUNT) {#PAPERLESS_CONSUMER_POLLING_RETRY_COUNT}
: If consumer polling is enabled, sets the number of times paperless
will check for a file to remain unmodified.
: If consumer polling is enabled, sets the maximum number of times
paperless will check for a file to remain unmodified. If a file's
modification time and size are identical for two consecutive checks, it
will be consumed.
Defaults to 5.
@@ -1159,6 +1253,55 @@ combination with PAPERLESS_CONSUMER_BARCODE_UPSCALE bigger than 1.0.
Defaults to "300"
#### [`PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE=<bool>`](#PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE) {#PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE}
: Enables the detection of barcodes in the scanned document and
assigns or creates tags if a properly formatted barcode is detected.
The barcode must match one of the (configurable) regular expressions.
If the barcode text contains ',' (comma), it is split into multiple
barcodes which are individually processed for tagging.
Matching is case insensitive.
Defaults to false.
#### [`PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING=<json dict>`](#PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING) {#PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING}
: Defines a dictionary of filter regex and substitute expressions.
Syntax: {"<regex>": "<substitute>" [,...]]}
A barcode is considered for tagging if the barcode text matches
at least one of the provided <regex> pattern.
If a match is found, the <substitute> rule is applied. This allows very
versatile reformatting and mapping of barcode pattern to tag values.
If a tag is not found it will be created.
Defaults to:
{"TAG:(.*)": "\\g<1>"} which defines
- a regex TAG:(.*) which includes barcodes beginning with TAG:
followed by any text that gets stored into match group #1 and
- a substitute \\g<1> that replaces the original barcode text
by the content in match group #1.
Consequently, the tag is the barcode text without its TAG: prefix.
More examples:
{"ASN12.*": "JOHN", "ASN13.*": "SMITH"} for example maps
- ASN12nnnn barcodes to the tag JOHN and
- ASN13nnnn barcodes to the tag SMITH.
{"T-J": "JOHN", "T-S": "SMITH", "T-D": "DOE"} directly maps
- T-J barcodes to the tag JOHN,
- T-S barcodes to the tag SMITH and
- T-D barcodes to the tag DOE.
Please refer to the Python regex documentation for more information.
## Audit Trail
#### [`PAPERLESS_AUDIT_LOG_ENABLED=<bool>`](#PAPERLESS_AUDIT_LOG_ENABLED) {#PAPERLESS_AUDIT_LOG_ENABLED}
@@ -1329,6 +1472,12 @@ started by the container.
You can read more about this in the [advanced documentation](advanced_usage.md#celery-monitoring).
#### [`PAPERLESS_SUPERVISORD_WORKING_DIR=<defined>`](#PAPERLESS_SUPERVISORD_WORKING_DIR) {#PAPERLESS_SUPERVISORD_WORKING_DIR}
: If this environment variable is defined, the `supervisord.log` and `supervisord.pid` file will be created under the specified path in `PAPERLESS_SUPERVISORD_WORKING_DIR`. Setting `PAPERLESS_SUPERVISORD_WORKING_DIR=/tmp` and `PYTHONPYCACHEPREFIX=/tmp/pycache` would allow paperless to work on a read-only filesystem.
Please take note that the `PAPERLESS_DATA_DIR` and `PAPERLESS_MEDIA_ROOT` paths still have to be writable, just like the `PAPERLESS_SUPERVISORD_WORKING_DIR`. The can be archived by using bind or volume mounts. Only works in the container is run as user *paperless*
## Frontend Settings
#### [`PAPERLESS_APP_TITLE=<bool>`](#PAPERLESS_APP_TITLE) {#PAPERLESS_APP_TITLE}

View File

@@ -8,6 +8,13 @@ physical documents into a searchable online archive so you can keep, well, _less
[Get started](setup.md){ .md-button .md-button--primary .index-callout }
[Demo](https://demo.paperless-ngx.com){ .md-button .md-button--secondary target=\_blank }
<div style="display: flex; justify-content: end; margin-top: -1.5rem;">
<a href="https://m.do.co/c/8d70b916d462" target="_blank">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/PoweredByDO/DO_Powered_by_Badge_white.svg#only-dark" class="no-lightbox" width="150px">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/PoweredByDO/DO_Powered_by_Badge_black.svg#only-light" class="no-lightbox" width="150px">
</a>
</div>
</div>
<div class="grid-right" markdown>
![image](assets/screenshots/documents-smallcards.png#only-light){.index-screenshot}

View File

@@ -253,7 +253,8 @@ permissions can be granted to limit access to certain parts of the UI (and corre
### Password reset
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)
[`PAPERLESS_EMAIL_HOST`](configuration.md#PAPERLESS_EMAIL_HOST). If your installation does not have
[`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) set, the reset link included in emails will use the server host.
## Workflows
@@ -328,14 +329,21 @@ Workflows allow you to filter by:
### Workflow Actions
There is currently one type of workflow action, "Assignment", which can assign:
There are currently two types of workflow actions, "Assignment", which can assign:
- Title, see [title placeholders](usage.md#title-placeholders) below
- Tags, correspondent, document types
- Tags, correspondent, document type and storage path
- Document owner
- View and / or edit permissions to users or groups
- Custom fields. Note that no value for the field will be set
and "Removal" actions, which can remove either all of or specific sets of the following:
- Tags, correspondents, document types or storage paths
- Document owner
- View and / or edit permissions
- Custom fields
#### Title placeholders
Workflow titles can include placeholders but the available options differ depending on the type of
@@ -406,7 +414,7 @@ The following custom field types are supported:
- `URL`: a valid url
- `Integer`: integer number e.g. 12
- `Number`: float number e.g. 12.3456
- `Monetary`: float number with exactly two decimals, e.g. 12.30
- `Monetary`: [ISO 4217 currency code](https://en.wikipedia.org/wiki/ISO_4217#List_of_ISO_4217_currency_codes) and a number with exactly two decimals, e.g. USD12.30
- `Document Link`: reference(s) to other document(s) displayed as links, automatically creates a symmetrical link in reverse
## Share Links

View File

@@ -56,8 +56,8 @@ if ! command -v docker &> /dev/null ; then
exit 1
fi
if ! command -v docker compose &> /dev/null ; then
echo "docker compose executable not found. Is docker compose installed?"
if ! docker compose &> /dev/null ; then
echo "docker compose plugin not found. Is docker compose installed?"
exit 1
fi
@@ -315,7 +315,7 @@ fi
wget "https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.$DOCKER_COMPOSE_VERSION.yml" -O docker-compose.yml
wget "https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/.env" -O .env
SECRET_KEY=$(LC_ALL=C tr -dc 'a-zA-Z0-9!"#$%&'\''()*+,-./:;<=>?@[\]^_`{|}~' < /dev/urandom | head --bytes 64)
SECRET_KEY=$(LC_ALL=C tr -dc 'a-zA-Z0-9!#$%&()*+,-./:;<=>?@[\]^_`{|}~' < /dev/urandom | dd bs=1 count=64 2>/dev/null)
DEFAULT_LANGUAGES=("deu eng fra ita spa")

View File

@@ -73,4 +73,6 @@ extra:
link: https://matrix.to/#/#paperless:matrix.org
plugins:
- search
- glightbox
- glightbox:
skip_classes:
- no-lightbox

View File

@@ -68,6 +68,8 @@
#PAPERLESS_CONSUMER_BARCODE_STRING=PATCHT
#PAPERLESS_CONSUMER_BARCODE_UPSCALE=0.0
#PAPERLESS_CONSUMER_BARCODE_DPI=300
#PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE=false
#PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING={"TAG:(.*)": "\\g<1>"}
#PAPERLESS_CONSUMER_ENABLE_COLLATE_DOUBLE_SIDED=false
#PAPERLESS_CONSUMER_COLLATE_DOUBLE_SIDED_SUBDIR_NAME=double-sided
#PAPERLESS_CONSUMER_COLLATE_DOUBLE_SIDED_TIFF_SUPPORT=false

View File

@@ -31,6 +31,7 @@
"fr-FR": "src/locale/messages.fr_FR.xlf",
"hu-HU": "src/locale/messages.hu_HU.xlf",
"it-IT": "src/locale/messages.it_IT.xlf",
"ja-JP": "src/locale/messages.ja_JP.xlf",
"lb-LU": "src/locale/messages.lb_LU.xlf",
"nl-NL": "src/locale/messages.nl_NL.xlf",
"no-NO": "src/locale/messages.no_NO.xlf",
@@ -76,7 +77,9 @@
"scripts": [],
"allowedCommonJsDependencies": [
"pdfjs-dist",
"pdfjs-dist/web/pdf_viewer"
"pdfjs-dist/web/pdf_viewer",
"filesize",
"file-saver"
],
"vendorChunk": true,
"extractLicenses": false,

File diff suppressed because it is too large Load Diff

5012
src-ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,57 +11,58 @@
},
"private": true,
"dependencies": {
"@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",
"@angular/cdk": "^17.2.1",
"@angular/common": "~17.2.3",
"@angular/compiler": "~17.2.3",
"@angular/core": "~17.2.3",
"@angular/forms": "~17.2.3",
"@angular/localize": "~17.2.3",
"@angular/platform-browser": "~17.2.3",
"@angular/platform-browser-dynamic": "~17.2.3",
"@angular/router": "~17.2.3",
"@ng-bootstrap/ng-bootstrap": "^16.0.0",
"@ng-select/ng-select": "^12.0.4",
"@ng-select/ng-select": "^12.0.7",
"@ngneat/dirty-check-forms": "^3.0.3",
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.2",
"bootstrap": "^5.3.3",
"file-saver": "^2.0.5",
"mime-names": "^1.0.0",
"ngx-bootstrap-icons": "^1.9.3",
"ngx-color": "^9.0.0",
"ngx-cookie-service": "^17.0.1",
"ngx-cookie-service": "^17.1.0",
"ngx-file-drop": "^16.0.0",
"ngx-ui-tour-ng-bootstrap": "^14.0.1",
"ngx-filesize": "^3.0.3",
"ngx-ui-tour-ng-bootstrap": "^14.0.2",
"pdfjs-dist": "^3.11.174",
"rxjs": "^7.8.1",
"tslib": "^2.6.2",
"uuid": "^9.0.1",
"zone.js": "^0.14.2"
"zone.js": "^0.14.4"
},
"devDependencies": {
"@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",
"@angular-builders/jest": "17.0.2",
"@angular-devkit/build-angular": "~17.2.2",
"@angular-eslint/builder": "17.2.1",
"@angular-eslint/eslint-plugin": "17.2.1",
"@angular-eslint/eslint-plugin-template": "17.2.1",
"@angular-eslint/schematics": "17.2.1",
"@angular-eslint/template-parser": "17.2.1",
"@angular/cli": "~17.2.2",
"@angular/compiler-cli": "~17.2.2",
"@playwright/test": "^1.42.0",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.24",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"concurrently": "^8.2.2",
"eslint": "^8.56.0",
"eslint": "^8.57.0",
"jest": "29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-preset-angular": "^13.1.4",
"jest-preset-angular": "^14.0.0",
"jest-websocket-mock": "^2.5.0",
"patch-package": "^8.0.0",
"ts-node": "~10.9.1",
"typescript": "^5.2.2",
"typescript": "^5.3.3",
"wait-on": "^7.2.0"
}
}

View File

@@ -23,6 +23,7 @@ import localeFi from '@angular/common/locales/fi'
import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu'
import localeIt from '@angular/common/locales/it'
import localeJa from '@angular/common/locales/ja'
import localeLb from '@angular/common/locales/lb'
import localeNl from '@angular/common/locales/nl'
import localeNo from '@angular/common/locales/no'
@@ -53,6 +54,7 @@ registerLocaleData(localeFi)
registerLocaleData(localeFr)
registerLocaleData(localeHu)
registerLocaleData(localeIt)
registerLocaleData(localeJa)
registerLocaleData(localeLb)
registerLocaleData(localeNl)
registerLocaleData(localeNo)
@@ -92,6 +94,10 @@ Object.defineProperty(navigator, 'clipboard', {
})
Object.defineProperty(navigator, 'canShare', { value: () => true })
Object.defineProperty(window, 'ResizeObserver', { value: mock() })
Object.defineProperty(window, 'location', {
configurable: true,
value: { reload: jest.fn() },
})
HTMLCanvasElement.prototype.getContext = <
typeof HTMLCanvasElement.prototype.getContext

View File

@@ -163,7 +163,7 @@ export const routes: Routes = [
canActivate: [PermissionsGuard],
data: {
requiredPermission: {
action: PermissionAction.View,
action: PermissionAction.Change,
type: PermissionType.UISettings,
},
},
@@ -186,8 +186,8 @@ export const routes: Routes = [
canActivate: [PermissionsGuard],
data: {
requiredPermission: {
action: PermissionAction.View,
type: PermissionType.Admin,
action: PermissionAction.Change,
type: PermissionType.AppConfig,
},
},
},

View File

@@ -112,7 +112,12 @@ import { SwitchComponent } from './components/common/input/switch/switch.compone
import { ConfigComponent } from './components/admin/config/config.component'
import { FileComponent } from './components/common/input/file/file.component'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { ConfirmButtonComponent } from './components/common/confirm-button/confirm-button.component'
import { MonetaryComponent } from './components/common/input/monetary/monetary.component'
import { SystemStatusDialogComponent } from './components/common/system-status-dialog/system-status-dialog.component'
import { NgxFilesizeModule } from 'ngx-filesize'
import {
airplane,
archive,
arrowCounterclockwise,
arrowDown,
@@ -127,16 +132,19 @@ import {
boxes,
calendar,
calendarEvent,
cardChecklist,
caretDown,
caretUp,
chatLeftText,
check,
check2All,
checkAll,
checkCircleFill,
checkLg,
chevronDoubleLeft,
chevronDoubleRight,
clipboard,
clipboardCheck,
clipboardCheckFill,
clipboardFill,
dash,
@@ -145,7 +153,9 @@ import {
doorOpen,
download,
envelope,
exclamationCircleFill,
exclamationTriangle,
exclamationTriangleFill,
eye,
fileEarmark,
fileEarmarkCheck,
@@ -197,6 +207,7 @@ import {
} from 'ngx-bootstrap-icons'
const icons = {
airplane,
archive,
arrowCounterclockwise,
arrowDown,
@@ -211,16 +222,19 @@ const icons = {
boxes,
calendar,
calendarEvent,
cardChecklist,
caretDown,
caretUp,
chatLeftText,
check,
check2All,
checkAll,
checkCircleFill,
checkLg,
chevronDoubleLeft,
chevronDoubleRight,
clipboard,
clipboardCheck,
clipboardCheckFill,
clipboardFill,
dash,
@@ -229,7 +243,9 @@ const icons = {
doorOpen,
download,
envelope,
exclamationCircleFill,
exclamationTriangle,
exclamationTriangleFill,
eye,
fileEarmark,
fileEarmarkCheck,
@@ -295,6 +311,7 @@ import localeFi from '@angular/common/locales/fi'
import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu'
import localeIt from '@angular/common/locales/it'
import localeJa from '@angular/common/locales/ja'
import localeLb from '@angular/common/locales/lb'
import localeNl from '@angular/common/locales/nl'
import localeNo from '@angular/common/locales/no'
@@ -325,6 +342,7 @@ registerLocaleData(localeFi)
registerLocaleData(localeFr)
registerLocaleData(localeHu)
registerLocaleData(localeIt)
registerLocaleData(localeJa)
registerLocaleData(localeLb)
registerLocaleData(localeNl)
registerLocaleData(localeNo)
@@ -437,6 +455,9 @@ function initializeApp(settings: SettingsService) {
SwitchComponent,
ConfigComponent,
FileComponent,
ConfirmButtonComponent,
MonetaryComponent,
SystemStatusDialogComponent,
],
imports: [
BrowserModule,
@@ -451,6 +472,7 @@ function initializeApp(settings: SettingsService) {
TourNgBootstrapModule,
DragDropModule,
NgxBootstrapIconsModule.pick(icons),
NgxFilesizeModule,
],
providers: [
{

View File

@@ -17,6 +17,7 @@ import { PageHeaderComponent } from '../../common/page-header/page-header.compon
import { SelectComponent } from '../../common/input/select/select.component'
import { FileComponent } from '../../common/input/file/file.component'
import { SettingsService } from 'src/app/services/settings.service'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('ConfigComponent', () => {
let component: ConfigComponent
@@ -43,6 +44,7 @@ describe('ConfigComponent', () => {
NgSelectModule,
FormsModule,
ReactiveFormsModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()

View File

@@ -11,6 +11,7 @@ import { of, throwError } from 'rxjs'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { NgbModule, NgbNavLink } from '@ng-bootstrap/ng-bootstrap'
import { BrowserModule, By } from '@angular/platform-browser'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const paperless_logs = [
'[2023-05-29 03:05:01,224] [DEBUG] [paperless.tasks] Training data unchanged.',
@@ -37,7 +38,12 @@ describe('LogsComponent', () => {
TestBed.configureTestingModule({
declarations: [LogsComponent, PageHeaderComponent],
providers: [],
imports: [HttpClientTestingModule, BrowserModule, NgbModule],
imports: [
HttpClientTestingModule,
BrowserModule,
NgbModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()
logService = TestBed.inject(LogService)

View File

@@ -4,10 +4,31 @@
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">
<button class="btn btn-sm btn-outline-primary" (click)="tourService.start()">
<i-bs class="me-1" name="airplane"></i-bs>&nbsp;<ng-container i18n>Start tour</ng-container>
</button>
<button class="btn btn-sm btn-outline-primary position-relative ms-md-5 me-1" (click)="showSystemStatus()"
[disabled]="!systemStatus"
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }">
@if (!systemStatus) {
<div class="spinner-border spinner-border-sm me-1 h-75" role="status"></div>
} @else {
<i-bs class="me-2" name="card-checklist"></i-bs>
@if (systemStatusHasErrors) {
<span class="badge bg-body position-absolute top-0 start-100 translate-middle rounded-pill p-0">
<i-bs name="exclamation-circle-fill" class="text-danger" width="1.75em" height="1.75em"></i-bs>
</span>
} @else {
<span class="badge bg-body position-absolute top-0 start-100 translate-middle rounded-pill p-0">
<i-bs name="check-circle-fill" class="text-primary" width="1.75em" height="1.75em"></i-bs>
</span>
}
}
<ng-container i18n>System Status</ng-container>
</button>
<a *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }" class="btn btn-sm btn-primary" href="admin/" target="_blank">
<ng-container i18n>Open Django Admin</ng-container>
<i-bs name="arrow-up-right"></i-bs>
&nbsp;<i-bs name="arrow-up-right"></i-bs>
</a>
</pngx-page-header>
@@ -158,6 +179,14 @@
</div>
</div>
<h4 class="mt-4" i18n>Document editing</h4>
<div class="row mb-3">
<div class="offset-md-3 col">
<pngx-input-check i18n-title title="Automatically remove inbox tag(s) on save" formControlName="documentEditingRemoveInboxTags"></pngx-input-check>
</div>
</div>
<h4 class="mt-4" i18n>Bulk editing</h4>
<div class="row mb-3">
@@ -311,7 +340,15 @@
</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>
<pngx-confirm-button
label="Delete"
i18n-label
(confirm)="deleteSavedView(view)"
*pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.SavedView }"
buttonClasses="btn-sm btn-outline-danger form-control"
iconName="trash">
</pngx-confirm-button>
</div>
</div>
}
@@ -335,5 +372,5 @@
<div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div>
<button type="submit" class="btn btn-primary mb-2" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }" [disabled]="(isDirty$ | async) === false" i18n>Save</button>
<button type="submit" class="btn btn-primary mb-2" [disabled]="(isDirty$ | async) === false" i18n>Save</button>
</form>

View File

@@ -9,6 +9,8 @@ import {
NgbModule,
NgbAlertModule,
NgbNavLink,
NgbModal,
NgbModalModule,
} from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select'
import { of, throwError } from 'rxjs'
@@ -37,6 +39,15 @@ import { TextComponent } from '../../common/input/text/text.component'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { SettingsComponent } from './settings.component'
import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component'
import { SystemStatusDialogComponent } from '../../common/system-status-dialog/system-status-dialog.component'
import { SystemStatusService } from 'src/app/services/system-status.service'
import {
SystemStatus,
InstallType,
SystemStatusItemStatus,
} from 'src/app/data/system-status'
const savedViews = [
{ id: 1, name: 'view1', show_in_sidebar: true, show_on_dashboard: true },
@@ -63,6 +74,8 @@ describe('SettingsComponent', () => {
let userService: UserService
let permissionsService: PermissionsService
let groupService: GroupService
let modalService: NgbModal
let systemStatusService: SystemStatusService
beforeEach(async () => {
TestBed.configureTestingModule({
@@ -82,6 +95,7 @@ describe('SettingsComponent', () => {
PermissionsUserComponent,
PermissionsGroupComponent,
IfOwnerDirective,
ConfirmButtonComponent,
],
providers: [CustomDatePipe, DatePipe, PermissionsGuard],
imports: [
@@ -92,6 +106,8 @@ describe('SettingsComponent', () => {
ReactiveFormsModule,
NgbAlertModule,
NgSelectModule,
NgxBootstrapIconsModule.pick(allIcons),
NgbModalModule,
],
}).compileComponents()
@@ -103,6 +119,8 @@ describe('SettingsComponent', () => {
settingsService.currentUser = users[0]
userService = TestBed.inject(UserService)
permissionsService = TestBed.inject(PermissionsService)
modalService = TestBed.inject(NgbModal)
systemStatusService = TestBed.inject(SystemStatusService)
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
jest
.spyOn(permissionsService, 'currentUserHasObjectPermissions')
@@ -287,7 +305,7 @@ describe('SettingsComponent', () => {
expect(toastErrorSpy).toHaveBeenCalled()
expect(storeSpy).toHaveBeenCalled()
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
expect(setSpy).toHaveBeenCalledTimes(24)
expect(setSpy).toHaveBeenCalledTimes(25)
// succeed
storeSpy.mockReturnValueOnce(of(true))
@@ -305,10 +323,15 @@ describe('SettingsComponent', () => {
component.store.getValue()['displayLanguage'] = 'en-US'
component.store.getValue()['updateCheckingEnabled'] = false
component.settingsForm.value.displayLanguage = 'en-GB'
component.settingsForm.value.updateCheckingEnabled = true
jest.spyOn(settingsService, 'storeSettings').mockReturnValueOnce(of(true))
jest.spyOn(settingsService, 'storeSettings').mockReturnValue(of(true))
component.saveSettings()
expect(toast.actionName).toEqual('Reload now')
component.settingsForm.value.updateCheckingEnabled = true
component.saveSettings()
expect(toast.actionName).toEqual('Reload now')
toast.action()
})
it('should allow setting theme color, visually apply change immediately but not save', () => {
@@ -363,4 +386,54 @@ describe('SettingsComponent', () => {
fixture.detectChanges()
expect(toastErrorSpy).toBeCalled()
})
it('should load system status on initialize, show errors if needed', () => {
const status: SystemStatus = {
pngx_version: '2.4.3',
server_os: 'macOS-14.1.1-arm64-arm-64bit',
install_type: InstallType.BareMetal,
storage: { total: 494384795648, available: 13573525504 },
database: {
type: 'sqlite',
url: '/paperless-ngx/data/db.sqlite3',
status: SystemStatusItemStatus.ERROR,
error: null,
migration_status: {
latest_migration: 'socialaccount.0006_alter_socialaccount_extra_data',
unapplied_migrations: [],
},
},
tasks: {
redis_url: 'redis://localhost:6379',
redis_status: SystemStatusItemStatus.ERROR,
redis_error:
'Error 61 connecting to localhost:6379. Connection refused.',
celery_status: SystemStatusItemStatus.ERROR,
index_status: SystemStatusItemStatus.OK,
index_last_modified: new Date().toISOString(),
index_error: null,
classifier_status: SystemStatusItemStatus.OK,
classifier_last_trained: new Date().toISOString(),
classifier_error: null,
},
}
jest.spyOn(systemStatusService, 'get').mockReturnValue(of(status))
completeSetup()
expect(component['systemStatus']).toEqual(status) // private
expect(component.systemStatusHasErrors).toBeTruthy()
// coverage
component['systemStatus'].database.status = SystemStatusItemStatus.OK
component['systemStatus'].tasks.redis_status = SystemStatusItemStatus.OK
component['systemStatus'].tasks.celery_status = SystemStatusItemStatus.OK
expect(component.systemStatusHasErrors).toBeFalsy()
})
it('should open system status dialog', () => {
const modalOpenSpy = jest.spyOn(modalService, 'open')
completeSetup()
component.showSystemStatus()
expect(modalOpenSpy).toHaveBeenCalledWith(SystemStatusDialogComponent, {
size: 'xl',
})
})
})

View File

@@ -9,7 +9,11 @@ import {
} from '@angular/core'
import { FormGroup, FormControl } from '@angular/forms'
import { ActivatedRoute, Router } from '@angular/router'
import { NgbNavChangeEvent } from '@ng-bootstrap/ng-bootstrap'
import {
NgbModal,
NgbModalRef,
NgbNavChangeEvent,
} from '@ng-bootstrap/ng-bootstrap'
import { DirtyComponent, dirtyCheck } from '@ngneat/dirty-check-forms'
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
import {
@@ -40,6 +44,12 @@ import {
} from 'src/app/services/settings.service'
import { ToastService, Toast } from 'src/app/services/toast.service'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { SystemStatusDialogComponent } from '../../common/system-status-dialog/system-status-dialog.component'
import { SystemStatusService } from 'src/app/services/system-status.service'
import {
SystemStatusItemStatus,
SystemStatus,
} from 'src/app/data/system-status'
enum SettingsNavIDs {
General = 1,
@@ -88,6 +98,7 @@ export class SettingsComponent
defaultPermsViewGroups: new FormControl(null),
defaultPermsEditUsers: new FormControl(null),
defaultPermsEditGroups: new FormControl(null),
documentEditingRemoveInboxTags: new FormControl(null),
notificationsConsumerNewDocument: new FormControl(null),
notificationsConsumerSuccess: new FormControl(null),
@@ -110,6 +121,18 @@ export class SettingsComponent
users: User[]
groups: Group[]
private systemStatus: SystemStatus
get systemStatusHasErrors(): boolean {
return (
this.systemStatus.database.status === SystemStatusItemStatus.ERROR ||
this.systemStatus.tasks.redis_status === SystemStatusItemStatus.ERROR ||
this.systemStatus.tasks.celery_status === SystemStatusItemStatus.ERROR ||
this.systemStatus.tasks.index_status === SystemStatusItemStatus.ERROR ||
this.systemStatus.tasks.classifier_status === SystemStatusItemStatus.ERROR
)
}
get computedDateLocale(): string {
return (
this.settingsForm.value.dateLocale ||
@@ -130,7 +153,9 @@ export class SettingsComponent
private usersService: UserService,
private groupsService: GroupService,
private router: Router,
public permissionsService: PermissionsService
public permissionsService: PermissionsService,
private modalService: NgbModal,
private systemStatusService: SystemStatusService
) {
super()
this.settings.settingsSaved.subscribe(() => {
@@ -271,6 +296,9 @@ export class SettingsComponent
defaultPermsEditGroups: this.settings.get(
SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS
),
documentEditingRemoveInboxTags: this.settings.get(
SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS
),
savedViews: {},
}
}
@@ -356,6 +384,17 @@ export class SettingsComponent
// prevents loss of unsaved changes
this.settingsForm.patchValue(currentFormValue)
}
if (
this.permissionsService.currentUserCan(
PermissionAction.View,
PermissionType.Admin
)
) {
this.systemStatusService.get().subscribe((status) => {
this.systemStatus = status
})
}
}
private emptyGroup(group: FormGroup) {
@@ -484,6 +523,10 @@ export class SettingsComponent
SETTINGS_KEYS.DEFAULT_PERMS_EDIT_GROUPS,
this.settingsForm.value.defaultPermsEditGroups
)
this.settings.set(
SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS,
this.settingsForm.value.documentEditingRemoveInboxTags
)
this.settings.setLanguage(this.settingsForm.value.displayLanguage)
this.settings
.storeSettings()
@@ -557,4 +600,14 @@ export class SettingsComponent
clearThemeColor() {
this.settingsForm.get('themeColor').patchValue('')
}
showSystemStatus() {
const modal: NgbModalRef = this.modalService.open(
SystemStatusDialogComponent,
{
size: 'xl',
}
)
modal.componentInstance.status = this.systemStatus
}
}

View File

@@ -28,6 +28,7 @@ import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dial
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { TasksComponent } from './tasks.component'
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const tasks: PaperlessTask[] = [
{
@@ -138,6 +139,7 @@ describe('TasksComponent', () => {
NgbModule,
HttpClientTestingModule,
RouterTestingModule.withRoutes(routes),
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()

View File

@@ -33,64 +33,64 @@
<div class="btn-group">
<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 }">
<i-bs width="1em" height="1em" name="trash"></i-bs>&nbsp;<ng-container i18n>Delete</ng-container>
</button>
</div>
</div>
</div>
</li>
}
</ul>
}
@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 }">
<i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Group</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>
</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)="deleteUser(user)" *pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.User }">
<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>
</button>
</div>
</div>
</li>
}
</ul>
}
@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 }">
<i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Group</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>
</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>
}
}
@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>
}
</li>
}
@if (groups.length === 0) {
<li class="list-group-item" i18n>No groups defined</li>
}
</ul>
}
}
@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

@@ -43,6 +43,7 @@ import { SettingsComponent } from '../settings/settings.component'
import { UsersAndGroupsComponent } from './users-groups.component'
import { User } from 'src/app/data/user'
import { Group } from 'src/app/data/group'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const users = [
{ id: 1, username: 'user1', is_superuser: false },
@@ -92,6 +93,7 @@ describe('UsersAndGroupsComponent', () => {
ReactiveFormsModule,
NgbAlertModule,
NgSelectModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()
fixture = TestBed.createComponent(UsersAndGroupsComponent)

View File

@@ -4,16 +4,16 @@
(click)="isMenuCollapsed = !isMenuCollapsed">
<span class="navbar-toggler-icon"></span>
</button>
<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 }"
<a class="navbar-brand d-flex align-items-center me-0 px-3 py-3 order-sm-0"
[ngClass]="{ 'slim': slimSidebarEnabled, '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">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 198.43 238.91" width="1em" height="1.5em" fill="currentColor">
<path
d="M194.7,0C164.22,70.94,17.64,79.74,64.55,194.06c.58,1.47-10.85,17-18.47,29.9-1.76-6.45-3.81-13.48-3.52-14.07,38.11-45.14-27.26-70.65-30.78-107.58C-4.64,131.62-10.5,182.92,39,212.53c.3,0,2.64,11.14,3.81,16.71a58.55,58.55,0,0,0-2.93,6.45c-1.17,2.93,7.62,2.64,7.62,3.22.88-.29,21.7-36.93,22.28-37.23C187.67,174.72,208.48,68.6,194.7,0ZM134.61,74.75C79.5,124,70.12,160.64,71.88,178.53,53.41,134.85,107.64,86.77,134.61,74.75ZM28.2,145.11c10.55,9.67,28.14,39.28,13.19,56.57C44.91,193.77,46.08,175.89,28.2,145.11Z"
transform="translate(0 0)" />
</svg>
<div class="ms-2 d-inline-block" [class.visually-hidden]="slimSidebarEnabled">
<div class="ms-2 ms-md-3 d-inline-block" [class.d-md-none]="slimSidebarEnabled">
@if (customAppTitle?.length) {
<div class="d-flex flex-column align-items-start">
<span class="title">{{customAppTitle}}</span>
@@ -27,12 +27,12 @@
<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">
<i-bs style="top: .25em;" width="1em" height="1em" name="search"></i-bs>
<i-bs 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>
@if (!searchFieldEmpty) {
<button type="button" class="btn btn-link btn-sm px-0 position-absolute top-0 end-0" (click)="resetSearchField()">
<button type="button" class="btn btn-link btn-sm ps-0 pe-1 position-absolute top-0 end-0" (click)="resetSearchField()">
<i-bs width="1em" height="1em" name="x"></i-bs>
</button>
}
@@ -55,7 +55,7 @@
<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 }">
*pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }">
<i-bs class="me-2" name="gear"></i-bs><ng-container i18n>Settings</ng-container>
</a>
<a ngbDropdownItem class="nav-link d-flex" href="accounts/logout/" (click)="onLogout()">
@@ -227,7 +227,7 @@
<span i18n>Administration</span>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.UISettings }"
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }"
tourAnchor="tour.settings">
<a class="nav-link" routerLink="settings" routerLinkActive="active" (click)="closeMenu()"
ngbPopover="Settings" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
@@ -235,7 +235,7 @@
<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 }">
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.AppConfig }">
<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">
@@ -256,10 +256,10 @@
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><span class="badge bg-danger ms-2 d-inline">{{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>
<span class="badge bg-danger position-absolute top-0 end-0 d-none d-md-block">{{tasksService.failedFileTasks.length}}</span>
}
</a>
</li>

View File

@@ -262,9 +262,15 @@ main {
> i-bs {
position: absolute;
left: 0.6rem;
top: 0.5rem;
top: .35rem;
color: rgba(255, 255, 255, 0.6);
@media screen and (min-width: 768px) {
// adjust for smaller font size on non-mobile
top: 0.25rem;
}
}
}

View File

@@ -21,6 +21,10 @@ import { IfPermissionsDirective } from 'src/app/directives/if-permissions.direct
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { of, throwError } from 'rxjs'
import { ToastService } from 'src/app/services/toast.service'
import {
DjangoMessageLevel,
DjangoMessagesService,
} from 'src/app/services/django-messages.service'
import { environment } from 'src/environments/environment'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { ActivatedRoute, Router } from '@angular/router'
@@ -33,6 +37,7 @@ import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { CdkDragDrop, DragDropModule } from '@angular/cdk/drag-drop'
import { SavedView } from 'src/app/data/saved-view'
import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const saved_views = [
{
@@ -82,6 +87,7 @@ describe('AppFrameComponent', () => {
let permissionsService: PermissionsService
let remoteVersionService: RemoteVersionService
let toastService: ToastService
let messagesService: DjangoMessagesService
let openDocumentsService: OpenDocumentsService
let searchService: SearchService
let documentListViewService: DocumentListViewService
@@ -101,6 +107,7 @@ describe('AppFrameComponent', () => {
ReactiveFormsModule,
DragDropModule,
NgbModalModule,
NgxBootstrapIconsModule.pick(allIcons),
],
providers: [
SettingsService,
@@ -121,6 +128,7 @@ describe('AppFrameComponent', () => {
RemoteVersionService,
IfPermissionsDirective,
ToastService,
DjangoMessagesService,
OpenDocumentsService,
SearchService,
NgbModal,
@@ -149,6 +157,7 @@ describe('AppFrameComponent', () => {
permissionsService = TestBed.inject(PermissionsService)
remoteVersionService = TestBed.inject(RemoteVersionService)
toastService = TestBed.inject(ToastService)
messagesService = TestBed.inject(DjangoMessagesService)
openDocumentsService = TestBed.inject(OpenDocumentsService)
searchService = TestBed.inject(SearchService)
documentListViewService = TestBed.inject(DocumentListViewService)
@@ -391,4 +400,19 @@ describe('AppFrameComponent', () => {
backdrop: 'static',
})
})
it('should show toasts for django messages', () => {
const toastErrorSpy = jest.spyOn(toastService, 'showError')
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
jest.spyOn(messagesService, 'get').mockReturnValue([
{ level: DjangoMessageLevel.WARNING, message: 'Test warning' },
{ level: DjangoMessageLevel.ERROR, message: 'Test error' },
{ level: DjangoMessageLevel.SUCCESS, message: 'Test success' },
{ level: DjangoMessageLevel.INFO, message: 'Test info' },
{ level: DjangoMessageLevel.DEBUG, message: 'Test debug' },
])
component.ngOnInit()
expect(toastErrorSpy).toHaveBeenCalledTimes(2)
expect(toastInfoSpy).toHaveBeenCalledTimes(3)
})
})

View File

@@ -12,6 +12,10 @@ import {
} from 'rxjs/operators'
import { Document } from 'src/app/data/document'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import {
DjangoMessageLevel,
DjangoMessagesService,
} from 'src/app/services/django-messages.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { SearchService } from 'src/app/services/rest/search.service'
import { environment } from 'src/environments/environment'
@@ -73,7 +77,8 @@ export class AppFrameComponent
public tasksService: TasksService,
private readonly toastService: ToastService,
private modalService: NgbModal,
permissionsService: PermissionsService
public permissionsService: PermissionsService,
private djangoMessagesService: DjangoMessagesService
) {
super()
@@ -92,6 +97,20 @@ export class AppFrameComponent
this.checkForUpdates()
}
this.tasksService.reload()
this.djangoMessagesService.get().forEach((message) => {
switch (message.level) {
case DjangoMessageLevel.ERROR:
case DjangoMessageLevel.WARNING:
this.toastService.showError(message.message)
break
case DjangoMessageLevel.SUCCESS:
case DjangoMessageLevel.INFO:
case DjangoMessageLevel.DEBUG:
this.toastService.showInfo(message.message)
break
}
})
}
toggleSlimSidebar(): void {

View File

@@ -1,5 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ClearableBadgeComponent } from './clearable-badge.component'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('ClearableBadgeComponent', () => {
let component: ClearableBadgeComponent
@@ -8,6 +9,7 @@ describe('ClearableBadgeComponent', () => {
beforeEach(async () => {
TestBed.configureTestingModule({
declarations: [ClearableBadgeComponent],
imports: [NgxBootstrapIconsModule.pick(allIcons)],
}).compileComponents()
fixture = TestBed.createComponent(ClearableBadgeComponent)

View File

@@ -0,0 +1,22 @@
<button
type="button"
class="btn {{buttonClasses}}"
(click)="onClick($event)"
[disabled]="disabled"
[ngbPopover]="popoverContent"
[autoClose]="true"
(hidden)="confirming = false"
#popover="ngbPopover"
popoverClass="popover-slim"
>
@if (iconName) {
<i-bs [class.me-1]="label" name="{{iconName}}"></i-bs>
}
<ng-container>{{label}}</ng-container>
</button>
<ng-template #popoverContent>
<div>
{{confirmMessage}}&nbsp;<button class="btn btn-link btn-sm text-danger p-0 m-0 lh-1" type="button" (click)="onConfirm($event)">Yes</button>
</div>
</ng-template>

View File

@@ -0,0 +1,12 @@
// Taken from bootstrap rules, obv
::ng-deep .input-group > pngx-confirm-button:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) > button,
::ng-deep .btn-group > pngx-confirm-button:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) > button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
::ng-deep .input-group:not(.has-validation) > pngx-confirm-button:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating) > button,
::ng-deep .btn-group:not(.has-validation) > pngx-confirm-button:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating) > button {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}

View File

@@ -0,0 +1,37 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ConfirmButtonComponent } from './confirm-button.component'
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('ConfirmButtonComponent', () => {
let component: ConfirmButtonComponent
let fixture: ComponentFixture<ConfirmButtonComponent>
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ConfirmButtonComponent],
imports: [NgbPopoverModule, NgxBootstrapIconsModule.pick(allIcons)],
}).compileComponents()
fixture = TestBed.createComponent(ConfirmButtonComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should show confirm on click', () => {
expect(component.popover.isOpen()).toBeFalsy()
expect(component.confirming).toBeFalsy()
component.onClick(new MouseEvent('click'))
expect(component.popover.isOpen()).toBeTruthy()
expect(component.confirming).toBeTruthy()
})
it('should emit confirm on confirm', () => {
const confirmSpy = jest.spyOn(component.confirm, 'emit')
component.onConfirm(new MouseEvent('click'))
expect(confirmSpy).toHaveBeenCalled()
expect(component.popover.isOpen()).toBeFalsy()
expect(component.confirming).toBeFalsy()
})
})

View File

@@ -0,0 +1,55 @@
import {
Component,
EventEmitter,
Input,
Output,
ViewChild,
} from '@angular/core'
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'
@Component({
selector: 'pngx-confirm-button',
templateUrl: './confirm-button.component.html',
styleUrl: './confirm-button.component.scss',
})
export class ConfirmButtonComponent {
@Input()
label: string
@Input()
confirmMessage: string = $localize`Are you sure?`
@Input()
buttonClasses: string = 'btn-primary'
@Input()
iconName: string
@Input()
disabled: boolean = false
@Output()
confirm: EventEmitter<void> = new EventEmitter<void>()
@ViewChild('popover') popover: NgbPopover
public confirming: boolean = false
public onClick(event: MouseEvent) {
if (!this.confirming) {
this.confirming = true
this.popover.open()
}
event.preventDefault()
event.stopImmediatePropagation()
}
public onConfirm(event: MouseEvent) {
this.confirm.emit()
this.confirming = false
event.preventDefault()
event.stopImmediatePropagation()
}
}

View File

@@ -20,6 +20,7 @@ import {
} from '@ng-bootstrap/ng-bootstrap'
import { CustomFieldEditDialogComponent } from '../edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
import { By } from '@angular/platform-browser'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const fields: CustomField[] = [
{
@@ -40,7 +41,6 @@ describe('CustomFieldsDropdownComponent', () => {
let customFieldService: CustomFieldsService
let toastService: ToastService
let modalService: NgbModal
let httpController: HttpTestingController
beforeEach(() => {
TestBed.configureTestingModule({
@@ -52,10 +52,10 @@ describe('CustomFieldsDropdownComponent', () => {
ReactiveFormsModule,
NgbModalModule,
NgbDropdownModule,
NgxBootstrapIconsModule.pick(allIcons),
],
})
customFieldService = TestBed.inject(CustomFieldsService)
httpController = TestBed.inject(HttpTestingController)
toastService = TestBed.inject(ToastService)
modalService = TestBed.inject(NgbModal)
jest.spyOn(customFieldService, 'listAll').mockReturnValue(

View File

@@ -10,20 +10,17 @@ import {
DateSelection,
RelativeDate,
} from './date-dropdown.component'
import {
HttpClientTestingModule,
HttpTestingController,
} from '@angular/common/http/testing'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { SettingsService } from 'src/app/services/settings.service'
import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { DatePipe } from '@angular/common'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('DateDropdownComponent', () => {
let component: DateDropdownComponent
let httpTestingController: HttpTestingController
let settingsService: SettingsService
let settingsSpy
@@ -40,10 +37,10 @@ describe('DateDropdownComponent', () => {
NgbModule,
FormsModule,
ReactiveFormsModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()
httpTestingController = TestBed.inject(HttpTestingController)
settingsService = TestBed.inject(SettingsService)
settingsSpy = jest.spyOn(settingsService, 'getLocalizedDateInputFormat')

View File

@@ -1,12 +1,15 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<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-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></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>

View File

@@ -1,11 +1,14 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<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-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></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>

View File

@@ -1,6 +1,9 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<button type="button" [disabled]="!closeEnabled" class="btn-close" aria-label="Close" (click)="cancel()">
</button>
</div>

View File

@@ -1,13 +1,16 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<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">
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></pngx-input-text>
<pngx-permissions-select i18n-title title="Permissions" formControlName="permissions" [error]="error?.permissions"></pngx-permissions-select>
</div>
</div>

View File

@@ -1,13 +1,16 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<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">
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></pngx-input-text>
<pngx-input-text i18n-title title="IMAP Server" formControlName="imap_server" [error]="error?.imap_server"></pngx-input-text>
<pngx-input-text i18n-title title="IMAP Port" formControlName="imap_port" [error]="error?.imap_port"></pngx-input-text>
<pngx-input-select i18n-title title="IMAP Security" [items]="imapSecurityOptions" formControlName="imap_security"></pngx-input-select>

View File

@@ -1,13 +1,16 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<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-4">
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></pngx-input-text>
<pngx-input-select i18n-title title="Account" [items]="accounts" formControlName="account"></pngx-input-select>
<pngx-input-text i18n-title title="Folder" formControlName="folder" i18n-hint hint="Subfolders must be separated by a delimiter, often a dot ('.') or slash ('/'), but it varies by mail server." [error]="error?.folder"></pngx-input-text>
<pngx-input-number i18n-title title="Maximum age (days)" formControlName="maximum_age" [showAdd]="false" [error]="error?.maximum_age"></pngx-input-number>
@@ -29,7 +32,6 @@
@if (showActionParamField) {
<pngx-input-text i18n-title title="Action parameter" formControlName="action_parameter" [error]="error?.action_parameter"></pngx-input-text>
}
<p class="small fst-italic mt-5" i18n>Assignments specified here will supersede any consumption templates.</p>
<pngx-input-select i18n-title title="Assign title from" [items]="metadataTitleOptions" formControlName="assign_title_from"></pngx-input-select>
<pngx-input-tags [allowCreate]="false" 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>

View File

@@ -1,12 +1,15 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<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-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></pngx-input-text>
<pngx-input-text i18n-title title="Path" formControlName="path" [error]="error?.path" [hint]="pathHint"></pngx-input-text>
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
@if (patternRequired) {

View File

@@ -1,11 +1,14 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<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-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></pngx-input-text>
<pngx-input-color i18n-title title="Color" formControlName="color" [error]="error?.color"></pngx-input-color>

View File

@@ -13,6 +13,7 @@ import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component'
import { EditDialogMode } from '../edit-dialog.component'
import { TagEditDialogComponent } from './tag-edit-dialog.component'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('TagEditDialogComponent', () => {
let component: TagEditDialogComponent
@@ -38,6 +39,7 @@ describe('TagEditDialogComponent', () => {
ReactiveFormsModule,
NgSelectModule,
NgbModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()

View File

@@ -1,6 +1,9 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<button type="button" [disabled]="!closeEnabled" class="btn-close" aria-label="Close" (click)="cancel()">
</button>
</div>

View File

@@ -1,13 +1,16 @@
<form [formGroup]="objectForm" (ngSubmit)="save()" autocomplete="off">
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
@if (object?.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{object.id}}</span>
}
<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-6">
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></pngx-input-text>
<pngx-input-text i18n-title title="Name" formControlName="name" [error]="error?.name" autocomplete="off"></pngx-input-text>
</div>
<div class="col-4">
<pngx-input-number i18n-title title="Sort order" formControlName="order" [showAdd]="false" [error]="error?.order"></pngx-input-number>
@@ -35,12 +38,16 @@
<div ngbAccordionItem>
<div ngbAccordionHeader>
<button ngbAccordionButton>{{i + 1}}. {{getTriggerTypeOptionName(triggerFields.controls[i].value.type)}}
@if(trigger.id > -1) {
@if(trigger.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{trigger.id}}</span>
}
<button type="button" class="btn btn-link text-danger ms-2" (click)="removeTrigger(i)">
<i-bs name="trash"></i-bs>&nbsp;<ng-container i18n>Delete</ng-container>
</button>
<pngx-confirm-button
label="Delete"
i18n-label
(confirm)="removeTrigger(i)"
buttonClasses="btn-link text-danger ms-2"
iconName="trash">
</pngx-confirm-button>
</button>
</div>
<div ngbAccordionCollapse>
@@ -73,73 +80,21 @@
<div ngbAccordionItem cdkDrag [formGroup]="actionFields.controls[i]">
<div ngbAccordionHeader>
<button ngbAccordionButton>{{i + 1}}. {{getActionTypeOptionName(actionFields.controls[i].value.type)}}
@if(action.id > -1) {
@if(action.id) {
<span class="badge bg-primary text-primary-text-contrast ms-2">ID: {{action.id}}</span>
}
<button type="button" class="btn btn-link text-danger ms-2" (click)="removeAction(i)">
<i-bs name="trash"></i-bs>&nbsp;<ng-container i18n>Delete</ng-container>
</button>
<pngx-confirm-button
label="Delete"
i18n-label
(confirm)="removeAction(i)"
buttonClasses="btn-link text-danger ms-2"
iconName="trash">
</pngx-confirm-button>
</button>
</div>
<div ngbAccordionCollapse>
<div ngbAccordionBody>
<pngx-input-select i18n-title title="Action type" [horizontal]="true" [items]="actionTypeOptions" formControlName="type"></pngx-input-select>
<input type="hidden" formControlName="id" />
<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/#workflows'>documentation</a>." [error]="error?.actions?.[i]?.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>
<pngx-input-select i18n-title title="Assign custom fields" multiple="true" [items]="customFields" [allowNull]="true" formControlName="assign_custom_fields"></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 text-nowrap" 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 text-nowrap" 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 text-nowrap" 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 text-nowrap" 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>
<ng-template [ngTemplateOutlet]="actionForm" [ngTemplateOutletContext]="{ formGroup: actionFields.controls[i], action: action }"></ng-template>
</div>
</div>
</div>
@@ -193,3 +148,154 @@
</div>
</div>
</ng-template>
<ng-template #actionForm let-formGroup="formGroup" let action="action">
<div [formGroup]="formGroup">
<input type="hidden" formControlName="id" />
<pngx-input-select i18n-title title="Action type" [horizontal]="true" [items]="actionTypeOptions" formControlName="type"></pngx-input-select>
@switch(formGroup.get('type').value) {
@case ( WorkflowActionType.Assignment) {
<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/#workflows'>documentation</a>." [error]="error?.actions?.[i]?.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>
<pngx-input-select i18n-title title="Assign custom fields" multiple="true" [items]="customFields" [allowNull]="true" formControlName="assign_custom_fields"></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 text-nowrap" 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 text-nowrap" 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 text-nowrap" 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 text-nowrap" 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>
}
@case (WorkflowActionType.Removal) {
<div class="row">
<div class="col">
<h6 class="form-label" i18n>Remove tags</h6>
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_tags"></pngx-input-switch>
<div class="mt-n3">
<pngx-input-tags [allowCreate]="false" title="" formControlName="remove_tags"></pngx-input-tags>
</div>
<h6 class="form-label" i18n>Remove correspondents</h6>
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_correspondents"></pngx-input-switch>
<div class="mt-n3">
<pngx-input-select i18n-title title="" multiple="true" [items]="correspondents" formControlName="remove_correspondents"></pngx-input-select>
</div>
<h6 class="form-label" i18n>Remove document types</h6>
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_document_types"></pngx-input-switch>
<div class="mt-n3">
<pngx-input-select i18n-title title="" multiple="true" [items]="documentTypes" formControlName="remove_document_types"></pngx-input-select>
</div>
<h6 class="form-label" i18n>Remove storage paths</h6>
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_storage_paths"></pngx-input-switch>
<div class="mt-n3">
<pngx-input-select i18n-title title="" multiple="true" [items]="storagePaths" formControlName="remove_storage_paths"></pngx-input-select>
</div>
<h6 class="form-label" i18n>Remove custom fields</h6>
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_custom_fields"></pngx-input-switch>
<div class="mt-n3">
<pngx-input-select i18n-title title="" multiple="true" [items]="customFields" formControlName="remove_custom_fields"></pngx-input-select>
</div>
</div>
<div class="col">
<h6 class="form-label" i18n>Remove owners</h6>
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_owners"></pngx-input-switch>
<div class="mt-n3">
<pngx-input-select i18n-title title="" multiple="true" [items]="users" bindLabel="username" formControlName="remove_owners"></pngx-input-select>
</div>
<h6 class="form-label" i18n>Remove permissions</h6>
<pngx-input-switch i18n-title title="Remove all" [horizontal]="true" formControlName="remove_all_permissions"></pngx-input-switch>
<div>
<label class="form-label" i18n>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 text-nowrap" i18n>Users:</label>
</div>
<div class="col-lg-9">
<pngx-permissions-user type="view" formControlName="remove_view_users"></pngx-permissions-user>
</div>
</div>
<div class="row">
<div class="col-lg-3">
<label class="form-label d-block my-2 text-nowrap" i18n>Groups:</label>
</div>
<div class="col-lg-9">
<pngx-permissions-group type="view" formControlName="remove_view_groups"></pngx-permissions-group>
</div>
</div>
</div>
<label class="form-label" i18n>Edit permissions</label>
<div>
<div class="row mb-1">
<div class="col-lg-3">
<label class="form-label d-block my-2 text-nowrap" i18n>Users:</label>
</div>
<div class="col-lg-9">
<pngx-permissions-user type="change" formControlName="remove_change_users"></pngx-permissions-user>
</div>
</div>
<div class="row">
<div class="col-lg-3">
<label class="form-label d-block my-2 text-nowrap" i18n>Groups:</label>
</div>
<div class="col-lg-9">
<pngx-permissions-group type="change" formControlName="remove_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>
</ng-template>

View File

@@ -38,6 +38,7 @@ import {
WorkflowActionType,
} from 'src/app/data/workflow-action'
import { MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model'
import { ConfirmButtonComponent } from '../../confirm-button/confirm-button.component'
const workflow: Workflow = {
name: 'Workflow 1',
@@ -85,6 +86,7 @@ describe('WorkflowEditDialogComponent', () => {
PermissionsUserComponent,
PermissionsGroupComponent,
SafeHtmlPipe,
ConfirmButtonComponent,
],
providers: [
NgbActiveModal,
@@ -233,4 +235,103 @@ describe('WorkflowEditDialogComponent', () => {
MATCHING_ALGORITHMS.find((a) => a.id === MATCH_AUTO)
)
})
it('should disable or enable action fields based on removal action type', () => {
const workflow: Workflow = {
name: 'Workflow 1',
id: 1,
order: 1,
enabled: true,
triggers: [],
actions: [
{
id: 1,
type: WorkflowActionType.Removal,
remove_all_tags: true,
remove_all_document_types: true,
remove_all_correspondents: true,
remove_all_storage_paths: true,
remove_all_custom_fields: true,
remove_all_owners: true,
remove_all_permissions: true,
},
],
}
component.object = workflow
component.ngOnInit()
component['checkRemovalActionFields'](workflow)
// Assert that the action fields are disabled or enabled correctly
expect(
component.actionFields.at(0).get('remove_tags').disabled
).toBeTruthy()
expect(
component.actionFields.at(0).get('remove_document_types').disabled
).toBeTruthy()
expect(
component.actionFields.at(0).get('remove_correspondents').disabled
).toBeTruthy()
expect(
component.actionFields.at(0).get('remove_storage_paths').disabled
).toBeTruthy()
expect(
component.actionFields.at(0).get('remove_custom_fields').disabled
).toBeTruthy()
expect(
component.actionFields.at(0).get('remove_owners').disabled
).toBeTruthy()
expect(
component.actionFields.at(0).get('remove_view_users').disabled
).toBeTruthy()
expect(
component.actionFields.at(0).get('remove_view_groups').disabled
).toBeTruthy()
expect(
component.actionFields.at(0).get('remove_change_users').disabled
).toBeTruthy()
expect(
component.actionFields.at(0).get('remove_change_groups').disabled
).toBeTruthy()
workflow.actions[0].remove_all_tags = false
workflow.actions[0].remove_all_document_types = false
workflow.actions[0].remove_all_correspondents = false
workflow.actions[0].remove_all_storage_paths = false
workflow.actions[0].remove_all_custom_fields = false
workflow.actions[0].remove_all_owners = false
workflow.actions[0].remove_all_permissions = false
component['checkRemovalActionFields'](workflow)
// Assert that the action fields are disabled or enabled correctly
expect(component.actionFields.at(0).get('remove_tags').disabled).toBeFalsy()
expect(
component.actionFields.at(0).get('remove_document_types').disabled
).toBeFalsy()
expect(
component.actionFields.at(0).get('remove_correspondents').disabled
).toBeFalsy()
expect(
component.actionFields.at(0).get('remove_storage_paths').disabled
).toBeFalsy()
expect(
component.actionFields.at(0).get('remove_custom_fields').disabled
).toBeFalsy()
expect(
component.actionFields.at(0).get('remove_owners').disabled
).toBeFalsy()
expect(
component.actionFields.at(0).get('remove_view_users').disabled
).toBeFalsy()
expect(
component.actionFields.at(0).get('remove_view_groups').disabled
).toBeFalsy()
expect(
component.actionFields.at(0).get('remove_change_users').disabled
).toBeFalsy()
expect(
component.actionFields.at(0).get('remove_change_groups').disabled
).toBeFalsy()
})
})

View File

@@ -68,6 +68,10 @@ export const WORKFLOW_ACTION_OPTIONS = [
id: WorkflowActionType.Assignment,
name: $localize`Assignment`,
},
{
id: WorkflowActionType.Removal,
name: $localize`Removal`,
},
]
const TRIGGER_MATCHING_ALGORITHMS = MATCHING_ALGORITHMS.filter(
@@ -84,6 +88,7 @@ export class WorkflowEditDialogComponent
implements OnInit
{
public WorkflowTriggerType = WorkflowTriggerType
public WorkflowActionType = WorkflowActionType
templates: Workflow[]
correspondents: Correspondent[]
@@ -159,6 +164,124 @@ export class WorkflowEditDialogComponent
ngOnInit(): void {
super.ngOnInit()
this.updateAllTriggerActionFields()
this.objectForm.valueChanges.subscribe(
this.checkRemovalActionFields.bind(this)
)
this.checkRemovalActionFields(this.objectForm.value)
}
private checkRemovalActionFields(formWorkflow: Workflow) {
formWorkflow.actions
.filter((action) => action.type === WorkflowActionType.Removal)
.forEach((action, i) => {
if (action.remove_all_tags) {
this.actionFields
.at(i)
.get('remove_tags')
.disable({ emitEvent: false })
} else {
this.actionFields
.at(i)
.get('remove_tags')
.enable({ emitEvent: false })
}
if (action.remove_all_document_types) {
this.actionFields
.at(i)
.get('remove_document_types')
.disable({ emitEvent: false })
} else {
this.actionFields
.at(i)
.get('remove_document_types')
.enable({ emitEvent: false })
}
if (action.remove_all_correspondents) {
this.actionFields
.at(i)
.get('remove_correspondents')
.disable({ emitEvent: false })
} else {
this.actionFields
.at(i)
.get('remove_correspondents')
.enable({ emitEvent: false })
}
if (action.remove_all_storage_paths) {
this.actionFields
.at(i)
.get('remove_storage_paths')
.disable({ emitEvent: false })
} else {
this.actionFields
.at(i)
.get('remove_storage_paths')
.enable({ emitEvent: false })
}
if (action.remove_all_custom_fields) {
this.actionFields
.at(i)
.get('remove_custom_fields')
.disable({ emitEvent: false })
} else {
this.actionFields
.at(i)
.get('remove_custom_fields')
.enable({ emitEvent: false })
}
if (action.remove_all_owners) {
this.actionFields
.at(i)
.get('remove_owners')
.disable({ emitEvent: false })
} else {
this.actionFields
.at(i)
.get('remove_owners')
.enable({ emitEvent: false })
}
if (action.remove_all_permissions) {
this.actionFields
.at(i)
.get('remove_view_users')
.disable({ emitEvent: false })
this.actionFields
.at(i)
.get('remove_view_groups')
.disable({ emitEvent: false })
this.actionFields
.at(i)
.get('remove_change_users')
.disable({ emitEvent: false })
this.actionFields
.at(i)
.get('remove_change_groups')
.disable({ emitEvent: false })
} else {
this.actionFields
.at(i)
.get('remove_view_users')
.enable({ emitEvent: false })
this.actionFields
.at(i)
.get('remove_view_groups')
.enable({ emitEvent: false })
this.actionFields
.at(i)
.get('remove_change_users')
.enable({ emitEvent: false })
this.actionFields
.at(i)
.get('remove_change_groups')
.enable({ emitEvent: false })
}
})
}
get triggerFields(): FormArray {
@@ -215,6 +338,31 @@ export class WorkflowEditDialogComponent
assign_change_users: new FormControl(action.assign_change_users),
assign_change_groups: new FormControl(action.assign_change_groups),
assign_custom_fields: new FormControl(action.assign_custom_fields),
remove_tags: new FormControl(action.remove_tags),
remove_all_tags: new FormControl(action.remove_all_tags),
remove_document_types: new FormControl(action.remove_document_types),
remove_all_document_types: new FormControl(
action.remove_all_document_types
),
remove_correspondents: new FormControl(action.remove_correspondents),
remove_all_correspondents: new FormControl(
action.remove_all_correspondents
),
remove_storage_paths: new FormControl(action.remove_storage_paths),
remove_all_storage_paths: new FormControl(
action.remove_all_storage_paths
),
remove_owners: new FormControl(action.remove_owners),
remove_all_owners: new FormControl(action.remove_all_owners),
remove_view_users: new FormControl(action.remove_view_users),
remove_view_groups: new FormControl(action.remove_view_groups),
remove_change_users: new FormControl(action.remove_change_users),
remove_change_groups: new FormControl(action.remove_change_groups),
remove_all_permissions: new FormControl(action.remove_all_permissions),
remove_custom_fields: new FormControl(action.remove_custom_fields),
remove_all_custom_fields: new FormControl(
action.remove_all_custom_fields
),
}),
{ emitEvent }
)
@@ -290,6 +438,23 @@ export class WorkflowEditDialogComponent
assign_change_users: [],
assign_change_groups: [],
assign_custom_fields: [],
remove_tags: [],
remove_all_tags: false,
remove_document_types: [],
remove_all_document_types: false,
remove_correspondents: [],
remove_all_correspondents: false,
remove_storage_paths: [],
remove_all_storage_paths: false,
remove_owners: [],
remove_all_owners: false,
remove_view_users: [],
remove_view_groups: [],
remove_change_users: [],
remove_change_groups: [],
remove_all_permissions: false,
remove_custom_fields: [],
remove_all_custom_fields: false,
}
this.object.actions.push(action)
this.createActionField(action)

View File

@@ -45,10 +45,18 @@
</div>
}
@if (editing) {
<button class="list-group-item list-group-item-action bg-light" (click)="applyClicked()" [disabled]="!modelIsDirty || disabled">
<small class="ms-2" [ngClass]="{'fw-bold': modelIsDirty}" i18n>Apply</small>
<i-bs width="1.5em" height="1em" name="arrow-right"></i-bs>
</button>
@if ((selectionModel.itemsSorted | filter: filterText).length === 0 && createRef !== undefined) {
<button class="list-group-item list-group-item-action bg-light" (click)="createClicked()" [disabled]="disabled">
<small class="ms-2"><ng-container i18n>Create</ng-container> "{{filterText}}"</small>
<i-bs width="1.5em" height="1em" name="plus"></i-bs>
</button>
}
@if ((selectionModel.itemsSorted | filter: filterText).length > 0) {
<button class="list-group-item list-group-item-action bg-light" (click)="applyClicked()" [disabled]="!modelIsDirty || disabled">
<small class="ms-2" [ngClass]="{'fw-bold': modelIsDirty}" i18n>Apply</small>
<i-bs width="1.5em" height="1em" name="arrow-right"></i-bs>
</button>
}
}
@if (!editing && manyToOne) {
<div class="list-group-item list-group-item-note pt-1 pb-2">

View File

@@ -25,6 +25,7 @@ import {
import { TagComponent } from '../tag/tag.component'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const items: Tag[] = [
{
@@ -63,7 +64,12 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
ClearableBadgeComponent,
],
providers: [FilterPipe],
imports: [NgbModule, FormsModule, ReactiveFormsModule],
imports: [
NgbModule,
FormsModule,
ReactiveFormsModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()
fixture = TestBed.createComponent(FilterableDropdownComponent)
@@ -215,6 +221,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should apply changes and close when apply button clicked', () => {
component.items = items
component.icon = 'tag-fill'
component.editing = true
component.selectionModel = selectionModel
fixture.nativeElement
@@ -236,6 +243,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should apply on close if enabled', () => {
component.items = items
component.icon = 'tag-fill'
component.editing = true
component.applyOnClose = true
component.selectionModel = selectionModel
@@ -253,6 +261,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should focus text filter on open, support filtering, clear on close', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
@@ -279,6 +288,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should toggle & close on enter inside filter field if 1 item remains', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
expect(component.selectionModel.getSelectedItems()).toEqual([])
fixture.nativeElement
.querySelector('button')
@@ -298,6 +308,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should apply & close on enter inside filter field if 1 item remains if editing', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
component.editing = true
let applyResult: ChangedItems
component.apply.subscribe((result) => (applyResult = result))
@@ -319,6 +330,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should support arrow keyboard navigation', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
@@ -363,6 +375,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should support arrow keyboard navigation after tab keyboard navigation', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
@@ -398,6 +411,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should support arrow keyboard navigation after click', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
@@ -422,6 +436,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should toggle logical operator', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
component.manyToOne = true
selectionModel.set(items[0].id, ToggleableItemState.Selected)
selectionModel.set(items[1].id, ToggleableItemState.Selected)
@@ -450,6 +465,7 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
it('should toggle intersection include / exclude', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
selectionModel.set(items[0].id, ToggleableItemState.Selected)
selectionModel.set(items[1].id, ToggleableItemState.Selected)
component.selectionModel = selectionModel
@@ -477,11 +493,88 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
expect(changedResult.getExcludedItems()).toEqual(items)
}))
it('FilterableDropdownSelectionModel should sort items by state', () => {
component.items = items
it('selection model should sort items by state', () => {
component.items = items.concat([{ id: null, name: 'Null B' }])
component.selectionModel = selectionModel
selectionModel.toggle(items[1].id)
selectionModel.apply()
expect(selectionModel.itemsSorted).toEqual([nullItem, items[1], items[0]])
expect(selectionModel.itemsSorted).toEqual([
nullItem,
{ id: null, name: 'Null B' },
items[1],
items[0],
])
})
it('should set support create, keep open model and call createRef method', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
component.selectionModel = selectionModel
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
component.filterText = 'Test Filter Text'
component.createRef = jest.fn()
component.createClicked()
expect(component.creating).toBeTruthy()
expect(component.createRef).toHaveBeenCalledWith('Test Filter Text')
const openSpy = jest.spyOn(component.dropdown, 'open')
component.dropdownOpenChange(false)
expect(openSpy).toHaveBeenCalled() // should keep open
}))
it('should call create on enter inside filter field if 0 items remain while editing', fakeAsync(() => {
component.items = items
component.icon = 'tag-fill'
component.editing = true
component.createRef = jest.fn()
const createSpy = jest.spyOn(component, 'createClicked')
expect(component.selectionModel.getSelectedItems()).toEqual([])
fixture.nativeElement
.querySelector('button')
.dispatchEvent(new MouseEvent('click')) // open
fixture.detectChanges()
tick(100)
component.filterText = 'FooBar'
fixture.detectChanges()
component.listFilterTextInput.nativeElement.dispatchEvent(
new KeyboardEvent('keyup', { key: 'Enter' })
)
expect(component.selectionModel.getSelectedItems()).toEqual([])
tick(300)
expect(createSpy).toHaveBeenCalled()
}))
it('should exclude item and trigger change event', () => {
const id = 1
const state = ToggleableItemState.Selected
component.selectionModel = selectionModel
component.manyToOne = true
component.selectionModel.singleSelect = true
component.selectionModel.intersection = Intersection.Include
component.selectionModel['temporarySelectionStates'].set(id, state)
const changedSpy = jest.spyOn(component.selectionModel.changed, 'next')
component.selectionModel.exclude(id)
expect(component.selectionModel.temporaryLogicalOperator).toBe(
LogicalOperator.And
)
expect(component.selectionModel['temporarySelectionStates'].get(id)).toBe(
ToggleableItemState.Excluded
)
expect(component.selectionModel['temporarySelectionStates'].size).toBe(1)
expect(changedSpy).toHaveBeenCalled()
})
it('should initialize selection states and apply changes', () => {
selectionModel.items = items
const map = new Map<number, ToggleableItemState>()
map.set(1, ToggleableItemState.Selected)
map.set(2, ToggleableItemState.Excluded)
selectionModel.init(map)
expect(selectionModel.getSelectedItems()).toEqual([items[0]])
expect(selectionModel.getExcludedItems()).toEqual([items[1]])
})
})

View File

@@ -275,7 +275,7 @@ export class FilterableDropdownSelectionModel {
)
}
init(map) {
init(map: Map<number, ToggleableItemState>) {
this.temporarySelectionStates = map
this.apply()
}
@@ -398,6 +398,11 @@ export class FilterableDropdownComponent {
@Input()
disabled = false
@Input()
createRef: (name) => void
creating: boolean = false
@Output()
apply = new EventEmitter<ChangedItems>()
@@ -437,6 +442,11 @@ export class FilterableDropdownComponent {
}
}
createClicked() {
this.creating = true
this.createRef(this.filterText)
}
dropdownOpenChange(open: boolean): void {
if (open) {
setTimeout(() => {
@@ -448,9 +458,14 @@ export class FilterableDropdownComponent {
}
this.opened.next(this)
} else {
this.filterText = ''
if (this.applyOnClose && this.selectionModel.isDirty()) {
this.apply.emit(this.selectionModel.diff())
if (this.creating) {
this.dropdown.open()
this.creating = false
} else {
this.filterText = ''
if (this.applyOnClose && this.selectionModel.isDirty()) {
this.apply.emit(this.selectionModel.diff())
}
}
}
}
@@ -466,6 +481,8 @@ export class FilterableDropdownComponent {
this.dropdown.close()
}
}, 200)
} else if (filtered.length == 0 && this.createRef) {
this.createClicked()
}
}

View File

@@ -1,4 +1,4 @@
import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core'
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { MatchingModel } from 'src/app/data/matching-model'
export enum ToggleableItemState {

View File

@@ -7,6 +7,7 @@ import {
import { ColorComponent } from './color.component'
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
import { ColorSliderModule } from 'ngx-color/slider'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('ColorComponent', () => {
let component: ColorComponent
@@ -22,6 +23,7 @@ describe('ColorComponent', () => {
ReactiveFormsModule,
NgbPopoverModule,
ColorSliderModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()

View File

@@ -12,6 +12,7 @@ import {
} from '@ng-bootstrap/ng-bootstrap'
import { RouterTestingModule } from '@angular/router/testing'
import { LocalizedDateParserFormatter } from 'src/app/utils/ngb-date-parser-formatter'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('DateComponent', () => {
let component: DateComponent
@@ -33,6 +34,7 @@ describe('DateComponent', () => {
HttpClientTestingModule,
NgbDatepickerModule,
RouterTestingModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()

View File

@@ -0,0 +1,27 @@
<div class="mb-3" [class.pb-3]="error">
<div class="row">
<div class="d-flex align-items-center position-relative hidden-button-container" [class.col-md-3]="horizontal">
@if (title) {
<label class="form-label" [class.mb-md-0]="horizontal" [for]="inputId">{{title}}</label>
}
@if (removable) {
<button type="button" class="btn btn-sm btn-danger position-absolute left-0" (click)="removed.emit(this)">
<i-bs name="x"></i-bs>&nbsp;<ng-container i18n>Remove</ng-container>
</button>
}
</div>
<div class="position-relative" [class.col-md-9]="horizontal">
<div class="input-group" [class.is-invalid]="error">
<span class="input-group-text fw-bold bg-light">{{monetaryValue | currency: currencyCode }}</span>
<input #currencyField class="form-control text-muted mw-60" tabindex="0" [(ngModel)]="currencyCode" maxlength="3" [class.is-invalid]="error" (change)="onChange(value)" [disabled]="disabled">
<input #inputField type="number" tabindex="0" class="form-control text-muted" step=".01" [id]="inputId" [(ngModel)]="monetaryValue" (change)="onChange(value)" [class.is-invalid]="error" [disabled]="disabled">
</div>
<div class="invalid-feedback position-absolute top-100">
{{error}}
</div>
@if (hint) {
<small class="form-text text-muted">{{hint}}</small>
}
</div>
</div>
</div>

View File

@@ -0,0 +1,11 @@
.input-group-text {
font-size: inherit;
}
.text-muted:focus-within {
color: var(--bs-body-color) !important;
}
.mw-60 {
max-width: 60px;
}

View File

@@ -0,0 +1,65 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import {
FormsModule,
NG_VALUE_ACCESSOR,
ReactiveFormsModule,
} from '@angular/forms'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { CurrencyPipe } from '@angular/common'
import { MonetaryComponent } from './monetary.component'
describe('MonetaryComponent', () => {
let component: MonetaryComponent
let fixture: ComponentFixture<MonetaryComponent>
let input: HTMLInputElement
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [MonetaryComponent],
providers: [CurrencyPipe],
imports: [FormsModule, ReactiveFormsModule, HttpClientTestingModule],
}).compileComponents()
fixture = TestBed.createComponent(MonetaryComponent)
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
component = fixture.componentInstance
fixture.detectChanges()
input = component.inputField.nativeElement
})
it('should set the currency code correctly', () => {
expect(component.currencyCode).toEqual('USD') // default
component.currencyCode = 'EUR'
expect(component.currencyCode).toEqual('EUR')
component.value = 'G123.4'
jest
.spyOn(document, 'activeElement', 'get')
.mockReturnValue(component.currencyField.nativeElement)
expect(component.currencyCode).toEqual('G')
})
it('should parse monetary value only when out of focus', () => {
component.monetaryValue = 10.5
jest.spyOn(document, 'activeElement', 'get').mockReturnValue(null)
expect(component.monetaryValue).toEqual('10.50')
component.value = 'GBP123.4'
jest
.spyOn(document, 'activeElement', 'get')
.mockReturnValue(component.inputField.nativeElement)
expect(component.monetaryValue).toEqual('123.4')
})
it('should report value including currency code and monetary value', () => {
component.currencyCode = 'EUR'
component.monetaryValue = 10.5
expect(component.value).toEqual('EUR10.50')
})
it('should set the default currency code based on LOCALE_ID', () => {
expect(component.defaultCurrencyCode).toEqual('USD') // default
component = new MonetaryComponent('pt-BR')
expect(component.defaultCurrencyCode).toEqual('BRL')
})
})

View File

@@ -0,0 +1,61 @@
import {
Component,
ElementRef,
forwardRef,
Inject,
LOCALE_ID,
ViewChild,
} from '@angular/core'
import { NG_VALUE_ACCESSOR } from '@angular/forms'
import { AbstractInputComponent } from '../abstract-input'
import { getLocaleCurrencyCode } from '@angular/common'
@Component({
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MonetaryComponent),
multi: true,
},
],
selector: 'pngx-input-monetary',
templateUrl: './monetary.component.html',
styleUrls: ['./monetary.component.scss'],
})
export class MonetaryComponent extends AbstractInputComponent<string> {
@ViewChild('currencyField')
currencyField: ElementRef
defaultCurrencyCode: string
constructor(@Inject(LOCALE_ID) currentLocale: string) {
super()
this.defaultCurrencyCode = getLocaleCurrencyCode(currentLocale)
}
get currencyCode(): string {
const focused = document.activeElement === this.currencyField?.nativeElement
if (focused && this.value) return this.value.match(/^([A-Z]{0,3})/)?.[0]
return (
this.value
?.toString()
.toUpperCase()
.match(/^([A-Z]{1,3})/)?.[0] ?? this.defaultCurrencyCode
)
}
set currencyCode(value: string) {
this.value = value + this.monetaryValue?.toString()
}
get monetaryValue(): string {
if (!this.value) return null
const focused = document.activeElement === this.inputField?.nativeElement
const val = parseFloat(this.value.toString().replace(/[^0-9.,]+/g, ''))
return focused ? val.toString() : val.toFixed(2)
}
set monetaryValue(value: number) {
this.value = this.currencyCode + value.toFixed(2)
}
}

View File

@@ -47,17 +47,25 @@ describe('NumberComponent', () => {
expect(component.value).toEqual(1002)
})
it('should support float & monetary values', () => {
component.writeValue(11.13)
expect(component.value).toEqual(11)
it('should support float, monetary values & scientific notation', () => {
const mockFn = jest.fn()
component.registerOnChange(mockFn)
component.step = 1
component.onChange(11.13)
expect(mockFn).toHaveBeenCalledWith(11)
component.onChange(1.23456789e8)
expect(mockFn).toHaveBeenCalledWith(123456789)
component.step = 0.01
component.onChange(11.1)
expect(mockFn).toHaveBeenCalledWith('11.10')
})
it('should display monetary values fixed to 2 decimals', () => {
component.step = 0.01
component.writeValue(11.1)
expect(component.value).toEqual('11.10')
component.step = 0.1
component.writeValue(12.3456)
expect(component.value).toEqual(12.3456)
// float (step = .1) doesn't force 2 decimals
component.writeValue(11.1)
expect(component.value).toEqual(11.1)
})
})

View File

@@ -36,8 +36,18 @@ export class NumberComponent extends AbstractInputComponent<number> {
})
}
registerOnChange(fn: any): void {
this.onChange = (newValue: any) => {
// number validation
if (this.step === 1 && newValue?.toString().indexOf('e') === -1)
newValue = parseInt(newValue, 10)
if (this.step === 0.01) newValue = parseFloat(newValue).toFixed(2)
fn(newValue)
}
}
writeValue(newValue: any): void {
if (this.step === 1) newValue = parseInt(newValue, 10)
// Allow monetary values to be displayed with 2 decimals
if (this.step === 0.01) newValue = parseFloat(newValue).toFixed(2)
super.writeValue(newValue)
}

View File

@@ -6,6 +6,7 @@ import {
} from '@angular/forms'
import { PasswordComponent } from './password.component'
import { By } from '@angular/platform-browser'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('PasswordComponent', () => {
let component: PasswordComponent
@@ -16,7 +17,11 @@ describe('PasswordComponent', () => {
TestBed.configureTestingModule({
declarations: [PasswordComponent],
providers: [],
imports: [FormsModule, ReactiveFormsModule],
imports: [
FormsModule,
ReactiveFormsModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()
fixture = TestBed.createComponent(PasswordComponent)

View File

@@ -1,4 +1,4 @@
<div class="paperless-input-select">
<div class="paperless-input-select" [class.disabled]="disabled">
<div>
<ng-select name="inputId" [(ngModel)]="value"
[disabled]="disabled"

View File

@@ -0,0 +1,11 @@
.paperless-input-select.disabled {
cursor: not-allowed;
::ng-deep ng-select {
pointer-events: none;
.ng-select-container {
background-color: var(--pngx-bg-alt) !important;
}
}
}

View File

@@ -1,4 +1,4 @@
<div class="paperless-input-select">
<div class="paperless-input-select" [class.disabled]="disabled">
<div>
<ng-select name="inputId" [(ngModel)]="value"
[disabled]="disabled"

View File

@@ -0,0 +1,11 @@
.paperless-input-select.disabled {
cursor: not-allowed;
::ng-deep ng-select {
pointer-events: none;
.ng-select-container {
background-color: var(--pngx-bg-alt) !important;
}
}
}

View File

@@ -1,6 +1,7 @@
// styles for ng-select child are in styles.scss
.paperless-input-select.disabled {
.input-group {
.input-group,
div > div {
cursor: not-allowed;
}

View File

@@ -118,4 +118,18 @@ describe('SelectComponent', () => {
tick(3000)
expect(clearSpy).toHaveBeenCalled()
}))
it('should emit filtered documents', () => {
component.value = 10
component.items = items
const emitSpy = jest.spyOn(component.filterDocuments, 'emit')
component.onFilterDocuments()
expect(emitSpy).toHaveBeenCalledWith([items[2]])
})
it('should return the correct filter button title', () => {
component.title = 'Tag'
const expectedTitle = `Filter documents with this ${component.title}`
expect(component.filterButtonTitle).toEqual(expectedTitle)
})
})

View File

@@ -15,7 +15,7 @@
}
</div>
}
<div [ngClass]="{'col-md-9': horizontal, 'align-items-center': horizontal, 'd-flex': horizontal}">
<div [ngClass]="{'align-items-center': horizontal, 'd-flex': horizontal}">
<div class="form-check form-switch">
<input #inputField type="checkbox" class="form-check-input" [id]="inputId" [(ngModel)]="value" (change)="onChange(value)" (blur)="onTouched()" [disabled]="disabled">
@if (horizontal) {

View File

@@ -30,6 +30,7 @@ import { ColorComponent } from '../color/color.component'
import { PermissionsFormComponent } from '../permissions/permissions-form/permissions-form.component'
import { SelectComponent } from '../select/select.component'
import { SettingsService } from 'src/app/services/settings.service'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const tags: Tag[] = [
{
@@ -99,6 +100,7 @@ describe('TagsComponent', () => {
NgbModalModule,
NgbAccordionModule,
NgbPopoverModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()
@@ -167,4 +169,12 @@ describe('TagsComponent', () => {
expect(component.getTag(2)).toEqual(tags[1])
expect(component.getTag(4)).toBeUndefined()
})
it('should emit filtered documents', () => {
component.value = [10]
component.tags = tags
const emitSpy = jest.spyOn(component.filterDocuments, 'emit')
component.onFilterDocuments()
expect(emitSpy).toHaveBeenCalledWith([tags[2]])
})
})

View File

@@ -5,6 +5,7 @@ import {
NG_VALUE_ACCESSOR,
} from '@angular/forms'
import { UrlComponent } from './url.component'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
describe('TextComponent', () => {
let component: UrlComponent
@@ -15,7 +16,11 @@ describe('TextComponent', () => {
TestBed.configureTestingModule({
declarations: [UrlComponent],
providers: [],
imports: [FormsModule, ReactiveFormsModule],
imports: [
FormsModule,
ReactiveFormsModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()
fixture = TestBed.createComponent(UrlComponent)

View File

@@ -1,5 +1,5 @@
@if (customLogo) {
<img src="{{customLogo}}" height="100%" width="100%" [attr.style]="'height:'+height" />
<img src="{{customLogo}}" [class]="getClasses()" [attr.style]="'height:'+height" />
} @else {
<svg [class]="getClasses()" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2897.4 896.6" [attr.style]="'height:'+height">
<path class="leaf" d="M140,713.7c-3.4-16.4-10.3-49.1-11.2-49.1c-145.7-87.1-128.4-238-80.2-324.2C59,449,251.2,524,139.1,656.8 c-0.9,1.7,5.2,22.4,10.3,41.4c22.4-37.9,56-83.6,54.3-87.9C65.9,273.9,496.9,248.1,586.6,39.4c40.5,201.8-20.7,513.9-367.2,593.2 c-1.7,0.9-62.9,108.6-65.5,109.5c0-1.7-25.9-0.9-22.4-9.5C133.1,727.4,136.6,720.6,140,713.7L140,713.7z M135.7,632.6 c44-50.9-7.8-137.9-38.8-166.4C149.5,556.7,146,609.3,135.7,632.6L135.7,632.6z" transform="translate(0)" style="fill:#17541f"/>

View File

@@ -19,7 +19,7 @@
}
</h3>
</div>
<div class="btn-toolbar col col-md-auto">
<div class="btn-toolbar col col-md-auto gap-2">
<ng-content></ng-content>
</div>
</div>

View File

@@ -5,12 +5,15 @@
</div>
<div class="modal-body">
@if (!object && message) {
<p class="mb-3" [innerHTML]="message | safeHtml"></p>
}
<form [formGroup]="form">
<pngx-permissions-form [users]="users" formControlName="permissions_form"></pngx-permissions-form>
<div class="form-group">
<pngx-permissions-form [users]="users" formControlName="permissions_form"></pngx-permissions-form>
</div>
<div class="form-group mt-4">
<div class="offset-lg-3 row">
<pngx-input-switch i18n-title title="Merge with existing permissions" [horizontal]="true" [hint]="hint" formControlName="merge"></pngx-input-switch>
</div>
</div>
</form>
</div>
@@ -20,5 +23,5 @@
<span class="visually-hidden" i18n>Loading...</span>
}
<button type="button" class="btn btn-outline-primary" (click)="cancelClicked()" [disabled]="!buttonsEnabled" i18n>Cancel</button>
<button type="button" class="btn btn-primary" (click)="confirmClicked.emit(permissions)" [disabled]="!buttonsEnabled" i18n>Confirm</button>
<button type="button" class="btn btn-primary" (click)="confirm()" [disabled]="!buttonsEnabled" i18n>Confirm</button>
</div>

View File

@@ -11,6 +11,7 @@ import { NgSelectModule } from '@ng-select/ng-select'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { PermissionsUserComponent } from '../input/permissions/permissions-user/permissions-user.component'
import { PermissionsGroupComponent } from '../input/permissions/permissions-group/permissions-group.component'
import { SwitchComponent } from '../input/switch/switch.component'
const set_permissions = {
owner: 10,
@@ -37,6 +38,7 @@ describe('PermissionsDialogComponent', () => {
PermissionsDialogComponent,
SafeHtmlPipe,
SelectComponent,
SwitchComponent,
PermissionsFormComponent,
PermissionsUserComponent,
PermissionsGroupComponent,
@@ -112,4 +114,23 @@ describe('PermissionsDialogComponent', () => {
expect(component.title).toEqual(`Edit permissions for ${obj.name}`)
expect(component.permissions).toEqual(set_permissions)
})
it('should toggle hint based on object existence (if editing) or merge flag', () => {
component.form.get('merge').setValue(true)
expect(component.hint.includes('Existing')).toBeTruthy()
component.form.get('merge').setValue(false)
expect(component.hint.includes('will be replaced')).toBeTruthy()
component.object = {}
expect(component.hint).toBeNull()
})
it('should emit permissions and merge flag on confirm', () => {
const confirmSpy = jest.spyOn(component.confirmClicked, 'emit')
component.form.get('permissions_form').setValue(set_permissions)
component.confirm()
expect(confirmSpy).toHaveBeenCalledWith({
permissions: set_permissions,
merge: true,
})
})
})

View File

@@ -32,6 +32,7 @@ export class PermissionsDialogComponent {
this.o = o
this.title = $localize`Edit permissions for ` + o['name']
this.form.patchValue({
merge: true,
permissions_form: {
owner: o.owner,
set_permissions: o.permissions,
@@ -43,8 +44,9 @@ export class PermissionsDialogComponent {
return this.o
}
form = new FormGroup({
public form = new FormGroup({
permissions_form: new FormControl(),
merge: new FormControl(true),
})
buttonsEnabled: boolean = true
@@ -66,11 +68,21 @@ export class PermissionsDialogComponent {
}
}
@Input()
message =
$localize`Note that permissions set here will override any existing permissions`
get hint(): string {
if (this.object) return null
return this.form.get('merge').value
? $localize`Existing owner, user and group permissions will be merged with these settings.`
: $localize`Any and all existing owner, user and group permissions will be replaced.`
}
cancelClicked() {
this.activeModal.close()
}
confirm() {
this.confirmClicked.emit({
permissions: this.permissions,
merge: this.form.get('merge').value,
})
}
}

View File

@@ -62,22 +62,24 @@
<i-bs width="1em" height="1em" name="check"></i-bs>
}
</div>
<div class="me-1 w-100">
<ng-select
name="user"
class="user-select small"
[(ngModel)]="selectionModel.includeUsers"
[disabled]="disabled"
[clearable]="false"
[items]="users"
bindLabel="username"
multiple="true"
bindValue="id"
placeholder="Users"
i18n-placeholder
(change)="onUserSelect()">
</ng-select>
</div>
@if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.User)) {
<div class="me-1 w-100">
<ng-select
name="user"
class="user-select small"
[(ngModel)]="selectionModel.includeUsers"
[disabled]="disabled"
[clearable]="false"
[items]="users"
bindLabel="username"
multiple="true"
bindValue="id"
placeholder="Users"
i18n-placeholder
(change)="onUserSelect()">
</ng-select>
</div>
}
</button>
@if (selectionModel.ownerFilter === OwnerFilterType.NONE || selectionModel.ownerFilter === OwnerFilterType.NOT_SELF) {
<div class="list-group-item list-group-item-action d-flex align-items-center p-2 ps-3 border-bottom-0 border-start-0 border-end-0">

View File

@@ -14,6 +14,7 @@ import {
import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component'
import { SettingsService } from 'src/app/services/settings.service'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const currentUserID = 13
@@ -69,6 +70,7 @@ describe('PermissionsFilterDropdownComponent', () => {
FormsModule,
ReactiveFormsModule,
NgbModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()

View File

@@ -67,7 +67,7 @@ export class PermissionsFilterDropdownComponent extends ComponentWithPermissions
}
constructor(
permissionsService: PermissionsService,
public permissionsService: PermissionsService,
userService: UserService,
private settingsService: SettingsService
) {

View File

@@ -11,6 +11,7 @@ import {
PermissionType,
} from 'src/app/services/permissions.service'
import { By } from '@angular/platform-browser'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const permissions = [
'add_document',
@@ -32,7 +33,12 @@ describe('PermissionsSelectComponent', () => {
TestBed.configureTestingModule({
declarations: [PermissionsSelectComponent],
providers: [],
imports: [FormsModule, ReactiveFormsModule, NgbModule],
imports: [
FormsModule,
ReactiveFormsModule,
NgbModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()
fixture = TestBed.createComponent(PermissionsSelectComponent)

View File

@@ -8,6 +8,7 @@ import { SettingsService } from 'src/app/services/settings.service'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { DocumentService } from 'src/app/services/rest/document.service'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
const doc = {
id: 10,
@@ -25,7 +26,10 @@ describe('PreviewPopupComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [PreviewPopupComponent, PdfViewerComponent, SafeUrlPipe],
imports: [HttpClientTestingModule],
imports: [
HttpClientTestingModule,
NgxBootstrapIconsModule.pick(allIcons),
],
})
settingsService = TestBed.inject(SettingsService)
documentService = TestBed.inject(DocumentService)

View File

@@ -41,14 +41,58 @@
}
<span class="visually-hidden" i18n>Copy</span>
</button>
<button type="button" class="btn btn-outline-secondary" (click)="generateAuthToken()" i18n-title title="Regenerate auth token">
<i-bs width="1.2em" height="1.2em" name="arrow-repeat"></i-bs>
</button>
<pngx-confirm-button
title="Regenerate auth token"
i18n-title
buttonClasses=" btn-outline-secondary"
iconName="arrow-repeat"
[disabled]="!hasUsablePassword"
(confirm)="generateAuthToken()">
</pngx-confirm-button>
</div>
<span class="badge copied-badge bg-primary small fade ms-4 position-absolute top-50 translate-middle-y pe-none z-3" [class.show]="copied" i18n>Copied!</span>
</div>
<div class="form-text text-muted text-end fst-italic" i18n>Warning: changing the token cannot be undone</div>
</div>
@if (socialAccounts?.length > 0) {
<div class="mb-3">
<p i18n>Connected social accounts</p>
<ul class="list-group">
@for (account of socialAccounts; track account.id) {
<li class="list-group-item"
ngbPopover="Set a password before disconnecting social account."
i18n-ngbPopover
[disablePopover]="hasUsablePassword"
triggers="mouseenter:mouseleave">
{{account.name}} ({{account.provider}})
<pngx-confirm-button
label="Disconnect"
i18n-label
title="Disconnect {{ account.name }} social account"
i18n-title
buttonClasses="btn-outline-danger btn-sm ms-2 align-baseline"
iconName="trash"
[disabled]="!hasUsablePassword"
(confirm)="disconnectSocialAccount(account.id)">
</pngx-confirm-button>
</li>
}
</ul>
<div class="form-text text-muted text-end fst-italic" i18n>Warning: disconnecting social accounts cannot be undone</div>
</div>
}
@if (socialAccountProviders?.length > 0) {
<div class="mb-3">
<p i18n>Connect new social account</p>
<div class="list-group">
@for (provider of socialAccountProviders; track provider.name) {
<a class="list-group-item list-group-item-action text-primary d-flex align-items-center" href="{{ provider.login_url }}" rel="noopener noreferrer">
{{provider.name}}&nbsp;<i-bs class="pb-1 ps-1" name="box-arrow-up-right"></i-bs>
</a>
}
</div>
</div>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" (click)="cancel()" i18n [disabled]="networkActive">Cancel</button>

View File

@@ -12,6 +12,7 @@ import {
NgbAccordionModule,
NgbActiveModal,
NgbModalModule,
NgbPopoverModule,
} from '@ng-bootstrap/ng-bootstrap'
import { HttpClientModule } from '@angular/common/http'
import { TextComponent } from '../input/text/text.component'
@@ -19,14 +20,25 @@ import { PasswordComponent } from '../input/password/password.component'
import { of, throwError } from 'rxjs'
import { ToastService } from 'src/app/services/toast.service'
import { Clipboard } from '@angular/cdk/clipboard'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { ConfirmButtonComponent } from '../confirm-button/confirm-button.component'
const socialAccount = {
id: 1,
provider: 'test_provider',
name: 'Test Provider',
}
const profile = {
email: 'foo@bar.com',
password: '*********',
first_name: 'foo',
last_name: 'bar',
auth_token: '123456789abcdef',
social_accounts: [socialAccount],
}
const socialAccountProviders = [
{ name: 'Test Provider', login_url: 'https://example.com' },
]
describe('ProfileEditDialogComponent', () => {
let component: ProfileEditDialogComponent
@@ -41,6 +53,7 @@ describe('ProfileEditDialogComponent', () => {
ProfileEditDialogComponent,
TextComponent,
PasswordComponent,
ConfirmButtonComponent,
],
providers: [NgbActiveModal],
imports: [
@@ -49,6 +62,8 @@ describe('ProfileEditDialogComponent', () => {
FormsModule,
NgbModalModule,
NgbAccordionModule,
NgxBootstrapIconsModule.pick(allIcons),
NgbPopoverModule,
],
})
profileService = TestBed.inject(ProfileService)
@@ -62,6 +77,11 @@ describe('ProfileEditDialogComponent', () => {
it('should get profile on init, display in form', () => {
const getSpy = jest.spyOn(profileService, 'get')
getSpy.mockReturnValue(of(profile))
const getProvidersSpy = jest.spyOn(
profileService,
'getSocialAccountProviders'
)
getProvidersSpy.mockReturnValue(of(socialAccountProviders))
component.ngOnInit()
expect(getSpy).toHaveBeenCalled()
fixture.detectChanges()
@@ -101,6 +121,11 @@ describe('ProfileEditDialogComponent', () => {
expect(component.form.get('email_confirm').enabled).toBeFalsy()
const getSpy = jest.spyOn(profileService, 'get')
getSpy.mockReturnValue(of(profile))
const getProvidersSpy = jest.spyOn(
profileService,
'getSocialAccountProviders'
)
getProvidersSpy.mockReturnValue(of(socialAccountProviders))
component.ngOnInit()
component.form.get('email').patchValue('foo@bar2.com')
component.onEmailKeyUp({ target: { value: 'foo@bar2.com' } } as any)
@@ -132,6 +157,12 @@ describe('ProfileEditDialogComponent', () => {
expect(component.form.get('password_confirm').enabled).toBeFalsy()
const getSpy = jest.spyOn(profileService, 'get')
getSpy.mockReturnValue(of(profile))
const getProvidersSpy = jest.spyOn(
profileService,
'getSocialAccountProviders'
)
getProvidersSpy.mockReturnValue(of(socialAccountProviders))
component.hasUsablePassword = true
component.ngOnInit()
component.form.get('password').patchValue('new*pass')
component.onPasswordKeyUp({
@@ -165,6 +196,11 @@ describe('ProfileEditDialogComponent', () => {
it('should logout on save if password changed', fakeAsync(() => {
const getSpy = jest.spyOn(profileService, 'get')
getSpy.mockReturnValue(of(profile))
const getProvidersSpy = jest.spyOn(
profileService,
'getSocialAccountProviders'
)
getProvidersSpy.mockReturnValue(of(socialAccountProviders))
component.ngOnInit()
component['newPassword'] = 'new*pass'
component.form.get('password').patchValue('new*pass')
@@ -187,6 +223,11 @@ describe('ProfileEditDialogComponent', () => {
it('should support auth token copy', fakeAsync(() => {
const getSpy = jest.spyOn(profileService, 'get')
getSpy.mockReturnValue(of(profile))
const getProvidersSpy = jest.spyOn(
profileService,
'getSocialAccountProviders'
)
getProvidersSpy.mockReturnValue(of(socialAccountProviders))
component.ngOnInit()
const copySpy = jest.spyOn(clipboard, 'copy')
component.copyAuthToken()
@@ -218,4 +259,40 @@ describe('ProfileEditDialogComponent', () => {
)
expect(component.form.get('auth_token').value).toEqual(newToken)
})
it('should get social account providers on init', () => {
const getSpy = jest.spyOn(profileService, 'get')
getSpy.mockReturnValue(of(profile))
const getProvidersSpy = jest.spyOn(
profileService,
'getSocialAccountProviders'
)
getProvidersSpy.mockReturnValue(of(socialAccountProviders))
component.ngOnInit()
expect(getProvidersSpy).toHaveBeenCalled()
})
it('should remove disconnected social account from component, show error if needed', () => {
const disconnectSpy = jest.spyOn(profileService, 'disconnectSocialAccount')
const getSpy = jest.spyOn(profileService, 'get')
getSpy.mockImplementation(() => of(profile))
component.ngOnInit()
const errorSpy = jest.spyOn(toastService, 'showError')
expect(component.socialAccounts).toContainEqual(socialAccount)
// fail first
disconnectSpy.mockReturnValueOnce(
throwError(() => new Error('unable to disconnect'))
)
component.disconnectSocialAccount(socialAccount.id)
expect(errorSpy).toHaveBeenCalled()
// succeed
disconnectSpy.mockReturnValue(of(socialAccount.id))
component.disconnectSocialAccount(socialAccount.id)
expect(disconnectSpy).toHaveBeenCalled()
expect(component.socialAccounts).not.toContainEqual(socialAccount)
})
})

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