Compare commits

...

628 Commits

Author SHA1 Message Date
shamoon
2368fd15cb Bump version to 2.13.3 2024-11-02 22:19:03 -07:00
shamoon
cc35b321c5 Merge branch 'dev' 2024-11-02 22:18:35 -07:00
github-actions[bot]
1ddbc31c59 New Crowdin translations by GitHub Action (#8159)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-11-02 21:58:06 -07:00
github-actions[bot]
6ae8ab4af3 New Crowdin translations by GitHub Action (#8130)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-11-02 21:56:35 -07:00
shamoon
37dc791301 Fix: fix auto-clean PDFs, create parent dir for storing unmodified original (#8157) 2024-11-02 20:54:28 -07:00
Yichi Yang
5fed311ffc Fix: correctly handle "exists, false" in custom field query filter (#8158) 2024-11-02 18:40:50 -07:00
dependabot[bot]
e74f182662 Chore(deps-dev): Bump the frontend-eslint-dependencies group (#8145)
Bumps the frontend-eslint-dependencies group in /src-ui with 4 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), [@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils) and [eslint](https://github.com/eslint/eslint).


Updates `@typescript-eslint/eslint-plugin` from 8.8.0 to 8.12.2
- [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/v8.12.2/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.8.0 to 8.12.2
- [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/v8.12.2/packages/parser)

Updates `@typescript-eslint/utils` from 8.8.0 to 8.12.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.12.2/packages/utils)

Updates `eslint` from 9.11.1 to 9.14.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/v9.11.1...v9.14.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-02 02:44:00 +00:00
dependabot[bot]
3f094c88bd Chore(deps-dev): Bump @types/node from 22.7.4 to 22.8.6 in /src-ui (#8148)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.7.4 to 22.8.6.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-01 23:44:28 +00:00
dependabot[bot]
631eaa2109 Chore(deps-dev): Bump @playwright/test from 1.47.2 to 1.48.2 in /src-ui (#8147)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.47.2 to 1.48.2.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.47.2...v1.48.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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-01 23:35:04 +00:00
dependabot[bot]
27fadf6963 Chore(deps): Bump uuid from 10.0.0 to 11.0.2 in /src-ui (#8146)
Bumps [uuid](https://github.com/uuidjs/uuid) from 10.0.0 to 11.0.2.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v10.0.0...v11.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-01 23:25:07 +00:00
dependabot[bot]
d53b300a7f Chore(deps): Bump tslib from 2.7.0 to 2.8.1 in /src-ui (#8149)
Bumps [tslib](https://github.com/Microsoft/tslib) from 2.7.0 to 2.8.1.
- [Release notes](https://github.com/Microsoft/tslib/releases)
- [Commits](https://github.com/Microsoft/tslib/compare/v2.7.0...v2.8.1)

---
updated-dependencies:
- dependency-name: tslib
  dependency-type: direct:production
  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-11-01 23:13:13 +00:00
dependabot[bot]
6cc626fd86 Chore(deps-dev): Bump @codecov/webpack-plugin in /src-ui (#8150)
Bumps @codecov/webpack-plugin from 1.2.0 to 1.2.1.

---
updated-dependencies:
- dependency-name: "@codecov/webpack-plugin"
  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-11-01 23:01:54 +00:00
dependabot[bot]
8b8f1af513 Chore(deps-dev): Bump @types/jest (#8144)
Bumps the frontend-jest-dependencies group in /src-ui with 1 update: [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest).


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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-01 22:45:03 +00:00
dependabot[bot]
4226a1bddc Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 21 updates (#8143)
* Chore(deps): Bump the frontend-angular-dependencies group

Bumps the frontend-angular-dependencies group in /src-ui with 21 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `18.2.6` | `18.2.11` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `18.2.6` | `18.2.10` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `18.2.6` | `18.2.10` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `18.2.6` | `18.2.10` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `18.2.6` | `18.2.10` |
| [@angular/localize](https://github.com/angular/angular) | `18.2.6` | `18.2.10` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `18.2.6` | `18.2.10` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `18.2.6` | `18.2.10` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `18.2.6` | `18.2.10` |
| [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `13.9.0` | `13.9.1` |
| [ng2-pdf-viewer](https://github.com/VadimDez/ng2-pdf-viewer) | `10.3.1` | `10.3.4` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `18.2.6` | `18.2.11` |
| [@angular-devkit/core](https://github.com/angular/angular-cli) | `18.2.6` | `18.2.11` |
| [@angular-devkit/schematics](https://github.com/angular/angular-cli) | `18.2.6` | `18.2.11` |
| [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `18.3.1` | `18.4.0` |
| [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `18.3.1` | `18.4.0` |
| [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `18.3.1` | `18.4.0` |
| [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `18.3.1` | `18.4.0` |
| [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `18.3.1` | `18.4.0` |
| [@angular/cli](https://github.com/angular/angular-cli) | `18.2.6` | `18.2.11` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `18.2.6` | `18.2.10` |


Updates `@angular/cdk` from 18.2.6 to 18.2.11
- [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/18.2.6...18.2.11)

Updates `@angular/common` from 18.2.6 to 18.2.10
- [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/18.2.10/packages/common)

Updates `@angular/compiler` from 18.2.6 to 18.2.10
- [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/18.2.10/packages/compiler)

Updates `@angular/core` from 18.2.6 to 18.2.10
- [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/18.2.10/packages/core)

Updates `@angular/forms` from 18.2.6 to 18.2.10
- [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/18.2.10/packages/forms)

Updates `@angular/localize` from 18.2.6 to 18.2.10
- [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/18.2.6...18.2.10)

Updates `@angular/platform-browser` from 18.2.6 to 18.2.10
- [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/18.2.10/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 18.2.6 to 18.2.10
- [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/18.2.10/packages/platform-browser-dynamic)

Updates `@angular/router` from 18.2.6 to 18.2.10
- [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/18.2.10/packages/router)

Updates `@ng-select/ng-select` from 13.9.0 to 13.9.1
- [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/v13.9.0...v13.9.1)

Updates `ng2-pdf-viewer` from 10.3.1 to 10.3.4
- [Release notes](https://github.com/VadimDez/ng2-pdf-viewer/releases)
- [Changelog](https://github.com/VadimDez/ng2-pdf-viewer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/VadimDez/ng2-pdf-viewer/compare/10.3.1...10.3.4)

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

Updates `@angular-devkit/core` from 18.2.6 to 18.2.11
- [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/18.2.6...18.2.11)

Updates `@angular-devkit/schematics` from 18.2.6 to 18.2.11
- [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/18.2.6...18.2.11)

Updates `@angular-eslint/builder` from 18.3.1 to 18.4.0
- [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/v18.4.0/packages/builder)

Updates `@angular-eslint/eslint-plugin` from 18.3.1 to 18.4.0
- [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/v18.4.0/packages/eslint-plugin)

Updates `@angular-eslint/eslint-plugin-template` from 18.3.1 to 18.4.0
- [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/v18.4.0/packages/eslint-plugin-template)

Updates `@angular-eslint/schematics` from 18.3.1 to 18.4.0
- [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/v18.4.0/packages/schematics)

Updates `@angular-eslint/template-parser` from 18.3.1 to 18.4.0
- [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/v18.4.0/packages/template-parser)

Updates `@angular/cli` from 18.2.6 to 18.2.11
- [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/18.2.6...18.2.11)

Updates `@angular/compiler-cli` from 18.2.6 to 18.2.10
- [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/18.2.10/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-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: "@ng-select/ng-select"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: ng2-pdf-viewer
  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-devkit/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-devkit/schematics"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  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-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>

* Change output name

---------

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-11-01 22:32:12 +00:00
shamoon
289a7a2348 Documentation: re-add 2.13.0 changelog (#8135) 2024-10-31 12:18:32 -07:00
horvatkm
702ab1b77a Chore: update field in bug-report.yml (#8123) 2024-10-30 22:07:35 +00:00
shamoon
e22ccbda26 Fix: dont use filters for inverted thumbnails (#8121) 2024-10-30 11:43:02 -07:00
shamoon
56d296f04b Fix: use static object for activedisplayfields to prevent changes (#8120) 2024-10-30 11:21:01 -07:00
shamoon
11cfa0871e Fix: dont invert pdf colors in FF (#8110) 2024-10-30 00:54:19 -07:00
shamoon
159344f033 Fix: make mail account password and refresh token text fields (#8107) 2024-10-29 21:54:47 -07:00
github-actions[bot]
d8cfed5f5e Changelog v2.13.2 - GHA (#8103)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-29 12:45:11 -07:00
shamoon
94fe7a9e3d Bump version to 2.13.2 2024-10-29 11:09:19 -07:00
shamoon
9c9b4effe2 Merge branch 'dev' 2024-10-29 11:08:50 -07:00
github-actions[bot]
75f5007ede New Crowdin translations by GitHub Action (#8101)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-10-29 11:07:03 -07:00
github-actions[bot]
d95baf4e6b New Crowdin translations by GitHub Action (#8093)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-10-29 11:05:08 -07:00
shamoon
aac04e73b9 Fix: correct serializing of auth tokens for export (#8100) 2024-10-29 17:02:32 +00:00
shamoon
3af3484a00 Fix: cf query dropdown styling affecting other components (#8095) 2024-10-28 23:14:11 -07:00
github-actions[bot]
90e68af6cf Documentation: Add v2.13.1 changelog (#8085)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-10-28 12:44:21 -07:00
shamoon
eee08d389f Bump version to 2.13.1 2024-10-28 11:27:57 -07:00
github-actions[bot]
b3b0e95d2d New Crowdin translations by GitHub Action (#8083)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-10-28 11:00:35 -07:00
shamoon
35907313e8 Fix: allow removing dead document links from UI, validate via API (#8081) 2024-10-28 10:39:17 -07:00
github-actions[bot]
d4a20c7e30 New Crowdin translations by GitHub Action (#8080)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-10-28 09:00:31 -07:00
shamoon
3b1dffe0dc Fix: dont display trashed docs in doc links 2024-10-28 08:57:38 -07:00
shamoon
fa0ab0de27 Merge branch 'main' into dev 2024-10-28 08:42:07 -07:00
Trenton H
3b2b4a9177 Removes whitenoise patches and upgrades it to 6.8.1 (#8079) 2024-10-28 15:34:26 +00:00
github-actions[bot]
3d030637ca New Crowdin translations by GitHub Action (#8025)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-10-28 07:03:42 -07:00
shamoon
79092c27c5 Fix: Make customfieldinstance soft delete, fix filepath when deleted (#8067) 2024-10-28 14:02:09 +00:00
shamoon
28fdb170bf Fix: handle uuid fields created under mariadb and Django 4 (#8034) 2024-10-28 13:54:16 +00:00
Trenton H
335c6c3820 Fix: Update filename correctly if the document is in the trash (#8066)
* Fixes an issue where the filename is not updated if the document is in the trash (but the file is moved)
2024-10-28 02:45:31 +00:00
Trenton H
ad23cce2e6 Handle a special case where the none marker exists in a way which could create an absolute path (#8060) 2024-10-28 01:51:07 +00:00
shamoon
0d96cd03d5 Fix: disable custom field signals during import in 2.13.0 (#8065) 2024-10-27 18:43:24 -07:00
shamoon
1888ee6a3f Fix: doc link documents search should exclude null (#8064) 2024-10-27 17:13:29 -07:00
marph91
605aa50b00 Documentation: Fix list indentation (#8050)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-10-28 00:02:06 +00:00
shamoon
149d770ad1 Enhancement: auto-update document filenames with CF select fields (#8045) 2024-10-27 23:45:21 +00:00
shamoon
b2e9f3195a Fix: fix custom field query empty element removal (#8056) 2024-10-27 06:53:59 -07:00
shamoon
33e9990ed5 Chore: fix changelog script (#8022) 2024-10-27 03:36:12 +00:00
shamoon
e775b6346a Fix: dont try to load PAPERLESS_MODEL_FILE as env from file (#8040) 2024-10-26 22:29:20 +00:00
shamoon
54e17f5b74 Documentation: fix docs index page on mobile (#8035) 2024-10-26 11:36:40 -07:00
shamoon
ff1639d58b Fix: dont include all allauth urls (#8010) 2024-10-26 15:06:22 +00:00
shamoon
7649903d3c Enhancement / fix: include social accounts and api tokens in export (#8016) 2024-10-26 06:51:22 -07:00
tooomm
7a5d707fc0 Documentation: Resolve display and link errors in changelog (#8019)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-10-26 01:13:22 -07:00
github-actions[bot]
85e00aecb4 Documentation: Add v2.13.0 changelog (#8009)
* Changelog v2.13.0 - GHA

* Update changelog.md

* Update changelog.md

* 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-10-26 01:00:19 -07:00
shamoon
53aa216a4a Fix: oauth settings without base url (#8020) 2024-10-26 00:57:36 -07:00
shamoon
2814cd110d Merge pull request #7902 from paperless-ngx/beta
[Beta] Paperless-ngx v2.13.0 Beta Release
2024-10-25 10:13:40 -07:00
shamoon
b9315b018a Merge branch 'dev' into beta 2024-10-25 09:33:44 -07:00
github-actions[bot]
27f7ba8cf8 New Crowdin translations by GitHub Action (#8008)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-10-25 09:33:22 -07:00
shamoon
df9917b0f4 Merge branch 'dev' into beta 2024-10-25 09:18:11 -07:00
github-actions[bot]
86418f6e04 New Crowdin translations by GitHub Action (#7911)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-10-25 09:17:18 -07:00
shamoon
69a6a12319 Correct repo maintenance documentation 2024-10-25 09:16:24 -07:00
shamoon
b501d89846 Update repo maintenance rules 2024-10-24 16:15:59 -07:00
shamoon
f6548e0e55 Enhancement: automatically refresh display list after custom field edit 2024-10-22 09:19:22 -07:00
shamoon
a4b8bf1250 Fix: prevent focus error on custom field edit when switching from select 2024-10-20 19:19:30 -07:00
shamoon
1726aec989 Enhancement: automatically refresh display list after custom field edit 2024-10-20 19:17:18 -07:00
shamoon
549312859e Merge branch 'dev' into beta 2024-10-20 19:06:52 -07:00
Trenton H
544e9c4fe2 Use a TextField for the storage path field (#7967) 2024-10-20 11:23:46 -07:00
shamoon
0520db5e93 Update translation strings 2024-10-19 22:56:57 -07:00
shamoon
1cb85d41f3 Change: dont show remove button at all from doc link dropdowns if disabled 2024-10-19 17:23:37 -07:00
tooomm
fb94a5d377 Fix: remove space before my profile button in dropdown (#7963) 2024-10-19 17:17:53 -07:00
shamoon
85e2081e40 Merge branch 'dev' into beta 2024-10-18 22:36:27 -07:00
shamoon
71e2565386 Enhancement: auto-focus default select field in custom field dropdown (#7961) 2024-10-18 22:35:57 -07:00
shamoon
82be90f7ff Merge branch 'dev' into beta 2024-10-17 14:33:05 -07:00
shamoon
f0ad073bb2 Fix: document link field consistent behavior with insufficient permissions (#7953) 2024-10-17 14:32:56 -07:00
shamoon
61c804a6e3 Merge branch 'dev' into beta 2024-10-16 16:57:08 -07:00
shamoon
de95b296a0 Change: open not edit (#7942) 2024-10-16 16:56:40 -07:00
shamoon
86a57838a8 Fix: tweak icon placement in small doc card buttons 2024-10-16 16:39:20 -07:00
shamoon
bddb9bfad8 Update preview-popup.component.scss 2024-10-15 21:58:25 -07:00
shamoon
7098ec9bf5 Merge branch 'dev' into beta 2024-10-15 10:56:53 -07:00
Trenton H
c2cfaaf8af Fix: Handling of Nones when using custom fields in filepath templating (#7933) 2024-10-15 17:54:15 +00:00
shamoon
6292296876 Fix: trigger move and rename after custom fields saved (#7927) 2024-10-15 10:08:50 -07:00
shamoon
9f68e0f76a Merge branch 'dev' into beta 2024-10-14 07:57:01 -07:00
shamoon
4e849b545a Fix: v2.13.0 RC1: increase field max lengths to accommodate larger tokens (#7916) 2024-10-14 07:56:47 -07:00
shamoon
e43fee41cb Merge branch 'main' into beta 2024-10-14 07:56:21 -07:00
shamoon
613f8a0065 Bump version to 2.13.0 2024-10-14 07:56:14 -07:00
shamoon
baf6484454 Chore: include beta in tagged version display 2024-10-14 07:55:03 -07:00
shamoon
9b84dc06b6 Enhancement: support retain barcode split pages (#7912) 2024-10-13 20:51:39 -07:00
shamoon
cb617531bc Fix: preserve text linebreaks in doc edit (#7908) 2024-10-13 10:45:05 -07:00
shamoon
8e61a29137 Chore: dont run repo maintenance in forks 2024-10-13 10:44:13 -07:00
tooomm
dfcecb3a5c Chore: do not run Crowdin action on forks (#7904) 2024-10-13 08:37:41 -07:00
github-actions[bot]
0a61b8e6fc New Crowdin translations by GitHub Action (#7728)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-10-12 20:58:10 -07:00
shamoon
e78d758656 Fix: resolve some silly frontend test things 2024-10-10 20:38:24 -07:00
shamoon
073c42984a Enhancement: dont wait to get preview (#7894) 2024-10-10 19:43:13 -07:00
shamoon
2994f3a740 Fix: only show colon on cards if correspondent and title shown (#7893) 2024-10-10 15:07:52 -07:00
shamoon
2353f7c2db Feature: OAuth2 Gmail and Outlook email support (#7866) 2024-10-10 20:57:32 +00:00
shamoon
dcc8d4046a Chore: Unify workflow logic (#7880) 2024-10-10 20:28:44 +00:00
shamoon
024b60638a Feature: live preview of storage path (#7870) 2024-10-09 23:35:36 +00:00
shamoon
8dd355f6bf Chore: fix test comments 2024-10-08 23:36:09 -07:00
Trenton H
cf3645c296 Fixes the ASN checking to allow an ASN of 0 (#7878) 2024-10-08 12:47:37 -07:00
shamoon
facec317ef Fix: fix auto-dismiss completed tasks on open document (#7869) 2024-10-07 07:25:52 -07:00
shamoon
95d1abd416 Fix: trigger change warning for saved views with default fields if changed (#7865) 2024-10-06 14:27:02 -07:00
Trenton H
7c11a37150 Feature: Enhanced templating for filename format (#7836)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-10-06 12:54:01 -07:00
shamoon
e49ed58f1a Fix: skip accounts without enabled rules 2024-10-04 23:59:31 -07:00
shamoon
54293bedb1 Enhancement: management list button improvements (#7848) 2024-10-03 23:00:28 -07:00
shamoon
fc683e150a Add missing interface to resolve test warning 2024-10-02 20:21:47 -07:00
Martin Richtarsky
b3487f1843 Enhancement: check for mail destination directory, log post-consume errors (#7808)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-10-02 20:21:35 -07:00
shamoon
f8d79b012f Feature: custom fields queries (#7761) 2024-10-03 00:15:42 +00:00
shamoon
2e3637d712 Update .codecov.yml 2024-10-01 21:37:35 -07:00
shamoon
74001bd0da Fix: wrap table header columns in row (#7832) 2024-10-01 17:37:52 -07:00
dependabot[bot]
374a1ceb05 Chore(deps-dev): Bump @codecov/webpack-plugin in /src-ui (#7830)
Bumps @codecov/webpack-plugin from 1.0.1 to 1.2.0.

---
updated-dependencies:
- dependency-name: "@codecov/webpack-plugin"
  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-10-01 21:38:03 +00:00
dependabot[bot]
59f726b2a2 Chore(deps-dev): Bump @types/node from 22.5.2 to 22.7.4 in /src-ui (#7829)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.5.2 to 22.7.4.
- [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-10-01 21:29:28 +00:00
dependabot[bot]
77bebc861d Chore(deps-dev): Bump the frontend-eslint-dependencies group (#7827)
Bumps the frontend-eslint-dependencies group in /src-ui with 4 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), [@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils) and [eslint](https://github.com/eslint/eslint).


Updates `@typescript-eslint/eslint-plugin` from 8.3.0 to 8.8.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/v8.8.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.3.0 to 8.8.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/v8.8.0/packages/parser)

Updates `@typescript-eslint/utils` from 8.3.0 to 8.8.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.8.0/packages/utils)

Updates `eslint` from 9.9.1 to 9.11.1
- [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/v9.9.1...v9.11.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-01 21:20:53 +00:00
dependabot[bot]
85e57ede9b Chore(deps-dev): Bump the frontend-jest-dependencies group (#7826)
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.12 to 29.5.13
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Updates `jest-preset-angular` from 14.2.2 to 14.2.4
- [Release notes](https://github.com/thymikee/jest-preset-angular/releases)
- [Changelog](https://github.com/thymikee/jest-preset-angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/thymikee/jest-preset-angular/compare/v14.2.2...v14.2.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-01 21:10:16 +00:00
dependabot[bot]
a7424a7bfe Chore(deps-dev): Bump @playwright/test from 1.46.1 to 1.47.2 in /src-ui (#7828)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.46.1 to 1.47.2.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.46.1...v1.47.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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-01 13:59:11 -07:00
dependabot[bot]
46b8e536a8 Chore(deps): Bump the frontend-angular-dependencies group (#7825)
Bumps the frontend-angular-dependencies group in /src-ui with 21 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `18.2.2` | `18.2.6` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `18.2.2` | `18.2.6` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `18.2.2` | `18.2.6` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `18.2.2` | `18.2.6` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `18.2.2` | `18.2.6` |
| [@angular/localize](https://github.com/angular/angular) | `18.2.2` | `18.2.6` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `18.2.2` | `18.2.6` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `18.2.2` | `18.2.6` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `18.2.2` | `18.2.6` |
| [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `13.7.0` | `13.9.0` |
| [ng2-pdf-viewer](https://github.com/VadimDez/ng2-pdf-viewer) | `10.3.0` | `10.3.1` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `18.2.2` | `18.2.6` |
| [@angular-devkit/core](https://github.com/angular/angular-cli) | `18.2.2` | `18.2.6` |
| [@angular-devkit/schematics](https://github.com/angular/angular-cli) | `18.2.2` | `18.2.6` |
| [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `18.3.0` | `18.3.1` |
| [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `18.3.0` | `18.3.1` |
| [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `18.3.0` | `18.3.1` |
| [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `18.3.0` | `18.3.1` |
| [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `18.3.0` | `18.3.1` |
| [@angular/cli](https://github.com/angular/angular-cli) | `18.2.2` | `18.2.6` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `18.2.2` | `18.2.6` |


Updates `@angular/cdk` from 18.2.2 to 18.2.6
- [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/18.2.2...18.2.6)

Updates `@angular/common` from 18.2.2 to 18.2.6
- [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/18.2.6/packages/common)

Updates `@angular/compiler` from 18.2.2 to 18.2.6
- [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/18.2.6/packages/compiler)

Updates `@angular/core` from 18.2.2 to 18.2.6
- [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/18.2.6/packages/core)

Updates `@angular/forms` from 18.2.2 to 18.2.6
- [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/18.2.6/packages/forms)

Updates `@angular/localize` from 18.2.2 to 18.2.6
- [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/18.2.2...18.2.6)

Updates `@angular/platform-browser` from 18.2.2 to 18.2.6
- [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/18.2.6/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 18.2.2 to 18.2.6
- [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/18.2.6/packages/platform-browser-dynamic)

Updates `@angular/router` from 18.2.2 to 18.2.6
- [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/18.2.6/packages/router)

Updates `@ng-select/ng-select` from 13.7.0 to 13.9.0
- [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/v13.7.0...v13.9.0)

Updates `ng2-pdf-viewer` from 10.3.0 to 10.3.1
- [Release notes](https://github.com/VadimDez/ng2-pdf-viewer/releases)
- [Changelog](https://github.com/VadimDez/ng2-pdf-viewer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/VadimDez/ng2-pdf-viewer/compare/10.3.0...10.3.1)

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

Updates `@angular-devkit/core` from 18.2.2 to 18.2.6
- [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/18.2.2...18.2.6)

Updates `@angular-devkit/schematics` from 18.2.2 to 18.2.6
- [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/18.2.2...18.2.6)

Updates `@angular-eslint/builder` from 18.3.0 to 18.3.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/v18.3.1/packages/builder)

Updates `@angular-eslint/eslint-plugin` from 18.3.0 to 18.3.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/v18.3.1/packages/eslint-plugin)

Updates `@angular-eslint/eslint-plugin-template` from 18.3.0 to 18.3.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/v18.3.1/packages/eslint-plugin-template)

Updates `@angular-eslint/schematics` from 18.3.0 to 18.3.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/v18.3.1/packages/schematics)

Updates `@angular-eslint/template-parser` from 18.3.0 to 18.3.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/v18.3.1/packages/template-parser)

Updates `@angular/cli` from 18.2.2 to 18.2.6
- [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/18.2.2...18.2.6)

Updates `@angular/compiler-cli` from 18.2.2 to 18.2.6
- [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/18.2.6/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-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: "@ng-select/ng-select"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: ng2-pdf-viewer
  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-devkit/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-devkit/schematics"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/builder"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/eslint-plugin-template"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/schematics"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-eslint/template-parser"
  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-10-01 13:43:28 -07:00
Trenton H
2ab71137b9 Chore: Upgrades OCRMyPDF to v16 (#7815) 2024-10-01 02:53:44 +00:00
shamoon
0b829cab32 Enhancement: workflow overview toggle enable button (#7818) 2024-09-30 19:44:02 -07:00
shamoon
991c9b0ca4 Enhancement: disable-able mail rules, add toggle to overview (#7810) 2024-09-30 19:42:19 -07:00
Trenton H
b9c1ba8a1d Documentation: Mention the Redis licensing and its forks (#7817)
* Mention the Redis licensing and its forks

* Note date

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-10-01 01:58:07 +00:00
Martin Richtarsky
c9e33a3401 Documentation: update development docker build steps (#7806) 2024-09-30 14:56:24 +00:00
Trenton H
dd9b10bdf8 Upgrades the Docker image to use Python 3.12 (#7796) 2024-09-28 18:42:55 +00:00
Trenton H
546fd2740b Chore: Upgrade Django to 5.1 (#7795)
* Upgrades Django to 5.1
2024-09-28 18:06:13 +00:00
shamoon
56e1365b4b Fix page_count migration 2024-09-27 21:47:30 -07:00
Trenton H
e6f59472e4 Chore: Drop Python 3.9 support (#7774) 2024-09-26 12:22:24 -07:00
shamoon
5e687d9a93 Feature: auto-clean some invalid pdfs (#7651) 2024-09-25 15:57:20 +00:00
s0llvan
c92c3e224a Feature: page count (#7750)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-09-25 08:22:12 -07:00
shamoon
4adf20af1e Fix: hidden canvas element causes scroll bug (#7770) 2024-09-24 12:26:48 -07:00
gawa971
a9b7965dcf Enhancement: use apt only when needed docker-entrypoint.sh (#7756) 2024-09-23 23:46:03 -07:00
shamoon
d7ba6d98d3 Feature: Enhanced backend custom field search API (#7589)
commit 910dae8413028f647e6295f30207cb5d4fc6605d
Author: Yichi Yang <yiy067@ucsd.edu>
Date:   Wed Sep 4 12:47:19 2024 -0700

    Fix: correctly handle the case where custom_field_lookup refers to multiple fields

commit e43f70d708b7d6b445f3ca8c8bf9dbdf5ee26085
Author: Yichi Yang <yiy067@ucsd.edu>
Date:   Sat Aug 31 14:06:45 2024 -0700

Co-Authored-By: Yichi Yang <yichiyan@usc.edu>
2024-09-23 23:33:49 -07:00
Matthieu
f6135f9ad0 Documentation: fix link to redis security docs (#7766) 2024-09-23 14:45:35 -07:00
shamoon
f06ff85b7d Simplify this a bit more 2024-09-23 10:46:20 -07:00
shamoon
1b7cacc877 Enhancement: compactify dates dropdown (#7759) 2024-09-23 10:30:13 -07:00
shamoon
870d6ee782 Fix: handle overflowing dropdowns on mobile (#7758)
See https://github.com/ng-bootstrap/ng-bootstrap/pull/4760
2024-09-23 10:29:37 -07:00
shamoon
3aba68c09f Fix sidebar mobile width 2024-09-23 10:27:16 -07:00
shamoon
609fa9a212 Enhancement: set Django SESSION_EXPIRE_AT_BROWSER_CLOSE from PAPERLESS_ACCOUNT_SESSION_REMEMBER (#7748) 2024-09-20 10:36:40 -07:00
shamoon
16069cde23 Documentation: fix session cookie config type 2024-09-19 19:20:13 -07:00
shamoon
a440c88b81 Enhancement: allow setting session cookie age (#7743) 2024-09-19 18:58:40 -07:00
shamoon
97030a807f Merge branch 'main' into dev 2024-09-19 16:24:34 -07:00
shamoon
4146b140d3 Documentation: clarify session remember 2024-09-19 07:13:37 -07:00
shamoon
fa6f013db5 Fix: chrome scrolling in >= 129 (#7738) 2024-09-18 17:09:42 -07:00
shamoon
6192c15c4d Feature: copy workflows and mail rules, improve layout (#7727) 2024-09-16 22:02:51 -07:00
shamoon
8aa35540b5 Fix a random test error 2024-09-16 19:36:44 -07:00
dependabot[bot]
e787055294 Chore(deps-dev): Bump the development group with 2 updates (#7723)
* 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 [pytest](https://github.com/pytest-dev/pytest).


Updates `ruff` from 0.6.4 to 0.6.5
- [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/0.6.4...0.6.5)

Updates `pytest` from 8.3.2 to 8.3.3
- [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.3.2...8.3.3)

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

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

* Update .pre-commit-config.yaml

---------

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-09-16 23:25:33 +00:00
github-actions[bot]
045b62ca66 Changelog v2.12.1 - GHA (#7715)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-09-15 20:31:11 -07:00
shamoon
3b7fdb2f37 Bump version to 2.12.1 2024-09-15 20:08:24 -07:00
shamoon
bd1f05df24 Merge branch 'dev' 2024-09-15 20:07:59 -07:00
github-actions[bot]
eeeec498d4 New Crowdin translations by GitHub Action (#7668)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-09-15 19:40:35 -07:00
shamoon
0af2b967e4 Fix: wait to apply tag changes until other changes saved with multiple workflows (#7711) 2024-09-16 01:26:24 +00:00
shamoon
4193401be7 Fix: delete_pages should require ownership (#7714) 2024-09-15 16:24:40 -07:00
shamoon
36df6fd3e5 Enhancement: improve text contrast for selected documents in list view dark mode (#7712) 2024-09-15 11:40:59 -07:00
shamoon
86a540e68e Fix: filter out shown custom fields that have been deleted from saved view edit (#7710) 2024-09-15 08:02:38 -07:00
shamoon
fb3a881387 Refactor: allow filterpipe to specify key 2024-09-13 22:21:38 -07:00
shamoon
8e555cce9e Fix: only filter by string or number properties for filter pipe (#7699) 2024-09-13 20:41:31 -07:00
shamoon
4f8e59030e Documentation: move usually-unneeded 'mime-support' from bare-metal docs (#7689) 2024-09-11 12:58:53 -07:00
shamoon
5075d0bab0 Documentation: missed incorrect gpg decryptor reference 2024-09-11 10:53:52 -07:00
shamoon
fb3a136b32 Documentation: fix PAPERLESS_ENABLE_GPG_DECRYPTOR (#7688) 2024-09-11 10:39:41 -07:00
shamoon
66a8057e31 Fix: fix display of permissions filter users dropdown 2024-09-11 09:47:48 -07:00
shamoon
cb6cf7f771 Chore: Add codecov bundle analysis (#7673) 2024-09-10 17:55:12 -07:00
shamoon
9a7f95865f Chore: mark some more tests as flaky 2024-09-10 16:41:11 -07:00
shamoon
0d1e0bc70e Chore(deps): Bump express and related dependencies in /src-ui (#7674)
* Chore(deps): Bump send and express in /src-ui

Bumps [send](https://github.com/pillarjs/send) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `send` from 0.18.0 to 0.19.0
- [Release notes](https://github.com/pillarjs/send/releases)
- [Changelog](https://github.com/pillarjs/send/blob/master/HISTORY.md)
- [Commits](https://github.com/pillarjs/send/compare/0.18.0...0.19.0)

Updates `express` from 4.19.2 to 4.20.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.20.0)

---
updated-dependencies:
- dependency-name: send
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

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

* Chore(deps): Bump body-parser and express in /src-ui (#7676)

Bumps [body-parser](https://github.com/expressjs/body-parser) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `body-parser` from 1.20.2 to 1.20.3
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.20.2...1.20.3)

Updates `express` from 4.19.2 to 4.20.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.20.0)

---
updated-dependencies:
- dependency-name: body-parser
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

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

* Chore(deps): Bump serve-static and express in /src-ui (#7677)

Bumps [serve-static](https://github.com/expressjs/serve-static) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `serve-static` from 1.15.0 to 1.16.0
- [Release notes](https://github.com/expressjs/serve-static/releases)
- [Changelog](https://github.com/expressjs/serve-static/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/serve-static/compare/v1.15.0...1.16.0)

Updates `express` from 4.19.2 to 4.20.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.20.0)

---
updated-dependencies:
- dependency-name: serve-static
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-10 14:02:33 -07:00
shamoon
bee963c23d Fix: saved view permissions fixes (#7672) 2024-09-10 13:33:19 -07:00
shamoon
f1559b7108 Fix: add permissions to OPTIONS requests for notes (#7661) 2024-09-09 08:34:21 -07:00
github-actions[bot]
a64a182fc3 Documentation: Add v2.12.0 changelog (#7659)
---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-09-08 18:37:26 -07:00
shamoon
aeb49898e5 Bump version to 2.12.0 2024-09-08 17:47:20 -07:00
shamoon
a2c8fcd46b Merge branch 'dev' 2024-09-08 17:46:41 -07:00
github-actions[bot]
357ae92d88 New Crowdin translations by GitHub Action (#7658)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-09-08 16:53:13 -07:00
github-actions[bot]
74330623b3 New Crowdin translations by GitHub Action (#7528)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-09-08 16:51:37 -07:00
Trenton H
3813dc2e18 Chore: Update backend dependencies in bulk (#7656) 2024-09-08 20:03:38 +00:00
Trenton H
3df8be0bc7 Fix: Rework system check so it won't crash if tesseract is not found (#7640) 2024-09-08 12:17:32 -07:00
Lukas Metzger
cc25cbc026 Refactor: performance and storage optimization of barcode scanning (#7646)
---------

Co-authored-by: Lukas Metzger <1814751+loewexy@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-09-07 16:11:36 -07:00
shamoon
e98d52830f Fix: use JSON for note audit log entries (#7650) 2024-09-07 16:09:11 -07:00
shamoon
4903e4290d Enhancement: re-work mail rule dialog, support multiple include patterns (#7635) 2024-09-05 15:32:03 -07:00
shamoon
e1ba1a1898 Fix: correct broken worker src after upgrade to pdfjs v4 (#7626) 2024-09-05 00:26:16 -07:00
shamoon
b29c1e91d1 Fix: Re-fix non-clickable scroll wheel in file uploads list (#7591)
This partially reverts commit ee529c2276.
2024-09-04 23:37:23 -07:00
dependabot[bot]
a03c7701a5 Chore(deps): Bump cryptography from 42.0.8 to 43.0.1 (#7620)
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.8 to 43.0.1.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.8...43.0.1)

---
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-09-03 20:25:09 -07:00
shamoon
b8c9d1316c Update bug-report.yml 2024-09-02 16:13:49 -07:00
dependabot[bot]
a63ef26d38 Chore(deps-dev): Bump the development group with 3 updates (#7608)
* Chore(deps-dev): Bump the development group with 3 updates

Bumps the development group with 3 updates: [ruff](https://github.com/astral-sh/ruff), [pytest-django](https://github.com/pytest-dev/pytest-django) and [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `ruff` from 0.6.2 to 0.6.3
- [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/0.6.2...0.6.3)

Updates `pytest-django` from 4.8.0 to 4.9.0
- [Release notes](https://github.com/pytest-dev/pytest-django/releases)
- [Changelog](https://github.com/pytest-dev/pytest-django/blob/main/docs/changelog.rst)
- [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.8.0...v4.9.0)

Updates `mkdocs-material` from 9.5.33 to 9.5.34
- [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.33...9.5.34)

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

* Update .pre-commit-config.yaml

---------

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-09-02 23:13:23 +00:00
dependabot[bot]
96b2884458 Chore(deps): Bump rapidfuzz in the small-changes group (#7611)
Bumps the small-changes group with 1 update: [rapidfuzz](https://github.com/rapidfuzz/RapidFuzz).


Updates `rapidfuzz` from 3.9.6 to 3.9.7
- [Release notes](https://github.com/rapidfuzz/RapidFuzz/releases)
- [Changelog](https://github.com/rapidfuzz/RapidFuzz/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/rapidfuzz/RapidFuzz/compare/v3.9.6...v3.9.7)

---
updated-dependencies:
- dependency-name: rapidfuzz
  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-09-02 15:58:15 -07:00
shamoon
8543202723 Chore: remove unused frontend dependencies (#7607) 2024-09-01 17:11:19 -07:00
dependabot[bot]
a63904b7af Chore(deps): Bump tslib from 2.6.3 to 2.7.0 in /src-ui (#7606)
Bumps [tslib](https://github.com/Microsoft/tslib) from 2.6.3 to 2.7.0.
- [Release notes](https://github.com/Microsoft/tslib/releases)
- [Commits](https://github.com/Microsoft/tslib/compare/v2.6.3...v2.7.0)

---
updated-dependencies:
- dependency-name: tslib
  dependency-type: direct:production
  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-09-02 00:07:08 +00:00
dependabot[bot]
eb27bc9e7d Chore(deps-dev): Bump @playwright/test from 1.45.3 to 1.46.1 in /src-ui (#7603)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.45.3 to 1.46.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.45.3...v1.46.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-01 16:58:36 -07:00
dependabot[bot]
489b24ad65 Chore(deps-dev): Bump typescript from 5.4.5 to 5.5.4 in /src-ui (#7604)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.4.5 to 5.5.4.
- [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.4.5...v5.5.4)

---
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-09-01 16:03:02 -07:00
dependabot[bot]
5349eb2302 Chore(deps-dev): Bump the frontend-eslint-dependencies group (#7600)
Bumps the frontend-eslint-dependencies group in /src-ui with 4 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), [@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils) and [eslint](https://github.com/eslint/eslint).


Updates `@typescript-eslint/eslint-plugin` from 8.0.0 to 8.3.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/v8.3.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.0.0 to 8.3.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/v8.3.0/packages/parser)

Updates `@typescript-eslint/utils` from 8.0.0 to 8.3.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.3.0/packages/utils)

Updates `eslint` from 9.8.0 to 9.9.1
- [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/v9.8.0...v9.9.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-01 22:32:56 +00:00
dependabot[bot]
a8fd023398 Chore(deps): Bump the frontend-angular-dependencies group (#7599)
Bumps the frontend-angular-dependencies group in /src-ui with 21 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `18.1.3` | `18.2.2` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `18.1.3` | `18.2.2` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `18.1.3` | `18.2.2` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `18.1.3` | `18.2.2` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `18.1.3` | `18.2.2` |
| [@angular/localize](https://github.com/angular/angular) | `18.1.3` | `18.2.2` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `18.1.3` | `18.2.2` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `18.1.3` | `18.2.2` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `18.1.3` | `18.2.2` |
| [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `13.5.0` | `13.7.0` |
| [ng2-pdf-viewer](https://github.com/VadimDez/ng2-pdf-viewer) | `10.2.2` | `10.3.0` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `18.1.3` | `18.2.2` |
| [@angular-devkit/core](https://github.com/angular/angular-cli) | `18.1.3` | `18.2.2` |
| [@angular-devkit/schematics](https://github.com/angular/angular-cli) | `18.1.3` | `18.2.2` |
| [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `18.2.0` | `18.3.0` |
| [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `18.2.0` | `18.3.0` |
| [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `18.2.0` | `18.3.0` |
| [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `18.2.0` | `18.3.0` |
| [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `18.2.0` | `18.3.0` |
| [@angular/cli](https://github.com/angular/angular-cli) | `18.1.3` | `18.2.2` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `18.1.3` | `18.2.2` |


Updates `@angular/cdk` from 18.1.3 to 18.2.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/18.1.3...18.2.2)

Updates `@angular/common` from 18.1.3 to 18.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/18.2.2/packages/common)

Updates `@angular/compiler` from 18.1.3 to 18.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/18.2.2/packages/compiler)

Updates `@angular/core` from 18.1.3 to 18.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/18.2.2/packages/core)

Updates `@angular/forms` from 18.1.3 to 18.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/18.2.2/packages/forms)

Updates `@angular/localize` from 18.1.3 to 18.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/18.1.3...18.2.2)

Updates `@angular/platform-browser` from 18.1.3 to 18.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/18.2.2/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 18.1.3 to 18.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/18.2.2/packages/platform-browser-dynamic)

Updates `@angular/router` from 18.1.3 to 18.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/18.2.2/packages/router)

Updates `@ng-select/ng-select` from 13.5.0 to 13.7.0
- [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/v13.5.0...v13.7.0)

Updates `ng2-pdf-viewer` from 10.2.2 to 10.3.0
- [Release notes](https://github.com/VadimDez/ng2-pdf-viewer/releases)
- [Changelog](https://github.com/VadimDez/ng2-pdf-viewer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/VadimDez/ng2-pdf-viewer/compare/10.2.2...10.3.0)

Updates `@angular-devkit/build-angular` from 18.1.3 to 18.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/18.1.3...18.2.2)

Updates `@angular-devkit/core` from 18.1.3 to 18.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/18.1.3...18.2.2)

Updates `@angular-devkit/schematics` from 18.1.3 to 18.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/18.1.3...18.2.2)

Updates `@angular-eslint/builder` from 18.2.0 to 18.3.0
- [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/v18.3.0/packages/builder)

Updates `@angular-eslint/eslint-plugin` from 18.2.0 to 18.3.0
- [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/v18.3.0/packages/eslint-plugin)

Updates `@angular-eslint/eslint-plugin-template` from 18.2.0 to 18.3.0
- [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/v18.3.0/packages/eslint-plugin-template)

Updates `@angular-eslint/schematics` from 18.2.0 to 18.3.0
- [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/v18.3.0/packages/schematics)

Updates `@angular-eslint/template-parser` from 18.2.0 to 18.3.0
- [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/v18.3.0/packages/template-parser)

Updates `@angular/cli` from 18.1.3 to 18.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/18.1.3...18.2.2)

Updates `@angular/compiler-cli` from 18.1.3 to 18.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/18.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-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: ng2-pdf-viewer
  dependency-type: direct:production
  update-type: version-update:semver-minor
  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-devkit/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-devkit/schematics"
  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-09-01 15:18:46 -07:00
tooomm
e34d48d913 Documentation: support use system preferences for light/dark mode (#7586) 2024-08-31 14:36:29 -07:00
shamoon
ee529c2276 Fix: fix non-clickable scroll wheel in file uploads list (#7591) 2024-08-31 14:02:47 -07:00
shamoon
3d5e45c20a Fix: deselect file tasks select all button on dismiss (#7592) 2024-08-31 14:02:37 -07:00
shamoon
b8283047ae Fix: saved view sidebar heading not always visible (#7584) 2024-08-30 15:43:08 -07:00
shamoon
dad3a1ff28 Feature: add Korean language (#7573) 2024-08-28 20:10:23 -07:00
Daniel Bankmann
ce663398e6 Enhancement: mail message preprocessor for gpg encrypted mails (#7456)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-08-29 00:22:44 +00:00
shamoon
f5ec6de047 Fix: correct select field wrapping with long text (#7572) 2024-08-28 16:39:14 -07:00
shamoon
807f788f92 Fix: update ng-bootstrap to fix datepicker bug (#7567) 2024-08-28 07:32:46 -07:00
Dennis Melzer
eaaaa575b8 Enhancement: allow multiple filename attachment exclusion patterns for a mail rule (#5524)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-08-27 09:31:46 -07:00
dependabot[bot]
e21552e053 Chore(deps): Bump pathvalidate in the small-changes group (#7548)
Bumps the small-changes group with 1 update: [pathvalidate](https://github.com/thombashi/pathvalidate).


Updates `pathvalidate` from 3.2.0 to 3.2.1
- [Release notes](https://github.com/thombashi/pathvalidate/releases)
- [Changelog](https://github.com/thombashi/pathvalidate/blob/master/CHANGELOG.md)
- [Commits](https://github.com/thombashi/pathvalidate/compare/v3.2.0...v3.2.1)

---
updated-dependencies:
- dependency-name: pathvalidate
  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-08-27 16:31:23 +00:00
dependabot[bot]
6a7274c414 Chore(deps): Bump micromatch from 4.0.5 to 4.0.8 in /src-ui (#7551)
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.5 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-26 19:48:12 -07:00
dependabot[bot]
35de04a2ce Chore(deps-dev): Bump the development group with 2 updates (#7545)
* 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.6.1 to 0.6.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/0.6.1...0.6.2)

Updates `mkdocs-material` from 9.5.32 to 9.5.33
- [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.32...9.5.33)

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

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

* Update .pre-commit-config.yaml

---------

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-08-27 00:53:37 +00:00
Yichi Yang
a0c227fe55 Refactor: Use django-filter logic for filtering full text search queries (#7507) 2024-08-24 21:20:43 -07:00
Yichi Yang
057ce29676 Refactor: Reduce number of SQL queries when serializing List[Document] (#7505) 2024-08-24 21:20:24 -07:00
github-actions[bot]
982eeb0d24 Documentation: Add v2.11.6 changelog (#7523)
* Changelog v2.11.6 - 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-08-22 21:06:24 -07:00
shamoon
dfa25343a3 Bump version to 2.11.6 2024-08-22 20:40:13 -07:00
shamoon
dcfb4494c9 Merge branch 'dev' 2024-08-22 20:40:01 -07:00
shamoon
5a1ef27224 Fix: fix nltk tokenizer breaking change (#7522) 2024-08-22 20:32:02 -07:00
github-actions[bot]
6f79ee9877 Changelog v2.11.5 - GHA (#7518)
Co-Authored-By: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-08-22 20:21:06 -07:00
github-actions[bot]
bc0e420d67 Changelog v2.11.5 - GHA (#7518)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-08-22 17:11:38 -07:00
shamoon
8e34756e6b Bump version to 2.11.5 2024-08-22 16:20:53 -07:00
github-actions[bot]
76951ea482 New Crowdin translations by GitHub Action (#7446)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-08-22 16:19:35 -07:00
shamoon
b5e4aaa778 Revert "Chore: add line to log indexing"
This reverts commit b34f9c3b20.
2024-08-20 22:11:20 -07:00
shamoon
39998cb34f Update backend translation strings 2024-08-19 23:30:59 -07:00
shamoon
a771d2afd9 Fix: use JSON for update archive file auditlog entries (#7503) 2024-08-19 23:29:24 -07:00
dependabot[bot]
dac3def6b9 Chore(deps): Bump the small-changes group across 1 directory with 6 updates (#7502)
* Chore(deps): Bump the small-changes group across 1 directory with 6 updates

Bumps the small-changes group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [django-filter](https://github.com/carltongibson/django-filter) | `24.2` | `24.3` |
| [django-soft-delete](https://github.com/san4ezy/django_softdelete) | `1.0.13` | `1.0.14` |
| [nltk](https://github.com/nltk/nltk) | `3.8.1` | `3.9.1` |
| [rapidfuzz](https://github.com/rapidfuzz/RapidFuzz) | `3.9.5` | `3.9.6` |
| [watchdog](https://github.com/gorakhargosh/watchdog) | `4.0.1` | `4.0.2` |
| [factory-boy](https://github.com/FactoryBoy/factory_boy) | `3.3.0` | `3.3.1` |



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

Updates `django-soft-delete` from 1.0.13 to 1.0.14
- [Commits](https://github.com/san4ezy/django_softdelete/commits)

Updates `nltk` from 3.8.1 to 3.9.1
- [Changelog](https://github.com/nltk/nltk/blob/develop/ChangeLog)
- [Commits](https://github.com/nltk/nltk/compare/3.8.1...3.9.1)

Updates `rapidfuzz` from 3.9.5 to 3.9.6
- [Release notes](https://github.com/rapidfuzz/RapidFuzz/releases)
- [Changelog](https://github.com/rapidfuzz/RapidFuzz/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/rapidfuzz/RapidFuzz/compare/v3.9.5...v3.9.6)

Updates `watchdog` from 4.0.1 to 4.0.2
- [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/v4.0.1...v4.0.2)

Updates `factory-boy` from 3.3.0 to 3.3.1
- [Changelog](https://github.com/FactoryBoy/factory_boy/blob/master/docs/changelog.rst)
- [Commits](https://github.com/FactoryBoy/factory_boy/compare/3.3.0...3.3.1)

---
updated-dependencies:
- dependency-name: django-filter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: django-soft-delete
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: nltk
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: rapidfuzz
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: watchdog
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: factory-boy
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: small-changes
...

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

* Create 1052_document_transaction_id.py

---------

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-08-19 19:51:02 -07:00
shamoon
674b4a839c Fix: respect deskew / rotate pages from AppConfig if set (#7501) 2024-08-19 19:38:40 -07:00
dependabot[bot]
037dcb6a11 Chore(deps-dev): Bump the development group with 2 updates (#7497)
* 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.5.7 to 0.6.1
- [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/0.5.7...0.6.1)

Updates `mkdocs-material` from 9.5.31 to 9.5.32
- [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.31...9.5.32)

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

* Update .pre-commit-config.yaml

---------

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-08-20 00:38:54 +00:00
Nicola Apicella
ad8c60d153 Documentation: add no-progress-bar option to importer/exporter (#7477)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-08-17 02:57:03 +00:00
dependabot[bot]
3ea312e136 Chore(deps-dev): Bump axios from 1.6.7 to 1.7.4 in /src-ui (#7472)
* Revert "Chore(deps): Bump the small-changes group with 3 updates (#7460)"

This reverts commit 0042e3eca4.

* Chore(deps-dev): Bump axios from 1.6.7 to 1.7.4 in /src-ui

Bumps [axios](https://github.com/axios/axios) from 1.6.7 to 1.7.4.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.6.7...v1.7.4)

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

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-15 15:32:14 +00:00
shamoon
d2a04743eb Revert "Chore(deps): Bump the small-changes group with 3 updates (#7460)"
This reverts commit 0042e3eca4.

See https://github.com/nltk/nltk/issues/3301
2024-08-15 08:24:17 -07:00
shamoon
b34f9c3b20 Chore: add line to log indexing 2024-08-15 08:03:35 -07:00
dependabot[bot]
fea7b0ec8c Chore(deps-dev): Bump ruff from 0.5.6 to 0.5.7 in the development group (#7457)
* Chore(deps-dev): Bump ruff from 0.5.6 to 0.5.7 in the development group

Bumps the development group with 1 update: [ruff](https://github.com/astral-sh/ruff).


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

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

* Update .pre-commit-config.yaml

---------

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-08-12 22:23:56 +00:00
dependabot[bot]
0042e3eca4 Chore(deps): Bump the small-changes group with 3 updates (#7460)
Bumps the small-changes group with 3 updates: [nltk](https://github.com/nltk/nltk), [rapidfuzz](https://github.com/rapidfuzz/RapidFuzz) and [watchdog](https://github.com/gorakhargosh/watchdog).


Updates `nltk` from 3.8.1 to 3.8.2
- [Changelog](https://github.com/nltk/nltk/blob/develop/ChangeLog)
- [Commits](https://github.com/nltk/nltk/compare/3.8.1...3.8.2)

Updates `rapidfuzz` from 3.9.5 to 3.9.6
- [Release notes](https://github.com/rapidfuzz/RapidFuzz/releases)
- [Changelog](https://github.com/rapidfuzz/RapidFuzz/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/rapidfuzz/RapidFuzz/compare/v3.9.5...v3.9.6)

Updates `watchdog` from 4.0.1 to 4.0.2
- [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/v4.0.1...v4.0.2)

---
updated-dependencies:
- dependency-name: nltk
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: rapidfuzz
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: watchdog
  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-08-12 15:08:38 -07:00
github-actions[bot]
36db6f3d4b Changelog v2.11.4 - GHA (#7442)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-08-10 09:06:56 -07:00
shamoon
3c633c2015 Bump version to 2.11.4 2024-08-10 08:48:26 -07:00
github-actions[bot]
dd8b51de67 New Crowdin translations by GitHub Action (#7350)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-08-10 08:36:39 -07:00
shamoon
5fe846de1d Fix: initial upload message not being dismissed (#7438) 2024-08-10 08:32:06 -07:00
github-actions[bot]
4711468598 Changelog v2.11.3 - GHA (#7422)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-08-08 08:17:59 -07:00
shamoon
19dfaf1b94 Bump version to 2.11.3 2024-08-08 07:46:22 -07:00
shamoon
183ea24c9f Merge branch 'dev' 2024-08-08 07:45:49 -07:00
shamoon
99693b6d30 Update frontend translation strings 2024-08-08 07:45:30 -07:00
shamoon
c0ad82b695 Fix: clear selection after reload for management lists (#7421) 2024-08-08 07:38:45 -07:00
dependabot[bot]
fd36323d1c Chore(deps): Bump django from 4.2.14 to 4.2.15 (#7412)
Bumps [django](https://github.com/django/django) from 4.2.14 to 4.2.15.
- [Commits](https://github.com/django/django/compare/4.2.14...4.2.15)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-07 12:49:51 -07:00
shamoon
4059c83a21 Documentation: correct DO logo URL 2024-08-07 12:19:50 -07:00
shamoon
b25c015516 Documentation: fix config formatting 2024-08-06 08:31:48 -07:00
shamoon
8fa52046e4 Enhancement: optimize tasks / stats reload (#7402) 2024-08-05 23:25:55 -07:00
shamoon
15554322dd Enhancement: allow specifying default currency for Monetary custom field (#7381) 2024-08-05 17:02:03 -07:00
shamoon
0ee85aae21 Enhancement: log when pre-check fails for documents in trash (#7355) 2024-08-05 17:01:01 -07:00
dependabot[bot]
839fb34c8e Chore(deps-dev): Bump the development group with 3 updates (#7394)
* Chore(deps-dev): Bump the development group with 3 updates

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


Updates `ruff` from 0.5.4 to 0.5.6
- [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/0.5.4...0.5.6)

Updates `pytest` from 8.3.1 to 8.3.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.3.1...8.3.2)

Updates `mkdocs-material` from 9.5.29 to 9.5.31
- [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.29...9.5.31)

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

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

* Update .pre-commit-config.yaml

---------

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-08-05 23:56:16 +00:00
shamoon
928580bf4f Fix: disable inline create buttons if insufficient permissions (#7401) 2024-08-05 16:45:48 -07:00
dependabot[bot]
9cca7aaa08 Chore(deps): Bump the small-changes group with 5 updates (#7397)
Bumps the small-changes group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [imap-tools](https://github.com/ikvk/imap_tools) | `1.7.0` | `1.7.2` |
| [rapidfuzz](https://github.com/rapidfuzz/RapidFuzz) | `3.9.4` | `3.9.5` |
| [redis](https://github.com/redis/redis-py) | `5.0.7` | `5.0.8` |
| [tqdm](https://github.com/tqdm/tqdm) | `4.66.4` | `4.66.5` |
| [pre-commit](https://github.com/pre-commit/pre-commit) | `3.7.1` | `3.8.0` |


Updates `imap-tools` from 1.7.0 to 1.7.2
- [Release notes](https://github.com/ikvk/imap_tools/releases)
- [Changelog](https://github.com/ikvk/imap_tools/blob/master/docs/release_notes.rst)
- [Commits](https://github.com/ikvk/imap_tools/compare/v1.7.0...v1.7.2)

Updates `rapidfuzz` from 3.9.4 to 3.9.5
- [Release notes](https://github.com/rapidfuzz/RapidFuzz/releases)
- [Changelog](https://github.com/rapidfuzz/RapidFuzz/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/rapidfuzz/RapidFuzz/compare/v3.9.4...v3.9.5)

Updates `redis` from 5.0.7 to 5.0.8
- [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.7...v5.0.8)

Updates `tqdm` from 4.66.4 to 4.66.5
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.4...v4.66.5)

Updates `pre-commit` from 3.7.1 to 3.8.0
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.7.1...v3.8.0)

---
updated-dependencies:
- dependency-name: imap-tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: rapidfuzz
  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
- 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-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-08-05 15:53:29 -07:00
dependabot[bot]
38560cf13a Chore(deps-dev): Bump @playwright/test from 1.42.1 to 1.45.3 in /src-ui (#7367)
* Chore(deps-dev): Bump @playwright/test from 1.42.1 to 1.45.3 in /src-ui

Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.42.1 to 1.45.3.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.42.1...v1.45.3)

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

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

* Fix failing test

---------

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-08-01 23:59:56 +00:00
dependabot[bot]
474ca08ef9 Chore(deps-dev): Bump @types/node from 20.12.2 to 22.0.2 in /src-ui (#7366)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.12.2 to 22.0.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-01 23:32:05 +00:00
dependabot[bot]
d4fd529e49 Chore(deps-dev): Bump the frontend-eslint-dependencies group (#7365)
Bumps the frontend-eslint-dependencies group in /src-ui with 4 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), [@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils) and [eslint](https://github.com/eslint/eslint).


Updates `@typescript-eslint/eslint-plugin` from 7.4.0 to 8.0.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/v8.0.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 7.4.0 to 8.0.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/v8.0.0/packages/parser)

Updates `@typescript-eslint/utils` from 7.13.0 to 8.0.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.0.0/packages/utils)

Updates `eslint` from 8.57.0 to 9.8.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.57.0...v9.8.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: "@typescript-eslint/utils"
  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-major
  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-08-01 16:24:30 -07:00
dependabot[bot]
2260617447 Chore(deps): Bump uuid from 9.0.1 to 10.0.0 in /src-ui (#7370)
Bumps [uuid](https://github.com/uuidjs/uuid) from 9.0.1 to 10.0.0.
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v9.0.1...v10.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-01 23:07:17 +00:00
dependabot[bot]
7c3ba3e518 Chore(deps): Bump stumpylog/image-cleaner-action in the actions group (#7371)
Bumps the actions group with 1 update: [stumpylog/image-cleaner-action](https://github.com/stumpylog/image-cleaner-action).


Updates `stumpylog/image-cleaner-action` from 0.7.0 to 0.8.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.7.0...v0.8.0)

---
updated-dependencies:
- 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-08-01 15:57:46 -07:00
dependabot[bot]
849e3a10ac Chore(deps): Bump zone.js from 0.14.4 to 0.14.8 in /src-ui (#7368)
Bumps [zone.js](https://github.com/angular/angular/tree/HEAD/packages/zone.js) from 0.14.4 to 0.14.8.
- [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.8/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-08-01 15:57:24 -07:00
dependabot[bot]
bdceeef3fb Chore(deps-dev): Bump jest-preset-angular (#7364)
Bumps the frontend-jest-dependencies group in /src-ui with 1 update: [jest-preset-angular](https://github.com/thymikee/jest-preset-angular).


Updates `jest-preset-angular` from 14.1.1 to 14.2.2
- [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/v14.1.1...v14.2.2)

---
updated-dependencies:
- dependency-name: jest-preset-angular
  dependency-type: direct:development
  update-type: version-update:semver-minor
  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-08-01 21:48:13 +00:00
dependabot[bot]
8630e5f5b6 Chore(deps): Bump the frontend-angular-dependencies group (#7363)
Bumps the frontend-angular-dependencies group in /src-ui with 20 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `18.1.0` | `18.1.3` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `18.0.6` | `18.1.3` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `18.0.6` | `18.1.3` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `18.0.6` | `18.1.3` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `18.0.6` | `18.1.3` |
| [@angular/localize](https://github.com/angular/angular) | `18.0.6` | `18.1.3` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `18.0.6` | `18.1.3` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `18.0.6` | `18.1.3` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `18.0.6` | `18.1.3` |
| [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `13.4.1` | `13.5.0` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `18.0.7` | `18.1.3` |
| [@angular-devkit/core](https://github.com/angular/angular-cli) | `18.0.7` | `18.1.3` |
| [@angular-devkit/schematics](https://github.com/angular/angular-cli) | `18.0.7` | `18.1.3` |
| [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `18.1.0` | `18.2.0` |
| [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `18.1.0` | `18.2.0` |
| [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `18.1.0` | `18.2.0` |
| [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `18.1.0` | `18.2.0` |
| [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `18.1.0` | `18.2.0` |
| [@angular/cli](https://github.com/angular/angular-cli) | `18.0.7` | `18.1.3` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `18.0.6` | `18.1.3` |


Updates `@angular/cdk` from 18.1.0 to 18.1.3
- [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/18.1.0...18.1.3)

Updates `@angular/common` from 18.0.6 to 18.1.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/18.1.3/packages/common)

Updates `@angular/compiler` from 18.0.6 to 18.1.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/18.1.3/packages/compiler)

Updates `@angular/core` from 18.0.6 to 18.1.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/18.1.3/packages/core)

Updates `@angular/forms` from 18.0.6 to 18.1.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/18.1.3/packages/forms)

Updates `@angular/localize` from 18.0.6 to 18.1.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/18.0.6...18.1.3)

Updates `@angular/platform-browser` from 18.0.6 to 18.1.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/18.1.3/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 18.0.6 to 18.1.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/18.1.3/packages/platform-browser-dynamic)

Updates `@angular/router` from 18.0.6 to 18.1.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/18.1.3/packages/router)

Updates `@ng-select/ng-select` from 13.4.1 to 13.5.0
- [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/v13.4.1...v13.5.0)

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

Updates `@angular-devkit/core` from 18.0.7 to 18.1.3
- [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/18.0.7...18.1.3)

Updates `@angular-devkit/schematics` from 18.0.7 to 18.1.3
- [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/18.0.7...18.1.3)

Updates `@angular-eslint/builder` from 18.1.0 to 18.2.0
- [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/v18.2.0/packages/builder)

Updates `@angular-eslint/eslint-plugin` from 18.1.0 to 18.2.0
- [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/v18.2.0/packages/eslint-plugin)

Updates `@angular-eslint/eslint-plugin-template` from 18.1.0 to 18.2.0
- [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/v18.2.0/packages/eslint-plugin-template)

Updates `@angular-eslint/schematics` from 18.1.0 to 18.2.0
- [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/v18.2.0/packages/schematics)

Updates `@angular-eslint/template-parser` from 18.1.0 to 18.2.0
- [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/v18.2.0/packages/template-parser)

Updates `@angular/cli` from 18.0.7 to 18.1.3
- [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/18.0.7...18.1.3)

Updates `@angular/compiler-cli` from 18.0.6 to 18.1.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/18.1.3/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  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-minor
  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-devkit/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-devkit/schematics"
  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-08-01 14:34:29 -07:00
shamoon
2312eba5b6 Merge branch 'main' into dev 2024-07-29 07:32:04 -07:00
shamoon
ad9d654886 Documentation: Add missing change to changelog 2024-07-29 07:31:51 -07:00
fusselkater
fa19a8975e Fix: correct nltk data path (#7338) 2024-07-29 07:27:08 -07:00
shamoon
8987cd448f Fix: use entire document for dropzone (#7342) 2024-07-29 07:26:29 -07:00
github-actions[bot]
a7536e3ebf Documentation: Add v2.11.2 changelog (#7336)
* Changelog v2.11.2 - 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-07-28 14:45:29 -07:00
shamoon
9c3bc2eb83 Bump version to 2.11.2 2024-07-28 14:11:20 -07:00
github-actions[bot]
d179efbd48 New Crowdin translations by GitHub Action (#7299)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-07-28 14:10:36 -07:00
shamoon
801df5f7bd Chore: reorganize trash location 2024-07-28 14:09:20 -07:00
shamoon
d1c3ea7faa Resolve test error 2024-07-28 14:08:16 -07:00
shamoon
dcfc53b7f2 Change: more clearly handle init permissions error (#7334) 2024-07-28 07:06:15 -07:00
shamoon
45002f8083 Merge branch 'main' into dev 2024-07-27 09:01:54 -07:00
shamoon
722a2ca1e4 Fix dashboard col width when wrapping on smaller screens with collapsed sidebar 2024-07-26 10:32:06 -07:00
shamoon
2bd8b67d02 Chore: add permissions info link from webUI (#7310) 2024-07-24 10:06:38 -07:00
shamoon
637efd5cb3 Documentation: clarify some global permissions details (#7309) 2024-07-24 10:06:08 -07:00
Jay Barker
ad3dd76c2f Fix: increase search input text contrast with light custom theme colors (#7303)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-07-24 02:57:26 +00:00
dependabot[bot]
75aba12589 Chore(deps-dev): Bump the development group with 2 updates (#7296)
* 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 [pytest](https://github.com/pytest-dev/pytest).


Updates `ruff` from 0.5.2 to 0.5.4
- [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/0.5.2...0.5.4)

Updates `pytest` from 8.2.2 to 8.3.1
- [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.2.2...8.3.1)

---
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-minor
  dependency-group: development
...

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

* Update ruff to match

---------

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-07-22 21:43:05 +00:00
dependabot[bot]
ec33edb2f4 Chore(deps): Bump tika-client in the small-changes group (#7297)
Bumps the small-changes group with 1 update: [tika-client](https://github.com/stumpylog/tika-rest-client).


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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-22 14:31:16 -07:00
github-actions[bot]
4b706fa4dd Changelog v2.11.1 - GHA (#7295)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-22 11:34:29 -07:00
shamoon
98799d9a69 Bump version to 2.11.1 2024-07-22 10:29:02 -07:00
github-actions[bot]
b80070ac0b New Crowdin translations by GitHub Action (#7221)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-07-22 10:09:22 -07:00
shamoon
6b2e5559ca Fix: support multiple inbox tags from stats widget (#7281) 2024-07-22 10:07:51 -07:00
dependabot[bot]
82340ad6e3 Chore(deps-dev): Bump the development group with 2 updates (#7261)
* 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.5.1 to 0.5.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/0.5.1...0.5.2)

Updates `mkdocs-material` from 9.5.28 to 9.5.29
- [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.28...9.5.29)

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

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

* Updates ruff to match

---------

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-07-22 09:56:43 -07:00
dependabot[bot]
f45ab723ae Chore(deps): Bump the small-changes group across 1 directory with 2 updates (#7266)
Bumps the small-changes group with 2 updates in the / directory: [django-allauth](https://github.com/pennersr/django-allauth) and [imap-tools](https://github.com/ikvk/imap_tools).


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

Updates `imap-tools` from 1.6.0 to 1.7.0
- [Release notes](https://github.com/ikvk/imap_tools/releases)
- [Changelog](https://github.com/ikvk/imap_tools/blob/master/docs/release_notes.rst)
- [Commits](https://github.com/ikvk/imap_tools/compare/v1.6.0...v1.7.0)

---
updated-dependencies:
- dependency-name: django-allauth
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: imap-tools
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 18:35:00 -07:00
Freddy0
8e3ca37b05 Enhancement: include owner username in post-consumption variables (#7270) 2024-07-16 15:23:29 -07:00
Trenton H
aef387ed69 Chore: Use self-documenting long options in Docker startup scripts (#7275) 2024-07-16 10:27:12 -07:00
Trenton H
b93c970635 Chore: Squash older automatic migrations (#7267) 2024-07-16 09:21:41 -07:00
shamoon
a7d8b5c960 Clarify has any tags 2024-07-15 15:37:43 -07:00
shamoon
56c9a3f270 Correct some indentation 2024-07-15 15:28:11 -07:00
shamoon
0c3dac45b5 Fix: use responsive table for compatibility with custom columns (#7255) 2024-07-15 15:17:46 -07:00
Trenton H
6965165c76 Removes Turkish from the NLTK languages (#7246) 2024-07-14 16:35:16 -07:00
shamoon
73d33ff25a Fix: include trashed docs in existing doc check (#7229) 2024-07-12 16:45:35 -07:00
github-actions[bot]
df153be30e Documentation: Add v2.11.0 changelog (#7218) 2024-07-10 21:55:01 -07:00
shamoon
9950ff2337 Bump version to 2.11.0 2024-07-10 21:21:29 -07:00
shamoon
9a9ab85baf Merge branch 'dev' 2024-07-10 21:02:57 -07:00
github-actions[bot]
eaea42334b New Crowdin translations by GitHub Action (#7124)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-07-10 20:29:17 -07:00
shamoon
de8ac013ee Enhancement: disable add split button when approrpriate (#7215) 2024-07-11 03:27:04 +00:00
shamoon
9e2bf4820a Enhancement: wrapping of saved view fields d-n-d UI (#7216) 2024-07-10 20:14:59 -07:00
Trenton H
9af879a2bf Bulk backend updates (#7209) 2024-07-10 12:45:29 -07:00
shamoon
ff4203938b Fix: correct card display custom field trackby variable 2024-07-09 13:06:06 -07:00
shamoon
a63f8809fa Fix: restore dashboard saved views display 2024-07-09 13:01:49 -07:00
shamoon
edcde1f142 Fix: move dashboard tour anchor to fix overlapping issues 2024-07-09 11:43:25 -07:00
shamoon
6dc094f760 Fix: unable to assign null select custom fields 2024-07-09 11:27:30 -07:00
shamoon
186f520819 Update backend translation strings 2024-07-09 11:06:18 -07:00
dependabot[bot]
0365fc5ac3 Chore(deps): Bump the frontend-angular-dependencies group (#7200)
Bumps the frontend-angular-dependencies group in /src-ui with 14 updates:

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


Updates `@angular/cdk` from 18.0.5 to 18.0.6
- [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/18.0.5...18.0.6)

Updates `@angular/common` from 18.0.5 to 18.0.6
- [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/18.0.6/packages/common)

Updates `@angular/compiler` from 18.0.5 to 18.0.6
- [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/18.0.6/packages/compiler)

Updates `@angular/core` from 18.0.5 to 18.0.6
- [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/18.0.6/packages/core)

Updates `@angular/forms` from 18.0.5 to 18.0.6
- [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/18.0.6/packages/forms)

Updates `@angular/localize` from 18.0.5 to 18.0.6
- [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/18.0.5...18.0.6)

Updates `@angular/platform-browser` from 18.0.5 to 18.0.6
- [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/18.0.6/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 18.0.5 to 18.0.6
- [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/18.0.6/packages/platform-browser-dynamic)

Updates `@angular/router` from 18.0.5 to 18.0.6
- [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/18.0.6/packages/router)

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

Updates `@angular-devkit/core` from 18.0.6 to 18.0.7
- [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/18.0.6...18.0.7)

Updates `@angular-devkit/schematics` from 18.0.6 to 18.0.7
- [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/18.0.6...18.0.7)

Updates `@angular/cli` from 18.0.6 to 18.0.7
- [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/18.0.6...18.0.7)

Updates `@angular/compiler-cli` from 18.0.5 to 18.0.6
- [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/18.0.6/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-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-devkit/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-devkit/schematics"
  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-07-09 18:01:39 +00:00
shamoon
61811a4bec Enhancement: support customfield filter for select type (#7199) 2024-07-09 10:54:34 -07:00
shamoon
bb83c1eb0a Chore: upgrade to DRF 3.15 (#7134) 2024-07-09 16:57:53 +00:00
shamoon
4ad4862641 Feature: select custom field type (#7167) 2024-07-09 07:57:07 -07:00
shamoon
c03aa03ac2 Feature: automatic sso redirect (#7168) 2024-07-08 22:38:23 +00:00
shamoon
ada283441c Fix: include documents in trash for existing asn check (#7189) 2024-07-08 16:28:40 +00:00
Trenton H
3cf73a77ac Chore: Initial conversion to pytest fixtures (#7110) 2024-07-08 07:46:20 -07:00
Psychi
71fedcb466 Documentation: fix rapidfuzz link (#7176) 2024-07-07 07:08:40 -07:00
shamoon
1b9cf5121b Add missing disable autocomplete for doc type edit dialog 2024-07-06 13:24:43 -07:00
shamoon
7fe76656f2 Fix: editing of existing custom fields violates throws unique error 2024-07-06 13:05:20 -07:00
dependabot[bot]
0deb8a11d6 Chore(deps): Bump certifi from 2024.6.2 to 2024.7.4 (#7166)
Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.6.2 to 2024.7.4.
- [Commits](https://github.com/certifi/python-certifi/compare/2024.06.02...2024.07.04)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-05 20:48:01 -07:00
shamoon
064d384d97 Enhancement: show more columns in mail frontend admin (#7158) 2024-07-03 08:09:28 -07:00
dependabot[bot]
5045d06744 Chore(deps): Bump the frontend-angular-dependencies group (#7148)
Bumps the frontend-angular-dependencies group in /src-ui with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `13.3.0` | `13.4.1` |
| [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `18.0.1` | `18.1.0` |
| [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `18.0.1` | `18.1.0` |
| [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `18.0.1` | `18.1.0` |
| [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `18.0.1` | `18.1.0` |
| [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `18.0.1` | `18.1.0` |


Updates `@ng-select/ng-select` from 13.3.0 to 13.4.1
- [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/v13.3.0...v13.4.1)

Updates `@angular-eslint/builder` from 18.0.1 to 18.1.0
- [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/v18.1.0/packages/builder)

Updates `@angular-eslint/eslint-plugin` from 18.0.1 to 18.1.0
- [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/v18.1.0/packages/eslint-plugin)

Updates `@angular-eslint/eslint-plugin-template` from 18.0.1 to 18.1.0
- [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/v18.1.0/packages/eslint-plugin-template)

Updates `@angular-eslint/schematics` from 18.0.1 to 18.1.0
- [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/v18.1.0/packages/schematics)

Updates `@angular-eslint/template-parser` from 18.0.1 to 18.1.0
- [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/v18.1.0/packages/template-parser)

---
updated-dependencies:
- dependency-name: "@ng-select/ng-select"
  dependency-type: direct:production
  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
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-01 23:36:31 +00:00
dependabot[bot]
c57aa81d15 Chore(deps): Bump django-multiselectfield in the django group (#7147)
Bumps the django group with 1 update: [django-multiselectfield](https://github.com/goinnn/django-multiselectfield).


Updates `django-multiselectfield` from 0.1.12 to 0.1.13
- [Release notes](https://github.com/goinnn/django-multiselectfield/releases)
- [Changelog](https://github.com/goinnn/django-multiselectfield/blob/master/CHANGES.rst)
- [Commits](https://github.com/goinnn/django-multiselectfield/commits)

---
updated-dependencies:
- dependency-name: django-multiselectfield
  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-07-01 16:23:09 -07:00
shamoon
d35e350c79 Fix: dont exclude documents in trash from sanity check (#7133) 2024-07-01 13:47:05 -07:00
dependabot[bot]
dd878c8d70 Chore(deps): Bump docker/build-push-action in the actions group (#7125)
Bumps the actions group with 1 update: [docker/build-push-action](https://github.com/docker/build-push-action).


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

---
updated-dependencies:
- dependency-name: docker/build-push-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-06-28 07:06:48 -07:00
dependabot[bot]
9cd1945a89 Chore(deps): Bump the small-changes group across 1 directory with 4 updates (#7128)
Bumps the small-changes group with 4 updates in the / directory: [django-cors-headers](https://github.com/adamchainz/django-cors-headers), [filelock](https://github.com/tox-dev/py-filelock), [redis](https://github.com/redis/redis-py) and [whitenoise](https://github.com/evansd/whitenoise).


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

Updates `filelock` from 3.15.1 to 3.15.4
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.15.1...3.15.4)

Updates `redis` from 5.0.6 to 5.0.7
- [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.6...v5.0.7)

Updates `whitenoise` from 6.6.0 to 6.7.0
- [Changelog](https://github.com/evansd/whitenoise/blob/main/docs/changelog.rst)
- [Commits](https://github.com/evansd/whitenoise/compare/6.6.0...6.7.0)

---
updated-dependencies:
- dependency-name: django-cors-headers
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: filelock
  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
- dependency-name: whitenoise
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-27 15:14:53 -07:00
dependabot[bot]
faab8a5560 Chore(deps): Bump the frontend-angular-dependencies group (#7126)
Bumps the frontend-angular-dependencies group in /src-ui with 16 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `18.0.3` | `18.0.5` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `18.0.3` | `18.0.5` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `18.0.3` | `18.0.5` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `18.0.3` | `18.0.5` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `18.0.3` | `18.0.5` |
| [@angular/localize](https://github.com/angular/angular) | `18.0.3` | `18.0.5` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `18.0.3` | `18.0.5` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `18.0.3` | `18.0.5` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `18.0.3` | `18.0.5` |
| [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `13.2.0` | `13.3.0` |
| [@angular-builders/jest](https://github.com/just-jeb/angular-builders/tree/HEAD/packages/jest) | `18.0.0-beta.3` | `18.0.0` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `18.0.4` | `18.0.6` |
| [@angular-devkit/core](https://github.com/angular/angular-cli) | `18.0.4` | `18.0.6` |
| [@angular-devkit/schematics](https://github.com/angular/angular-cli) | `18.0.4` | `18.0.6` |
| [@angular/cli](https://github.com/angular/angular-cli) | `18.0.4` | `18.0.6` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `18.0.3` | `18.0.5` |


Updates `@angular/cdk` from 18.0.3 to 18.0.5
- [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/18.0.3...18.0.5)

Updates `@angular/common` from 18.0.3 to 18.0.5
- [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/18.0.5/packages/common)

Updates `@angular/compiler` from 18.0.3 to 18.0.5
- [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/18.0.5/packages/compiler)

Updates `@angular/core` from 18.0.3 to 18.0.5
- [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/18.0.5/packages/core)

Updates `@angular/forms` from 18.0.3 to 18.0.5
- [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/18.0.5/packages/forms)

Updates `@angular/localize` from 18.0.3 to 18.0.5
- [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/18.0.3...18.0.5)

Updates `@angular/platform-browser` from 18.0.3 to 18.0.5
- [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/18.0.5/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 18.0.3 to 18.0.5
- [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/18.0.5/packages/platform-browser-dynamic)

Updates `@angular/router` from 18.0.3 to 18.0.5
- [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/18.0.5/packages/router)

Updates `@ng-select/ng-select` from 13.2.0 to 13.3.0
- [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/v13.2.0...v13.3.0)

Updates `@angular-builders/jest` from 18.0.0-beta.3 to 18.0.0
- [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@18.0.0/packages/jest)

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

Updates `@angular-devkit/core` from 18.0.4 to 18.0.6
- [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/18.0.4...18.0.6)

Updates `@angular-devkit/schematics` from 18.0.4 to 18.0.6
- [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/18.0.4...18.0.6)

Updates `@angular/cli` from 18.0.4 to 18.0.6
- [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/18.0.4...18.0.6)

Updates `@angular/compiler-cli` from 18.0.3 to 18.0.5
- [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/18.0.5/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-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: "@ng-select/ng-select"
  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-devkit/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-devkit/schematics"
  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-06-27 15:14:32 -07:00
phail
fcc9847bc3 Development: Add VS Code Devcontainer Configuration (#7041) 2024-06-27 15:13:55 -07:00
shamoon
a64d457c30 Enhancement: use request user as owner of split / merge docs (#7112) 2024-06-27 13:46:49 -07:00
Trenton H
e799d757c2 Ignores DRF 3.15.2 (#7122) 2024-06-27 20:40:16 +00:00
shamoon
ac0ed0def8 Fix: handle errors for trash actions and only show documents user can restore or delete (#7119) 2024-06-27 20:33:39 +00:00
dependabot[bot]
f01283c309 Chore(deps-dev): Bump ruff from 0.4.9 to 0.5.0 in the development group across 1 directory (#7120)
* Chore(deps-dev): Bump ruff in the development group across 1 directory

Bumps the development group with 1 update in the / directory: [ruff](https://github.com/astral-sh/ruff).


Updates `ruff` from 0.4.9 to 0.5.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.4.9...0.5.0)

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

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

* Update .pre-commit-config.yaml

* Lets ruff fix this new lint

---------

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-06-27 19:06:37 +00:00
dependabot[bot]
a3c468a004 Chore(deps-dev): Bump ws from 8.17.0 to 8.17.1 in /src-ui (#7114)
Bumps [ws](https://github.com/websockets/ws) from 8.17.0 to 8.17.1.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.17.0...8.17.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-27 04:33:27 +00:00
shamoon
3435ffd00c Chore: update to Angular v18 (#7106) 2024-06-26 20:57:39 -07:00
Fabien Dubuy
4f1185c65d Enhancement: improve date parsing with accented characters (#7100) 2024-06-26 20:47:37 -07:00
shamoon
2b1498cc6d Chore: display docker tag in UI for ci test builds (#7083) 2024-06-27 03:02:52 +00:00
shamoon
0643db4347 Feature: improve history display of object names etc (#7102) 2024-06-27 02:52:08 +00:00
Trenton H
29e6371cd1 Feature: Upgrade Gotenberg to v8 (#7094) 2024-06-27 02:37:50 +00:00
shamoon
80c2d90e74 Fix: dont include documents in trash in counts (#7111) 2024-06-26 19:30:12 -07:00
shamoon
f3cf608caa Fix: use temp dir for split / merge (#7105) 2024-06-26 10:46:59 -07:00
shamoon
c6d0557a3b Update configuration.md 2024-06-24 13:37:01 -07:00
github-actions[bot]
f3b7ae93f0 Changelog v2.10.2 - GHA (#7082)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-23 22:31:52 -07:00
shamoon
e4265d0594 Bump version to 2.10.2 2024-06-23 22:13:33 -07:00
shamoon
deda49c204 Merge branch 'dev' 2024-06-23 22:13:01 -07:00
github-actions[bot]
276abc1404 New Crowdin translations by GitHub Action (#7034)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-06-23 21:57:48 -07:00
shamoon
6defe24ae7 Fix: always update document modified property on bulk edit operations (#7079) 2024-06-23 12:11:24 -07:00
shamoon
6ed5d11758 Fix: correct frontend retrieval of trash delay setting (#7067) 2024-06-22 12:57:33 -07:00
Trenton H
9d34327a6d Resolves the casing warning in newer Docker versions 2024-06-22 11:30:06 -07:00
shamoon
63f164d099 Documentation: fix PAPERLESS_EMPTY_TRASH_DELAY name 2024-06-22 06:46:47 -07:00
shamoon
0f9710dc8f Fix: index fresh document data after update archive file (#7057) 2024-06-21 18:33:01 +00:00
shamoon
cccba47bd7 Fix: remove type attribute from object for Safari (#7056) 2024-06-21 11:22:18 -07:00
Trenton H
91585a1fa6 Prefer the metadata JSON file over the version JSON file (#7048) 2024-06-20 12:49:54 -07:00
shamoon
3bb6a32ab9 Update bug report note 2024-06-19 00:54:40 -07:00
shamoon
31f592453e Reset -dev version string 2024-06-18 21:23:34 -07:00
github-actions[bot]
56f5f93c48 Documentation: Add v2.10.1 changelog (#7029)
* Changelog v2.10.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-06-18 21:22:59 -07:00
shamoon
e6aefd1063 Bump version to 2.10.1 2024-06-18 21:00:22 -07:00
github-actions[bot]
6187ee82af New Crowdin translations by GitHub Action (#7027)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-06-18 20:59:28 -07:00
shamoon
a066ccff4f Fix: dont require admin to view trash on frontend (#7028) 2024-06-18 20:17:36 -07:00
shamoon
f73be01897 Reset -dev version string 2024-06-18 16:01:37 -07:00
github-actions[bot]
07ee25be06 Changelog v2.10.0 - GHA (#7020)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-06-18 15:12:59 -07:00
shamoon
4347c87e92 Bump version to 2.10.0 2024-06-18 10:19:49 -07:00
shamoon
807f0f1345 Merge branch 'dev' 2024-06-18 10:18:08 -07:00
github-actions[bot]
12857890cc New Crowdin translations by GitHub Action (#6896)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-06-18 09:52:40 -07:00
Trenton H
2ad0f8325c Upgrades to pipenv 2024.0.1 (#7019) 2024-06-18 08:37:11 -07:00
dependabot[bot]
6aae8bf440 Chore(deps): Bump the small-changes group with 2 updates (#7013)
* Chore(deps): Bump the small-changes group with 3 updates

Bumps the small-changes group with 3 updates: [filelock](https://github.com/tox-dev/py-filelock), [gotenberg-client](https://github.com/stumpylog/gotenberg-client) and [redis](https://github.com/redis/redis-py).


Updates `filelock` from 3.14.0 to 3.15.1
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.14.0...3.15.1)

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

Updates `redis` from 5.0.4 to 5.0.6
- [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.4...v5.0.6)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: gotenberg-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- 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>

* Downgrades gotenberg-client to 0.5.0

---------

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-06-18 15:03:14 +00:00
dependabot[bot]
5c7522b423 Chore(deps-dev): Bump the development group with 2 updates (#7012)
* 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.4.8 to 0.4.9
- [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.4.8...v0.4.9)

Updates `mkdocs-material` from 9.5.26 to 9.5.27
- [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.26...9.5.27)

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

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

* Updates ruff to match

---------

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-06-18 14:41:34 +00:00
dependabot[bot]
37e607abb9 Chore(deps-dev): Bump ws from 8.15.1 to 8.17.1 in /src-ui (#7015)
Bumps [ws](https://github.com/websockets/ws) from 8.15.1 to 8.17.1.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.15.1...8.17.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 16:42:38 -07:00
dependabot[bot]
8045f3d58c Chore(deps): Bump urllib3 from 2.2.1 to 2.2.2 (#7014)
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.2.1...2.2.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 23:07:59 +00:00
shamoon
a796e58a94 Feature: documents trash aka soft delete (#6944) 2024-06-17 08:07:08 -07:00
shamoon
9d4e2d4652 Enhancement: better boolean custom field display (#7001) 2024-06-15 09:00:18 -07:00
Trenton H
28db7e84e6 Documentation: Corrections and clarifications for Python support (#6995)
* Clarifies Python version support and a rough policy of what versions are supported
2024-06-13 11:53:34 -07:00
martin f. krafft
22a6360edf Fix: default order of documents gets lost in QuerySet pipeline (#6982)
* Send ordered document list to Django REST pagination

Currently, when pages of documents are requested from the API, the
webserver logs a warning:

```
gunicorn[1550]: /home/madduck/code/paperless-ngx/.direnv/python-3.11.2/lib/python3.11/site-packages/rest_framework/pagination.py:200: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 'documents.models.Document'> QuerySet.
```

This can yield unexpected and problematic results, including duplicate
and missing IDs in the enumeration, as demonstrated in
https://github.com/paperless-ngx/paperless-ngx/discussions/6859

The patch is simple: turn the unordered Documents QuerySet into
one that's ordered by reverse creation date, which is the default
ordering for `Document`.

Note that the default ordering for `Document` means that
`QuerySet.ordered` is actually `True` following the call to
`distinct()`, but after `annotate()`, the flag changes to `False`,
unless `order_by()` is used explicitly, as per this patch.

Closes: https://github.com/paperless-ngx/paperless-ngx/discussions/6859

Signed-off-by: martin f. krafft <madduck@madduck.net>

* Ensure order of documents in permissions test

The patch for #6982 changes the ordering of documents returned by the
API, which was previously implicit, and is now explicit. Therefore,
this patch masssages the API result to ensure the previous order.

Signed-off-by: martin f. krafft <madduck@madduck.net>

---------

Signed-off-by: martin f. krafft <madduck@madduck.net>
2024-06-13 14:46:18 +00:00
Trenton H
61485b0f1d Fix: Document history could include extra fields (#6989)
* Fixes creation of a custom field being included in a document's history even if not attached

* Show custom field creation in UI

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-06-12 16:23:47 -07:00
shamoon
fa7a5451db Fix: use local pdf worker js (#6990) 2024-06-12 21:37:42 +00:00
shamoon
70069cd502 Add type attribute to object tag 2024-06-12 14:09:48 -07:00
Felix Eckhofer
9e8b96cd34 Fix: Revert masking the content field in auditlog (#6981) 2024-06-12 06:34:49 -07:00
shamoon
d03058e539 Chore: update packages used by mail html template (#6970) 2024-06-11 20:25:57 -07:00
dependabot[bot]
c929a18da2 Chore(deps): Bump stumpylog/image-cleaner-action in the actions group (#6968)
Bumps the actions group with 1 update: [stumpylog/image-cleaner-action](https://github.com/stumpylog/image-cleaner-action).


Updates `stumpylog/image-cleaner-action` from 0.6.0 to 0.7.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.6.0...v0.7.0)

---
updated-dependencies:
- 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-06-11 14:57:10 -07:00
Trenton H
5bd248578a Configures dependabot to ignore djangorestframework specific versions (#6967) 2024-06-11 21:36:43 +00:00
dependabot[bot]
ebfb72a691 Chore(deps-dev): Bump the development group with 3 updates (#6953)
* Chore(deps-dev): Bump the development group with 3 updates

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


Updates `ruff` from 0.4.7 to 0.4.8
- [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.4.7...v0.4.8)

Updates `pytest` from 8.2.1 to 8.2.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.2.1...8.2.2)

Updates `mkdocs-material` from 9.5.25 to 9.5.26
- [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.25...9.5.26)

---
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: 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 in .pre-commit-config.yaml

---------

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-06-11 21:26:20 +00:00
Trenton H
fc440d8317 Updates to latest Trixie version of Ghostscript 10.03.1 (#6956) 2024-06-11 15:38:07 +00:00
shamoon
b6f6d524d6 Fix: respect model permissions for ui_settings API endpoint 2024-06-11 01:00:25 -07:00
shamoon
f225f72145 Fix: respect model permissions for tasks API endpoint (#6958) 2024-06-11 00:51:18 -07:00
Trenton H
d9002005b1 Feature: Allow encrypting sensitive fields in export (#6927)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-06-09 14:41:18 +00:00
Daniel Böhme
6ddb62bf3f Enhancement: allow consumption of odg files (#6940) 2024-06-09 07:34:22 -07:00
Dominik Bruhn
d1ac15baa9 Enhancement: support delete originals after split / merge (#6935)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-06-08 11:29:03 -07:00
shamoon
81e4092f53 Enhancement: unique mail rule names by owner 2024-06-08 11:29:03 -07:00
shamoon
d8c96b6e4a Enhancement: dont require document model permissions for notes (#6913) 2024-06-08 01:23:45 +00:00
dependabot[bot]
3d6aa8a656 Chore(deps): Bump tornado from 6.4 to 6.4.1 (#6930)
Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.4 to 6.4.1.
- [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst)
- [Commits](https://github.com/tornadoweb/tornado/compare/v6.4.0...v6.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-06 17:37:46 -07:00
Trenton H
6d2ae3df1f Resolves test issues with Python 3.12 (#6902) 2024-06-03 12:33:46 -07:00
Trenton H
de7c22e8d6 Fixes the logging of an email message to be something useful (#6901) 2024-06-03 10:11:31 -07:00
github-actions[bot]
74c44fe418 Documentation: Add v2.9.0 changelog (#6894) 2024-06-02 22:49:15 -07:00
shamoon
a6407d64e9 Reset dev version string 2024-06-02 22:36:53 -07:00
shamoon
e553e872df Bump version to 2.9.0 2024-06-02 22:17:59 -07:00
shamoon
e9e3ec5597 Merge branch 'dev' 2024-06-02 22:17:48 -07:00
github-actions[bot]
3dbf2e73f9 New Crowdin translations by GitHub Action (#6753)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-06-02 22:01:06 -07:00
dependabot[bot]
33c0b692e6 Chore(deps-dev): Bump jest-preset-angular (#6879)
Bumps the frontend-jest-dependencies group in /src-ui with 1 update: [jest-preset-angular](https://github.com/thymikee/jest-preset-angular).


Updates `jest-preset-angular` from 14.0.4 to 14.1.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/v14.0.4...v14.1.0)

---
updated-dependencies:
- dependency-name: jest-preset-angular
  dependency-type: direct:development
  update-type: version-update:semver-minor
  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-06-03 04:53:12 +00:00
Trenton H
4abc185a13 Updates backend deps (#6892) 2024-06-02 14:01:49 -07:00
Trenton H
43ede21c35 Recommends some extensions and sets the default interpreter path (#6884) 2024-06-02 03:34:40 +00:00
Trenton H
5fc6736666 Fixes up various doc sections (#6883) 2024-06-02 03:18:12 +00:00
shamoon
d2883b83c5 Fix: including ordering param for id__in retrievals from frontend (#6875) 2024-06-02 02:28:31 +00:00
Trenton H
085447e7c4 Feature: Allow a data only export/import cycle (#6871) 2024-06-01 18:22:59 -07:00
dependabot[bot]
04f52f553a Chore(deps): Bump crowdin/github-action from 1 to 2 in the actions group (#6881)
Bumps the actions group with 1 update: [crowdin/github-action](https://github.com/crowdin/github-action).


Updates `crowdin/github-action` from 1 to 2
- [Release notes](https://github.com/crowdin/github-action/releases)
- [Commits](https://github.com/crowdin/github-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: crowdin/github-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-06-01 14:50:21 -07:00
shamoon
d558367642 Change: rename redo OCR to reprocess (#6866) 2024-05-31 18:41:52 +00:00
Lino
324a2aa1c6 Allow to set a custom path for the classification file (#6858) 2024-05-31 09:42:18 -07:00
Trenton H
617bb30f29 Updates Ghostscript to 10.03.1 (#6854) 2024-05-29 09:43:33 -07:00
martin f. krafft
d6191d2f2b Add copy-paste list of Python build dependencies (#6824)
To be consistent, let's provide an easily copy-pastable list of packages
even for the final set of build dependencies.

Signed-off-by: martin f. krafft <madduck@madduck.net>
2024-05-29 09:03:51 -07:00
shamoon
f7347bdb69 Fix: remove unnecessary i18ns 2024-05-29 00:07:32 -07:00
dependabot[bot]
ce3d5b0065 Chore(deps-dev): Bump the development group across 1 directory with 2 updates (#6851)
* Chore(deps-dev): Bump the development group across 1 directory with 2 updates

Bumps the development group with 2 updates in the / directory: [ruff](https://github.com/astral-sh/ruff) and [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `ruff` from 0.4.4 to 0.4.6
- [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.4.4...v0.4.6)

Updates `mkdocs-material` from 9.5.24 to 9.5.25
- [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.24...9.5.25)

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

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

* Updates hook versions to match

* New codespell fixes

* Remove unneeded i18n

---------

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>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-05-29 07:04:01 +00:00
shamoon
235b0a4c33 Update messages.xlf 2024-05-28 23:54:51 -07:00
shamoon
fdf873ad6a Fix: enforce dropdown input min width 2024-05-28 23:52:45 -07:00
dependabot[bot]
b996022003 Chore(deps): Bump the small-changes group with 3 updates (#6843)
Bumps the small-changes group with 3 updates: [rapidfuzz](https://github.com/rapidfuzz/RapidFuzz), [scikit-learn](https://github.com/scikit-learn/scikit-learn) and [watchdog](https://github.com/gorakhargosh/watchdog).


Updates `rapidfuzz` from 3.9.0 to 3.9.1
- [Release notes](https://github.com/rapidfuzz/RapidFuzz/releases)
- [Changelog](https://github.com/rapidfuzz/RapidFuzz/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/rapidfuzz/RapidFuzz/compare/v3.9.0...v3.9.1)

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

Updates `watchdog` from 4.0.0 to 4.0.1
- [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/v4.0.0...v4.0.1)

---
updated-dependencies:
- dependency-name: rapidfuzz
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: scikit-learn
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: watchdog
  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-05-28 13:22:44 -07:00
Trenton H
6d4897a1b8 Refresh the document instance before doing workflow work, in case some other process has updated it (#6849) 2024-05-28 12:56:40 -07:00
shamoon
180b32651d Chore: add stale to any-of-labels 2024-05-27 15:23:40 -07:00
shamoon
3d56a56eb8 Fix: check original render type for split button 2024-05-25 09:59:28 -05:00
Trenton H
2b85e812f8 Chore(deps): Use psycopg as recommended (#6811) 2024-05-23 13:09:05 -07:00
shamoon
e6b856e13f Documentation: Add PAPERLESS_OCR_LANGUAGE config note (#6821) 2024-05-23 12:52:40 -07:00
shamoon
02ebcd29ee Bump @angular/cdk to 17.3.10
Fixes https://github.com/angular/components/issues/29053
2024-05-22 23:12:54 -07:00
shamoon
719f76060b Enhancement: default to title/content search, allow choosing full search link from global search (#6805) 2024-05-22 16:19:46 -07:00
shamoon
8abb0cd75d Enhancement: only include correspondent 'last_correspondence' if requested (#6792) 2024-05-22 23:15:58 +00:00
shamoon
c0c44b512c Enhancement: accessibility improvements for tags, doc links, dashboard views (#6786) 2024-05-22 23:08:25 +00:00
shamoon
3e62f13f96 Enhancement: delete pages PDF action (#6772) 2024-05-22 23:01:15 +00:00
Bruno Willenborg
2116964f67 docs: drop obsolete docker compose version (#6806) 2024-05-22 15:21:48 -07:00
Trenton H
f7ce32f471 Updates the Tika image to the official now that Apache publishes multi-arch images (#6802) 2024-05-21 20:45:56 +00:00
dependabot[bot]
d87208be51 Chore(deps-dev): Bump the development group with 2 updates (#6793)
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
- dependency-name: mkdocs-material
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-21 12:16:44 -07:00
dependabot[bot]
bb8ee1e5fb --- (#6795)
updated-dependencies:
- dependency-name: requests
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-21 08:10:45 -07:00
shamoon
2a0c03eda0 Enhancement: support custom logo / title on login page (#6775) 2024-05-20 09:40:19 -07:00
shamoon
3061c59c06 Chore: add system status to bug report 2024-05-17 23:50:57 -07:00
shamoon
79067041dd Documentation: correct usage 2024-05-17 23:44:55 -07:00
Trenton H
622f624132 Chore: Change the code formatter to Ruff (#6756)
* Changing the formatting to ruff-format

* Replaces references to black to ruff or ruff format, removes black from dependencies
2024-05-18 02:26:50 +00:00
shamoon
3facdefa40 Fix: tweak pdf preview page borders 2024-05-17 19:05:11 -07:00
dependabot[bot]
e283bbe5c2 Chore(deps): Bump the frontend-angular-dependencies group (#6761)
Bumps the frontend-angular-dependencies group in /src-ui with 19 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `17.3.6` | `17.3.9` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `17.3.7` | `17.3.9` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `17.3.7` | `17.3.9` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `17.3.7` | `17.3.9` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `17.3.7` | `17.3.9` |
| [@angular/localize](https://github.com/angular/angular) | `17.3.7` | `17.3.9` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `17.3.7` | `17.3.9` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `17.3.7` | `17.3.9` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `17.3.7` | `17.3.9` |
| [ng2-pdf-viewer](https://github.com/VadimDez/ng2-pdf-viewer) | `10.2.1` | `10.2.2` |
| [ngx-ui-tour-ng-bootstrap](https://github.com/hakimio/ngx-ui-tour) | `14.0.2` | `14.0.3` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `17.3.6` | `17.3.7` |
| [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `17.3.0` | `17.4.1` |
| [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `17.3.0` | `17.4.1` |
| [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `17.3.0` | `17.4.1` |
| [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `17.3.0` | `17.4.1` |
| [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `17.3.0` | `17.4.1` |
| [@angular/cli](https://github.com/angular/angular-cli) | `17.3.6` | `17.3.7` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `17.3.7` | `17.3.9` |


Updates `@angular/cdk` from 17.3.6 to 17.3.9
- [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.3.6...17.3.9)

Updates `@angular/common` from 17.3.7 to 17.3.9
- [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.3.9/packages/common)

Updates `@angular/compiler` from 17.3.7 to 17.3.9
- [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.3.9/packages/compiler)

Updates `@angular/core` from 17.3.7 to 17.3.9
- [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.3.9/packages/core)

Updates `@angular/forms` from 17.3.7 to 17.3.9
- [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.3.9/packages/forms)

Updates `@angular/localize` from 17.3.7 to 17.3.9
- [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.3.7...17.3.9)

Updates `@angular/platform-browser` from 17.3.7 to 17.3.9
- [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.3.9/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 17.3.7 to 17.3.9
- [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.3.9/packages/platform-browser-dynamic)

Updates `@angular/router` from 17.3.7 to 17.3.9
- [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.3.9/packages/router)

Updates `ng2-pdf-viewer` from 10.2.1 to 10.2.2
- [Release notes](https://github.com/VadimDez/ng2-pdf-viewer/releases)
- [Changelog](https://github.com/VadimDez/ng2-pdf-viewer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/VadimDez/ng2-pdf-viewer/compare/10.2.1...10.2.2)

Updates `ngx-ui-tour-ng-bootstrap` from 14.0.2 to 14.0.3
- [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.3.6 to 17.3.7
- [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.3.6...17.3.7)

Updates `@angular-eslint/builder` from 17.3.0 to 17.4.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.4.1/packages/builder)

Updates `@angular-eslint/eslint-plugin` from 17.3.0 to 17.4.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.4.1/packages/eslint-plugin)

Updates `@angular-eslint/eslint-plugin-template` from 17.3.0 to 17.4.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.4.1/packages/eslint-plugin-template)

Updates `@angular-eslint/schematics` from 17.3.0 to 17.4.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.4.1/packages/schematics)

Updates `@angular-eslint/template-parser` from 17.3.0 to 17.4.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.4.1/packages/template-parser)

Updates `@angular/cli` from 17.3.6 to 17.3.7
- [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.3.6...17.3.7)

Updates `@angular/compiler-cli` from 17.3.7 to 17.3.9
- [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.3.9/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-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: ng2-pdf-viewer
  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-patch
  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-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-05-17 22:57:34 +00:00
shamoon
c4f9828a10 Chore: indent system status code copy 2024-05-17 14:30:03 -07:00
shamoon
cb160212d4 Fix: consistently use created_date for doc display (#6758) 2024-05-17 10:20:40 -07:00
Trenton H
3fa448ecb5 Updates deps, unlocks those which have some resolution of issues (#6755) 2024-05-17 09:02:17 -07:00
shamoon
019a255753 Chore: revert pngx pdf viewer to third party package (#6741) 2024-05-16 11:12:19 -07:00
shamoon
50a6181e48 Reset dev version string 2024-05-15 14:15:56 -07:00
github-actions[bot]
5ff791e4c5 Documentation: Add v2.8.6 changelog (#6740)
---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-05-15 14:15:03 -07:00
shamoon
317a9114eb Bump version to 2.8.6 2024-05-15 13:54:33 -07:00
shamoon
2597d312ed Merge branch 'dev' 2024-05-15 13:52:02 -07:00
github-actions[bot]
0e95b0a64b New Crowdin translations by GitHub Action (#6735)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-05-15 13:50:24 -07:00
shamoon
853c745039 Update frontend translation strings 2024-05-15 13:24:05 -07:00
shamoon
ed05b40ba4 Security: disallow API remote-user auth if disabled (#6739) 2024-05-15 20:18:50 +00:00
shamoon
97eec44647 Fix: retain sort field from global search filtering, use FILTER_HAS_TAGS_ALL (#6737) 2024-05-15 13:10:07 -07:00
shamoon
45138a1881 Reset dev version string 2024-05-14 22:38:23 -07:00
github-actions[bot]
67565ea1ff Documentation: Add v2.8.5 changelog (#6729) 2024-05-14 22:37:48 -07:00
shamoon
05a240b6ed Bump version to v2.8.5 2024-05-14 22:11:43 -07:00
github-actions[bot]
4c6faa698b New Crowdin translations by GitHub Action (#6714)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-05-14 21:41:31 -07:00
shamoon
654685873a Fix: restore search highlighting on large cards results (#6728) 2024-05-14 21:38:10 -07:00
shamoon
2ac5407dd4 Fix: global search filtering links broken in 2.8.4 (#6726) 2024-05-14 21:37:55 -07:00
shamoon
76ddc09dba Fix: button alignment on mobile 2024-05-14 20:14:45 -07:00
shamoon
f45daa9445 Fix: some buttons incorrectly aligned in 2.8.4 (#6715) 2024-05-13 17:26:41 -07:00
shamoon
953ba9160e Fix: dont format ASN as number on dashboard (#6708) 2024-05-13 13:58:47 -07:00
shamoon
64de6b8571 Reset dev version string 2024-05-13 13:25:22 -07:00
github-actions[bot]
5455850168 Documentation: Add v2.8.4 changelog (#6706) 2024-05-13 13:24:49 -07:00
shamoon
e91af06189 Fix changelog 2024-05-13 13:03:57 -07:00
shamoon
779f091c04 Bump version to 2.8.4 2024-05-13 12:59:32 -07:00
shamoon
0627c7f43e Merge branch 'dev' 2024-05-13 12:58:58 -07:00
github-actions[bot]
302bc9e9f6 New Crowdin translations by GitHub Action (#6663)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-05-13 12:44:05 -07:00
shamoon
a1e4365ff2 Enhancement: global search tweaks (#6674) 2024-05-13 16:12:02 +00:00
shamoon
7983487430 Security: Correctly disable eval in pdfjs (#6702) 2024-05-13 08:44:35 -07:00
shamoon
ac666df4ce Fix: only use pointer for sortable table headings, fix title width 2024-05-12 19:57:08 -07:00
shamoon
68ca27c27c Fix: history timestamp tooltip illegible in dark mode (#6696) 2024-05-13 01:43:05 +00:00
Daniel
52350f8b51 Enhancement: display current ASN in statistics (#6692) 2024-05-12 16:58:04 -07:00
shamoon
6fa3522618 Fix: only count inbox documents from inbox tags with permissions (#6670) 2024-05-10 09:00:37 -07:00
github-actions[bot]
84c3e7893e Changelog v2.8.3 - GHA (#6664)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-09 14:47:55 -07:00
Trenton H
74b850423f Resets version to -dev string 2024-05-09 13:21:55 -07:00
Trenton H
43a6e3985d Bumps version to 2.8.3 2024-05-09 13:21:16 -07:00
github-actions[bot]
83e3f8efb8 New Crowdin translations by GitHub Action (#6634)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-05-09 13:19:58 -07:00
Trenton H
8c93d1db42 Resets version to -dev string 2024-05-09 13:05:17 -07:00
Trenton H
5b8cd96f37 Bumps version to 2.8.3 2024-05-09 13:03:06 -07:00
shamoon
5fec764018 Fix: correctly respect superuser for document history (#6661) 2024-05-09 19:27:59 +00:00
shamoon
22c8d8ef2a Fix: allow 0 in monetary field (#6658) 2024-05-09 10:43:27 -07:00
shamoon
9b3a29cddd Fix: custom field removal doesnt always trigger change detection (#6653) 2024-05-09 17:10:11 +00:00
Trenton H
d461dcbe29 Downgrades and locks lxml to before 5.2.0, which released a breaking change (#6655) 2024-05-09 09:42:19 -07:00
shamoon
3e22f033c7 Fix: correctly handle global search esc key when open and button focused (#6644) 2024-05-09 06:41:42 -07:00
shamoon
e7a5ebc64c Fix: consistent monetary field display in list and cards (#6645) 2024-05-09 04:32:14 +00:00
shamoon
e1f5edc0a1 Fix: doc links and more illegible in light mode (#6643) 2024-05-08 21:01:01 -07:00
Trenton H
48092d47c5 Updates the recommended versions of databases to their latest (#6639) 2024-05-08 20:32:17 +00:00
Trenton H
d4d0604da2 Moves additional auditlog imports into protected blocks (#6638) 2024-05-08 09:04:32 -07:00
shamoon
f7db5f3821 Reset dev version string 2024-05-07 18:39:04 -07:00
shamoon
ddb65d371a Fix changelog spelling 2024-05-07 18:38:12 -07:00
github-actions[bot]
e17b91b87c Changelog v2.8.2 - GHA (#6628)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-07 18:18:50 -07:00
shamoon
47ce797ee9 Bump version to 2.8.2 2024-05-07 17:13:59 -07:00
github-actions[bot]
f8057ed4f1 New Crowdin translations by GitHub Action (#6606)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-05-07 17:09:33 -07:00
Trenton H
d3ff0ff8e0 Restores the compression of static files for x86_64 (#6627) 2024-05-07 15:17:15 -07:00
shamoon
6ea25a96a3 Fix: make backend monetary validation accept unpadded decimals (#6626) 2024-05-07 21:38:52 +00:00
shamoon
caec0ed4d1 Fix: allow bulk edit with existing fields (#6625) 2024-05-07 14:26:07 -07:00
shamoon
ce08400f4e Enhancement: show name on cards if custom field empty, add tooltip (#6620) 2024-05-07 11:09:15 -07:00
shamoon
076b5b1af5 Security: Disable eval in pdfjs (#6615)
Closes https://github.com/paperless-ngx/paperless-ngx/security/dependabot/181 see https://github.com/advisories/GHSA-wgrm-67xf-hhpq
2024-05-07 16:45:19 +00:00
shamoon
44ed78b442 Documentation: correct display of PAPERLESS_CONSUMER_TAG_BARCODE_MAPPING (#6616) 2024-05-07 09:36:44 -07:00
shamoon
3bd6a6fcfa Update frontend translation strings 2024-05-07 09:11:11 -07:00
shamoon
4fa08a9c96 Change: allow modifier+arrow keyboard shortcuts in input fields 2024-05-07 09:10:38 -07:00
shamoon
8ea3259fe7 Fix: table view doesnt immediately display custom fields on app startup (#6600) 2024-05-07 15:30:34 +00:00
shamoon
fae2399e46 Fix: dont use limit in subqueries for mariadb compatibility (#6611) 2024-05-07 15:14:00 +00:00
shamoon
0d49314593 Fix: exclude admin perms from group permissions serializer (#6608) 2024-05-07 14:39:31 +00:00
shamoon
fda4742e86 Fix: global search text illegible in light mode (#6602) 2024-05-07 01:48:47 -07:00
shamoon
190b648c72 Fix: document history text color illegible in light mode (#6601) 2024-05-07 01:37:13 -07:00
shamoon
22e88046bc Reset dev version string 2024-05-06 23:50:52 -07:00
github-actions[bot]
06447c72c5 Documentation: Add v2.8.1 changelog (#6595)
* Changelog v2.8.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-05-06 23:49:53 -07:00
shamoon
7a6fe2da7c Update environment.prod.ts 2024-05-06 23:34:21 -07:00
shamoon
f04cf1a974 Fix: saved views dont immediately display custom fields in table view (#6594) 2024-05-06 23:30:06 -07:00
dependabot[bot]
1ebce6f3e0 Chore(deps-dev): Bump jinja2 from 3.1.3 to 3.1.4 (#6579)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-07 06:00:47 +00:00
dependabot[bot]
e07777e38a Chore(deps-dev): Bump mkdocs-glightbox in the small-changes group (#6581)
Bumps the small-changes group with 1 update: [mkdocs-glightbox](https://github.com/Blueswen/mkdocs-glightbox).


Updates `mkdocs-glightbox` from 0.3.7 to 0.4.0
- [Release notes](https://github.com/Blueswen/mkdocs-glightbox/releases)
- [Changelog](https://github.com/blueswen/mkdocs-glightbox/blob/main/CHANGELOG)
- [Commits](https://github.com/Blueswen/mkdocs-glightbox/compare/v0.3.7...v0.4.0)

---
updated-dependencies:
- dependency-name: mkdocs-glightbox
  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-05-07 05:50:54 +00:00
shamoon
2c69d0fd2e Reset dev version string 2024-05-06 22:34:40 -07:00
github-actions[bot]
b58c114a76 Changelog v2.8.1 - GHA (#6590)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-05-06 22:34:06 -07:00
github-actions[bot]
9aee6f5a78 Documentation: Add v2.8.0 changelog (#6585)
* Changelog v2.8.0 - 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-05-06 22:19:01 -07:00
shamoon
54edad29ba Bump version to v2.8.1 2024-05-06 22:15:18 -07:00
shamoon
0bf711259a Merge branch 'dev' 2024-05-06 22:14:41 -07:00
github-actions[bot]
1b6250ae24 New Crowdin translations by GitHub Action (#6587)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-05-06 22:13:22 -07:00
shamoon
f2b3521e6c Fix: bulk edit custom fields should support multiple items (#6589) 2024-05-06 22:07:55 -07:00
shamoon
c2944402fa Reset dev version string 2024-05-06 14:50:39 -07:00
shamoon
a0f1f6faa1 Merge branch 'dev' 2024-05-06 14:36:39 -07:00
github-actions[bot]
eaec0014c5 New Crowdin translations by GitHub Action (#6582)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-05-06 14:27:55 -07:00
shamoon
571f3444d1 Merge branch 'dev' 2024-05-06 14:06:20 -07:00
shamoon
57032e234c New Crowdin translations by GitHub Action (#6578)
Co-Authored-By: Crowdin Bot <support+bot@crowdin.com>
2024-05-06 14:05:46 -07:00
shamoon
321adaeb8b Bump version to 2.8.0 2024-05-06 13:41:13 -07:00
shamoon
5d937cf639 Merge branch 'dev' 2024-05-06 13:41:08 -07:00
github-actions[bot]
3e7656e1e1 New Crowdin translations by GitHub Action (#6331)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-05-06 20:40:33 +00:00
Lino
93555cf2e7 Documentation: add link to list of hosting providers (#6577) 2024-05-06 20:33:13 +00:00
shamoon
f60c201eb9 Update README.md 2024-05-06 13:18:16 -07:00
shamoon
78af59ec17 Fix: correct admin permissions check for system status 2024-05-06 12:58:57 -07:00
shamoon
b305372ed1 Updated affiliated projects to related projects 2024-05-05 16:01:32 -07:00
Daniel Rheinbay
16b8b58533 Documentation: fix PAPERLESS_APP_TITLE config type (#6568) 2024-05-05 00:04:44 -07:00
Trenton H
5802163a0e Chore(deps): Bump all allowed backend packages (#6562) 2024-05-04 13:26:15 -07:00
shamoon
b403b9d9d5 Fix ngbDropdown stealing keyboard events 2024-05-04 09:47:27 -07:00
shamoon
c6e7d06bb7 Feature: global search, keyboard shortcuts / hotkey support (#6449) 2024-05-02 16:15:56 +00:00
dependabot[bot]
40289cd714 Chore(deps): Bump stumpylog/image-cleaner-action in the actions group (#6541)
Bumps the actions group with 1 update: [stumpylog/image-cleaner-action](https://github.com/stumpylog/image-cleaner-action).


Updates `stumpylog/image-cleaner-action` from 0.5.0 to 0.6.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.5.0...v0.6.0)

---
updated-dependencies:
- 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-05-02 15:20:57 +00:00
shamoon
2de9d1b7ae Enhancement: improve layout for custom fields dropdown (#6362) 2024-05-01 21:57:18 -07:00
dependabot[bot]
39b57f695a Chore(deps-dev): Bump ejs from 3.1.9 to 3.1.10 in /src-ui (#6540)
Bumps [ejs](https://github.com/mde/ejs) from 3.1.9 to 3.1.10.
- [Release notes](https://github.com/mde/ejs/releases)
- [Commits](https://github.com/mde/ejs/compare/v3.1.9...v3.1.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-01 19:27:19 +00:00
dependabot[bot]
f503cd8758 Chore(deps): Bump the frontend-angular-dependencies group (#6539)
Bumps the frontend-angular-dependencies group in /src-ui with 13 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `17.3.2` | `17.3.6` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `17.3.2` | `17.3.7` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `17.3.2` | `17.3.7` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `17.3.2` | `17.3.7` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `17.3.2` | `17.3.7` |
| [@angular/localize](https://github.com/angular/angular) | `17.3.2` | `17.3.7` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `17.3.2` | `17.3.7` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `17.3.2` | `17.3.7` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `17.3.2` | `17.3.7` |
| [@angular-builders/jest](https://github.com/just-jeb/angular-builders/tree/HEAD/packages/jest) | `17.0.2` | `17.0.3` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `17.3.2` | `17.3.6` |
| [@angular/cli](https://github.com/angular/angular-cli) | `17.3.2` | `17.3.6` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `17.3.2` | `17.3.7` |


Updates `@angular/cdk` from 17.3.2 to 17.3.6
- [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.3.2...17.3.6)

Updates `@angular/common` from 17.3.2 to 17.3.7
- [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.3.7/packages/common)

Updates `@angular/compiler` from 17.3.2 to 17.3.7
- [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.3.7/packages/compiler)

Updates `@angular/core` from 17.3.2 to 17.3.7
- [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.3.7/packages/core)

Updates `@angular/forms` from 17.3.2 to 17.3.7
- [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.3.7/packages/forms)

Updates `@angular/localize` from 17.3.2 to 17.3.7
- [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.3.2...17.3.7)

Updates `@angular/platform-browser` from 17.3.2 to 17.3.7
- [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.3.7/packages/platform-browser)

Updates `@angular/platform-browser-dynamic` from 17.3.2 to 17.3.7
- [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.3.7/packages/platform-browser-dynamic)

Updates `@angular/router` from 17.3.2 to 17.3.7
- [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.3.7/packages/router)

Updates `@angular-builders/jest` from 17.0.2 to 17.0.3
- [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.3/packages/jest)

Updates `@angular-devkit/build-angular` from 17.3.2 to 17.3.6
- [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.3.2...17.3.6)

Updates `@angular/cli` from 17.3.2 to 17.3.6
- [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.3.2...17.3.6)

Updates `@angular/compiler-cli` from 17.3.2 to 17.3.7
- [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.3.7/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-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-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-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-05-01 19:20:06 +00:00
shamoon
8d516c08f0 Fix relative date test that will fail across months 2024-05-01 12:07:27 -07:00
shamoon
8b4fc02955 Hide unusable filters 2024-05-01 12:07:27 -07:00
shamoon
6c24686509 Fix: always refresh document after save 2024-05-01 12:07:27 -07:00
Trenton H
7be7185418 Handcrafts SQL queries a little more to reduce the query count and/or the amount of returned data (#6489) 2024-04-30 07:37:09 -07:00
shamoon
63e1f9f5d3 Feature: custom fields filtering & bulk editing (#6484) 2024-04-26 15:10:03 -07:00
shamoon
bd4476d484 Feature: customizable fields display for documents, saved views & dashboard widgets (#6439) 2024-04-26 06:41:12 -07:00
shamoon
7a0334f353 Fix: cast custom fields values list to list for other DB types
Closes #6482
2024-04-24 01:35:45 -07:00
shamoon
d03e48ea88 Fix missing test import 2024-04-24 00:28:18 -07:00
shamoon
342e6d4679 Fix: include number placeholder in relative date strings 2024-04-24 00:22:08 -07:00
dependabot[bot]
584f1361ad Chore(deps): Bump python-ipware in the major-versions group (#6468)
Bumps the major-versions group with 1 update: [python-ipware](https://github.com/un33k/python-ipware).


Updates `python-ipware` from 2.0.3 to 3.0.0
- [Changelog](https://github.com/un33k/python-ipware/blob/main/CHANGELOG.md)
- [Commits](https://github.com/un33k/python-ipware/compare/v2.0.3...v3.0.0)

---
updated-dependencies:
- dependency-name: python-ipware
  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-04-23 09:08:46 -07:00
shamoon
05b1ff9738 Feature: document history (audit log UI) (#6388) 2024-04-23 15:16:28 +00:00
dependabot[bot]
d65fcf70f3 Chore(deps-dev): Bump the development group with 2 updates (#6466)
* 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.3.7 to 0.4.1
- [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.3.7...v0.4.1)

Updates `mkdocs-material` from 9.5.17 to 9.5.18
- [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.17...9.5.18)

---
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 the ruff hook version to match

---------

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-04-23 14:53:56 +00:00
shamoon
a5d3d51cc5 Fix: always check workflow filter_mailrule if set (#6474) 2024-04-23 07:37:14 -07:00
shamoon
f4489ca2e7 Enhancement: larger documents in rotate / split dialogs 2024-04-23 00:49:20 -07:00
Benedikt Schwering
e40893e74f Fix: scroll for large tables (#6460)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-04-21 23:31:33 +00:00
shamoon
d002ae2e05 Fix: monetary field with null values 2024-04-20 21:44:31 -07:00
shamoon
bf430865b4 Fix: check original file for rotate 2024-04-20 20:14:44 -07:00
shamoon
a47d36f5e5 Enhancement: speed up merge document list retrieval 2024-04-19 10:17:17 -07:00
shamoon
4392628bd7 Fix: password reset done template error (#6444) 2024-04-19 09:34:34 -07:00
shamoon
6d25eb26a1 Update translation strings 2024-04-19 01:13:11 -07:00
shamoon
95fd1ae879 Enhancement: refactor monetary field (#6370) 2024-04-19 08:08:46 +00:00
shamoon
78f338484f Enhancement: improve layout, button labels for custom fields dropdown (#6362) 2024-04-19 06:57:17 +00:00
Trenton H
40db1065dc Updates QPDF to 11.9.0 from trixie (#6423) 2024-04-18 22:16:44 +00:00
shamoon
c644e57533 Documentation: correct permissions bulk edit parameter 2024-04-18 02:10:40 -07:00
Trenton H
b720aa3cd1 Chore: Convert the consumer to a plugin (#6361) 2024-04-18 02:59:14 +00:00
dependabot[bot]
e837f1e85b Chore(deps): Bump gunicorn from 21.2.0 to 22.0.0 (#6416)
Bumps [gunicorn](https://github.com/benoitc/gunicorn) from 21.2.0 to 22.0.0.
- [Release notes](https://github.com/benoitc/gunicorn/releases)
- [Commits](https://github.com/benoitc/gunicorn/compare/21.2.0...22.0.0)

---
updated-dependencies:
- dependency-name: gunicorn
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-18 00:38:03 +00:00
dependabot[bot]
ea2012bc81 Chore(deps): Bump the small-changes group with 11 updates (#6405)
* Chore(deps): Bump the small-changes group with 11 updates

Bumps the small-changes group with 11 updates:

| Package | From | To |
| --- | --- | --- |
| [django-filter](https://github.com/carltongibson/django-filter) | `24.1` | `24.2` |
| [djangorestframework](https://github.com/encode/django-rest-framework) | `3.14.0` | `3.15.1` |
| [channels](https://github.com/django/channels) | `4.0.0` | `4.1.0` |
| [filelock](https://github.com/tox-dev/py-filelock) | `3.13.3` | `3.13.4` |
| [imap-tools](https://github.com/ikvk/imap_tools) | `1.5.0` | `1.6.0` |
| [python-ipware](https://github.com/un33k/python-ipware) | `2.0.2` | `2.0.3` |
| [rapidfuzz](https://github.com/rapidfuzz/RapidFuzz) | `3.7.0` | `3.8.1` |
| [scikit-learn](https://github.com/scikit-learn/scikit-learn) | `1.4.1.post1` | `1.4.2` |
| [black](https://github.com/psf/black) | `24.3.0` | `24.4.0` |
| [ruff](https://github.com/astral-sh/ruff) | `0.3.5` | `0.3.7` |
| [daphne](https://github.com/django/daphne) | `4.1.0` | `4.1.2` |


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

Updates `djangorestframework` from 3.14.0 to 3.15.1
- [Release notes](https://github.com/encode/django-rest-framework/releases)
- [Commits](https://github.com/encode/django-rest-framework/compare/3.14.0...3.15.1)

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

Updates `filelock` from 3.13.3 to 3.13.4
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.13.3...3.13.4)

Updates `imap-tools` from 1.5.0 to 1.6.0
- [Release notes](https://github.com/ikvk/imap_tools/releases)
- [Changelog](https://github.com/ikvk/imap_tools/blob/master/docs/release_notes.rst)
- [Commits](https://github.com/ikvk/imap_tools/compare/v1.5.0...v1.6.0)

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

Updates `rapidfuzz` from 3.7.0 to 3.8.1
- [Release notes](https://github.com/rapidfuzz/RapidFuzz/releases)
- [Changelog](https://github.com/rapidfuzz/RapidFuzz/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/rapidfuzz/RapidFuzz/compare/v3.7.0...v3.8.1)

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

Updates `black` from 24.3.0 to 24.4.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.3.0...24.4.0)

Updates `ruff` from 0.3.5 to 0.3.7
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.3.5...v0.3.7)

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

---
updated-dependencies:
- dependency-name: django-filter
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: djangorestframework
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: channels
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: filelock
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: imap-tools
  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: rapidfuzz
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: scikit-learn
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: small-changes
- dependency-name: black
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: small-changes
- dependency-name: ruff
  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-patch
  dependency-group: small-changes
...

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

* Reverts DRF update

* Also bumps the hook versions to match

---------

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-04-18 00:26:10 +00:00
Dominik Bruhn
8e39315586 Enhancement: Hide columns in document list if user does not have permissions (#6415)
---------

Co-authored-by: Dominik Bruhn <dominik@menlo79.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-04-16 21:41:15 +00:00
shamoon
ea8127202d Documentation: Remove SQLite to Postgres in favor of export/import (#6401) 2024-04-15 11:50:04 -07:00
Benedikt Schwering
f009d9868e Fix: show message on empty group list (#6393) 2024-04-14 17:53:48 +00:00
shamoon
1bbcd0961b Fix select dropdown pixel alignment 2024-04-14 08:55:07 -07:00
shamoon
4fa2b54aed Update frontend translation strings 2024-04-13 20:12:54 -07:00
shamoon
7281c110c6 Fix: exclude admin perms from frontend 2024-04-13 20:12:54 -07:00
shamoon
f812f2af4d Fix: remove admin.logentry perm, use admin (staff) status (#6380) 2024-04-14 00:35:34 +00:00
dependabot[bot]
47b4a602a7 Chore(deps): Bump idna from 3.6 to 3.7 (#6377)
Bumps [idna](https://github.com/kjd/idna) from 3.6 to 3.7.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.rst)
- [Commits](https://github.com/kjd/idna/compare/v3.6...v3.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-12 07:38:28 -07:00
Péter Szász
21c7675f66 Documentation: typo fixes in the configuration docs 2024-04-11 15:27:39 -07:00
dependabot[bot]
ca73c0d1f3 Chore(deps): Bump tar from 6.2.0 to 6.2.1 in /src-ui (#6373)
Bumps [tar](https://github.com/isaacs/node-tar) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.2.0...v6.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 06:51:53 -07:00
shamoon
7f6a50be5b Fix: dont dismiss active alerts on "dismiss completed" (#6364) 2024-04-10 10:55:37 -07:00
shamoon
10e10f9ff4 Fix: Allow lowercase letters in monetary currency code field (#6359) 2024-04-10 08:27:03 -07:00
Trenton H
95c24a50f7 Fix: Allow negative monetary values with a current code (#6358)
* Updates the currency validation to allow an optional negative

* Update frontend regex

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-04-10 07:33:13 -07:00
Harald
d06faa2fcb Fix: add timezone fallback to install script (#6336) 2024-04-08 17:45:43 -07:00
shamoon
bed66cced0 Reset dev version string 2024-04-07 19:04:16 -07:00
github-actions[bot]
ceaf60e6ad Changelog v2.7.2 - GHA (#6329)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-04-07 19:03:01 -07:00
shamoon
9885ca5103 Bump version to 2.7.2 2024-04-07 18:44:14 -07:00
shamoon
2f22beaaee Merge branch 'dev' 2024-04-07 18:43:43 -07:00
github-actions[bot]
fb2c6282a4 New Crowdin translations by GitHub Action (#6313)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-04-07 18:43:07 -07:00
shamoon
8c5b5d3948 Update translation strings 2024-04-07 18:36:14 -07:00
shamoon
4e5135fe70 Fix: hide page chooser if not loaded yet 2024-04-07 15:41:45 -07:00
shamoon
579c35a3fe Fix: isFiltered check before filter editor init 2024-04-07 15:38:38 -07:00
shamoon
4aedcb856d Fix navbar-brand width on large screens 2024-04-07 15:21:13 -07:00
shamoon
0b34e70f6c Fix: select dropdown background colors not visible in light mode (#6323) 2024-04-07 21:03:15 +00:00
shamoon
7afc91e7b1 Fix: spacing in reset and incorrect display in saved views (#6324) 2024-04-07 20:50:31 +00:00
shamoon
56b17ce6a2 Documentation: add more info to rotation note 2024-04-07 12:46:55 -07:00
shamoon
954912cac3 Update translation strings 2024-04-07 12:41:08 -07:00
shamoon
e46f6b1156 Fix frontend tests warning 2024-04-07 11:55:27 -07:00
shamoon
1d85caa8d0 Fix: disable invalid create endpoints (#6320) 2024-04-07 18:50:40 +00:00
shamoon
622fcf96a0 Fix: initialize current page to 1 2024-04-07 11:15:49 -07:00
shamoon
654cc05f0e Fix: also clear select all checkbox in tasks view 2024-04-07 10:47:27 -07:00
shamoon
974dd24e69 Fix: dont initialize page numbers, allow split with browser pdf viewer (#6314) 2024-04-07 08:16:33 -07:00
shamoon
fe824e0faa Reset dev version string 2024-04-06 18:19:59 -07:00
github-actions[bot]
377d89ae06 Changelog v2.7.1 - GHA (#6311)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-04-06 18:19:22 -07:00
shamoon
629e24e031 Bump version to 2.7.1 2024-04-06 17:42:39 -07:00
shamoon
38414025c8 Fix: update notification spacing 2024-04-06 17:40:22 -07:00
github-actions[bot]
1dc5b7a707 New Crowdin translations by GitHub Action (#6301)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-04-06 17:35:48 -07:00
shamoon
f076418c50 Reset dev version string 2024-04-06 16:50:18 -07:00
shamoon
bbaad2cdfb Merge branch 'main' into dev 2024-04-06 16:50:11 -07:00
github-actions[bot]
ef01658335 Documentation: Add v2.7.0 changelog (#6293) 2024-04-06 16:49:41 -07:00
shamoon
9f4a6c3b42 Fix: Only disable split button if pages = 1 (#6304) 2024-04-06 23:49:00 +00:00
shamoon
5450bfb67b Fix: Use correct custom field id when splitting (#6303) 2024-04-06 23:39:59 +00:00
Trenton H
ae2b302962 Fix: Rotation fails due to celery chord (#6306)
Use Celery immutable signature so it does not take the result of the header task
2024-04-06 23:26:48 +00:00
shamoon
957691c454 Fix: split user / group objects error (#6302) 2024-04-06 13:51:50 -07:00
shamoon
6b17ba2934 Bump version to 2.7.0 2024-04-05 18:00:05 -07:00
shamoon
4d3616cda9 Merge branch 'dev' 2024-04-05 17:59:32 -07:00
github-actions[bot]
c4a9697e02 New Crowdin translations by GitHub Action (#6127)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-04-05 17:59:03 -07:00
shamoon
c57b7520b9 Update translation strings 2024-04-04 14:32:00 -07:00
dependabot[bot]
46bd09227f Chore(deps): Bump pillow from 10.2.0 to 10.3.0 (#6268)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 10.2.0 to 10.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/10.2.0...10.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 20:39:08 +00:00
dependabot[bot]
971f92a05c Chore(deps-dev): Bump the development group with 2 updates (#6276)
* 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.3.4 to 0.3.5
- [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.3.4...v0.3.5)

Updates `mkdocs-material` from 9.5.15 to 9.5.17
- [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.15...9.5.17)

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

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

* Updates ruff hook version to match

---------

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-04-04 20:26:40 +00:00
Trenton H
2c43b06910 Chore: Standardize subprocess running and logging (#6275) 2024-04-04 13:11:43 -07:00
shamoon
0f8b2e69c9 Change: enable auditlog by default, fix import / export (#6267) 2024-04-04 18:51:15 +00:00
Trenton H
00b04c2e86 Escape the secret key when writing it to the env file (#6243) 2024-04-02 03:02:27 +00:00
shamoon
b3c66cae06 Fix: hide sidebar labels if group is empty (#6254) 2024-04-01 22:00:51 +00:00
shamoon
6a79d417b4 Fix: management list clear all should clear header checkbox (#6253) 2024-04-01 21:43:40 +00:00
dependabot[bot]
98ef68f720 Chore(deps): Bump the frontend-angular-dependencies group (#6248)
Bumps the frontend-angular-dependencies group in /src-ui with 17 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `17.2.1` | `17.3.2` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `17.2.3` | `17.3.2` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `17.2.3` | `17.3.2` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `17.2.3` | `17.3.2` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `17.2.3` | `17.3.2` |
| [@angular/localize](https://github.com/angular/angular) | `17.2.3` | `17.3.2` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `17.2.3` | `17.3.2` |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular/tree/HEAD/packages/platform-browser-dynamic) | `17.2.3` | `17.3.2` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `17.2.3` | `17.3.2` |
| [@angular-devkit/build-angular](https://github.com/angular/angular-cli) | `17.2.2` | `17.3.2` |
| [@angular-eslint/builder](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/builder) | `17.2.1` | `17.3.0` |
| [@angular-eslint/eslint-plugin](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin) | `17.2.1` | `17.3.0` |
| [@angular-eslint/eslint-plugin-template](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/eslint-plugin-template) | `17.2.1` | `17.3.0` |
| [@angular-eslint/schematics](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/schematics) | `17.2.1` | `17.3.0` |
| [@angular-eslint/template-parser](https://github.com/angular-eslint/angular-eslint/tree/HEAD/packages/template-parser) | `17.2.1` | `17.3.0` |
| [@angular/cli](https://github.com/angular/angular-cli) | `17.2.2` | `17.3.2` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `17.2.3` | `17.3.2` |


Updates `@angular/cdk` from 17.2.1 to 17.3.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.2.1...17.3.2)

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

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

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

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

Updates `@angular/localize` from 17.2.3 to 17.3.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.2.3...17.3.2)

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

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

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

Updates `@angular-devkit/build-angular` from 17.2.2 to 17.3.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.2...17.3.2)

Updates `@angular-eslint/builder` from 17.2.1 to 17.3.0
- [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.3.0/packages/builder)

Updates `@angular-eslint/eslint-plugin` from 17.2.1 to 17.3.0
- [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.3.0/packages/eslint-plugin)

Updates `@angular-eslint/eslint-plugin-template` from 17.2.1 to 17.3.0
- [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.3.0/packages/eslint-plugin-template)

Updates `@angular-eslint/schematics` from 17.2.1 to 17.3.0
- [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.3.0/packages/schematics)

Updates `@angular-eslint/template-parser` from 17.2.1 to 17.3.0
- [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.3.0/packages/template-parser)

Updates `@angular/cli` from 17.2.2 to 17.3.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.2...17.3.2)

Updates `@angular/compiler-cli` from 17.2.3 to 17.3.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.3.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: "@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-04-01 14:36:48 -07:00
dependabot[bot]
ed3b7aa8f2 Chore(deps-dev): Bump @playwright/test from 1.42.0 to 1.42.1 in /src-ui (#6250)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.42.0 to 1.42.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.42.0...v1.42.1)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  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-04-01 21:16:18 +00:00
dependabot[bot]
e536600052 Chore(deps-dev): Bump @types/node from 20.11.24 to 20.12.2 in /src-ui (#6251)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.24 to 20.12.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 21:05:56 +00:00
dependabot[bot]
bb820a2127 Chore(deps-dev): Bump the frontend-eslint-dependencies group (#6249)
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 7.1.0 to 7.4.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.4.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 7.1.0 to 7.4.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.4.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-04-01 13:59:08 -07:00
shamoon
129933ff30 Enhancement: support custom fields in post_document endpoint (#6222) 2024-03-30 13:00:53 -07:00
Elias Probst
41fc11efff Enhancement: add ASN to consume rejection message (#6217) 2024-03-28 19:38:29 -07:00
Sander
a712bc72ca Documentation: document_importer does not directly support zip archives (#6209)
---------

Co-authored-by: Sander <biocoder-xbmc@xs4all.nl>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
Co-authored-by: Trenton H <797416+stumpylog@users.noreply.github.com>
2024-03-27 19:53:04 -07:00
dependabot[bot]
fbe7acc6b0 Chore(deps-dev): Bump express from 4.18.3 to 4.19.2 in /src-ui (#6207)
Bumps [express](https://github.com/expressjs/express) from 4.18.3 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.3...4.19.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-27 11:43:33 -05:00
Trenton H
c4153b6fbf Locks DRF version to 3.14, updates other dependencies (#6200) 2024-03-27 09:13:02 -07:00
shamoon
1f355a22e0 Documentation update info about logout URL 2024-03-27 00:59:18 -05:00
shamoon
4af8070450 Feature: PDF actions - merge, split & rotate (#6094) 2024-03-25 18:41:24 -07:00
shamoon
d6d0071175 Fix: start-align object names in some UI lists (#6188) 2024-03-25 10:01:54 -07:00
shamoon
ef51633b2c Fix: allow scroll long upload files alerts list (#6184) 2024-03-25 07:53:29 -07:00
shamoon
d4963b9cbe Fix: document_renamer fails with audit_log enabled (#6175) 2024-03-24 07:26:25 -07:00
dependabot[bot]
01dabf7c05 Chore(deps-dev): Bump webpack-dev-middleware in /src-ui (#6161)
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 13:25:29 -07:00
shamoon
fc68f79cc8 Enhancement: always place search term first in autocomplete results (#6142) 2024-03-21 19:03:17 +00:00
shamoon
ebe1479503 Fix: catch sessionStorage errors for large documents (#6150) 2024-03-21 06:31:25 -07:00
dependabot[bot]
8c9fe4da06 Chore(deps-dev): Bump the development group with 4 updates (#6131)
* Chore(deps-dev): Bump the development group with 4 updates

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


Updates `black` from 24.2.0 to 24.3.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.2.0...24.3.0)

Updates `ruff` from 0.3.2 to 0.3.3
- [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.3.2...v0.3.3)

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

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

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

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

* Also matches pre-commit hook versions to Pipfile

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Trenton Holmes <797416+stumpylog@users.noreply.github.com>
2024-03-21 02:09:37 +00:00
shamoon
b2ef51af55 Reset dev version string 2024-03-17 22:58:35 -07:00
github-actions[bot]
32b35d8e4b Changelog v2.6.3 - GHA (#6125)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-17 21:52:09 -07:00
shamoon
c8bda18cf2 Bump version to 2.6.3 2024-03-17 20:45:09 -07:00
shamoon
0a944975cc Merge branch 'dev' 2024-03-17 20:44:39 -07:00
github-actions[bot]
48eaa31ecf New Crowdin translations by GitHub Action (#6055)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-03-17 20:44:19 -07:00
shamoon
1540e88a06 Documentation: fix small typos in file handling docs 2024-03-17 16:43:48 -07:00
shamoon
b1aa57abcb Fix: workflow action reordering 2024-03-17 15:44:10 -07:00
dependabot[bot]
df359730fe Chore(deps-dev): Bump follow-redirects from 1.15.5 to 1.15.6 in /src-ui (#6120)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-17 00:01:11 +00:00
shamoon
43ec154bc2 Fix: allow setting allauth ACCOUNT_SESSION_REMEMBER (#6105) 2024-03-14 15:34:23 +00:00
shamoon
2c4a664df4 Change: remove credentials from redis url in system status (#6104) 2024-03-14 08:20:34 -07:00
dependabot[bot]
a196c14a58 Chore(deps-dev): Bump the development group with 3 updates (#6079)
* Chore(deps-dev): Bump the development group with 3 updates

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


Updates `ruff` from 0.3.0 to 0.3.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.3.0...v0.3.2)

Updates `pytest` from 8.0.2 to 8.1.1
- [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.2...8.1.1)

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

---
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-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 pre-commit hook versions and runs it against all files

---------

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-12 07:56:01 -07:00
dependabot[bot]
6f549506d6 Chore(deps): Bump the django group with 1 update (#6080)
Bumps the django group with 1 update: [django-filter](https://github.com/carltongibson/django-filter).


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

---
updated-dependencies:
- dependency-name: django-filter
  dependency-type: direct:production
  update-type: version-update:semver-major
  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-11 17:33:50 -07:00
dependabot[bot]
8d463e05ae Chore(deps): Bump the small-changes group with 2 updates (#6081)
Bumps the small-changes group with 2 updates: [rapidfuzz](https://github.com/rapidfuzz/RapidFuzz) and [redis](https://github.com/redis/redis-py).


Updates `rapidfuzz` from 3.6.1 to 3.6.2
- [Release notes](https://github.com/rapidfuzz/RapidFuzz/releases)
- [Changelog](https://github.com/rapidfuzz/RapidFuzz/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/rapidfuzz/RapidFuzz/compare/v3.6.1...v3.6.2)

Updates `redis` from 5.0.2 to 5.0.3
- [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.2...v5.0.3)

---
updated-dependencies:
- dependency-name: rapidfuzz
  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-11 13:32:28 -07:00
shamoon
373c91911d Documentation: improve permissions docs (#6046) 2024-03-11 08:20:52 -07:00
shamoon
1d3ac99c02 Merge branch 'main' into dev 2024-03-09 15:11:53 -08:00
Matt Patterson
cda4c8f87e Documentation: correct bulk edit documents endpoint (#6057) 2024-03-09 15:11:09 -08:00
shamoon
ef4f589094 Change: dont require empty bulk edit parameters (#6059) 2024-03-09 07:39:35 -08:00
shamoon
3aeb45bf34 Merge branch 'main' into dev 2024-03-08 17:05:33 -08:00
shamoon
b91da77a8a Reset dev version string 2024-03-08 17:04:09 -08:00
github-actions[bot]
33357a3fc2 Documentation: Add v2.6.2 changelog (#6049) 2024-03-08 12:14:25 -08:00
Dimitri
025001499d Fix: missing translation string (#6054)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2024-03-08 07:55:56 -08:00
586 changed files with 217998 additions and 101180 deletions

View File

@@ -14,6 +14,9 @@ flag_management:
# codecov will only comment if coverage changes # codecov will only comment if coverage changes
comment: comment:
require_changes: true require_changes: true
# https://docs.codecov.com/docs/javascript-bundle-analysis
require_bundle_changes: true
bundle_change_threshold: "50Kb"
coverage: coverage:
status: status:
project: project:
@@ -22,7 +25,12 @@ coverage:
threshold: 1% threshold: 1%
patch: patch:
default: default:
# For the changed lines only, target 75% covered, but # For the changed lines only, target 100% covered, but
# allow as low as 50% # allow as low as 75%
target: 75% target: 100%
threshold: 25% threshold: 25%
# https://docs.codecov.com/docs/javascript-bundle-analysis
bundle_analysis:
# Fail if the bundle size increases by more than 1MB
warning_threshold: "1MB"
status: true

View File

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

180
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,180 @@
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM docker.io/node:20-bookworm-slim as main-app
ARG DEBIAN_FRONTEND=noninteractive
# Buildx provided, must be defined to use though
ARG TARGETARCH
# Can be workflow provided, defaults set for manual building
ARG JBIG2ENC_VERSION=0.29
ARG QPDF_VERSION=11.9.0
ARG GS_VERSION=10.03.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
#
# Packages need for running
ARG RUNTIME_PACKAGES="\
# General utils
curl \
# Docker specific
gosu \
# Timezones support
tzdata \
# fonts for text file thumbnail generation
fonts-liberation \
gettext \
ghostscript \
gnupg \
icc-profiles-free \
imagemagick \
# PostgreSQL
postgresql-client \
# MySQL / MariaDB
mariadb-client \
# OCRmyPDF dependencies
tesseract-ocr \
tesseract-ocr-eng \
tesseract-ocr-deu \
tesseract-ocr-fra \
tesseract-ocr-ita \
tesseract-ocr-spa \
unpaper \
pngquant \
jbig2dec \
# lxml
libxml2 \
libxslt1.1 \
# itself
qpdf \
# Mime type detection
file \
libmagic1 \
media-types \
zlib1g \
# Barcode splitter
libzbar0 \
poppler-utils \
htop \
sudo"
# Install basic runtime packages.
# These change very infrequently
RUN set -eux \
echo "Installing system packages" \
&& apt-get update \
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES}
ARG PYTHON_PACKAGES="\
python3 \
python3-pip \
python3-wheel \
pipenv \
ca-certificates"
RUN set -eux \
echo "Installing python packages" \
&& apt-get update \
&& apt-get install --yes --quiet ${PYTHON_PACKAGES}
RUN set -eux \
&& echo "Installing pre-built updates" \
&& echo "Installing qpdf ${QPDF_VERSION}" \
&& curl --fail --silent --show-error --location \
--output libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \
--output qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
&& dpkg --install ./libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
&& dpkg --install ./qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
&& echo "Installing Ghostscript ${GS_VERSION}" \
&& curl --fail --silent --show-error --location \
--output libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \
--output ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \
--output libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
&& dpkg --install ./libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
&& dpkg --install ./libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& dpkg --install ./ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& echo "Installing jbig2enc" \
&& curl --fail --silent --show-error --location \
--output jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/jbig2enc-${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
&& dpkg --install ./jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb
# setup docker-specific things
# These change sometimes, but rarely
WORKDIR /usr/src/paperless/src/docker/
COPY [ \
"docker/imagemagick-policy.xml", \
"./" \
]
RUN set -eux \
&& echo "Configuring ImageMagick" \
&& mv imagemagick-policy.xml /etc/ImageMagick-6/policy.xml
# Packages needed only for building a few quick Python
# dependencies
ARG BUILD_PACKAGES="\
build-essential \
git \
# https://www.psycopg.org/docs/install.html#prerequisites
libpq-dev \
# https://github.com/PyMySQL/mysqlclient#linux
default-libmysqlclient-dev \
pkg-config \
pre-commit"
# hadolint ignore=DL3042
RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
set -eux \
&& echo "Installing build system packages" \
&& apt-get update \
&& apt-get install --yes --quiet ${BUILD_PACKAGES}
RUN set -eux \
&& npm update npm -g
# add users, setup scripts
# Mount the compiled frontend to expected location
RUN set -eux \
&& echo "Setting up user/group" \
&& groupmod --new-name paperless node \
&& usermod --login paperless --home /usr/src/paperless node \
&& usermod -s /bin/bash paperless \
&& echo "paperless ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
&& echo "Creating volume directories" \
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/data \
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/media \
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/consume \
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/export \
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/.venv \
&& echo "Adjusting all permissions" \
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless
# && echo "Collecting static files" \
# && gosu paperless python3 manage.py collectstatic --clear --no-input --link \
# && gosu paperless python3 manage.py compilemessages
VOLUME ["/usr/src/paperless/paperless-ngx/data", \
"/usr/src/paperless/paperless-ngx/media", \
"/usr/src/paperless/paperless-ngx/consume", \
"/usr/src/paperless/paperless-ngx/export", \
"/usr/src/paperless/paperless-ngx/.venv"]

117
.devcontainer/README.md Normal file
View File

@@ -0,0 +1,117 @@
# Paperless NGX Development Environment
## Overview
Welcome to the Paperless NGX development environment! This setup uses VSCode DevContainers to provide a consistent and seamless development experience.
### What are DevContainers?
DevContainers are a feature in VSCode that allows you to develop within a Docker container. This ensures that your development environment is consistent across different machines and setups. By defining a containerized environment, you can eliminate the "works on my machine" problem.
### Advantages of DevContainers
- **Consistency**: Same environment for all developers.
- **Isolation**: Separate development environment from your local machine.
- **Reproducibility**: Easily recreate the environment on any machine.
- **Pre-configured Tools**: Include all necessary tools and dependencies in the container.
## DevContainer Setup
The DevContainer configuration provides up all the necessary services for Paperless NGX, including:
- Redis
- Gotenberg
- Tika
Data is stored using Docker volumes to ensure persistence across container restarts.
## Configuration Files
The setup includes debugging configurations (`launch.json`) and tasks (`tasks.json`) to help you manage and debug various parts of the project:
- **Backend Debugging:**
- `manage.py runserver`
- `manage.py document-consumer`
- `celery`
- **Maintenance Tasks:**
- Create superuser
- Run migrations
- Recreate virtual environment (`.venv` with pipenv)
- Compile frontend assets
## Getting Started
### Step 1: Running the DevContainer
To start the DevContainer:
1. Open VSCode.
2. Open the project folder.
3. Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
4. Type and select `Dev Containers: Rebuild and Reopen in Container`.
VSCode will build and start the DevContainer environment.
### Step 2: Initial Setup
Once the DevContainer is up and running, perform the following steps:
1. **Compile Frontend Assets**:
- Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
- Select `Tasks: Run Task`.
- Choose `Frontend Compile`.
2. **Run Database Migrations**:
- Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
- Select `Tasks: Run Task`.
- Choose `Migrate Database`.
3. **Create Superuser**:
- Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
- Select `Tasks: Run Task`.
- Choose `Create Superuser`.
### Debugging and Running Services
You can start and debug backend services either as debugging sessions via `launch.json` or as tasks.
#### Using `launch.json`:
1. Press `F5` or go to the **Run and Debug** view in VSCode.
2. Select the desired configuration:
- `Runserver`
- `Document Consumer`
- `Celery`
#### Using Tasks:
1. Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
2. Select `Tasks: Run Task`.
3. Choose the desired task:
- `Runserver`
- `Document Consumer`
- `Celery`
### Additional Maintenance Tasks
Additional tasks are available for common maintenance operations:
- **Recreate .venv**: For setting up the virtual environment using pipenv.
- **Migrate Database**: To apply database migrations.
- **Create Superuser**: To create an admin user for the application.
## Let's Get Started!
Follow the steps above to get your development environment up and running. Happy coding!

View File

@@ -0,0 +1,16 @@
{
"name": "Paperless Development",
"dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml",
"service": "paperless-development",
"workspaceFolder": "/usr/src/paperless/paperless-ngx",
"postCreateCommand": "/bin/bash -c pre-commit install && pipenv install --dev",
"customizations": {
"vscode": {
"extensions": [
"mhutchie.git-graph",
"ms-python.python"
]
}
},
"remoteUser": "paperless"
}

View File

@@ -0,0 +1,84 @@
# Docker Compose file for developing Paperless NGX in VSCode DevContainers.
# This file contains everything Paperless NGX needs to run.
# Paperless supports amd64, arm, and arm64 hardware.
# All compose files of Paperless configure it in the following way:
#
# - Paperless is (re)started on system boot if it was running before shutdown.
# - Docker volumes for storing data are managed by Docker.
# - Folders for importing and exporting files are created in the same directory
# as this file and mounted to the correct folders inside the container.
# - Paperless listens on port 8000.
#
# SQLite is used as the database. The SQLite file is stored in the data volume.
#
# In addition, this Docker Compose file adds the following optional
# configurations:
#
# - Apache Tika and Gotenberg servers are started with Paperless NGX and Paperless
# is configured to use these services. These provide support for consuming
# Office documents (Word, Excel, PowerPoint, and their LibreOffice counterparts).
#
# This file is intended only to be used through VSCOde devcontainers. See README.md
# in the folder .devcontainer.
services:
broker:
image: docker.io/library/redis:7
restart: unless-stopped
volumes:
- redisdata:/data
# No ports need to be exposed; the VSCode DevContainer plugin manages them.
paperless-development:
image: paperless-ngx
build:
context: ../ # Dockerfile cannot access files from parent directories if context is not set.
dockerfile: ./.devcontainer/Dockerfile
restart: unless-stopped
depends_on:
- broker
- gotenberg
- tika
volumes:
- ..:/usr/src/paperless/paperless-ngx:delegated
- ../.devcontainer/vscode:/usr/src/paperless/paperless-ngx/.vscode:delegated # VSCode config files
- pipenv:/usr/src/paperless/paperless-ngx/.venv # Pipenv environment persisted in volume
- /usr/src/paperless/paperless-ngx/src/documents/static/frontend # Static frontend files exist only in container
- /usr/src/paperless/paperless-ngx/src/.pytest_cache
- /usr/src/paperless/paperless-ngx/.ruff_cache
- /usr/src/paperless/paperless-ngx/htmlcov
- /usr/src/paperless/paperless-ngx/.coverage
- data:/usr/src/paperless/paperless-ngx/data
- media:/usr/src/paperless/paperless-ngx/media
environment:
PAPERLESS_REDIS: redis://broker:6379
PAPERLESS_TIKA_ENABLED: 1
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
PAPERLESS_STATICDIR: ./src/documents/static
PAPERLESS_DEBUG: true
# Overrides default command so things don't shut down after the process ends.
command: /bin/sh -c "chown -R paperless:paperless /usr/src/paperless/paperless-ngx/src/documents/static/frontend && chown -R paperless:paperless /usr/src/paperless/paperless-ngx/.ruff_cache && while sleep 1000; do :; done"
gotenberg:
image: docker.io/gotenberg/gotenberg:7.10
restart: unless-stopped
# The Gotenberg Chromium route is used to convert .eml files. We do not
# want to allow external content like tracking pixels or even JavaScript.
command:
- "gotenberg"
- "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*"
tika:
image: docker.io/apache/tika:latest
restart: unless-stopped
volumes:
data:
media:
redisdata:
pipenv:

View File

@@ -0,0 +1,43 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "manage.py runserver",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/src/manage.py",
"console": "integratedTerminal",
"justMyCode": true,
"args": ["runserver"],
"django": true
},
{
"name": "manage.py document_consumer",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/src/manage.py",
"console": "integratedTerminal",
"justMyCode": true,
"args": ["document_consumer"],
"django": true
},
{
"name": "celery",
"type": "python",
"cwd": "${workspaceFolder}/src",
"request": "launch",
"module": "celery",
"console": "integratedTerminal",
"env": {
"PYTHONPATH": "${workspaceFolder}/src"
},
"args": [
"-A",
"paperless",
"worker",
"-l",
"DEBUG"
]
}
]
}

View File

@@ -0,0 +1,11 @@
{
"python.testing.pytestArgs": [
"src"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"files.watcherExclude": {
"**/.venv/**": true,
"**/pytest_cache/**": true
}
}

View File

@@ -0,0 +1,136 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "manage.py document_consumer",
"type": "shell",
"command": "pipenv run python manage.py document_consumer",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"clear": true,
"revealProblems": "onProblem"
},
"options": {
"cwd": "${workspaceFolder}/src"
}
},
{
"label": "manage.py runserver",
"type": "shell",
"command": "pipenv run python manage.py runserver",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared",
"showReuseMessage": false,
"clear": true,
"revealProblems": "onProblem"
},
"options": {
"cwd": "${workspaceFolder}/src"
}
},
{
"label": "Maintenance: manage.py migrate",
"type": "shell",
"command": "pipenv run python manage.py migrate",
"group": "none",
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "shared",
"showReuseMessage": false,
"clear": true,
"revealProblems": "onProblem"
},
"options": {
"cwd": "${workspaceFolder}/src"
}
},
{
"label": "Maintenance: manage.py createsuperuser",
"type": "shell",
"command": "pipenv run python manage.py createsuperuser",
"group": "none",
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "shared",
"showReuseMessage": false,
"clear": true,
"revealProblems": "onProblem"
},
"options": {
"cwd": "${workspaceFolder}/src"
}
},
{
"label": "compile frontend",
"type": "shell",
"command": "npm ci && ./node_modules/.bin/ng build --configuration production",
"group": "none",
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "shared",
"showReuseMessage": false,
"clear": true,
"revealProblems": "onProblem"
},
"options": {
"cwd": "${workspaceFolder}/src-ui"
}
},
{
"label": "Maintenance: recreate .venv",
"type": "shell",
"command": "rm -R -v .venv/* || pipenv install --dev",
"group": "none",
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "shared",
"showReuseMessage": false,
"clear": true,
"revealProblems": "onProblem"
},
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "Celery Worker",
"type": "shell",
"command": "pipenv run celery --app paperless worker -l DEBUG",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "shared",
"showReuseMessage": false,
"clear": true,
"revealProblems": "onProblem"
},
"options": {
"cwd": "${workspaceFolder}/src"
}
}
]
}

View File

@@ -9,7 +9,7 @@ body:
### ⚠️ Please remember: issues are for *bugs* ### ⚠️ Please remember: issues are for *bugs*
That is, something you believe affects every single user of Paperless-ngx, not just you. If you're not sure, start with one of the other options below. That is, something you believe affects every single user of Paperless-ngx, not just you. If you're not sure, start with one of the other options below.
Also, note that **Paperless-ngx does not perform OCR itself**, that is handled by other tools. Problems with OCR of specific files should likely be raised 'upstream', see https://github.com/ocrmypdf/OCRmyPDF/issues or https://github.com/tesseract-ocr/tesseract/issues Also, note that **Paperless-ngx does not perform OCR or archive file creation itself**, those are handled by other tools. Problems with OCR or archive versions of specific files should likely be raised 'upstream', see https://github.com/ocrmypdf/OCRmyPDF/issues or https://github.com/tesseract-ocr/tesseract/issues
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
@@ -86,22 +86,23 @@ body:
description: Note there are significant differences from the official image and linuxserver.io, please check if your issue is specific to the third-party image. description: Note there are significant differences from the official image and linuxserver.io, please check if your issue is specific to the third-party image.
validations: validations:
required: true required: true
- type: textarea
id: system-status
attributes:
label: System status
description: If available, copy & paste the system status output from Settings > System Status > Copy
render: json
- type: input - type: input
id: browser id: browser
attributes: attributes:
label: Browser label: Browser
description: Which browser you are using, if relevant. description: Which browser you are using, if relevant.
placeholder: e.g. Chrome, Safari placeholder: e.g. Chrome, Safari
- type: input - type: textarea
id: config-changes id: config-changes
attributes: attributes:
label: Configuration changes label: Configuration changes
description: Any configuration changes you made in `docker-compose.yml`, `docker-compose.env` or `paperless.conf`. description: Any configuration changes you made in `docker-compose.yml`, `docker-compose.env` or `paperless.conf`.
- type: input
id: other
attributes:
label: Other
description: Any other relevant details.
- type: checkboxes - type: checkboxes
id: required-checks id: required-checks
attributes: attributes:
@@ -109,6 +110,8 @@ body:
options: options:
- label: I believe this issue is a bug that affects all users of Paperless-ngx, not something specific to my installation. - label: I believe this issue is a bug that affects all users of Paperless-ngx, not something specific to my installation.
required: true required: true
- label: This issue is not about the OCR or archive creation of a specific file(s). Otherwise, please see above regarding OCR tools.
required: true
- label: I have already searched for relevant existing issues and discussions before opening this report. - label: I have already searched for relevant existing issues and discussions before opening this report.
required: true required: true
- label: I have updated the title field above with a concise description. - label: I have updated the title field above with a concise description.

View File

@@ -53,7 +53,6 @@ updates:
development: development:
patterns: patterns:
- "*pytest*" - "*pytest*"
- "black"
- "ruff" - "ruff"
- "mkdocs-material" - "mkdocs-material"
django: django:

View File

@@ -16,9 +16,9 @@ on:
env: env:
# This is the version of pipenv all the steps will use # This is the version of pipenv all the steps will use
# If changing this, change Dockerfile # If changing this, change Dockerfile
DEFAULT_PIP_ENV_VERSION: "2023.12.1" DEFAULT_PIP_ENV_VERSION: "2024.0.3"
# This is the default version of Python to use in most steps which aren't specific # This is the default version of Python to use in most steps which aren't specific
DEFAULT_PYTHON_VERSION: "3.10" DEFAULT_PYTHON_VERSION: "3.11"
jobs: jobs:
pre-commit: pre-commit:
@@ -100,7 +100,7 @@ jobs:
- pre-commit - pre-commit
strategy: strategy:
matrix: matrix:
python-version: ['3.9', '3.10', '3.11'] python-version: ['3.10', '3.11', '3.12']
fail-fast: false fail-fast: false
steps: steps:
- -
@@ -260,7 +260,7 @@ jobs:
retention-days: 7 retention-days: 7
tests-coverage-upload: tests-coverage-upload:
name: "Upload Coverage" name: "Upload to Codecov"
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: needs:
- tests-backend - tests-backend
@@ -306,6 +306,30 @@ jobs:
# future expansion # future expansion
flags: backend flags: backend
directory: src/ directory: src/
-
name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20.x
cache: 'npm'
cache-dependency-path: 'src-ui/package-lock.json'
-
name: Cache frontend dependencies
id: cache-frontend-deps
uses: actions/cache@v4
with:
path: |
~/.npm
~/.cache
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
-
name: Re-link Angular cli
run: cd src-ui && npm link @angular/cli
-
name: Build frontend and upload analysis
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: cd src-ui && ng build --configuration=production
build-docker-image: build-docker-image:
name: Build Docker image for ${{ github.ref_name }} name: Build Docker image for ${{ github.ref_name }}
@@ -398,7 +422,7 @@ jobs:
password: ${{ secrets.QUAY_ROBOT_TOKEN }} password: ${{ secrets.QUAY_ROBOT_TOKEN }}
- -
name: Build and push name: Build and push
uses: docker/build-push-action@v5 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile
@@ -406,6 +430,8 @@ jobs:
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.docker-meta.outputs.tags }} tags: ${{ steps.docker-meta.outputs.tags }}
labels: ${{ steps.docker-meta.outputs.labels }} labels: ${{ steps.docker-meta.outputs.labels }}
build-args: |
PNGX_TAG_VERSION=${{ steps.docker-meta.outputs.version }}
# Get cache layers from this branch, then dev # Get cache layers from this branch, then dev
# This allows new branches to get at least some cache benefits, generally from dev # This allows new branches to get at least some cache benefits, generally from dev
cache-from: | cache-from: |
@@ -456,12 +482,6 @@ jobs:
name: Install Python dependencies name: Install Python dependencies
run: | run: |
pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
-
name: Patch whitenoise
run: |
curl --fail --silent --show-error --location --output 484.patch https://github.com/evansd/whitenoise/pull/484.patch
patch -d $(pipenv --venv)/lib/python3.10/site-packages --verbose -p2 < 484.patch
rm 484.patch
- -
name: Install system dependencies name: Install system dependencies
run: | run: |
@@ -629,7 +649,9 @@ jobs:
git checkout ${{ needs.publish-release.outputs.version }}-changelog git checkout ${{ needs.publish-release.outputs.version }}-changelog
echo -e "# Changelog\n\n${{ needs.publish-release.outputs.changelog }}\n" > changelog-new.md echo -e "# Changelog\n\n${{ needs.publish-release.outputs.changelog }}\n" > changelog-new.md
echo "Manually linking usernames" echo "Manually linking usernames"
sed -i -r 's|@(.+?) \(\[#|[@\1](https://github.com/\1) ([#|ig' changelog-new.md sed -i -r 's|@([a-zA-Z0-9_]+) \(\[#|[@\1](https://github.com/\1) ([#|g' changelog-new.md
echo "Removing unneeded comment tags"
sed -i -r 's|@<!---->|@|g' changelog-new.md
CURRENT_CHANGELOG=`tail --lines +2 changelog.md` CURRENT_CHANGELOG=`tail --lines +2 changelog.md`
echo -e "$CURRENT_CHANGELOG" >> changelog-new.md echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
mv changelog-new.md changelog.md mv changelog-new.md changelog.md

View File

@@ -33,7 +33,7 @@ jobs:
- -
name: Clean temporary images name: Clean temporary images
if: "${{ env.TOKEN != '' }}" if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/ephemeral@v0.5.0 uses: stumpylog/image-cleaner-action/ephemeral@v0.8.0
with: with:
token: "${{ env.TOKEN }}" token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}" owner: "${{ github.repository_owner }}"
@@ -61,7 +61,7 @@ jobs:
- -
name: Clean untagged images name: Clean untagged images
if: "${{ env.TOKEN != '' }}" if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/untagged@v0.5.0 uses: stumpylog/image-cleaner-action/untagged@v0.8.0
with: with:
token: "${{ env.TOKEN }}" token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}" owner: "${{ github.repository_owner }}"

View File

@@ -15,13 +15,14 @@ on:
jobs: jobs:
synchronize-with-crowdin: synchronize-with-crowdin:
name: Crowdin Sync name: Crowdin Sync
if: github.repository_owner == 'paperless-ngx'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: crowdin action - name: crowdin action
uses: crowdin/github-action@v1 uses: crowdin/github-action@v2
with: with:
upload_translations: false upload_translations: false
download_translations: true download_translations: true

View File

@@ -16,13 +16,14 @@ concurrency:
jobs: jobs:
stale: stale:
name: 'Stale' name: 'Stale'
if: github.repository_owner == 'paperless-ngx'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v9 - uses: actions/stale@v9
with: with:
days-before-stale: 7 days-before-stale: 7
days-before-close: 14 days-before-close: 14
any-of-labels: 'cant-reproduce,not a bug' any-of-labels: 'stale,cant-reproduce,not a bug'
stale-issue-label: stale stale-issue-label: stale
stale-pr-label: stale stale-pr-label: stale
stale-issue-message: > stale-issue-message: >
@@ -31,6 +32,7 @@ jobs:
for your contributions. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details. 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: lock-threads:
name: 'Lock Old Threads' name: 'Lock Old Threads'
if: github.repository_owner == 'paperless-ngx'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v5 - uses: dessant/lock-threads@v5
@@ -56,6 +58,7 @@ jobs:
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
close-answered-discussions: close-answered-discussions:
name: 'Close Answered Discussions' name: 'Close Answered Discussions'
if: github.repository_owner == 'paperless-ngx'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/github-script@v7 - uses: actions/github-script@v7
@@ -112,6 +115,7 @@ jobs:
} }
close-outdated-discussions: close-outdated-discussions:
name: 'Close Outdated Discussions' name: 'Close Outdated Discussions'
if: github.repository_owner == 'paperless-ngx'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/github-script@v7 - uses: actions/github-script@v7
@@ -203,6 +207,7 @@ jobs:
} }
close-unsupported-feature-requests: close-unsupported-feature-requests:
name: 'Close Unsupported Feature Requests' name: 'Close Unsupported Feature Requests'
if: github.repository_owner == 'paperless-ngx'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/github-script@v7 - uses: actions/github-script@v7
@@ -212,15 +217,20 @@ jobs:
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
const CUTOFF_MAX_COUNT = 80;
const CUTOFF_1_DAYS = 180; const CUTOFF_1_DAYS = 180;
const CUTOFF_1_COUNT = 5; const CUTOFF_1_COUNT = 5;
const CUTOFF_2_DAYS = 365; const CUTOFF_2_DAYS = 365;
const CUTOFF_2_COUNT = 10; const CUTOFF_2_COUNT = 20;
const CUTOFF_3_DAYS = 730;
const CUTOFF_3_COUNT = 40;
const cutoff1Date = new Date(); const cutoff1Date = new Date();
cutoff1Date.setDate(cutoff1Date.getDate() - CUTOFF_1_DAYS); cutoff1Date.setDate(cutoff1Date.getDate() - CUTOFF_1_DAYS);
const cutoff2Date = new Date(); const cutoff2Date = new Date();
cutoff2Date.setDate(cutoff2Date.getDate() - CUTOFF_2_DAYS); cutoff2Date.setDate(cutoff2Date.getDate() - CUTOFF_2_DAYS);
const cutoff3Date = new Date();
cutoff3Date.setDate(cutoff3Date.getDate() - CUTOFF_3_DAYS);
const query = `query( const query = `query(
$owner:String!, $owner:String!,
@@ -250,9 +260,12 @@ jobs:
const result = await github.graphql(query, variables); const result = await github.graphql(query, variables);
for (const discussion of result.repository.discussions.nodes) { for (const discussion of result.repository.discussions.nodes) {
const discussionDate = new Date(discussion.updatedAt); const discussionUpdatedDate = new Date(discussion.updatedAt);
if ((discussionDate < cutoff1Date && discussion.upvoteCount < CUTOFF_1_COUNT) || const discussionCreatedDate = new Date(discussion.createdAt);
(discussionDate < cutoff2Date && discussion.upvoteCount < CUTOFF_2_COUNT)) { if ((discussionUpdatedDate < cutoff1Date && discussion.upvoteCount < CUTOFF_MAX_COUNT) ||
(discussionCreatedDate < cutoff1Date && discussion.upvoteCount < CUTOFF_1_COUNT) ||
(discussionCreatedDate < cutoff2Date && discussion.upvoteCount < CUTOFF_2_COUNT) ||
(discussionCreatedDate < cutoff3Date && discussion.upvoteCount < CUTOFF_3_COUNT)) {
console.log(`Closing discussion #${discussion.number} (${discussion.id}), last updated at ${discussion.updatedAt} with votes ${discussion.upvoteCount}`); console.log(`Closing discussion #${discussion.number} (${discussion.id}), last updated at ${discussion.updatedAt} with votes ${discussion.upvoteCount}`);
const addCommentMutation = `mutation($discussion:ID!, $body:String!) { const addCommentMutation = `mutation($discussion:ID!, $body:String!) {
addDiscussionComment(input:{discussionId:$discussion, body:$body}) { addDiscussionComment(input:{discussionId:$discussion, body:$body}) {

3
.gitignore vendored
View File

@@ -22,6 +22,7 @@ var/
*.egg-info/ *.egg-info/
.installed.cfg .installed.cfg
*.egg *.egg
/src/paperless_mail/templates/node_modules
# PyInstaller # PyInstaller
# Usually these files are written by a python script from a template # Usually these files are written by a python script from a template
@@ -65,6 +66,8 @@ target/
.vscode .vscode
/src-ui/.vscode /src-ui/.vscode
/docs/.vscode /docs/.vscode
.vscode-server
*CommandMarker
# Other stuff that doesn't belong # Other stuff that doesn't belong
.virtualenv .virtualenv

View File

@@ -5,7 +5,7 @@
repos: repos:
# General hooks # General hooks
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 rev: v4.6.0
hooks: hooks:
- id: check-docstring-first - id: check-docstring-first
- id: check-json - id: check-json
@@ -29,15 +29,16 @@ repos:
- id: check-case-conflict - id: check-case-conflict
- id: detect-private-key - id: detect-private-key
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: v2.2.6 rev: v2.3.0
hooks: hooks:
- id: codespell - id: codespell
exclude: "(^src-ui/src/locale/)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)" exclude: "(^src-ui/src/locale/)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)"
exclude_types: exclude_types:
- pofile - pofile
- json - json
- repo: https://github.com/pre-commit/mirrors-prettier # See https://github.com/prettier/prettier/issues/15742 for the fork reason
rev: 'v3.1.0' - repo: https://github.com/rbubley/mirrors-prettier
rev: 'v3.3.3'
hooks: hooks:
- id: prettier - id: prettier
types_or: types_or:
@@ -47,13 +48,10 @@ repos:
exclude: "(^Pipfile\\.lock$)" exclude: "(^Pipfile\\.lock$)"
# Python hooks # Python hooks
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.3.0' rev: 'v0.6.8'
hooks: hooks:
- id: ruff - id: ruff
- repo: https://github.com/psf/black-pre-commit-mirror - id: ruff-format
rev: 24.2.0
hooks:
- id: black
# Dockerfile hooks # Dockerfile hooks
- repo: https://github.com/AleksaC/hadolint-py - repo: https://github.com/AleksaC/hadolint-py
rev: v2.12.0.3 rev: v2.12.0.3
@@ -64,9 +62,11 @@ repos:
rev: v6.2.1 rev: v6.2.1
hooks: hooks:
- id: beautysh - id: beautysh
additional_dependencies:
- setuptools
args: args:
- "--tab" - "--tab"
- repo: https://github.com/shellcheck-py/shellcheck-py - repo: https://github.com/shellcheck-py/shellcheck-py
rev: "v0.9.0.6" rev: "v0.10.0.1"
hooks: hooks:
- id: shellcheck - id: shellcheck

View File

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

View File

@@ -1 +1 @@
3.9.18 3.10.15

View File

@@ -2,7 +2,7 @@ fix = true
line-length = 88 line-length = 88
respect-gitignore = true respect-gitignore = true
src = ["src"] src = ["src"]
target-version = "py39" target-version = "py310"
output-format = "grouped" output-format = "grouped"
show-fixes = true show-fixes = true

View File

@@ -5,7 +5,7 @@
We as members, contributors, and leaders pledge to make participation in our We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status, identity and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, religion, or sexual identity nationality, personal appearance, race, religion, or sexual identity
and orientation. and orientation.

View File

@@ -11,7 +11,7 @@ If you want to implement something big:
## Python ## Python
Paperless supports python 3.9 - 3.11. We format Python code with [Black](https://github.com/psf/black). Paperless supports python 3.10 - 3.12 at this time. We format Python code with [ruff](https://docs.astral.sh/ruff/formatter/).
## Branches ## Branches
@@ -147,7 +147,7 @@ community members. That said, in an effort to keep the repository organized and
- Issues, pull requests and discussions that are closed will be locked after 30 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 with a marked answer will be automatically closed.
- Discussions in the 'General' or 'Support' categories will be closed after 180 days of inactivity. - 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. - Feature requests that do not meet the following thresholds will be closed: 180 days of inactivity, < 5 "up-votes" after 180 days, < 20 "up-votes" after 1 year or < 80 "up-votes" at 2 years.
In all cases, threads can be re-opened by project maintainers and, of course, users can always create a new discussion for related concerns. 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. Finally, remember that all information remains searchable and 'closed' feature requests can still serve as inspiration for new features.

View File

@@ -13,6 +13,16 @@ WORKDIR /src/src-ui
RUN set -eux \ RUN set -eux \
&& npm update npm -g \ && npm update npm -g \
&& npm ci && npm ci
ARG PNGX_TAG_VERSION=
# Add the tag to the environment file if its a tagged dev build
RUN set -eux && \
case "${PNGX_TAG_VERSION}" in \
dev|beta|fix*|feature*) \
sed -i -E "s/version: '([0-9\.]+)'/version: '\1 #${PNGX_TAG_VERSION}'/g" /src/src-ui/src/environments/environment.prod.ts \
;; \
esac
RUN set -eux \ RUN set -eux \
&& ./node_modules/.bin/ng build --configuration production && ./node_modules/.bin/ng build --configuration production
@@ -21,7 +31,7 @@ RUN set -eux \
# Comments: # Comments:
# - pipenv dependencies are not left in the final image # - pipenv dependencies are not left in the final image
# - pipenv can't touch the final image somehow # - pipenv can't touch the final image somehow
FROM --platform=$BUILDPLATFORM docker.io/python:3.11-alpine as pipenv-base FROM --platform=$BUILDPLATFORM docker.io/python:3.12-alpine AS pipenv-base
WORKDIR /usr/src/pipenv WORKDIR /usr/src/pipenv
@@ -29,7 +39,7 @@ COPY Pipfile* ./
RUN set -eux \ RUN set -eux \
&& echo "Installing pipenv" \ && echo "Installing pipenv" \
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2023.12.1 \ && python3 -m pip install --no-cache-dir --upgrade pipenv==2024.0.3 \
&& echo "Generating requirement.txt" \ && echo "Generating requirement.txt" \
&& pipenv requirements > requirements.txt && pipenv requirements > requirements.txt
@@ -37,7 +47,7 @@ RUN set -eux \
# Purpose: The final image # Purpose: The final image
# Comments: # Comments:
# - Don't leave anything extra in here # - Don't leave anything extra in here
FROM docker.io/python:3.11-slim-bookworm as main-app FROM docker.io/python:3.12-slim-bookworm AS main-app
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>" 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.documentation="https://docs.paperless-ngx.com/"
@@ -52,8 +62,8 @@ ARG TARGETARCH
# Can be workflow provided, defaults set for manual building # Can be workflow provided, defaults set for manual building
ARG JBIG2ENC_VERSION=0.29 ARG JBIG2ENC_VERSION=0.29
ARG QPDF_VERSION=11.6.4 ARG QPDF_VERSION=11.9.0
ARG GS_VERSION=10.02.1 ARG GS_VERSION=10.03.1
# Set Python environment variables # Set Python environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \ ENV PYTHONDONTWRITEBYTECODE=1 \
@@ -83,7 +93,6 @@ ARG RUNTIME_PACKAGES="\
icc-profiles-free \ icc-profiles-free \
imagemagick \ imagemagick \
# PostgreSQL # PostgreSQL
libpq5 \
postgresql-client \ postgresql-client \
# MySQL / MariaDB # MySQL / MariaDB
mariadb-client \ mariadb-client \
@@ -129,17 +138,17 @@ RUN set -eux \
&& dpkg --install ./qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \ && dpkg --install ./qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
&& echo "Installing Ghostscript ${GS_VERSION}" \ && echo "Installing Ghostscript ${GS_VERSION}" \
&& curl --fail --silent --show-error --location \ && curl --fail --silent --show-error --location \
--output libgs10_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \ --output libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \ && curl --fail --silent --show-error --location \
--output ghostscript_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \ --output ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \ && curl --fail --silent --show-error --location \
--output libgs10-common_${GS_VERSION}.dfsg-2_all.deb \ --output libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \ https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
&& dpkg --install ./libgs10-common_${GS_VERSION}.dfsg-2_all.deb \ && dpkg --install ./libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
&& dpkg --install ./libgs10_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \ && dpkg --install ./libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& dpkg --install ./ghostscript_${GS_VERSION}.dfsg-2_${TARGETARCH}.deb \ && dpkg --install ./ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& echo "Installing jbig2enc" \ && echo "Installing jbig2enc" \
&& curl --fail --silent --show-error --location \ && curl --fail --silent --show-error --location \
--output jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \ --output jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
@@ -223,19 +232,22 @@ RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \ && apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
&& python3 -m pip install --no-cache-dir --upgrade wheel \ && python3 -m pip install --no-cache-dir --upgrade wheel \
&& echo "Installing Python requirements" \ && echo "Installing Python requirements" \
&& python3 -m pip install --default-timeout=1000 --requirement requirements.txt \ && curl --fail --silent --show-error --location \
&& echo "Patching whitenoise for compression speedup" \ --output psycopg_c-3.2.2-cp312-cp312-linux_x86_64.whl \
&& curl --fail --silent --show-error --location --output 484.patch https://github.com/evansd/whitenoise/pull/484.patch \ https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.2/psycopg_c-3.2.2-cp312-cp312-linux_x86_64.whl \
&& patch -d /usr/local/lib/python3.11/site-packages --verbose -p2 < 484.patch \ && curl --fail --silent --show-error --location \
&& rm 484.patch \ --output psycopg_c-3.2.2-cp312-cp312-linux_aarch64.whl \
https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.2/psycopg_c-3.2.2-cp312-cp312-linux_aarch64.whl \
&& python3 -m pip install --default-timeout=1000 --find-links . --requirement requirements.txt \
&& echo "Installing NLTK data" \ && echo "Installing NLTK data" \
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \ && python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \ && python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" punkt \ && python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" punkt_tab \
&& echo "Cleaning up image" \ && echo "Cleaning up image" \
&& apt-get --yes purge ${BUILD_PACKAGES} \ && apt-get --yes purge ${BUILD_PACKAGES} \
&& apt-get --yes autoremove --purge \ && apt-get --yes autoremove --purge \
&& apt-get clean --yes \ && apt-get clean --yes \
&& rm --recursive --force --verbose *.whl \
&& rm --recursive --force --verbose /var/lib/apt/lists/* \ && rm --recursive --force --verbose /var/lib/apt/lists/* \
&& rm --recursive --force --verbose /tmp/* \ && rm --recursive --force --verbose /tmp/* \
&& rm --recursive --force --verbose /var/tmp/* \ && rm --recursive --force --verbose /var/tmp/* \
@@ -259,6 +271,8 @@ RUN set -eux \
&& mkdir --parents --verbose /usr/src/paperless/media \ && mkdir --parents --verbose /usr/src/paperless/media \
&& mkdir --parents --verbose /usr/src/paperless/consume \ && mkdir --parents --verbose /usr/src/paperless/consume \
&& mkdir --parents --verbose /usr/src/paperless/export \ && mkdir --parents --verbose /usr/src/paperless/export \
&& echo "Creating gnupg directory" \
&& mkdir -m700 --verbose /usr/src/paperless/.gnupg \
&& echo "Adjusting all permissions" \ && echo "Adjusting all permissions" \
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless \ && chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless \
&& echo "Collecting static files" \ && echo "Collecting static files" \

30
Pipfile
View File

@@ -7,37 +7,40 @@ name = "pypi"
dateparser = "~=1.2" dateparser = "~=1.2"
# WARNING: django does not use semver. # WARNING: django does not use semver.
# Only patch versions are guaranteed to not introduce breaking changes. # Only patch versions are guaranteed to not introduce breaking changes.
django = "~=4.2.11" django = "~=5.1.1"
django-allauth = "*" django-allauth = {extras = ["socialaccount"], version = "*"}
django-auditlog = "*" django-auditlog = "*"
django-celery-results = "*" django-celery-results = "*"
django-compression-middleware = "*" django-compression-middleware = "*"
django-cors-headers = "*" django-cors-headers = "*"
django-extensions = "*" django-extensions = "*"
django-filter = "~=23.5" django-filter = "~=24.3"
django-guardian = "*" django-guardian = "*"
django-multiselectfield = "*" django-multiselectfield = "*"
djangorestframework = "~=3.14" django-soft-delete = "*"
djangorestframework = "==3.15.2"
djangorestframework-guardian = "*" djangorestframework-guardian = "*"
drf-writable-nested = "*" drf-writable-nested = "*"
bleach = "*" bleach = "*"
celery = {extras = ["redis"], version = "*"} celery = {extras = ["redis"], version = "*"}
channels = "~=4.0" channels = "~=4.1"
channels-redis = "*" channels-redis = "*"
concurrent-log-handler = "*" concurrent-log-handler = "*"
filelock = "*" filelock = "*"
flower = "*" flower = "*"
gotenberg-client = "*" gotenberg-client = "*"
gunicorn = "*" gunicorn = "*"
httpx-oauth = "*"
imap-tools = "*" imap-tools = "*"
inotifyrecursive = "~=0.3" inotifyrecursive = "~=0.3"
jinja2 = "~=3.1"
langdetect = "*" langdetect = "*"
mysqlclient = "*" mysqlclient = "*"
nltk = "*" nltk = "*"
ocrmypdf = "~=15.4" ocrmypdf = "~=16.5"
pathvalidate = "*" pathvalidate = "*"
pdf2image = "*" pdf2image = "*"
psycopg2 = "*" psycopg = {version = "*", extras = ["c"]}
python-dateutil = "*" python-dateutil = "*"
python-dotenv = "*" python-dotenv = "*"
python-gnupg = "*" python-gnupg = "*"
@@ -46,23 +49,24 @@ python-magic = "*"
pyzbar = "*" pyzbar = "*"
rapidfuzz = "*" rapidfuzz = "*"
redis = {extras = ["hiredis"], version = "*"} redis = {extras = ["hiredis"], version = "*"}
scikit-learn = "~=1.4" scikit-learn = "~=1.5"
setproctitle = "*" setproctitle = "*"
tika-client = "*" tika-client = "*"
tqdm = "*" tqdm = "*"
# See https://github.com/paperless-ngx/paperless-ngx/issues/5494
uvicorn = {extras = ["standard"], version = "==0.25.0"} uvicorn = {extras = ["standard"], version = "==0.25.0"}
watchdog = "~=4.0" watchdog = "~=4.0"
whitenoise = "~=6.6" whitenoise = "~=6.8"
whoosh="~=2.7" whoosh = "~=2.7"
zxing-cpp = {version = "*", platform_machine = "== 'x86_64'"} zxing-cpp = {version = "*", platform_machine = "== 'x86_64'"}
[dev-packages] [dev-packages]
# Linting # Linting
black = "*"
pre-commit = "*" pre-commit = "*"
ruff = "*" ruff = "*"
# Testing
factory-boy = "*" factory-boy = "*"
# Testing
pytest = "*" pytest = "*"
pytest-cov = "*" pytest-cov = "*"
pytest-django = "*" pytest-django = "*"
@@ -70,6 +74,7 @@ pytest-httpx = "*"
pytest-env = "*" pytest-env = "*"
pytest-sugar = "*" pytest-sugar = "*"
pytest-xdist = "*" pytest-xdist = "*"
pytest-mock = "*"
pytest-rerunfailures = "*" pytest-rerunfailures = "*"
imagehash = "*" imagehash = "*"
daphne = "*" daphne = "*"
@@ -92,5 +97,4 @@ types-tqdm = "*"
types-Markdown = "*" types-Markdown = "*"
types-Pygments = "*" types-Pygments = "*"
types-colorama = "*" types-colorama = "*"
types-psycopg2 = "*"
types-setuptools = "*" types-setuptools = "*"

4677
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -30,14 +30,14 @@ Thanks to the generous folks at [DigitalOcean](https://m.do.co/c/8d70b916d462),
- [Translation](#translation) - [Translation](#translation)
- [Feature Requests](#feature-requests) - [Feature Requests](#feature-requests)
- [Bugs](#bugs) - [Bugs](#bugs)
- [Affiliated Projects](#affiliated-projects) - [Related Projects](#related-projects)
- [Important Note](#important-note) - [Important Note](#important-note)
<p align="right">This project is supported by:<br/> <p align="right">This project is supported by:<br/>
<a href="https://m.do.co/c/8d70b916d462" style="padding-top: 4px; display: block;"> <a href="https://m.do.co/c/8d70b916d462" style="padding-top: 4px; display: block;">
<picture> <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: 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"> <source media="(prefers-color-scheme: light)" srcset="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="140px">
<img src="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> </picture>
</a> </a>
@@ -63,7 +63,7 @@ If you'd like to jump right in, you can configure a `docker compose` environment
bash -c "$(curl -L https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)" bash -c "$(curl -L https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/install-paperless-ngx.sh)"
``` ```
Alternatively, you can install the dependencies and setup apache and a database server yourself. The [documentation](https://docs.paperless-ngx.com/setup/#installation) has a step by step guide on how to do it. More details and step-by-step guides for alternative installation methods can be found in [the documentation](https://docs.paperless-ngx.com/setup/#installation).
Migrating from Paperless-ng is easy, just drop in the new docker image! See the [documentation on migrating](https://docs.paperless-ngx.com/setup/#migrating-to-paperless-ngx) for more details. Migrating from Paperless-ng is easy, just drop in the new docker image! See the [documentation on migrating](https://docs.paperless-ngx.com/setup/#migrating-to-paperless-ngx) for more details.
@@ -93,9 +93,9 @@ Feature requests can be submitted via [GitHub Discussions](https://github.com/pa
For bugs please [open an issue](https://github.com/paperless-ngx/paperless-ngx/issues) or [start a discussion](https://github.com/paperless-ngx/paperless-ngx/discussions) if you have questions. For bugs please [open an issue](https://github.com/paperless-ngx/paperless-ngx/issues) or [start a discussion](https://github.com/paperless-ngx/paperless-ngx/discussions) if you have questions.
# Affiliated Projects # Related Projects
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Affiliated-Projects) for a user-maintained list of affiliated projects and software that is compatible with Paperless-ngx. Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Related-Projects) for a user-maintained list of related projects and software that is compatible with Paperless-ngx.
# Important Note # Important Note

View File

@@ -3,10 +3,9 @@
# Can be used locally or by the CI to start the necessary containers with the # Can be used locally or by the CI to start the necessary containers with the
# correct networking for the tests # correct networking for the tests
version: "3.7"
services: services:
gotenberg: gotenberg:
image: docker.io/gotenberg/gotenberg:7.10 image: docker.io/gotenberg/gotenberg:8.7
hostname: gotenberg hostname: gotenberg
container_name: gotenberg container_name: gotenberg
network_mode: host network_mode: host
@@ -20,7 +19,7 @@ services:
- "--log-level=warn" - "--log-level=warn"
- "--log-format=text" - "--log-format=text"
tika: tika:
image: ghcr.io/paperless-ngx/tika:latest image: docker.io/apache/tika:latest
hostname: tika hostname: tika
container_name: tika container_name: tika
network_mode: host network_mode: host

View File

@@ -30,7 +30,6 @@
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
# documentation. # documentation.
version: "3.4"
services: services:
broker: broker:
image: docker.io/library/redis:7 image: docker.io/library/redis:7
@@ -39,7 +38,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/mariadb:10 image: docker.io/library/mariadb:11
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- dbdata:/var/lib/mysql - dbdata:/var/lib/mysql
@@ -78,7 +77,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998 PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg: gotenberg:
image: docker.io/gotenberg/gotenberg:7.10 image: docker.io/gotenberg/gotenberg:8.7
restart: unless-stopped restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not # The gotenberg chromium route is used to convert .eml files. We do not
# want to allow external content like tracking pixels or even javascript. # want to allow external content like tracking pixels or even javascript.
@@ -88,7 +87,7 @@ services:
- "--chromium-allow-list=file:///tmp/.*" - "--chromium-allow-list=file:///tmp/.*"
tika: tika:
image: ghcr.io/paperless-ngx/tika:latest image: docker.io/apache/tika:latest
restart: unless-stopped restart: unless-stopped
volumes: volumes:

View File

@@ -26,7 +26,6 @@
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
# documentation. # documentation.
version: "3.4"
services: services:
broker: broker:
image: docker.io/library/redis:7 image: docker.io/library/redis:7
@@ -35,7 +34,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/mariadb:10 image: docker.io/library/mariadb:11
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- dbdata:/var/lib/mysql - dbdata:/var/lib/mysql

View File

@@ -28,7 +28,6 @@
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
# documentation. # documentation.
version: "3.4"
services: services:
broker: broker:
image: docker.io/library/redis:7 image: docker.io/library/redis:7
@@ -37,7 +36,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/postgres:15 image: docker.io/library/postgres:16
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data

View File

@@ -30,7 +30,6 @@
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
# documentation. # documentation.
version: "3.4"
services: services:
broker: broker:
image: docker.io/library/redis:7 image: docker.io/library/redis:7
@@ -39,7 +38,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/postgres:15 image: docker.io/library/postgres:16
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data
@@ -72,7 +71,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998 PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg: gotenberg:
image: docker.io/gotenberg/gotenberg:7.10 image: docker.io/gotenberg/gotenberg:8.7
restart: unless-stopped restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not # The gotenberg chromium route is used to convert .eml files. We do not
@@ -83,7 +82,7 @@ services:
- "--chromium-allow-list=file:///tmp/.*" - "--chromium-allow-list=file:///tmp/.*"
tika: tika:
image: ghcr.io/paperless-ngx/tika:latest image: docker.io/apache/tika:latest
restart: unless-stopped restart: unless-stopped
volumes: volumes:

View File

@@ -26,7 +26,6 @@
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
# documentation. # documentation.
version: "3.4"
services: services:
broker: broker:
image: docker.io/library/redis:7 image: docker.io/library/redis:7
@@ -35,7 +34,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/postgres:15 image: docker.io/library/postgres:16
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data

View File

@@ -30,7 +30,6 @@
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
# documentation. # documentation.
version: "3.4"
services: services:
broker: broker:
image: docker.io/library/redis:7 image: docker.io/library/redis:7
@@ -60,7 +59,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998 PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg: gotenberg:
image: docker.io/gotenberg/gotenberg:7.10 image: docker.io/gotenberg/gotenberg:8.7
restart: unless-stopped restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not # The gotenberg chromium route is used to convert .eml files. We do not
@@ -71,7 +70,7 @@ services:
- "--chromium-allow-list=file:///tmp/.*" - "--chromium-allow-list=file:///tmp/.*"
tika: tika:
image: ghcr.io/paperless-ngx/tika:latest image: docker.io/apache/tika:latest
restart: unless-stopped restart: unless-stopped
volumes: volumes:

View File

@@ -23,7 +23,6 @@
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
# documentation. # documentation.
version: "3.4"
services: services:
broker: broker:
image: docker.io/library/redis:7 image: docker.io/library/redis:7

View File

@@ -10,8 +10,8 @@ map_uidgid() {
local -r usermap_new_gid=${USERMAP_GID:-${usermap_original_gid:-$usermap_new_uid}} local -r usermap_new_gid=${USERMAP_GID:-${usermap_original_gid:-$usermap_new_uid}}
if [[ ${usermap_new_uid} != "${usermap_original_uid}" || ${usermap_new_gid} != "${usermap_original_gid}" ]]; then if [[ ${usermap_new_uid} != "${usermap_original_uid}" || ${usermap_new_gid} != "${usermap_original_gid}" ]]; then
echo "Mapping UID and GID for paperless:paperless to $usermap_new_uid:$usermap_new_gid" echo "Mapping UID and GID for paperless:paperless to $usermap_new_uid:$usermap_new_gid"
usermod -o -u "${usermap_new_uid}" paperless usermod --non-unique --uid "${usermap_new_uid}" paperless
groupmod -o -g "${usermap_new_gid}" paperless groupmod --non-unique --gid "${usermap_new_gid}" paperless
fi fi
} }
@@ -42,7 +42,7 @@ custom_container_init() {
fi fi
# Make sure custom init directory has files in it # Make sure custom init directory has files in it
if [ -n "$(/bin/ls -A "${custom_script_dir}" 2>/dev/null)" ]; then if [ -n "$(/bin/ls --almost-all "${custom_script_dir}" 2>/dev/null)" ]; then
echo "[custom-init] files found in ${custom_script_dir} executing" echo "[custom-init] files found in ${custom_script_dir} executing"
# Loop over files in the directory # Loop over files in the directory
for SCRIPT in "${custom_script_dir}"/*; do for SCRIPT in "${custom_script_dir}"/*; do
@@ -86,13 +86,13 @@ initialize() {
"${CONSUME_DIR}"; do "${CONSUME_DIR}"; do
if [[ ! -d "${dir}" ]]; then if [[ ! -d "${dir}" ]]; then
echo "Creating directory ${dir}" echo "Creating directory ${dir}"
mkdir --parents "${dir}" mkdir --parents --verbose "${dir}"
fi fi
done done
local -r tmp_dir="${PAPERLESS_SCRATCH_DIR:=/tmp/paperless}" local -r tmp_dir="${PAPERLESS_SCRATCH_DIR:=/tmp/paperless}"
echo "Creating directory scratch directory ${tmp_dir}" echo "Creating directory scratch directory ${tmp_dir}"
mkdir --parents "${tmp_dir}" mkdir --parents --verbose "${tmp_dir}"
set +e set +e
echo "Adjusting permissions of paperless files. This may take a while." echo "Adjusting permissions of paperless files. This may take a while."
@@ -102,7 +102,7 @@ initialize() {
"${DATA_DIR}" \ "${DATA_DIR}" \
"${MEDIA_ROOT_DIR}" \ "${MEDIA_ROOT_DIR}" \
"${CONSUME_DIR}"; do "${CONSUME_DIR}"; do
find "${dir}" -not \( -user paperless -and -group paperless \) -exec chown paperless:paperless {} + find "${dir}" -not \( -user paperless -and -group paperless \) -exec chown --changes paperless:paperless {} +
done done
set -e set -e
@@ -122,33 +122,44 @@ install_languages() {
if [ ${#langs[@]} -eq 0 ]; then if [ ${#langs[@]} -eq 0 ]; then
return return
fi fi
apt-get update
# Build list of packages to install
to_install=()
for lang in "${langs[@]}"; do for lang in "${langs[@]}"; do
pkg="tesseract-ocr-$lang" pkg="tesseract-ocr-$lang"
if dpkg -s "$pkg" &>/dev/null; then if dpkg --status "$pkg" &>/dev/null; then
echo "Package $pkg already installed!" echo "Package $pkg already installed!"
continue continue
fi else
to_install+=("$pkg")
if ! apt-cache show "$pkg" &>/dev/null; then
echo "Package $pkg not found! :("
continue
fi
echo "Installing package $pkg..."
if ! apt-get -y install "$pkg" &>/dev/null; then
echo "Could not install $pkg"
exit 1
fi fi
done done
# Use apt only when we install packages
if [ ${#to_install[@]} -gt 0 ]; then
apt-get update
for pkg in "${to_install[@]}"; do
if ! apt-cache show "$pkg" &>/dev/null; then
echo "Skipped $pkg: Package not found! :("
continue
fi
echo "Installing package $pkg..."
if ! apt-get --assume-yes install "$pkg" &>/dev/null; then
echo "Could not install $pkg"
exit 1
fi
done
fi
} }
echo "Paperless-ngx docker container starting..." echo "Paperless-ngx docker container starting..."
gosu_cmd=(gosu paperless) gosu_cmd=(gosu paperless)
if [ "$(id -u)" == "$(id -u paperless)" ]; then if [ "$(id --user)" == "$(id --user paperless)" ]; then
gosu_cmd=() gosu_cmd=()
fi fi

View File

@@ -13,7 +13,7 @@ wait_for_postgres() {
# Disable warning, host and port can't have spaces # Disable warning, host and port can't have spaces
# shellcheck disable=SC2086 # shellcheck disable=SC2086
while [ ! "$(pg_isready -h ${host} -p ${port})" ]; do while [ ! "$(pg_isready --host ${host} --port ${port})" ]; do
if [ $attempt_num -eq $max_attempts ]; then if [ $attempt_num -eq $max_attempts ]; then
echo "Unable to connect to database." echo "Unable to connect to database."
@@ -25,6 +25,7 @@ wait_for_postgres() {
attempt_num=$(("$attempt_num" + 1)) attempt_num=$(("$attempt_num" + 1))
sleep 5 sleep 5
done done
echo "Connected to PostgreSQL"
} }
wait_for_mariadb() { wait_for_mariadb() {
@@ -51,6 +52,7 @@ wait_for_mariadb() {
attempt_num=$(("$attempt_num" + 1)) attempt_num=$(("$attempt_num" + 1))
sleep 5 sleep 5
done done
echo "Connected to MariaDB"
} }
wait_for_redis() { wait_for_redis() {
@@ -80,7 +82,7 @@ django_checks() {
search_index() { search_index() {
local -r index_version=8 local -r index_version=9
local -r index_version_file=${DATA_DIR}/.index_version local -r index_version_file=${DATA_DIR}/.index_version
if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then

View File

@@ -16,7 +16,7 @@ do
# Check if it starts with "PAPERLESS_" and ends in "_FILE" # Check if it starts with "PAPERLESS_" and ends in "_FILE"
if [[ ${env_name} == PAPERLESS_*_FILE ]]; then if [[ ${env_name} == PAPERLESS_*_FILE ]]; then
# This should have been named different.. # This should have been named different..
if [[ ${env_name} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" ]]; then if [[ ${env_name} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" || ${env_name} == "PAPERLESS_MODEL_FILE" ]]; then
continue continue
fi fi
# Extract the value of the environment # Extract the value of the environment

View File

@@ -14,7 +14,8 @@ for command in decrypt_documents \
document_thumbnails \ document_thumbnails \
document_sanity_checker \ document_sanity_checker \
document_fuzzy_match \ document_fuzzy_match \
manage_superuser; manage_superuser \
convert_mariadb_uuid;
do do
echo "installing $command..." echo "installing $command..."
sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command

View File

@@ -4,6 +4,7 @@ Simple script which attempts to ping the Redis broker as set in the environment
a certain number of times, waiting a little bit in between a certain number of times, waiting a little bit in between
""" """
import os import os
import sys import sys
import time import time

View File

@@ -19,6 +19,8 @@ Options available to any installation of paperless:
export. Therefore, incremental backups with `rsync` are entirely export. Therefore, incremental backups with `rsync` are entirely
possible. possible.
The exporter does not include API tokens and they will need to be re-generated after importing.
!!! caution !!! caution
You cannot import the export generated with one version of paperless in You cannot import the export generated with one version of paperless in
@@ -185,34 +187,12 @@ For PostgreSQL, refer to [Upgrading a PostgreSQL Cluster](https://www.postgresql
For MariaDB, refer to [Upgrading MariaDB](https://mariadb.com/kb/en/upgrading/) For MariaDB, refer to [Upgrading MariaDB](https://mariadb.com/kb/en/upgrading/)
## Downgrading Paperless {#downgrade-paperless} You may also use the exporter and importer with the `--data-only` flag, after creating a new database with the updated version of PostgreSQL or MariaDB.
Downgrades are possible. However, some updates also contain database !!! warning
migrations (these change the layout of the database and may move data).
In order to move back from a version that applied database migrations,
you'll have to revert the database migration _before_ downgrading, and
then downgrade paperless.
This table lists the compatible versions for each database migration You should not change any settings, especially paths, when doing this or there is a
number. risk of data loss
| Migration number | Version range |
| ---------------- | --------------- |
| 1011 | 1.0.0 |
| 1012 | 1.1.0 - 1.2.1 |
| 1014 | 1.3.0 - 1.3.1 |
| 1016 | 1.3.2 - current |
Execute the following management command to migrate your database:
```shell-session
$ python3 manage.py migrate documents <migration number>
```
!!! note
Some migrations cannot be undone. The command will issue errors if that
happens.
## Management utilities {#management-commands} ## Management utilities {#management-commands}
@@ -269,6 +249,9 @@ optional arguments:
-sm, --split-manifest -sm, --split-manifest
-z, --zip -z, --zip
-zn, --zip-name -zn, --zip-name
--data-only
--no-progress-bar
--passphrase
``` ```
`target` is a folder to which the data gets written. This includes `target` is a folder to which the data gets written. This includes
@@ -327,6 +310,16 @@ If `-z` or `--zip` is provided, the export will be a zip file
in the target directory, named according to the current local date or the in the target directory, named according to the current local date or the
value set in `-zn` or `--zip-name`. value set in `-zn` or `--zip-name`.
If `--data-only` is provided, only the database will be exported. This option is intended
to facilitate database upgrades without needing to clean documents and thumbnails from the media directory.
If `--no-progress-bar` is provided, the progress bar will be hidden, rendering the
exporter quiet. This option is useful for scripting scenarios, such as when using the
exporter with `crontab`.
If `--passphrase` is provided, it will be used to encrypt certain fields in the export. This value
must be provided to import. If this value is lost, the export cannot be imported.
!!! warning !!! warning
If exporting with the file name format, there may be errors due to If exporting with the file name format, there may be errors due to
@@ -341,19 +334,34 @@ exporter](#exporter) and imports it into paperless.
The importer works just like the exporter. You point it at a directory, The importer works just like the exporter. You point it at a directory,
and the script does the rest of the work: and the script does the rest of the work:
``` ```shell
document_importer source document_importer source
``` ```
| Option | Required | Default | Description |
| ------------------- | -------- | ------- | ------------------------------------------------------------------------- |
| source | Yes | N/A | The directory containing an export |
| `--no-progress-bar` | No | False | If provided, the progress bar will be hidden |
| `--data-only` | No | False | If provided, only import data, do not import document files or thumbnails |
| `--passphrase` | No | N/A | If your export was encrypted with a passphrase, must be provided |
When you use the provided docker compose script, put the export inside When you use the provided docker compose script, put the export inside
the `export` folder in your paperless source directory. Specify the `export` folder in your paperless source directory. Specify
`../export` as the `source`. `../export` as the `source`.
Note that .zip files (as can be generated from the exporter) are not supported. You must unzip them into
the target directory first.
!!! note !!! note
Importing from a previous version of Paperless may work, but for best Importing from a previous version of Paperless may work, but for best
results it is suggested to match the versions. results it is suggested to match the versions.
!!! warning
The importer should be run against a completely empty installation (database and directories) of Paperless-ngx.
If using a data only import, only the database must be empty.
### Document retagger {#retagger} ### Document retagger {#retagger}
Say you've imported a few hundred documents and now want to introduce a Say you've imported a few hundred documents and now want to introduce a
@@ -580,7 +588,7 @@ Enabling encryption is no longer supported.
Basic usage to disable encryption of your document store: Basic usage to disable encryption of your document store:
(Note: If [`PAPERLESS_PASSPHRASE`](configuration.md#PAPERLESS_PASSPHRASE) isn't set already, you need to specify (Note: If `PAPERLESS_PASSPHRASE` isn't set already, you need to specify
it here) it here)
``` ```

View File

@@ -25,20 +25,20 @@ documents.
The following algorithms are available: The following algorithms are available:
- **None:** No matching will be performed. - **None:** No matching will be performed.
- **Any:** Looks for any occurrence of any word provided in match in - **Any:** Looks for any occurrence of any word provided in match in
the PDF. If you define the match as `Bank1 Bank2`, it will match the PDF. If you define the match as `Bank1 Bank2`, it will match
documents containing either of these terms. documents containing either of these terms.
- **All:** Requires that every word provided appears in the PDF, - **All:** Requires that every word provided appears in the PDF,
albeit not in the order provided. albeit not in the order provided.
- **Exact:** Matches only if the match appears exactly as provided - **Exact:** Matches only if the match appears exactly as provided
(i.e. preserve ordering) in the PDF. (i.e. preserve ordering) in the PDF.
- **Regular expression:** Parses the match as a regular expression and - **Regular expression:** Parses the match as a regular expression and
tries to find a match within the document. tries to find a match within the document.
- **Fuzzy match:** Uses a partial matching based on locating the tag text - **Fuzzy match:** Uses a partial matching based on locating the tag text
inside the document, using a [partial ratio](https://maxbachmann.github.io/RapidFuzz/Usage/fuzz.html#partial-ratio) inside the document, using a [partial ratio](https://rapidfuzz.github.io/RapidFuzz/Usage/fuzz.html#partial-ratio)
- **Auto:** Tries to automatically match new documents. This does not - **Auto:** Tries to automatically match new documents. This does not
require you to set a match. See the [notes below](#automatic-matching). require you to set a match. See the [notes below](#automatic-matching).
When using the _any_ or _all_ matching algorithms, you can search for When using the _any_ or _all_ matching algorithms, you can search for
terms that consist of multiple words by enclosing them in double quotes. terms that consist of multiple words by enclosing them in double quotes.
@@ -69,33 +69,33 @@ Paperless tries to hide much of the involved complexity with this
approach. However, there are a couple caveats you need to keep in mind approach. However, there are a couple caveats you need to keep in mind
when using this feature: when using this feature:
- Changes to your documents are not immediately reflected by the - Changes to your documents are not immediately reflected by the
matching algorithm. The neural network needs to be _trained_ on your matching algorithm. The neural network needs to be _trained_ on your
documents after changes. Paperless periodically (default: once each documents after changes. Paperless periodically (default: once each
hour) checks for changes and does this automatically for you. hour) checks for changes and does this automatically for you.
- The Auto matching algorithm only takes documents into account which - The Auto matching algorithm only takes documents into account which
are NOT placed in your inbox (i.e. have any inbox tags assigned to are NOT placed in your inbox (i.e. have any inbox tags assigned to
them). This ensures that the neural network only learns from them). This ensures that the neural network only learns from
documents which you have correctly tagged before. documents which you have correctly tagged before.
- The matching algorithm can only work if there is a correlation - The matching algorithm can only work if there is a correlation
between the tag, correspondent, document type, or storage path and between the tag, correspondent, document type, or storage path and
the document itself. Your bank statements usually contain your bank the document itself. Your bank statements usually contain your bank
account number and the name of the bank, so this works reasonably account number and the name of the bank, so this works reasonably
well, However, tags such as "TODO" cannot be automatically well, However, tags such as "TODO" cannot be automatically
assigned. assigned.
- The matching algorithm needs a reasonable number of documents to - The matching algorithm needs a reasonable number of documents to
identify when to assign tags, correspondents, storage paths, and identify when to assign tags, correspondents, storage paths, and
types. If one out of a thousand documents has the correspondent types. If one out of a thousand documents has the correspondent
"Very obscure web shop I bought something five years ago", it will "Very obscure web shop I bought something five years ago", it will
probably not assign this correspondent automatically if you buy probably not assign this correspondent automatically if you buy
something from them again. The more documents, the better. something from them again. The more documents, the better.
- Paperless also needs a reasonable amount of negative examples to - Paperless also needs a reasonable amount of negative examples to
decide when not to assign a certain tag, correspondent, document decide when not to assign a certain tag, correspondent, document
type, or storage path. This will usually be the case as you start type, or storage path. This will usually be the case as you start
filling up paperless with documents. Example: If all your documents filling up paperless with documents. Example: If all your documents
are either from "Webshop" or "Bank", paperless will assign one are either from "Webshop" or "Bank", paperless will assign one
of these correspondents to ANY new document, if both are set to of these correspondents to ANY new document, if both are set to
automatic matching. automatic matching.
## Hooking into the consumption process {#consume-hooks} ## Hooking into the consumption process {#consume-hooks}
@@ -187,6 +187,7 @@ variables:
| `DOCUMENT_THUMBNAIL_PATH` | Path to the generated thumbnail | | `DOCUMENT_THUMBNAIL_PATH` | Path to the generated thumbnail |
| `DOCUMENT_DOWNLOAD_URL` | URL for document download | | `DOCUMENT_DOWNLOAD_URL` | URL for document download |
| `DOCUMENT_THUMBNAIL_URL` | URL for the document thumbnail | | `DOCUMENT_THUMBNAIL_URL` | URL for the document thumbnail |
| `DOCUMENT_OWNER` | Username of the document owner (if any) |
| `DOCUMENT_CORRESPONDENT` | Assigned correspondent (if any) | | `DOCUMENT_CORRESPONDENT` | Assigned correspondent (if any) |
| `DOCUMENT_TAGS` | Comma separated list of tags applied (if any) | | `DOCUMENT_TAGS` | Comma separated list of tags applied (if any) |
| `DOCUMENT_ORIGINAL_FILENAME` | Filename of original document | | `DOCUMENT_ORIGINAL_FILENAME` | Filename of original document |
@@ -241,12 +242,12 @@ webserver:
Troubleshooting: Troubleshooting:
- Monitor the Docker Compose log - Monitor the Docker Compose log
`cd ~/paperless-ngx; docker compose logs -f` `cd ~/paperless-ngx; docker compose logs -f`
- Check your script's permission e.g. in case of permission error - Check your script's permission e.g. in case of permission error
`sudo chmod 755 post-consumption-example.sh` `sudo chmod 755 post-consumption-example.sh`
- Pipe your scripts's output to a log file e.g. - Pipe your scripts's output to a log file e.g.
`echo "${DOCUMENT_ID}" | tee --append /usr/src/paperless/scripts/post-consumption-example.log` `echo "${DOCUMENT_ID}" | tee --append /usr/src/paperless/scripts/post-consumption-example.log`
## File name handling {#file-name-handling} ## File name handling {#file-name-handling}
@@ -264,7 +265,7 @@ This variable allows you to configure the filename (folders are allowed)
using placeholders. For example, configuring this to using placeholders. For example, configuring this to
```bash ```bash
PAPERLESS_FILENAME_FORMAT={created_year}/{correspondent}/{title} PAPERLESS_FILENAME_FORMAT={{ created_year }}/{{ correspondent }}/{{ title }}
``` ```
will create a directory structure as follows: will create a directory structure as follows:
@@ -293,43 +294,43 @@ will create a directory structure as follows:
!!! tip !!! tip
Paperless checks the filename of a document whenever it is saved. Changing (or deleting) 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, a [storage path](#storage-paths) will automatically be reflected in the file system. However,
when changing `PAPERLESS_FILENAME_FORMAT` you will need to manually run the when changing `PAPERLESS_FILENAME_FORMAT` you will need to manually run the
[`document renamer`](administration.md#renamer) to move any existing documents. [`document renamer`](administration.md#renamer) to move any existing documents.
#### Placeholders ### Placeholders {#filename-format-variables}
Paperless provides the following placeholders within filenames: Paperless provides the following variables for use within filenames:
- `{asn}`: The archive serial number of the document, or "none". - `{{ asn }}`: The archive serial number of the document, or "none".
- `{correspondent}`: The name of the correspondent, or "none". - `{{ correspondent }}`: The name of the correspondent, or "none".
- `{document_type}`: The name of the document type, or "none". - `{{ document_type }}`: The name of the document type, or "none".
- `{tag_list}`: A comma separated list of all tags assigned to the - `{{ tag_list }}`: A comma separated list of all tags assigned to the
document. document.
- `{title}`: The title of the document. - `{{ title }}`: The title of the document.
- `{created}`: The full date (ISO format) the document was created. - `{{ created }}`: The full date (ISO format) the document was created.
- `{created_year}`: Year created only, formatted as the year with - `{{ created_year }}`: Year created only, formatted as the year with
century. century.
- `{created_year_short}`: Year created only, formatted as the year - `{{ created_year_short }}`: Year created only, formatted as the year
without century, zero padded. without century, zero padded.
- `{created_month}`: Month created only (number 01-12). - `{{ created_month }}`: Month created only (number 01-12).
- `{created_month_name}`: Month created name, as per locale - `{{ created_month_name }}`: Month created name, as per locale
- `{created_month_name_short}`: Month created abbreviated name, as per - `{{ created_month_name_short }}`: Month created abbreviated name, as per
locale locale
- `{created_day}`: Day created only (number 01-31). - `{{ created_day }}`: Day created only (number 01-31).
- `{added}`: The full date (ISO format) the document was added to - `{{ added }}`: The full date (ISO format) the document was added to
paperless. paperless.
- `{added_year}`: Year added only. - `{{ added_year }}`: Year added only.
- `{added_year_short}`: Year added only, formatted as the year without - `{{ added_year_short }}`: Year added only, formatted as the year without
century, zero padded. century, zero padded.
- `{added_month}`: Month added only (number 01-12). - `{{ added_month }}`: Month added only (number 01-12).
- `{added_month_name}`: Month added name, as per locale - `{{ added_month_name }}`: Month added name, as per locale
- `{added_month_name_short}`: Month added abbreviated name, as per - `{{ added_month_name_short }}`: Month added abbreviated name, as per
locale locale
- `{added_day}`: Day added only (number 01-31). - `{{ added_day }}`: Day added only (number 01-31).
- `{owner_username}`: Username of document owner, if any, or "none" - `{{ owner_username }}`: Username of document owner, if any, or "none"
- `{original_name}`: Document original filename, minus the extension, if any, or "none" - `{{ original_name }}`: Document original filename, minus the extension, if any, or "none"
- `{doc_pk}`: The paperless identifier (primary key) for the document. - `{{ doc_pk }}`: The paperless identifier (primary key) for the document.
!!! warning !!! warning
@@ -337,6 +338,11 @@ Paperless provides the following placeholders within filenames:
you may run into the limits of your operating system's maximum path lengths. 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. In that case, files will retain the previous path instead and the issue logged.
!!! tip
These variables are all simple strings, but the format can be a full template.
See [Filename Templates](#filename-templates) for even more advanced formatting.
Paperless will try to conserve the information from your database as Paperless will try to conserve the information from your database as
much as possible. However, some characters that you can use in document much as possible. However, some characters that you can use in document
titles and correspondent names (such as `: \ /` and a couple more) are titles and correspondent names (such as `: \ /` and a couple more) are
@@ -348,7 +354,7 @@ This happens if all the placeholders in a filename evaluate to the same
value. value.
If there are any errors in the placeholders included in `PAPERLESS_FILENAME_FORMAT`, If there are any errors in the placeholders included in `PAPERLESS_FILENAME_FORMAT`,
paperless will fallback to using the default naming scheme instead. paperless will fall back to using the default naming scheme instead.
!!! caution !!! caution
@@ -362,7 +368,7 @@ paperless will fallback to using the default naming scheme instead.
However, keep in mind that inside docker, if files get stored outside of However, keep in mind that inside docker, if files get stored outside of
the predefined volumes, they will be lost after a restart. the predefined volumes, they will be lost after a restart.
##### Empty placeholders #### Empty placeholders
You can affect how empty placeholders are treated by changing the You can affect how empty placeholders are treated by changing the
[`PAPERLESS_FILENAME_FORMAT_REMOVE_NONE`](configuration.md#PAPERLESS_FILENAME_FORMAT_REMOVE_NONE) setting. [`PAPERLESS_FILENAME_FORMAT_REMOVE_NONE`](configuration.md#PAPERLESS_FILENAME_FORMAT_REMOVE_NONE) setting.
@@ -375,10 +381,10 @@ before empty placeholders are removed as well, empty directories are omitted.
When a single storage layout is not sufficient for your use case, storage paths allow for more complex 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. 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 - Each storage path is a [`PAPERLESS_FILENAME_FORMAT`](configuration.md#PAPERLESS_FILENAME_FORMAT) and
follows the rules described above follows the rules described above
- Each document is assigned a storage path using the matching algorithms described above, but can be - Each document is assigned a storage path using the matching algorithms described above, but can be
overwritten at any time overwritten at any time
For example, you could define the following two storage paths: For example, you could define the following two storage paths:
@@ -389,8 +395,8 @@ For example, you could define the following two storage paths:
the correspondence. the correspondence.
``` ```
By Year = {created_year}/{correspondent}/{title} By Year = {{ created_year }}/{{ correspondent }}/{{ title }}
Insurances = Insurances/{correspondent}/{created_year}-{created_month}-{created_day} {title} Insurances = Insurances/{{ correspondent }}/{{ created_year }}-{{ created_month }}-{{ created_day }} {{ title }}
``` ```
If you then map these storage paths to the documents, you might get the If you then map these storage paths to the documents, you might get the
@@ -417,6 +423,97 @@ Insurances/ # Insurances
Defining a storage path is optional. If no storage path is defined for a Defining a storage path is optional. If no storage path is defined for a
document, the global [`PAPERLESS_FILENAME_FORMAT`](configuration.md#PAPERLESS_FILENAME_FORMAT) is applied. document, the global [`PAPERLESS_FILENAME_FORMAT`](configuration.md#PAPERLESS_FILENAME_FORMAT) is applied.
### Filename Templates {#filename-templates}
The filename formatting uses [Jinja templates](https://jinja.palletsprojects.com/en/3.1.x/templates/) to build the filename.
This allows for complex logic to be included in the format, including [logical structures](https://jinja.palletsprojects.com/en/3.1.x/templates/#list-of-control-structures)
and [filters](https://jinja.palletsprojects.com/en/3.1.x/templates/#id11) to manipulate the [variables](#filename-format-variables)
provided. The template is provided as a string, potentially multiline, and rendered into a single line.
In addition, the entire Document instance is available to be utilized in a more advanced way, as well as some variables which only make sense to be accessed
with more complex logic.
#### Additional Variables
- `{{ tag_name_list }}`: A list of tag names applied to the document, ordered by the tag name. Note this is a list, not a single string
- `{{ custom_fields }}`: A mapping of custom field names to their type and value. A user can access the mapping by field name or check if a field is applied by checking its existence in the variable.
!!! tip
To access a custom field which has a space in the name, use the `get_cf_value` filter. See the examples below.
This helps get fields by name and handle a default value if the named field is not attached to a Document.
#### Examples
This example will construct a path based on the archive serial number range:
```jinja
somepath/
{% if document.archive_serial_number >= 0 and document.archive_serial_number <= 200 %}
asn-000-200/{{title}}
{% elif document.archive_serial_number >= 201 and document.archive_serial_number <= 400 %}
asn-201-400
{% if document.archive_serial_number >= 201 and document.archive_serial_number < 300 %}
/asn-2xx
{% elif document.archive_serial_number >= 300 and document.archive_serial_number < 400 %}
/asn-3xx
{% endif %}
{% endif %}
/{{ title }}
```
For a document with an ASN of 205, it would result in `somepath/asn-201-400/asn-2xx/Title.pdf`, but
a document with an ASN of 355 would be placed in `somepath/asn-201-400/asn-3xx/Title.pdf`.
```jinja
{% if document.mime_type == "application/pdf" %}
pdfs
{% elif document.mime_type == "image/png" %}
pngs
{% else %}
others
{% endif %}
/{{ title }}
```
For a PDF document, it would result in `pdfs/Title.pdf`, but for a PNG document, the path would be `pngs/Title.pdf`.
To use custom fields:
```jinja
{% if "Invoice" in custom_fields %}
invoices/{{ custom_fields.Invoice.value }}
{% else %}
not-invoices/{{ title }}
{% endif %}
```
If the document has a custom field named "Invoice" with a value of 123, it would be filed into the `invoices/123.pdf`, but a document without the custom field
would be filed to `not-invoices/Title.pdf`
If the custom field is named "Invoice Number", you would access the value of it via the `get_cf_value` filter due to quirks of the Django Template Language:
```jinja
"invoices/{{ custom_fields|get_cf_value('Invoice Number') }}"
```
You can also use a custom `datetime` filter to format dates:
```jinja
invoices/
{{ custom_fields|get_cf_value("Date Field","2024-01-01")|datetime('%Y') }}/
{{ custom_fields|get_cf_value("Date Field","2024-01-01")|datetime('%m') }}/
{{ custom_fields|get_cf_value("Date Field","2024-01-01")|datetime('%d') }}/
Invoice_{{ custom_fields|get_cf_value("Select Field") }}_{{ custom_fields|get_cf_value("Date Field","2024-01-01")|replace("-", "") }}.pdf
```
This will create a path like `invoices/2022/01/01/Invoice_OptionTwo_20220101.pdf` if the custom field "Date Field" is set to January 1, 2022 and "Select Field" is set to `OptionTwo`.
## Automatic recovery of invalid PDFs {#pdf-recovery}
Paperless will attempt to "clean" certain invalid PDFs with `qpdf` before processing if, for example, the mime_type
detection is incorrect. This can happen if the PDF is not properly formatted or contains errors.
## Celery Monitoring {#celery-monitoring} ## Celery Monitoring {#celery-monitoring}
The monitoring tool The monitoring tool
@@ -435,15 +532,15 @@ installation, you can use volumes to accomplish this:
```yaml ```yaml
services: services:
# ...
webserver:
environment:
- PAPERLESS_ENABLE_FLOWER
ports:
- 5555:5555 # (2)!
# ... # ...
volumes: webserver:
- /path/to/my/flowerconfig.py:/usr/src/paperless/src/paperless/flowerconfig.py:ro # (1)! environment:
- PAPERLESS_ENABLE_FLOWER
ports:
- 5555:5555 # (2)!
# ...
volumes:
- /path/to/my/flowerconfig.py:/usr/src/paperless/src/paperless/flowerconfig.py:ro # (1)!
``` ```
1. Note the `:ro` tag means the file will be mounted as read only. 1. Note the `:ro` tag means the file will be mounted as read only.
@@ -474,11 +571,11 @@ For example, using Docker Compose:
```yaml ```yaml
services: services:
# ...
webserver:
# ... # ...
volumes: webserver:
- /path/to/my/scripts:/custom-cont-init.d:ro # (1)! # ...
volumes:
- /path/to/my/scripts:/custom-cont-init.d:ro # (1)!
``` ```
1. Note the `:ro` tag means the folder will be mounted as read only. This is for extra security against changes 1. Note the `:ro` tag means the folder will be mounted as read only. This is for extra security against changes
@@ -526,16 +623,16 @@ Paperless is able to utilize barcodes for automatically performing some tasks.
At this time, the library utilized for detection of barcodes supports the following types: At this time, the library utilized for detection of barcodes supports the following types:
- AN-13/UPC-A - AN-13/UPC-A
- UPC-E - UPC-E
- EAN-8 - EAN-8
- Code 128 - Code 128
- Code 93 - Code 93
- Code 39 - Code 39
- Codabar - Codabar
- Interleaved 2 of 5 - Interleaved 2 of 5
- QR Code - QR Code
- SQ Code - SQ Code
You may check for updates on the [zbar library homepage](https://github.com/mchehab/zbar). You may check for updates on the [zbar library homepage](https://github.com/mchehab/zbar).
For usage in Paperless, the type of barcode does not matter, only the contents of it. For usage in Paperless, the type of barcode does not matter, only the contents of it.
@@ -649,8 +746,9 @@ external authentication solution using one of the following methods:
This is a simple option that uses remote user authentication made available by certain SSO This is a simple option that uses remote user authentication made available by certain SSO
applications. See the relevant configuration options for more information: applications. See the relevant configuration options for more information:
[PAPERLESS_ENABLE_HTTP_REMOTE_USER](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER) and [PAPERLESS_ENABLE_HTTP_REMOTE_USER](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER),
[PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME](configuration.md#PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME) [PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME](configuration.md#PAPERLESS_HTTP_REMOTE_USER_HEADER_NAME)
and [PAPERLESS_LOGOUT_REDIRECT_URL](configuration.md#PAPERLESS_LOGOUT_REDIRECT_URL)
### OpenID Connect and social authentication ### OpenID Connect and social authentication
@@ -686,4 +784,59 @@ More details about configuration option for various providers can be found in th
### Disabling Regular Login ### 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. Once external auth is set up, 'regular' login can be disabled with the [PAPERLESS_DISABLE_REGULAR_LOGIN](configuration.md#PAPERLESS_DISABLE_REGULAR_LOGIN) setting and / or users can be automatically
redirected with the [PAPERLESS_REDIRECT_LOGIN_TO_SSO](configuration.md#PAPERLESS_REDIRECT_LOGIN_TO_SSO) setting.
## Decryption of encrypted emails before consumption {#gpg-decryptor}
Paperless-ngx can be configured to decrypt gpg encrypted emails before consumption.
### Requirements
You need a recent version of `gpg-agent >= 2.1.1` installed on your host.
Your host needs to be setup for decrypting your emails via `gpg-agent`, see this [tutorial](https://www.digitalocean.com/community/tutorials/how-to-use-gpg-to-encrypt-and-sign-messages#encrypt-and-decrypt-messages-with-gpg) for instance.
Test your setup and make sure that you can encrypt and decrypt files using your key
```
gpg --encrypt --armor -r person@email.com name_of_file
gpg --decrypt name_of_file.asc
```
### Setup
First, enable the [PAPERLESS_ENABLE_GPG_DECRYPTOR environment variable](configuration.md#PAPERLESS_ENABLE_GPG_DECRYPTOR).
Then determine your local `gpg-agent.extra` socket by invoking
```
gpgconf --list-dir agent-extra-socket
```
on your host. A possible output is `~/.gnupg/S.gpg-agent.extra`.
Also find the location of your public keyring.
If using docker, you'll need to add the following volume mounts to your `docker-compose.yml` file:
```yaml
webserver:
volumes:
- /home/user/.gnupg/pubring.gpg:/usr/src/paperless/.gnupg/pubring.gpg
- <path to gpg-agent.extra socket>:/usr/src/paperless/.gnupg/S.gpg-agent
```
For a 'bare-metal' installation no further configuration is necessary. If you
want to use a separate `GNUPG_HOME`, you can do so by configuring the [PAPERLESS_EMAIL_GNUPG_HOME environment variable](configuration.md#PAPERLESS_EMAIL_GNUPG_HOME).
### Troubleshooting
- Make sure, that `gpg-agent` is running on your host machine
- Make sure, that encryption and decryption works from inside the container using the `gpg` commands from above.
- Check that all files in `/usr/src/paperless/.gnupg` have correct permissions
```shell
paperless@9da1865df327:~/.gnupg$ ls -al
drwx------ 1 paperless paperless 4096 Aug 18 17:52 .
drwxr-xr-x 1 paperless paperless 4096 Aug 18 17:52 ..
srw------- 1 paperless paperless 0 Aug 18 17:22 S.gpg-agent
-rw------- 1 paperless paperless 147940 Jul 24 10:23 pubring.gpg
```

View File

@@ -8,22 +8,23 @@ most of the available filters and ordering fields.
The API provides the following main endpoints: The API provides the following main endpoints:
- `/api/correspondents/`: Full CRUD support. - `/api/correspondents/`: Full CRUD support.
- `/api/custom_fields/`: Full CRUD support. - `/api/custom_fields/`: Full CRUD support.
- `/api/documents/`: Full CRUD support, except POSTing new documents. - `/api/documents/`: Full CRUD support, except POSTing new documents.
See below. See [below](#file-uploads).
- `/api/document_types/`: Full CRUD support. - `/api/document_types/`: Full CRUD support.
- `/api/groups/`: Full CRUD support. - `/api/groups/`: Full CRUD support.
- `/api/logs/`: Read-Only. - `/api/logs/`: Read-Only.
- `/api/mail_accounts/`: Full CRUD support. - `/api/mail_accounts/`: Full CRUD support.
- `/api/mail_rules/`: Full CRUD support. - `/api/mail_rules/`: Full CRUD support.
- `/api/profile/`: GET, PATCH - `/api/profile/`: GET, PATCH
- `/api/share_links/`: Full CRUD support. - `/api/share_links/`: Full CRUD support.
- `/api/storage_paths/`: Full CRUD support. - `/api/storage_paths/`: Full CRUD support.
- `/api/tags/`: Full CRUD support. - `/api/tags/`: Full CRUD support.
- `/api/tasks/`: Read-only. - `/api/tasks/`: Read-only.
- `/api/users/`: Full CRUD support. - `/api/users/`: Full CRUD support.
- `/api/workflows/`: Full CRUD support. - `/api/workflows/`: Full CRUD support.
- `/api/search/` GET, see [below](#global-search).
All of these endpoints except for the logging endpoint allow you to All of these endpoints except for the logging endpoint allow you to
fetch (and edit and delete where appropriate) individual objects by fetch (and edit and delete where appropriate) individual objects by
@@ -32,31 +33,32 @@ appending their primary key to the path, e.g. `/api/documents/454/`.
The objects served by the document endpoint contain the following The objects served by the document endpoint contain the following
fields: fields:
- `id`: ID of the document. Read-only. - `id`: ID of the document. Read-only.
- `title`: Title of the document. - `title`: Title of the document.
- `content`: Plain text content of the document. - `content`: Plain text content of the document.
- `tags`: List of IDs of tags assigned to this document, or empty - `tags`: List of IDs of tags assigned to this document, or empty
list. list.
- `document_type`: Document type of this document, or null. - `document_type`: Document type of this document, or null.
- `correspondent`: Correspondent of this document or null. - `correspondent`: Correspondent of this document or null.
- `created`: The date time at which this document was created. - `created`: The date time at which this document was created.
- `created_date`: The date (YYYY-MM-DD) at which this document was - `created_date`: The date (YYYY-MM-DD) at which this document was
created. Optional. If also passed with created, this is ignored. created. Optional. If also passed with created, this is ignored.
- `modified`: The date at which this document was last edited in - `modified`: The date at which this document was last edited in
paperless. Read-only. paperless. Read-only.
- `added`: The date at which this document was added to paperless. - `added`: The date at which this document was added to paperless.
Read-only. Read-only.
- `archive_serial_number`: The identifier of this document in a - `archive_serial_number`: The identifier of this document in a
physical document archive. physical document archive.
- `original_file_name`: Verbose filename of the original document. - `original_file_name`: Verbose filename of the original document.
Read-only. Read-only.
- `archived_file_name`: Verbose filename of the archived document. - `archived_file_name`: Verbose filename of the archived document.
Read-only. Null if no archived document is available. Read-only. Null if no archived document is available.
- `notes`: Array of notes associated with the document. - `notes`: Array of notes associated with the document.
- `set_permissions`: Allows setting document permissions. Optional, - `page_count`: Number of pages.
write-only. See [below](#permissions). - `set_permissions`: Allows setting document permissions. Optional,
- `custom_fields`: Array of custom fields & values, specified as write-only. See [below](#permissions).
`{ field: CUSTOM_FIELD_ID, value: VALUE }` - `custom_fields`: Array of custom fields & values, specified as
`{ field: CUSTOM_FIELD_ID, value: VALUE }`
!!! note !!! note
@@ -67,11 +69,11 @@ fields:
In addition to that, the document endpoint offers these additional In addition to that, the document endpoint offers these additional
actions on individual documents: actions on individual documents:
- `/api/documents/<pk>/download/`: Download the document. - `/api/documents/<pk>/download/`: Download the document.
- `/api/documents/<pk>/preview/`: Display the document inline, without - `/api/documents/<pk>/preview/`: Display the document inline, without
downloading it. downloading it.
- `/api/documents/<pk>/thumb/`: Download the PNG thumbnail of a - `/api/documents/<pk>/thumb/`: Download the PNG thumbnail of a
document. document.
Paperless generates archived PDF/A documents from consumed files and Paperless generates archived PDF/A documents from consumed files and
stores both the original files as well as the archived files. By stores both the original files as well as the archived files. By
@@ -105,30 +107,30 @@ Access the metadata of a document with an ID `id` at
The endpoint reports the following data: The endpoint reports the following data:
- `original_checksum`: MD5 checksum of the original document. - `original_checksum`: MD5 checksum of the original document.
- `original_size`: Size of the original document, in bytes. - `original_size`: Size of the original document, in bytes.
- `original_mime_type`: Mime type of the original document. - `original_mime_type`: Mime type of the original document.
- `media_filename`: Current filename of the document, under which it - `media_filename`: Current filename of the document, under which it
is stored inside the media directory. is stored inside the media directory.
- `has_archive_version`: True, if this document is archived, false - `has_archive_version`: True, if this document is archived, false
otherwise. otherwise.
- `original_metadata`: A list of metadata associated with the original - `original_metadata`: A list of metadata associated with the original
document. See below. document. See below.
- `archive_checksum`: MD5 checksum of the archived document, or null. - `archive_checksum`: MD5 checksum of the archived document, or null.
- `archive_size`: Size of the archived document in bytes, or null. - `archive_size`: Size of the archived document in bytes, or null.
- `archive_metadata`: Metadata associated with the archived document, - `archive_metadata`: Metadata associated with the archived document,
or null. See below. or null. See below.
File metadata is reported as a list of objects in the following form: File metadata is reported as a list of objects in the following form:
```json ```json
[ [
{ {
"namespace": "http://ns.adobe.com/pdf/1.3/", "namespace": "http://ns.adobe.com/pdf/1.3/",
"prefix": "pdf", "prefix": "pdf",
"key": "Producer", "key": "Producer",
"value": "SparklePDF, Fancy edition" "value": "SparklePDF, Fancy edition"
} }
] ]
``` ```
@@ -138,8 +140,9 @@ document. Paperless only reports PDF metadata at this point.
## Documents additional endpoints ## Documents additional endpoints
- `/api/documents/<id>/notes/`: Retrieve notes for a document. - `/api/documents/<id>/notes/`: Retrieve notes for a document.
- `/api/documents/<id>/share_links/`: Retrieve share links for a document. - `/api/documents/<id>/share_links/`: Retrieve share links for a document.
- `/api/documents/<id>/history/`: Retrieve history of changes for a document.
## Authorization ## Authorization
@@ -187,26 +190,52 @@ The REST api provides four different forms of authentication.
[configuration](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER_API)), [configuration](configuration.md#PAPERLESS_ENABLE_HTTP_REMOTE_USER_API)),
you can authenticate against the API using Remote User auth. you can authenticate against the API using Remote User auth.
## Global search
A global search endpoint is available at `/api/search/` and requires a search term
of > 2 characters e.g. `?query=foo`. This endpoint returns a maximum of 3 results
across nearly all objects, e.g. documents, tags, saved views, mail rules, etc.
Results are only included if the requesting user has the appropriate permissions.
Results are returned in the following format:
```json
{
total: number
documents: []
saved_views: []
correspondents: []
document_types: []
storage_paths: []
tags: []
users: []
groups: []
mail_accounts: []
mail_rules: []
custom_fields: []
workflows: []
}
```
Global search first searches objects by name (or title for documents) matching the query.
If the optional `db_only` parameter is set, only document titles will be searched. Otherwise,
if the amount of documents returned by a simple title string search is < 3, results from the
search index will also be included.
## Searching for documents ## Searching for documents
Full text searching is available on the `/api/documents/` endpoint. Two Full text searching is available on the `/api/documents/` endpoint. Two
specific query parameters cause the API to return full text search specific query parameters cause the API to return full text search
results: results:
- `/api/documents/?query=your%20search%20query`: Search for a document - `/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). using a full text query. For details on the syntax, see [Basic Usage - Searching](usage.md#basic-usage_searching).
- `/api/documents/?more_like_id=1234`: Search for documents similar to - `/api/documents/?more_like_id=1234`: Search for documents similar to
the document with id 1234. the document with id 1234.
Pagination works exactly the same as it does for normal requests on this Pagination works exactly the same as it does for normal requests on this
endpoint. endpoint.
Certain limitations apply to full text queries:
- Results are always sorted by search score. The results matching the
query best will show up first.
- Only a small subset of filtering parameters are supported.
Furthermore, each returned document has an additional `__search_hit__` Furthermore, each returned document has an additional `__search_hit__`
attribute with various information about the search results: attribute with various information about the search results:
@@ -239,12 +268,57 @@ attribute with various information about the search results:
} }
``` ```
- `score` is an indication how well this document matches the query - `score` is an indication how well this document matches the query
relative to the other search results. relative to the other search results.
- `highlights` is an excerpt from the document content and highlights - `highlights` is an excerpt from the document content and highlights
the search terms with `<span>` tags as shown above. the search terms with `<span>` tags as shown above.
- `rank` is the index of the search results. The first result will - `rank` is the index of the search results. The first result will
have rank 0. have rank 0.
### Filtering by custom fields
You can filter documents by their custom field values by specifying the
`custom_field_query` query parameter. Here are some recipes for common
use cases:
1. Documents with a custom field "due" (date) between Aug 1, 2024 and
Sept 1, 2024 (inclusive):
`?custom_field_query=["due", "range", ["2024-08-01", "2024-09-01"]]`
2. Documents with a custom field "customer" (text) that equals "bob"
(case sensitive):
`?custom_field_query=["customer", "exact", "bob"]`
3. Documents with a custom field "answered" (boolean) set to `true`:
`?custom_field_query=["answered", "exact", true]`
4. Documents with a custom field "favorite animal" (select) set to either
"cat" or "dog":
`?custom_field_query=["favorite animal", "in", ["cat", "dog"]]`
5. Documents with a custom field "address" (text) that is empty:
`?custom_field_query=["OR", ["address", "isnull", true], ["address", "exact", ""]]`
6. Documents that don't have a field called "foo":
`?custom_field_query=["foo", "exists", false]`
7. Documents that have document links "references" to both document 3 and 7:
`?custom_field_query=["references", "contains", [3, 7]]`
All field types support basic operations including `exact`, `in`, `isnull`,
and `exists`. String, URL, and monetary fields support case-insensitive
substring matching operations including `icontains`, `istartswith`, and
`iendswith`. Integer, float, and date fields support arithmetic comparisons
including `gt` (>), `gte` (>=), `lt` (<), `lte` (<=), and `range`.
Lastly, document link fields support a `contains` operator that behaves
like a "is superset of" check.
### `/api/search/autocomplete/` ### `/api/search/autocomplete/`
@@ -252,8 +326,8 @@ Get auto completions for a partial search term.
Query parameters: Query parameters:
- `term`: The incomplete term. - `term`: The incomplete term.
- `limit`: Amount of results. Defaults to 10. - `limit`: Amount of results. Defaults to 10.
Results returned by the endpoint are ordered by importance of the term Results returned by the endpoint are ordered by importance of the term
in the document index. The first result is the term that has the highest in the document index. The first result is the term that has the highest
@@ -277,17 +351,19 @@ from there.
The endpoint supports the following optional form fields: The endpoint supports the following optional form fields:
- `title`: Specify a title that the consumer should use for the - `title`: Specify a title that the consumer should use for the
document. document.
- `created`: Specify a DateTime where the document was created (e.g. - `created`: Specify a DateTime where the document was created (e.g.
"2016-04-19" or "2016-04-19 06:15:00+02:00"). "2016-04-19" or "2016-04-19 06:15:00+02:00").
- `correspondent`: Specify the ID of a correspondent that the consumer - `correspondent`: Specify the ID of a correspondent that the consumer
should use for the document. should use for the document.
- `document_type`: Similar to correspondent. - `document_type`: Similar to correspondent.
- `storage_path`: Similar to correspondent. - `storage_path`: Similar to correspondent.
- `tags`: Similar to correspondent. Specify this multiple times to - `tags`: Similar to correspondent. Specify this multiple times to
have multiple tags added to the document. have multiple tags added to the document.
- `archive_serial_number`: An optional archive serial number to set. - `archive_serial_number`: An optional archive serial number to set.
- `custom_fields`: An array of custom field ids to assign (with an empty
value) to the document.
The endpoint will immediately return HTTP 200 if the document consumption The endpoint will immediately return HTTP 200 if the document consumption
process was started successfully, with the UUID of the consumption task process was started successfully, with the UUID of the consumption task
@@ -340,7 +416,7 @@ The API supports various bulk-editing operations which are executed asynchronous
### Documents ### Documents
For bulk operations on documents, use the endpoint `/api/bulk_edit/` which accepts For bulk operations on documents, use the endpoint `/api/documents/bulk_edit/` which accepts
a json payload of the format: a json payload of the format:
```json ```json
@@ -353,29 +429,50 @@ a json payload of the format:
The following methods are supported: The following methods are supported:
- `set_correspondent` - `set_correspondent`
- Requires `parameters`: `{ "correspondent": CORRESPONDENT_ID }` - Requires `parameters`: `{ "correspondent": CORRESPONDENT_ID }`
- `set_document_type` - `set_document_type`
- Requires `parameters`: `{ "document_type": DOCUMENT_TYPE_ID }` - Requires `parameters`: `{ "document_type": DOCUMENT_TYPE_ID }`
- `set_storage_path` - `set_storage_path`
- Requires `parameters`: `{ "storage_path": STORAGE_PATH_ID }` - Requires `parameters`: `{ "storage_path": STORAGE_PATH_ID }`
- `add_tag` - `add_tag`
- Requires `parameters`: `{ "tag": TAG_ID }` - Requires `parameters`: `{ "tag": TAG_ID }`
- `remove_tag` - `remove_tag`
- Requires `parameters`: `{ "tag": TAG_ID }` - Requires `parameters`: `{ "tag": TAG_ID }`
- `modify_tags` - `modify_tags`
- Requires `parameters`: `{ "add_tags": [LIST_OF_TAG_IDS] }` and / or `{ "remove_tags": [LIST_OF_TAG_IDS] }` - Requires `parameters`: `{ "add_tags": [LIST_OF_TAG_IDS] }` and / or `{ "remove_tags": [LIST_OF_TAG_IDS] }`
- `delete` - `delete`
- No `parameters` required - No `parameters` required
- `redo_ocr` - `reprocess`
- No `parameters` required - No `parameters` required
- `set_permissions` - `set_permissions`
- Requires `parameters`: - Requires `parameters`:
- `"permissions": PERMISSIONS_OBJ` (see format [above](#permissions)) and / or - `"set_permissions": PERMISSIONS_OBJ` (see format [above](#permissions)) and / or
- `"owner": OWNER_ID or null` - `"owner": OWNER_ID or null`
- `"merge": true or false` (defaults to false) - `"merge": true or false` (defaults to false)
- The `merge` flag determines if the supplied permissions will overwrite all existing permissions (including - The `merge` flag determines if the supplied permissions will overwrite all existing permissions (including
removing them) or be merged with existing permissions. removing them) or be merged with existing permissions.
- `merge`
- No additional `parameters` required.
- The ordering of the merged document is determined by the list of IDs.
- Optional `parameters`:
- `"metadata_document_id": DOC_ID` apply metadata (tags, correspondent, etc.) from this document to the merged document.
- `"delete_originals": true` to delete the original documents. This requires the calling user being the owner of
all documents that are merged.
- `split`
- Requires `parameters`:
- `"pages": [..]` The list should be a list of pages and/or a ranges, separated by commas e.g. `"[1,2-3,4,5-7]"`
- Optional `parameters`:
- `"delete_originals": true` to delete the original document after consumption. This requires the calling user being the owner of
the document.
- The split operation only accepts a single document.
- `rotate`
- Requires `parameters`:
- `"degrees": DEGREES`. Must be an integer i.e. 90, 180, 270
- `delete_pages`
- Requires `parameters`:
- `"pages": [..]` The list should be a list of integers e.g. `"[2,3,4]"`
- The delete_pages operation only accepts a single document.
### Objects ### Objects
@@ -397,16 +494,16 @@ operations, using the endpoint: `/api/bulk_edit_objects/`, which requires a json
The REST API is versioned since Paperless-ngx 1.3.0. The REST API is versioned since Paperless-ngx 1.3.0.
- Versioning ensures that changes to the API don't break older - Versioning ensures that changes to the API don't break older
clients. clients.
- Clients specify the specific version of the API they wish to use - Clients specify the specific version of the API they wish to use
with every request and Paperless will handle the request using the with every request and Paperless will handle the request using the
specified API version. specified API version.
- Even if the underlying data model changes, older API versions will - Even if the underlying data model changes, older API versions will
always serve compatible data. always serve compatible data.
- If no version is specified, Paperless will serve version 1 to ensure - If no version is specified, Paperless will serve version 1 to ensure
compatibility with older clients that do not request a specific API compatibility with older clients that do not request a specific API
version. version.
API versions are specified by submitting an additional HTTP `Accept` API versions are specified by submitting an additional HTTP `Accept`
header with every request: header with every request:
@@ -443,19 +540,19 @@ Initial API version.
#### Version 2 #### Version 2
- Added field `Tag.color`. This read/write string field contains a hex - Added field `Tag.color`. This read/write string field contains a hex
color such as `#a6cee3`. color such as `#a6cee3`.
- Added read-only field `Tag.text_color`. This field contains the text - Added read-only field `Tag.text_color`. This field contains the text
color to use for a specific tag, which is either black or white color to use for a specific tag, which is either black or white
depending on the brightness of `Tag.color`. depending on the brightness of `Tag.color`.
- Removed field `Tag.colour`. - Removed field `Tag.colour`.
#### Version 3 #### Version 3
- Permissions endpoints have been added. - Permissions endpoints have been added.
- The format of the `/api/ui_settings/` has changed. - The format of the `/api/ui_settings/` has changed.
#### Version 4 #### Version 4
- Consumption templates were refactored to workflows and API endpoints - Consumption templates were refactored to workflows and API endpoints
changed as such. changed as such.

View File

@@ -10,7 +10,7 @@
--md-hue: 222; --md-hue: 222;
} }
@media (min-width: 400px) { @media (min-width: 768px) {
.grid-left { .grid-left {
width: 33%; width: 33%;
float: left; float: left;

File diff suppressed because it is too large Load Diff

View File

@@ -8,17 +8,17 @@ common [OCR](#ocr) related settings and some frontend settings. If set, these wi
preference over the settings via environment variables. If not set, the environment setting preference over the settings via environment variables. If not set, the environment setting
or applicable default will be utilized instead. or applicable default will be utilized instead.
- If you run paperless on docker, `paperless.conf` is not used. - If you run paperless on docker, `paperless.conf` is not used.
Rather, configure paperless by copying necessary options to Rather, configure paperless by copying necessary options to
`docker-compose.env`. `docker-compose.env`.
- If you are running paperless on anything else, paperless will search - If you are running paperless on anything else, paperless will search
for the configuration file in these locations and use the first one for the configuration file in these locations and use the first one
it finds: it finds:
- The environment variable `PAPERLESS_CONFIGURATION_PATH` - The environment variable `PAPERLESS_CONFIGURATION_PATH`
- `/path/to/paperless/paperless.conf` - `/path/to/paperless/paperless.conf`
- `/etc/paperless.conf` - `/etc/paperless.conf`
- `/usr/local/etc/paperless.conf` - `/usr/local/etc/paperless.conf`
## Required services ## Required services
@@ -38,7 +38,7 @@ matcher.
`redis://<username>:<password>@<host>:<port>/<DBIndex>` `redis://<username>:<password>@<host>:<port>/<DBIndex>`
[More information on securing your Redis [More information on securing your Redis
Instance](https://redis.io/docs/getting-started/#securing-redis). Instance](https://redis.io/docs/latest/operate/oss_and_stack/management/security).
Defaults to `redis://localhost:6379`. Defaults to `redis://localhost:6379`.
@@ -177,13 +177,13 @@ configure their endpoints, and enable the feature.
#### [`PAPERLESS_TIKA_ENDPOINT=<url>`](#PAPERLESS_TIKA_ENDPOINT) {#PAPERLESS_TIKA_ENDPOINT} #### [`PAPERLESS_TIKA_ENDPOINT=<url>`](#PAPERLESS_TIKA_ENDPOINT) {#PAPERLESS_TIKA_ENDPOINT}
: Set the endpoint URL were Paperless can reach your Tika server. : Set the endpoint URL where Paperless can reach your Tika server.
Defaults to "<http://localhost:9998>". Defaults to "<http://localhost:9998>".
#### [`PAPERLESS_TIKA_GOTENBERG_ENDPOINT=<url>`](#PAPERLESS_TIKA_GOTENBERG_ENDPOINT) {#PAPERLESS_TIKA_GOTENBERG_ENDPOINT} #### [`PAPERLESS_TIKA_GOTENBERG_ENDPOINT=<url>`](#PAPERLESS_TIKA_GOTENBERG_ENDPOINT) {#PAPERLESS_TIKA_GOTENBERG_ENDPOINT}
: Set the endpoint URL were Paperless can reach your Gotenberg server. : Set the endpoint URL where Paperless can reach your Gotenberg server.
Defaults to "<http://localhost:3000>". Defaults to "<http://localhost:3000>".
@@ -202,7 +202,7 @@ and watch out for indentation if editing the YAML file.
#### [`PAPERLESS_CONSUMPTION_DIR=<path>`](#PAPERLESS_CONSUMPTION_DIR) {#PAPERLESS_CONSUMPTION_DIR} #### [`PAPERLESS_CONSUMPTION_DIR=<path>`](#PAPERLESS_CONSUMPTION_DIR) {#PAPERLESS_CONSUMPTION_DIR}
: This where your documents should go to be consumed. Make sure that : This is where your documents should go to be consumed. Make sure that
it exists and that the user running the paperless service can it exists and that the user running the paperless service can
read/write its contents before you start Paperless. read/write its contents before you start Paperless.
@@ -219,10 +219,10 @@ database, classification model, etc).
Defaults to "../data/", relative to the "src" directory. Defaults to "../data/", relative to the "src" directory.
#### [`PAPERLESS_TRASH_DIR=<path>`](#PAPERLESS_TRASH_DIR) {#PAPERLESS_TRASH_DIR} #### [`PAPERLESS_EMPTY_TRASH_DIR=<path>`](#PAPERLESS_EMPTY_TRASH_DIR) {#PAPERLESS_EMPTY_TRASH_DIR}
: Instead of removing deleted documents, they are moved to this : When documents are deleted (e.g. after emptying the trash) the original files will be moved here
directory. instead of being removed from the filesystem. Only the original version is kept.
This must be writeable by the user running paperless. When running This must be writeable by the user running paperless. When running
inside docker, ensure that this path is within a permanent volume inside docker, ensure that this path is within a permanent volume
@@ -230,7 +230,9 @@ directory.
Note that the directory must exist prior to using this setting. Note that the directory must exist prior to using this setting.
Defaults to empty (i.e. really delete documents). Defaults to empty (i.e. really delete files).
This setting was previously named PAPERLESS_TRASH_DIR.
#### [`PAPERLESS_MEDIA_ROOT=<path>`](#PAPERLESS_MEDIA_ROOT) {#PAPERLESS_MEDIA_ROOT} #### [`PAPERLESS_MEDIA_ROOT=<path>`](#PAPERLESS_MEDIA_ROOT) {#PAPERLESS_MEDIA_ROOT}
@@ -288,6 +290,12 @@ this folder is no longer needed and can be removed manually.
Defaults to `/usr/share/nltk_data` Defaults to `/usr/share/nltk_data`
#### [`PAPERLESS_MODEL_FILE=<path>`](#PAPERLESS_MODEL_FILE) {#PAPERLESS_MODEL_FILE}
: This is where paperless will store the classification model.
Defaults to `PAPERLESS_DATA_DIR/classification_model.pickle`.
## Logging ## Logging
#### [`PAPERLESS_LOGROTATE_MAX_SIZE=<num>`](#PAPERLESS_LOGROTATE_MAX_SIZE) {#PAPERLESS_LOGROTATE_MAX_SIZE} #### [`PAPERLESS_LOGROTATE_MAX_SIZE=<num>`](#PAPERLESS_LOGROTATE_MAX_SIZE) {#PAPERLESS_LOGROTATE_MAX_SIZE}
@@ -491,8 +499,9 @@ followed by the normalized actual header name.
#### [`PAPERLESS_LOGOUT_REDIRECT_URL=<str>`](#PAPERLESS_LOGOUT_REDIRECT_URL) {#PAPERLESS_LOGOUT_REDIRECT_URL} #### [`PAPERLESS_LOGOUT_REDIRECT_URL=<str>`](#PAPERLESS_LOGOUT_REDIRECT_URL) {#PAPERLESS_LOGOUT_REDIRECT_URL}
: URL to redirect the user to after a logout. This can be used : URL to redirect the user to after a logout. This can be used
together with PAPERLESS_ENABLE_HTTP_REMOTE_USER to together with PAPERLESS_ENABLE_HTTP_REMOTE_USER and SSO to
redirect the user back to the SSO application's logout page. redirect the user back to the SSO application's logout page to
complete the logout process.
Defaults to None, which disables this feature. Defaults to None, which disables this feature.
@@ -585,10 +594,32 @@ system. See the corresponding
#### [`PAPERLESS_DISABLE_REGULAR_LOGIN=<bool>`](#PAPERLESS_DISABLE_REGULAR_LOGIN) {#PAPERLESS_DISABLE_REGULAR_LOGIN} #### [`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). : 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 nor logging in with local credentials via the API. To prevent access to the Django admin, 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).
You can optionally also automatically redirect users to the SSO login with [PAPERLESS_REDIRECT_LOGIN_TO_SSO](#PAPERLESS_REDIRECT_LOGIN_TO_SSO)
Defaults to False Defaults to False
#### [`PAPERLESS_REDIRECT_LOGIN_TO_SSO=<bool>`](#PAPERLESS_REDIRECT_LOGIN_TO_SSO) {#PAPERLESS_REDIRECT_LOGIN_TO_SSO}
: When this setting is enabled users will automatically be redirected (using javascript) to the first SSO provider login. You may still want to disable the frontend login form for clarity.
Defaults to False
#### [`PAPERLESS_ACCOUNT_SESSION_REMEMBER=<bool>`](#PAPERLESS_ACCOUNT_SESSION_REMEMBER) {#PAPERLESS_ACCOUNT_SESSION_REMEMBER}
: If false, sessions will expire at browser close, if true will use `PAPERLESS_SESSION_COOKIE_AGE` for expiration. See the corresponding
[django-allauth documentation](https://docs.allauth.org/en/latest/account/configuration.html)
Defaults to True
#### [`PAPERLESS_SESSION_COOKIE_AGE=<int>`](#PAPERLESS_SESSION_COOKIE_AGE) {#PAPERLESS_SESSION_COOKIE_AGE}
: Login session cookie expiration. Applies if `PAPERLESS_ACCOUNT_SESSION_REMEMBER` is enabled. See the corresponding
[django documentation](https://docs.djangoproject.com/en/5.1/ref/settings/#std-setting-SESSION_COOKIE_AGE)
Defaults to 1209600 (2 weeks)
## OCR settings {#ocr} ## OCR settings {#ocr}
Paperless uses [OCRmyPDF](https://ocrmypdf.readthedocs.io/en/latest/) Paperless uses [OCRmyPDF](https://ocrmypdf.readthedocs.io/en/latest/)
@@ -610,6 +641,8 @@ parsing documents.
Keep in mind that Tesseract uses much more CPU time with multiple Keep in mind that Tesseract uses much more CPU time with multiple
languages enabled. languages enabled.
If you are including languages that are not installed by default, you will need to also set [`PAPERLESS_OCR_LANGUAGES`](configuration.md#PAPERLESS_OCR_LANGUAGES) for docker deployments or install the tesseract language packages manually for bare metal installations.
Defaults to "eng". Defaults to "eng".
!!! note !!! note
@@ -1080,7 +1113,7 @@ document text will be checked as normal.
: Paperless searches an entire document for dates. The first date : Paperless searches an entire document for dates. The first date
found will be used as the initial value for the created date. When found will be used as the initial value for the created date. When
this variable is greater than 0 (or left to it's default value), this variable is greater than 0 (or left to its default value),
paperless will also suggest other dates found in the document, up to paperless will also suggest other dates found in the document, up to
a maximum of this setting. Note that duplicates will be removed, a maximum of this setting. Note that duplicates will be removed,
which can result in fewer dates displayed in the frontend than this which can result in fewer dates displayed in the frontend than this
@@ -1105,11 +1138,11 @@ This font can be changed here.
#### [`PAPERLESS_IGNORE_DATES=<string>`](#PAPERLESS_IGNORE_DATES) {#PAPERLESS_IGNORE_DATES} #### [`PAPERLESS_IGNORE_DATES=<string>`](#PAPERLESS_IGNORE_DATES) {#PAPERLESS_IGNORE_DATES}
: Paperless parses a documents creation date from filename and file : Paperless parses a document's creation date from filename and file
content. You may specify a comma separated list of dates that should content. You may specify a comma separated list of dates that should
be ignored during this process. This is useful for special dates be ignored during this process. This is useful for special dates
(like date of birth) that appear in documents regularly but are very (like date of birth) that appear in documents regularly but are very
unlikely to be the documents creation date. unlikely to be the document's creation date.
The date is parsed using the order specified in PAPERLESS_DATE_ORDER The date is parsed using the order specified in PAPERLESS_DATE_ORDER
@@ -1125,6 +1158,12 @@ within your documents.
second, and year last order. Characters D, M, or Y can be shuffled second, and year last order. Characters D, M, or Y can be shuffled
to meet the required order. to meet the required order.
#### [`PAPERLESS_ENABLE_GPG_DECRYPTOR=<bool>`](#PAPERLESS_ENABLE_GPG_DECRYPTOR) {#PAPERLESS_ENABLE_GPG_DECRYPTOR}
: Enable or disable the GPG decryptor for encrypted emails. See [GPG Decryptor](advanced_usage.md#gpg-decryptor) for more information.
Defaults to false.
### Polling {#polling} ### Polling {#polling}
#### [`PAPERLESS_CONSUMER_POLLING=<num>`](#PAPERLESS_CONSUMER_POLLING) {#PAPERLESS_CONSUMER_POLLING} #### [`PAPERLESS_CONSUMER_POLLING=<num>`](#PAPERLESS_CONSUMER_POLLING) {#PAPERLESS_CONSUMER_POLLING}
@@ -1168,6 +1207,48 @@ consumers working on the same file. Configure this to prevent that.
Defaults to 0.5 seconds. Defaults to 0.5 seconds.
## Incoming Mail {#incoming_mail}
### Email OAuth {#email_oauth}
#### [`PAPERLESS_OAUTH_CALLBACK_BASE_URL=<str>`](#PAPERLESS_OAUTH_CALLBACK_BASE_URL) {#PAPERLESS_OAUTH_CALLBACK_BASE_URL}
: The base URL for the OAuth callback. This is used to construct the full URL for the OAuth callback. This should be the URL that the Paperless instance is accessible at. If not set, defaults to the `PAPERLESS_URL` setting. At least one of these settings must be set to enable OAuth Email setup.
Defaults to none (thus will use [PAPERLESS_URL](#PAPERLESS_URL)).
#### [`PAPERLESS_GMAIL_OAUTH_CLIENT_ID=<str>`](#PAPERLESS_GMAIL_OAUTH_CLIENT_ID) {#PAPERLESS_GMAIL_OAUTH_CLIENT_ID}
: The OAuth client ID for Gmail. This is required for Gmail OAuth Email setup. See [OAuth Email Setup](usage.md#oauth-email-setup) for more information.
Defaults to none.
#### [`PAPERLESS_GMAIL_OAUTH_CLIENT_SECRET=<str>`](#PAPERLESS_GMAIL_OAUTH_CLIENT_SECRET) {#PAPERLESS_GMAIL_OAUTH_CLIENT_SECRET}
: The OAuth client secret for Gmail. This is required for Gmail OAuth Email setup. See [OAuth Email Setup](usage.md#oauth-email-setup) for more information.
Defaults to none.
#### [`PAPERLESS_OUTLOOK_OAUTH_CLIENT_ID=<str>`](#PAPERLESS_OUTLOOK_OAUTH_CLIENT_ID) {#PAPERLESS_OUTLOOK_OAUTH_CLIENT_ID}
: The OAuth client ID for Outlook. This is required for Outlook OAuth Email setup. See [OAuth Email Setup](usage.md#oauth-email-setup) for more information.
Defaults to none.
#### [`PAPERLESS_OUTLOOK_OAUTH_CLIENT_SECRET=<str>`](#PAPERLESS_OUTLOOK_OAUTH_CLIENT_SECRET) {#PAPERLESS_OUTLOOK_OAUTH_CLIENT_SECRET}
: The OAuth client secret for Outlook. This is required for Outlook OAuth Email setup. See [OAuth Email Setup](usage.md#oauth-email-setup) for more information.
Defaults to none.
### Encrypted Emails {#encrypted_emails}
#### [`PAPERLESS_EMAIL_GNUPG_HOME=<str>`](#PAPERLESS_EMAIL_GNUPG_HOME) {#PAPERLESS_EMAIL_GNUPG_HOME}
: Optional, sets the `GNUPG_HOME` path to use with GPG decryptor for encrypted emails. See [GPG Decryptor](advanced_usage.md#gpg-decryptor) for more information. If not set, defaults to the default `GNUPG_HOME` path.
Defaults to <not set>.
## Barcodes {#barcodes} ## Barcodes {#barcodes}
#### [`PAPERLESS_CONSUMER_ENABLE_BARCODES=<bool>`](#PAPERLESS_CONSUMER_ENABLE_BARCODES) {#PAPERLESS_CONSUMER_ENABLE_BARCODES} #### [`PAPERLESS_CONSUMER_ENABLE_BARCODES=<bool>`](#PAPERLESS_CONSUMER_ENABLE_BARCODES) {#PAPERLESS_CONSUMER_ENABLE_BARCODES}
@@ -1206,6 +1287,12 @@ change this.
Defaults to "PATCHT" Defaults to "PATCHT"
#### [`PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES=<bool>`](#PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES) {#PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES}
: If set to true, all pages that are split by a barcode (such as PATCHT) will be kept.
Defaults to false.
#### [`PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE=<bool>`](#PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE) {#PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE} #### [`PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE=<bool>`](#PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE) {#PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE}
: Enables the detection of barcodes in the scanned document and : Enables the detection of barcodes in the scanned document and
@@ -1236,7 +1323,7 @@ barcode.
: Defines the upscale factor used in barcode detection. : Defines the upscale factor used in barcode detection.
Improves the detection of small barcodes, i.e. with a value of 1.5 by Improves the detection of small barcodes, i.e. with a value of 1.5 by
upscaling the document beforce the detection process. Upscaling will upscaling the document before the detection process. Upscaling will
only take place if value is bigger than 1.0. Otherwise upscaling will only take place if value is bigger than 1.0. Otherwise upscaling will
not be performed to save resources. Try using in combination with not be performed to save resources. Try using in combination with
PAPERLESS_CONSUMER_BARCODE_DPI set to a value higher than default. PAPERLESS_CONSUMER_BARCODE_DPI set to a value higher than default.
@@ -1253,6 +1340,15 @@ combination with PAPERLESS_CONSUMER_BARCODE_UPSCALE bigger than 1.0.
Defaults to "300" Defaults to "300"
#### [`PAPERLESS_CONSUMER_BARCODE_MAX_PAGES=<int>`](#PAPERLESS_CONSUMER_BARCODE_MAX_PAGES) {#PAPERLESS_CONSUMER_BARCODE_MAX_PAGES}
: Because barcode detection is a computationally-intensive operation, this setting
limits the detection of barcodes to a number of first pages. If your scanner has
a limit for the number of pages that can be scanned it would be sensible to set this
as the limit here.
Defaults to "0", allowing all pages to be checked for barcodes.
#### [`PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE=<bool>`](#PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE) {#PAPERLESS_CONSUMER_ENABLE_TAG_BARCODE} #### [`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 : Enables the detection of barcodes in the scanned document and
@@ -1270,7 +1366,7 @@ assigns or creates tags if a properly formatted barcode is detected.
: Defines a dictionary of filter regex and substitute expressions. : Defines a dictionary of filter regex and substitute expressions.
Syntax: {"<regex>": "<substitute>" [,...]]} Syntax: `{"<regex>": "<substitute>" [,...]]}`
A barcode is considered for tagging if the barcode text matches A barcode is considered for tagging if the barcode text matches
at least one of the provided <regex> pattern. at least one of the provided <regex> pattern.
@@ -1282,20 +1378,20 @@ assigns or creates tags if a properly formatted barcode is detected.
Defaults to: Defaults to:
{"TAG:(.*)": "\\g<1>"} which defines `{"TAG:(.*)": "\\g<1>"}` which defines
- a regex TAG:(.*) which includes barcodes beginning with TAG: - a regex TAG:(.*) which includes barcodes beginning with TAG:
followed by any text that gets stored into match group #1 and followed by any text that gets stored into match group #1 and
- a substitute \\g<1> that replaces the original barcode text - a substitute `\\g<1>` that replaces the original barcode text
by the content in match group #1. by the content in match group #1.
Consequently, the tag is the barcode text without its TAG: prefix. Consequently, the tag is the barcode text without its TAG: prefix.
More examples: More examples:
{"ASN12.*": "JOHN", "ASN13.*": "SMITH"} for example maps `{"ASN12.*": "JOHN", "ASN13.*": "SMITH"}` for example maps
- ASN12nnnn barcodes to the tag JOHN and - ASN12nnnn barcodes to the tag JOHN and
- ASN13nnnn barcodes to the tag SMITH. - ASN13nnnn barcodes to the tag SMITH.
{"T-J": "JOHN", "T-S": "SMITH", "T-D": "DOE"} directly maps `{"T-J": "JOHN", "T-S": "SMITH", "T-D": "DOE"}` directly maps
- T-J barcodes to the tag JOHN, - T-J barcodes to the tag JOHN,
- T-S barcodes to the tag SMITH and - T-S barcodes to the tag SMITH and
- T-D barcodes to the tag DOE. - T-D barcodes to the tag DOE.
@@ -1306,11 +1402,9 @@ assigns or creates tags if a properly formatted barcode is detected.
#### [`PAPERLESS_AUDIT_LOG_ENABLED=<bool>`](#PAPERLESS_AUDIT_LOG_ENABLED) {#PAPERLESS_AUDIT_LOG_ENABLED} #### [`PAPERLESS_AUDIT_LOG_ENABLED=<bool>`](#PAPERLESS_AUDIT_LOG_ENABLED) {#PAPERLESS_AUDIT_LOG_ENABLED}
: Enables an audit trail for documents, document types, correspondents, and tags. Log entries can be viewed in the Django backend only. : Enables the audit trail for documents, document types, correspondents, and tags.
!!! warning Defaults to true.
Once enabled cannot be disabled
## Collate Double-Sided Documents {#collate} ## Collate Double-Sided Documents {#collate}
@@ -1350,6 +1444,20 @@ processing. This only has an effect if
Defaults to false. Defaults to false.
## Trash
#### [`PAPERLESS_EMPTY_TRASH_DELAY=<num>`](#PAPERLESS_EMPTY_TRASH_DELAY) {#PAPERLESS_EMPTY_TRASH_DELAY}
: Sets how long in days documents remain in the 'trash' before they are permanently deleted.
Defaults to 30 days, minimum of 1 day.
#### [`PAPERLESS_EMPTY_TRASH_TASK_CRON=<cron expression>`](#PAPERLESS_EMPTY_TRASH_TASK_CRON) {#PAPERLESS_EMPTY_TRASH_TASK_CRON}
: Configures the schedule to empty the trash of expired deleted documents.
Defaults to `0 1 * * *`, once per day.
## Binaries ## Binaries
There are a few external software packages that Paperless expects to There are a few external software packages that Paperless expects to
@@ -1449,7 +1557,7 @@ specified as "chi-tra".
PAPERLESS_OCR_LANGUAGES=tur ces chi-tra PAPERLESS_OCR_LANGUAGES=tur ces chi-tra
``` ```
Make sure it's a space separated list when using several values. Make sure it's a space-separated list when using several values.
To actually use these languages, also set the default OCR language To actually use these languages, also set the default OCR language
of paperless: of paperless:
@@ -1480,7 +1588,7 @@ started by the container.
## Frontend Settings ## Frontend Settings
#### [`PAPERLESS_APP_TITLE=<bool>`](#PAPERLESS_APP_TITLE) {#PAPERLESS_APP_TITLE} #### [`PAPERLESS_APP_TITLE=<str>`](#PAPERLESS_APP_TITLE) {#PAPERLESS_APP_TITLE}
: If set, overrides the default name "Paperless-ngx" : If set, overrides the default name "Paperless-ngx"

View File

@@ -6,23 +6,23 @@ on Paperless-ngx.
Check out the source from GitHub. The repository is organized in the Check out the source from GitHub. The repository is organized in the
following way: following way:
- `main` always represents the latest release and will only see - `main` always represents the latest release and will only see
changes when a new release is made. changes when a new release is made.
- `dev` contains the code that will be in the next release. - `dev` contains the code that will be in the next release.
- `feature-X` contains bigger changes that will be in some release, but - `feature-X` contains bigger changes that will be in some release, but
not necessarily the next one. not necessarily the next one.
When making functional changes to Paperless-ngx, _always_ make your changes When making functional changes to Paperless-ngx, _always_ make your changes
on the `dev` branch. on the `dev` branch.
Apart from that, the folder structure is as follows: Apart from that, the folder structure is as follows:
- `docs/` - Documentation. - `docs/` - Documentation.
- `src-ui/` - Code of the front end. - `src-ui/` - Code of the front end.
- `src/` - Code of the back end. - `src/` - Code of the back end.
- `scripts/` - Various scripts that help with different parts of - `scripts/` - Various scripts that help with different parts of
development. development.
- `docker/` - Files required to build the docker image. - `docker/` - Files required to build the docker image.
## Contributing to Paperless-ngx ## Contributing to Paperless-ngx
@@ -47,7 +47,7 @@ early on.
Once installed, hooks will run when you commit. If the formatting isn't Once installed, hooks will run when you commit. If the formatting isn't
quite right or a linter catches something, the commit will be rejected. quite right or a linter catches something, the commit will be rejected.
You'll need to look at the output and fix the issue. Some hooks, such You'll need to look at the output and fix the issue. Some hooks, such
as the Python formatting tool `black`, will format failing as the Python linting and formatting tool `ruff`, will format failing
files, so all you need to do is `git add` those files again files, so all you need to do is `git add` those files again
and retry your commit. and retry your commit.
@@ -81,10 +81,6 @@ first-time setup.
!!! note !!! note
Using a virtual environment is highly recommended. You can spawn one via `pipenv shell`. Using a virtual environment is highly recommended. You can spawn one via `pipenv shell`.
Make sure you're using Python 3.10.x or lower. Otherwise you might
get issues with building dependencies. You can use
[pyenv](https://github.com/pyenv/pyenv) to install a specific
Python version.
5. Install pre-commit hooks: 5. Install pre-commit hooks:
@@ -103,17 +99,17 @@ first-time setup.
7. You can now either ... 7. You can now either ...
- install redis or - install redis or
- use the included `scripts/start_services.sh` to use docker to fire - use the included `scripts/start_services.sh` to use docker to fire
up a redis instance (and some other services such as tika, up a redis instance (and some other services such as tika,
gotenberg and a database server) or gotenberg and a database server) or
- spin up a bare redis container - spin up a bare redis container
``` ```
$ docker run -d -p 6379:6379 --restart unless-stopped redis:latest $ docker run -d -p 6379:6379 --restart unless-stopped redis:latest
``` ```
8. Continue with either back-end or front-end development or both :-). 8. Continue with either back-end or front-end development or both :-).
@@ -126,9 +122,9 @@ work well for development, but you can use whatever you want.
Configure the IDE to use the `src/`-folder as the base source folder. Configure the IDE to use the `src/`-folder as the base source folder.
Configure the following launch configurations in your IDE: Configure the following launch configurations in your IDE:
- `python3 manage.py runserver` - `python3 manage.py runserver`
- `python3 manage.py document_consumer` - `python3 manage.py document_consumer`
- `celery --app paperless worker -l DEBUG` (or any other log level) - `celery --app paperless worker -l DEBUG` (or any other log level)
To start them all: To start them all:
@@ -154,11 +150,11 @@ $ ng build --configuration production
### Testing ### Testing
- Run `pytest` in the `src/` directory to execute all tests. This also - Run `pytest` in the `src/` directory to execute all tests. This also
generates a HTML coverage report. When runnings test, `paperless.conf` generates a HTML coverage report. When runnings test, `paperless.conf`
is loaded as well. However, the tests rely on the default is loaded as well. However, the tests rely on the default
configuration. This is not ideal. But for now, make sure no settings configuration. This is not ideal. But for now, make sure no settings
except for DEBUG are overridden when testing. except for DEBUG are overridden when testing.
!!! note !!! note
@@ -249,14 +245,14 @@ these parts have to be translated separately.
### Front end localization ### Front end localization
- The AngularJS front end does localization according to the [Angular - The AngularJS front end does localization according to the [Angular
documentation](https://angular.io/guide/i18n). documentation](https://angular.io/guide/i18n).
- The source language of the project is "en_US". - The source language of the project is "en_US".
- The source strings end up in the file `src-ui/messages.xlf`. - The source strings end up in the file `src-ui/messages.xlf`.
- The translated strings need to be placed in the - The translated strings need to be placed in the
`src-ui/src/locale/` folder. `src-ui/src/locale/` folder.
- In order to extract added or changed strings from the source files, - In order to extract added or changed strings from the source files,
call `ng extract-i18n`. call `ng extract-i18n`.
Adding new languages requires adding the translated files in the Adding new languages requires adding the translated files in the
`src-ui/src/locale/` folder and adjusting a couple files. `src-ui/src/locale/` folder and adjusting a couple files.
@@ -302,18 +298,18 @@ A majority of the strings that appear in the back end appear only when
the admin is used. However, some of these are still shown on the front the admin is used. However, some of these are still shown on the front
end (such as error messages). end (such as error messages).
- The django application does localization according to the [Django - The django application does localization according to the [Django
documentation](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/). documentation](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/).
- The source language of the project is "en_US". - The source language of the project is "en_US".
- Localization files end up in the folder `src/locale/`. - Localization files end up in the folder `src/locale/`.
- In order to extract strings from the application, call - In order to extract strings from the application, call
`python3 manage.py makemessages -l en_US`. This is important after `python3 manage.py makemessages -l en_US`. This is important after
making changes to translatable strings. making changes to translatable strings.
- The message files need to be compiled for them to show up in the - The message files need to be compiled for them to show up in the
application. Call `python3 manage.py compilemessages` to do this. application. Call `python3 manage.py compilemessages` to do this.
The generated files don't get committed into git, since these are The generated files don't get committed into git, since these are
derived artifacts. The build pipeline takes care of executing this derived artifacts. The build pipeline takes care of executing this
command. command.
Adding new languages requires adding the translated files in the Adding new languages requires adding the translated files in the
`src/locale/`-folder and adjusting the file `src/locale/`-folder and adjusting the file
@@ -364,10 +360,10 @@ If you want to build the documentation locally, this is how you do it:
The docker image is primarily built by the GitHub actions workflow, but The docker image is primarily built by the GitHub actions workflow, but
it can be faster when developing to build and tag an image locally. it can be faster when developing to build and tag an image locally.
Building the image works as with any image: Make sure you have the `docker-buildx` package installed. Building the image works as with any image:
``` ```
docker build --file Dockerfile --tag paperless:local --progress simple . docker build --file Dockerfile --tag paperless:local .
``` ```
## Extending Paperless-ngx ## Extending Paperless-ngx
@@ -382,10 +378,10 @@ base code.
Paperless-ngx uses parsers to add documents. A parser is Paperless-ngx uses parsers to add documents. A parser is
responsible for: responsible for:
- Retrieving the content from the original - Retrieving the content from the original
- Creating a thumbnail - Creating a thumbnail
- _optional:_ Retrieving a created date from the original - _optional:_ Retrieving a created date from the original
- _optional:_ Creating an archived document from the original - _optional:_ Creating an archived document from the original
Custom parsers can be added to Paperless-ngx to support more file types. In Custom parsers can be added to Paperless-ngx to support more file types. In
order to do that, you need to write the parser itself and announce its order to do that, you need to write the parser itself and announce its
@@ -443,14 +439,14 @@ def myparser_consumer_declaration(sender, **kwargs):
} }
``` ```
- `parser` is a reference to a class that extends `DocumentParser`. - `parser` is a reference to a class that extends `DocumentParser`.
- `weight` is used whenever two or more parsers are able to parse a - `weight` is used whenever two or more parsers are able to parse a
file: The parser with the higher weight wins. This can be used to file: The parser with the higher weight wins. This can be used to
override the parsers provided by Paperless-ngx. override the parsers provided by Paperless-ngx.
- `mime_types` is a dictionary. The keys are the mime types your - `mime_types` is a dictionary. The keys are the mime types your
parser supports and the value is the default file extension that parser supports and the value is the default file extension that
Paperless-ngx should use when storing files and serving them for Paperless-ngx should use when storing files and serving them for
download. We could guess that from the file extensions, but some download. We could guess that from the file extensions, but some
mime types have many extensions associated with them and the Python mime types have many extensions associated with them and the Python
methods responsible for guessing the extension do not always return methods responsible for guessing the extension do not always return
the same value. the same value.

View File

@@ -40,28 +40,28 @@ system. On Linux, chances are high that this location is
You can always drag those files out of that folder to use them You can always drag those files out of that folder to use them
elsewhere. Here are a couple notes about that. elsewhere. Here are a couple notes about that.
- Paperless-ngx never modifies your original documents. It keeps - Paperless-ngx never modifies your original documents. It keeps
checksums of all documents and uses a scheduled sanity checker to checksums of all documents and uses a scheduled sanity checker to
check that they remain the same. check that they remain the same.
- By default, paperless uses the internal ID of each document as its - By default, paperless uses the internal ID of each document as its
filename. This might not be very convenient for export. However, you filename. This might not be very convenient for export. However, you
can adjust the way files are stored in paperless by can adjust the way files are stored in paperless by
[configuring the filename format](advanced_usage.md#file-name-handling). [configuring the filename format](advanced_usage.md#file-name-handling).
- [The exporter](administration.md#exporter) is - [The exporter](administration.md#exporter) is
another easy way to get your files out of paperless with reasonable another easy way to get your files out of paperless with reasonable
file names. file names.
## _What file types does paperless-ngx support?_ ## _What file types does paperless-ngx support?_
**A:** Currently, the following files are supported: **A:** Currently, the following files are supported:
- PDF documents, PNG images, JPEG images, TIFF images, GIF images and - PDF documents, PNG images, JPEG images, TIFF images, GIF images and
WebP images are processed with OCR and converted into PDF documents. WebP images are processed with OCR and converted into PDF documents.
- Plain text documents are supported as well and are added verbatim to - Plain text documents are supported as well and are added verbatim to
paperless. paperless.
- With the optional Tika integration enabled (see [Tika configuration](https://docs.paperless-ngx.com/configuration#tika)), - With the optional Tika integration enabled (see [Tika configuration](https://docs.paperless-ngx.com/configuration#tika)),
Paperless also supports various Office documents (.docx, .doc, odt, Paperless also supports various Office documents (.docx, .doc, odt,
.ppt, .pptx, .odp, .xls, .xlsx, .ods). .ppt, .pptx, .odp, .xls, .xlsx, .ods).
Paperless-ngx determines the type of a file by inspecting its content. Paperless-ngx determines the type of a file by inspecting its content.
The file extensions do not matter. The file extensions do not matter.
@@ -127,8 +127,16 @@ ASGI-enabled web server as well that processes WebSocket connections,
and configure Apache to redirect WebSocket connections to this server. and configure Apache to redirect WebSocket connections to this server.
Multiple options for ASGI servers exist: Multiple options for ASGI servers exist:
- `gunicorn` with `uvicorn` as the worker implementation (the default - `gunicorn` with `uvicorn` as the worker implementation (the default
of paperless) of paperless)
- `daphne` as a standalone server, which is the reference - `daphne` as a standalone server, which is the reference
implementation for ASGI. implementation for ASGI.
- `uvicorn` as a standalone server - `uvicorn` as a standalone server
## _What about the Redis licensing change and using one of the open source forks_?
Currently (October 2024), forks of Redis such as Valkey or Redirect are not officially supported by our upstream
libraries, so using one of these to replace Redis is not officially supported.
However, they do claim to be compatible with the Redis protocol and will likely work, but we will
not be updating from using Redis as the broker officially just yet.

View File

@@ -2,10 +2,11 @@
You can go multiple routes to setup and run Paperless: You can go multiple routes to setup and run Paperless:
- [Use the easy install docker script](#docker_script) - [Use the easy install docker script](#docker_script)
- [Pull the image from Docker Hub](#docker_hub) - [Pull the image from Docker Hub](#docker_hub)
- [Build the Docker image yourself](#docker_build) - [Build the Docker image yourself](#docker_build)
- [Install Paperless directly on your system manually (bare metal)](#bare_metal) - [Install Paperless directly on your system manually (bare metal)](#bare_metal)
- A user-maintained list of commercial hosting providers can be found [in the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Related-Projects)
The Docker routes are quick & easy. These are the recommended routes. The Docker routes are quick & easy. These are the recommended routes.
This configures all the stuff from the above automatically so that it This configures all the stuff from the above automatically so that it
@@ -104,14 +105,14 @@ steps described in [Docker setup](#docker_hub) automatically.
```yaml ```yaml
ports: ports:
- 8000:8000 - 8000:8000
``` ```
Replace the part BEFORE the colon with a port of your choice: Replace the part BEFORE the colon with a port of your choice:
```yaml ```yaml
ports: ports:
- 8010:8000 - 8010:8000
``` ```
Don't change the part after the colon or edit other lines that Don't change the part after the colon or edit other lines that
@@ -128,11 +129,11 @@ steps described in [Docker setup](#docker_hub) automatically.
If you want to run Paperless as a rootless container, you will need If you want to run Paperless as a rootless container, you will need
to do the following in your `docker-compose.yml`: to do the following in your `docker-compose.yml`:
- set the `user` running the container to map to the `paperless` - set the `user` running the container to map to the `paperless`
user in the container. This value (`user_id` below), should be user in the container. This value (`user_id` below), should be
the same id that `USERMAP_UID` and `USERMAP_GID` are set to in the same id that `USERMAP_UID` and `USERMAP_GID` are set to in
the next step. See `USERMAP_UID` and `USERMAP_GID` the next step. See `USERMAP_UID` and `USERMAP_GID`
[here](configuration.md#docker). [here](configuration.md#docker).
Your entry for Paperless should contain something like: Your entry for Paperless should contain something like:
@@ -221,7 +222,7 @@ steps described in [Docker setup](#docker_hub) automatically.
```yaml ```yaml
webserver: webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:latest image: ghcr.io/paperless-ngx/paperless-ngx:latest
``` ```
and replace it with a line that instructs Docker Compose to build and replace it with a line that instructs Docker Compose to build
@@ -229,8 +230,8 @@ steps described in [Docker setup](#docker_hub) automatically.
```yaml ```yaml
webserver: webserver:
build: build:
context: . context: .
``` ```
4. Follow steps 3 to 8 of [Docker Setup](#docker_hub). When asked to run 4. Follow steps 3 to 8 of [Docker Setup](#docker_hub). When asked to run
@@ -249,44 +250,48 @@ a minimal installation of Debian/Buster, which is the current stable
release at the time of writing. Windows is not and will never be release at the time of writing. Windows is not and will never be
supported. supported.
Paperless requires Python 3. At this time, 3.10 - 3.12 are tested versions.
Newer versions may work, but some dependencies may not fully support newer versions.
Support for older Python versions may be dropped as they reach end of life or as newer versions
are released, dependency support is confirmed, etc.
1. Install dependencies. Paperless requires the following packages. 1. Install dependencies. Paperless requires the following packages.
- `python3` - 3.9 - 3.11 are supported - `python3`
- `python3-pip` - `python3-pip`
- `python3-dev` - `python3-dev`
- `default-libmysqlclient-dev` for MariaDB - `default-libmysqlclient-dev` for MariaDB
- `pkg-config` for mysqlclient (python dependency) - `pkg-config` for mysqlclient (python dependency)
- `fonts-liberation` for generating thumbnails for plain text - `fonts-liberation` for generating thumbnails for plain text
files files
- `imagemagick` >= 6 for PDF conversion - `imagemagick` >= 6 for PDF conversion
- `gnupg` for handling encrypted documents - `gnupg` for handling encrypted documents
- `libpq-dev` for PostgreSQL - `libpq-dev` for PostgreSQL
- `libmagic-dev` for mime type detection - `libmagic-dev` for mime type detection
- `mariadb-client` for MariaDB compile time - `mariadb-client` for MariaDB compile time
- `mime-support` for mime type detection - `libzbar0` for barcode detection
- `libzbar0` for barcode detection - `poppler-utils` for barcode detection
- `poppler-utils` for barcode detection
Use this list for your preferred package management: Use this list for your preferred package management:
``` ```
python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev default-libmysqlclient-dev pkg-config libmagic-dev mime-support libzbar0 poppler-utils python3 python3-pip python3-dev imagemagick fonts-liberation gnupg libpq-dev default-libmysqlclient-dev pkg-config libmagic-dev libzbar0 poppler-utils
``` ```
These dependencies are required for OCRmyPDF, which is used for text These dependencies are required for OCRmyPDF, which is used for text
recognition. recognition.
- `unpaper` - `unpaper`
- `ghostscript` - `ghostscript`
- `icc-profiles-free` - `icc-profiles-free`
- `qpdf` - `qpdf`
- `liblept5` - `liblept5`
- `libxml2` - `libxml2`
- `pngquant` (suggested for certain PDF image optimizations) - `pngquant` (suggested for certain PDF image optimizations)
- `zlib1g` - `zlib1g`
- `tesseract-ocr` >= 4.0.0 for OCR - `tesseract-ocr` >= 4.0.0 for OCR
- `tesseract-ocr` language packs (`tesseract-ocr-eng`, - `tesseract-ocr` language packs (`tesseract-ocr-eng`,
`tesseract-ocr-deu`, etc) `tesseract-ocr-deu`, etc)
Use this list for your preferred package management: Use this list for your preferred package management:
@@ -296,11 +301,21 @@ supported.
On Raspberry Pi, these libraries are required as well: On Raspberry Pi, these libraries are required as well:
- `libatlas-base-dev` - `libatlas-base-dev`
- `libxslt1-dev` - `libxslt1-dev`
- `mime-support`
You will also need `build-essential`, `python3-setuptools` and You will also need these for installing some of the python dependencies:
`python3-wheel` for installing some of the python dependencies.
- `build-essential`
- `python3-setuptools`
- `python3-wheel`
Use this list for your preferred package management:
```
build-essential python3-setuptools python3-wheel
```
2. Install `redis` >= 6.0 and configure it to start automatically. 2. Install `redis` >= 6.0 and configure it to start automatically.
@@ -346,33 +361,33 @@ supported.
needs. Required settings for getting needs. Required settings for getting
paperless running are: paperless running are:
- [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS) should point to your redis server, such as - [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS) should point to your redis server, such as
<redis://localhost:6379>. <redis://localhost:6379>.
- [`PAPERLESS_DBENGINE`](configuration.md#PAPERLESS_DBENGINE) optional, and should be one of `postgres`, - [`PAPERLESS_DBENGINE`](configuration.md#PAPERLESS_DBENGINE) optional, and should be one of `postgres`,
`mariadb`, or `sqlite` `mariadb`, or `sqlite`
- [`PAPERLESS_DBHOST`](configuration.md#PAPERLESS_DBHOST) should be the hostname on which your - [`PAPERLESS_DBHOST`](configuration.md#PAPERLESS_DBHOST) should be the hostname on which your
PostgreSQL server is running. Do not configure this to use PostgreSQL server is running. Do not configure this to use
SQLite instead. Also configure port, database name, user and SQLite instead. Also configure port, database name, user and
password as necessary. password as necessary.
- [`PAPERLESS_CONSUMPTION_DIR`](configuration.md#PAPERLESS_CONSUMPTION_DIR) should point to a folder which - [`PAPERLESS_CONSUMPTION_DIR`](configuration.md#PAPERLESS_CONSUMPTION_DIR) should point to a folder which
paperless should watch for documents. You might want to have paperless should watch for documents. You might want to have
this somewhere else. Likewise, [`PAPERLESS_DATA_DIR`](configuration.md#PAPERLESS_DATA_DIR) and this somewhere else. Likewise, [`PAPERLESS_DATA_DIR`](configuration.md#PAPERLESS_DATA_DIR) and
[`PAPERLESS_MEDIA_ROOT`](configuration.md#PAPERLESS_MEDIA_ROOT) define where paperless stores its data. [`PAPERLESS_MEDIA_ROOT`](configuration.md#PAPERLESS_MEDIA_ROOT) define where paperless stores its data.
If you like, you can point both to the same directory. If you like, you can point both to the same directory.
- [`PAPERLESS_SECRET_KEY`](configuration.md#PAPERLESS_SECRET_KEY) should be a random sequence of - [`PAPERLESS_SECRET_KEY`](configuration.md#PAPERLESS_SECRET_KEY) should be a random sequence of
characters. It's used for authentication. Failure to do so characters. It's used for authentication. Failure to do so
allows third parties to forge authentication credentials. allows third parties to forge authentication credentials.
- [`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) if you are behind a reverse proxy. This should - [`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) if you are behind a reverse proxy. This should
point to your domain. Please see point to your domain. Please see
[configuration](configuration.md) for more [configuration](configuration.md) for more
information. information.
Many more adjustments can be made to paperless, especially the OCR Many more adjustments can be made to paperless, especially the OCR
part. The following options are recommended for everyone: part. The following options are recommended for everyone:
- Set [`PAPERLESS_OCR_LANGUAGE`](configuration.md#PAPERLESS_OCR_LANGUAGE) to the language most of your - Set [`PAPERLESS_OCR_LANGUAGE`](configuration.md#PAPERLESS_OCR_LANGUAGE) to the language most of your
documents are written in. documents are written in.
- Set [`PAPERLESS_TIME_ZONE`](configuration.md#PAPERLESS_TIME_ZONE) to your local time zone. - Set [`PAPERLESS_TIME_ZONE`](configuration.md#PAPERLESS_TIME_ZONE) to your local time zone.
!!! warning !!! warning
@@ -380,9 +395,9 @@ supported.
7. Create the following directories if they are missing: 7. Create the following directories if they are missing:
- `/opt/paperless/media` - `/opt/paperless/media`
- `/opt/paperless/data` - `/opt/paperless/data`
- `/opt/paperless/consume` - `/opt/paperless/consume`
Adjust as necessary if you configured different folders. Adjust as necessary if you configured different folders.
Ensure that the paperless user has write permissions for every one Ensure that the paperless user has write permissions for every one
@@ -400,8 +415,7 @@ supported.
sudo chown paperless:paperless /opt/paperless/consume sudo chown paperless:paperless /opt/paperless/consume
``` ```
8. Install python requirements from the `requirements.txt` file. It is 8. Install python requirements from the `requirements.txt` file.
up to you if you wish to use a virtual environment or not. First you should update your pip, so it gets the actual packages.
```shell-session ```shell-session
sudo -Hu paperless pip3 install -r requirements.txt sudo -Hu paperless pip3 install -r requirements.txt
@@ -410,6 +424,12 @@ supported.
This will install all python dependencies in the home directory of This will install all python dependencies in the home directory of
the new paperless user. the new paperless user.
!!! tip
It is up to you if you wish to use a virtual environment or not for the Python
dependencies. This is an alternative to the above and may require adjusting
the example scripts to utilize the virtual environment paths
9. Go to `/opt/paperless/src`, and execute the following commands: 9. Go to `/opt/paperless/src`, and execute the following commands:
```bash ```bash
@@ -520,8 +540,7 @@ supported.
15. Optional: If using the NLTK machine learning processing (see 15. Optional: If using the NLTK machine learning processing (see
[`PAPERLESS_ENABLE_NLTK`](configuration.md#PAPERLESS_ENABLE_NLTK) for details), [`PAPERLESS_ENABLE_NLTK`](configuration.md#PAPERLESS_ENABLE_NLTK) for details),
download the NLTK data for the Snowball download the NLTK data for the Snowball
Stemmer, Stopwords and Punkt tokenizer to your Stemmer, Stopwords and Punkt tokenizer to `/usr/share/nltk_data`. Refer to the [NLTK
`PAPERLESS_DATA_DIR/nltk`. Refer to the [NLTK
instructions](https://www.nltk.org/data.html) for details on how to instructions](https://www.nltk.org/data.html) for details on how to
download the data. download the data.
@@ -567,21 +586,21 @@ your setup depending on how you installed paperless.
This setup describes how to update an existing paperless Docker This setup describes how to update an existing paperless Docker
installation. The important things to keep in mind are as follows: installation. The important things to keep in mind are as follows:
- Read the [changelog](changelog.md) and - Read the [changelog](changelog.md) and
take note of breaking changes. take note of breaking changes.
- You should decide if you want to stick with SQLite or want to - You should decide if you want to stick with SQLite or want to
migrate your database to PostgreSQL. See [documentation](#sqlite_to_psql) migrate your database to PostgreSQL. See [documentation](#sqlite_to_psql)
for details on for details on
how to move your data from SQLite to PostgreSQL. Both work fine with how to move your data from SQLite to PostgreSQL. Both work fine with
paperless. However, if you already have a database server running paperless. However, if you already have a database server running
for other services, you might as well use it for paperless as well. for other services, you might as well use it for paperless as well.
- The task scheduler of paperless, which is used to execute periodic - The task scheduler of paperless, which is used to execute periodic
tasks such as email checking and maintenance, requires a tasks such as email checking and maintenance, requires a
[redis](https://redis.io/) message broker instance. The [redis](https://redis.io/) message broker instance. The
Docker Compose route takes care of that. Docker Compose route takes care of that.
- The layout of the folder structure for your documents and data - The layout of the folder structure for your documents and data
remains the same, so you can just plug your old docker volumes into remains the same, so you can just plug your old docker volumes into
paperless-ngx and expect it to find everything where it should be. paperless-ngx and expect it to find everything where it should be.
Migration to paperless-ngx is then performed in a few simple steps: Migration to paperless-ngx is then performed in a few simple steps:
@@ -666,24 +685,37 @@ commands as well.
1. Stop and remove the paperless container 1. Stop and remove the paperless container
2. If using an external database, stop the container 2. If using an external database, stop the container
3. Update Redis configuration 3. Update Redis configuration
a) If `REDIS_URL` is already set, change it to [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS)
and continue to step 4. 1. If `REDIS_URL` is already set, change it to [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS)
b) Otherwise, in the `docker-compose.yml` add a new service for and continue to step 4.
Redis, following [the example compose
files](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose) 1. Otherwise, in the `docker-compose.yml` add a new service for
c) Set the environment variable [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS) so it points to Redis, following [the example compose
the new Redis container files](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose)
1. Set the environment variable [`PAPERLESS_REDIS`](configuration.md#PAPERLESS_REDIS) so it points to
the new Redis container
4. Update user mapping 4. Update user mapping
a) If set, change the environment variable `PUID` to `USERMAP_UID`
b) If set, change the environment variable `PGID` to `USERMAP_GID` 1. If set, change the environment variable `PUID` to `USERMAP_UID`
1. If set, change the environment variable `PGID` to `USERMAP_GID`
5. Update configuration paths 5. Update configuration paths
a) Set the environment variable [`PAPERLESS_DATA_DIR`](configuration.md#PAPERLESS_DATA_DIR) to `/config`
1. Set the environment variable [`PAPERLESS_DATA_DIR`](configuration.md#PAPERLESS_DATA_DIR) to `/config`
6. Update media paths 6. Update media paths
a) Set the environment variable [`PAPERLESS_MEDIA_ROOT`](configuration.md#PAPERLESS_MEDIA_ROOT) to
`/data/media` 1. Set the environment variable [`PAPERLESS_MEDIA_ROOT`](configuration.md#PAPERLESS_MEDIA_ROOT) to
`/data/media`
7. Update timezone 7. Update timezone
a) Set the environment variable [`PAPERLESS_TIME_ZONE`](configuration.md#PAPERLESS_TIME_ZONE) to the same
value as `TZ` 1. Set the environment variable [`PAPERLESS_TIME_ZONE`](configuration.md#PAPERLESS_TIME_ZONE) to the same
value as `TZ`
8. Modify the `image:` to point to 8. Modify the `image:` to point to
`ghcr.io/paperless-ngx/paperless-ngx:latest` or a specific version `ghcr.io/paperless-ngx/paperless-ngx:latest` or a specific version
if preferred. if preferred.
@@ -691,95 +723,8 @@ commands as well.
## Moving data from SQLite to PostgreSQL or MySQL/MariaDB {#sqlite_to_psql} ## Moving data from SQLite to PostgreSQL or MySQL/MariaDB {#sqlite_to_psql}
Moving your data from SQLite to PostgreSQL or MySQL/MariaDB is done via The best way to migrate between database types is to perform an [export](administration.md#exporter) and then
executing a series of django management commands as below. The commands [import](administration.md#importer) into a clean installation of Paperless-ngx.
below use PostgreSQL, but are applicable to MySQL/MariaDB with the
!!! warning
Make sure that your SQLite database is migrated to the latest version.
Starting paperless will make sure that this is the case. If your try to
load data from an old database schema in SQLite into a newer database
schema in PostgreSQL, you will run into trouble.
!!! warning
On some database fields, PostgreSQL enforces predefined limits on
maximum length, whereas SQLite does not. The fields in question are the
title of documents (128 characters), names of document types, tags and
correspondents (128 characters), and filenames (1024 characters). If you
have data in these fields that surpasses these limits, migration to
PostgreSQL is not possible and will fail with an error.
!!! warning
MySQL is case insensitive by default, treating values like "Name" and
"NAME" as identical. See [MySQL caveats](advanced_usage.md#mysql-caveats) for details.
!!! warning
MySQL also enforces limits on maximum lengths, but does so differently than
PostgreSQL. It may not be possible to migrate to MySQL due to this.
!!! warning
Using mariadb version 10.4+ is recommended. Using the `utf8mb3` character set on
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).
1. Stop paperless, if it is running.
2. Tell paperless to use PostgreSQL:
a) With docker, copy the provided `docker-compose.postgres.yml`
file to `docker-compose.yml`. Remember to adjust the consumption
directory, if necessary.
b) Without docker, configure the database in your `paperless.conf`
file. See [configuration](configuration.md) for
details.
3. Open a shell and initialize the database:
a) With docker, run the following command to open a shell within
the paperless container:
``` shell-session
$ cd /path/to/paperless
$ docker compose run --rm webserver /bin/bash
```
This will launch the container and initialize the PostgreSQL
database.
b) Without docker, remember to activate any virtual environment,
switch to the `src` directory and create the database schema:
``` shell-session
$ cd /path/to/paperless/src
$ python3 manage.py migrate
```
This will not copy any data yet.
4. Dump your data from SQLite:
```shell-session
$ python3 manage.py dumpdata --database=sqlite --exclude=contenttypes --exclude=auth.Permission > data.json
```
5. Load your data into PostgreSQL:
```shell-session
$ python3 manage.py loaddata data.json
```
6. If operating inside Docker, you may exit the shell now.
```shell-session
$ exit
```
7. Start paperless.
## Moving back to Paperless ## Moving back to Paperless
@@ -818,30 +763,30 @@ Paperless runs on Raspberry Pi. However, some things are rather slow on
the Pi and configuring some options in paperless can help improve the Pi and configuring some options in paperless can help improve
performance immensely: performance immensely:
- Stick with SQLite to save some resources. - Stick with SQLite to save some resources.
- Consider setting [`PAPERLESS_OCR_PAGES`](configuration.md#PAPERLESS_OCR_PAGES) to 1, so that paperless will - Consider setting [`PAPERLESS_OCR_PAGES`](configuration.md#PAPERLESS_OCR_PAGES) to 1, so that paperless will
only OCR the first page of your documents. In most cases, this page only OCR the first page of your documents. In most cases, this page
contains enough information to be able to find it. contains enough information to be able to find it.
- [`PAPERLESS_TASK_WORKERS`](configuration.md#PAPERLESS_TASK_WORKERS) and [`PAPERLESS_THREADS_PER_WORKER`](configuration.md#PAPERLESS_THREADS_PER_WORKER) are - [`PAPERLESS_TASK_WORKERS`](configuration.md#PAPERLESS_TASK_WORKERS) and [`PAPERLESS_THREADS_PER_WORKER`](configuration.md#PAPERLESS_THREADS_PER_WORKER) are
configured to use all cores. The Raspberry Pi models 3 and up have 4 configured to use all cores. The Raspberry Pi models 3 and up have 4
cores, meaning that paperless will use 2 workers and 2 threads per cores, meaning that paperless will use 2 workers and 2 threads per
worker. This may result in sluggish response times during worker. This may result in sluggish response times during
consumption, so you might want to lower these settings (example: 2 consumption, so you might want to lower these settings (example: 2
workers and 1 thread to always have some computing power left for workers and 1 thread to always have some computing power left for
other tasks). other tasks).
- Keep [`PAPERLESS_OCR_MODE`](configuration.md#PAPERLESS_OCR_MODE) at its default value `skip` and consider - Keep [`PAPERLESS_OCR_MODE`](configuration.md#PAPERLESS_OCR_MODE) at its default value `skip` and consider
OCR'ing your documents before feeding them into paperless. Some OCR'ing your documents before feeding them into paperless. Some
scanners are able to do this! scanners are able to do this!
- Set [`PAPERLESS_OCR_SKIP_ARCHIVE_FILE`](configuration.md#PAPERLESS_OCR_SKIP_ARCHIVE_FILE) to `with_text` to skip archive - Set [`PAPERLESS_OCR_SKIP_ARCHIVE_FILE`](configuration.md#PAPERLESS_OCR_SKIP_ARCHIVE_FILE) to `with_text` to skip archive
file generation for already ocr'ed documents, or `always` to skip it file generation for already ocr'ed documents, or `always` to skip it
for all documents. for all documents.
- If you want to perform OCR on the device, consider using - If you want to perform OCR on the device, consider using
`PAPERLESS_OCR_CLEAN=none`. This will speed up OCR times and use `PAPERLESS_OCR_CLEAN=none`. This will speed up OCR times and use
less memory at the expense of slightly worse OCR results. less memory at the expense of slightly worse OCR results.
- If using docker, consider setting [`PAPERLESS_WEBSERVER_WORKERS`](configuration.md#PAPERLESS_WEBSERVER_WORKERS) to 1. This will save some memory. - If using docker, consider setting [`PAPERLESS_WEBSERVER_WORKERS`](configuration.md#PAPERLESS_WEBSERVER_WORKERS) to 1. This will save some memory.
- Consider setting [`PAPERLESS_ENABLE_NLTK`](configuration.md#PAPERLESS_ENABLE_NLTK) to false, to disable the - Consider setting [`PAPERLESS_ENABLE_NLTK`](configuration.md#PAPERLESS_ENABLE_NLTK) to false, to disable the
more advanced language processing, which can take more memory and more advanced language processing, which can take more memory and
processing time. processing time.
For details, refer to [configuration](configuration.md). For details, refer to [configuration](configuration.md).

View File

@@ -4,27 +4,27 @@
Check for the following issues: Check for the following issues:
- Ensure that the directory you're putting your documents in is the - Ensure that the directory you're putting your documents in is the
folder paperless is watching. With docker, this setting is performed folder paperless is watching. With docker, this setting is performed
in the `docker-compose.yml` file. Without Docker, look at the in the `docker-compose.yml` file. Without Docker, look at the
`CONSUMPTION_DIR` setting. Don't adjust this setting if you're `CONSUMPTION_DIR` setting. Don't adjust this setting if you're
using docker. using docker.
- Ensure that redis is up and running. Paperless does its task - Ensure that redis is up and running. Paperless does its task
processing asynchronously, and for documents to arrive at the task processing asynchronously, and for documents to arrive at the task
processor, it needs redis to run. processor, it needs redis to run.
- Ensure that the task processor is running. Docker does this - Ensure that the task processor is running. Docker does this
automatically. Manually invoke the task processor by executing automatically. Manually invoke the task processor by executing
```shell-session ```shell-session
$ celery --app paperless worker $ celery --app paperless worker
``` ```
- Look at the output of paperless and inspect it for any errors. - Look at the output of paperless and inspect it for any errors.
- Go to the admin interface, and check if there are failed tasks. If - Go to the admin interface, and check if there are failed tasks. If
so, the tasks will contain an error message. so, the tasks will contain an error message.
## Consumer warns `OCR for XX failed` ## Consumer warns `OCR for XX failed`
@@ -78,12 +78,12 @@ Ensure that `chown` is possible on these directories.
This indicates that the Auto matching algorithm found no documents to This indicates that the Auto matching algorithm found no documents to
learn from. This may have two reasons: learn from. This may have two reasons:
- You don't use the Auto matching algorithm: The error can be safely - You don't use the Auto matching algorithm: The error can be safely
ignored in this case. ignored in this case.
- You are using the Auto matching algorithm: The classifier explicitly - You are using the Auto matching algorithm: The classifier explicitly
excludes documents with Inbox tags. Verify that there are documents excludes documents with Inbox tags. Verify that there are documents
in your archive without inbox tags. The algorithm will only learn in your archive without inbox tags. The algorithm will only learn
from documents not in your inbox. from documents not in your inbox.
## UserWarning in sklearn on every single document ## UserWarning in sklearn on every single document
@@ -127,10 +127,10 @@ change in the `docker-compose.yml` file:
# The gotenberg chromium route is used to convert .eml files. We do not # The gotenberg chromium route is used to convert .eml files. We do not
# want to allow external content like tracking pixels or even javascript. # want to allow external content like tracking pixels or even javascript.
command: command:
- 'gotenberg' - 'gotenberg'
- '--chromium-disable-javascript=true' - '--chromium-disable-javascript=true'
- '--chromium-allow-list=file:///tmp/.*' - '--chromium-allow-list=file:///tmp/.*'
- '--api-timeout=60' - '--api-timeout=60'
``` ```
## Permission denied errors in the consumption directory ## Permission denied errors in the consumption directory
@@ -353,6 +353,20 @@ ways from the original. As the logs indicate, if you encounter this error you ca
`PAPERLESS_OCR_USER_ARGS: '{"continue_on_soft_render_error": true}'` to try to 'force' `PAPERLESS_OCR_USER_ARGS: '{"continue_on_soft_render_error": true}'` to try to 'force'
processing documents with this issue. processing documents with this issue.
## Logs show "possible incompatible database column" when deleting documents {#convert-uuid-field}
You may see errors when deleting documents like:
```
Data too long for column 'transaction_id' at row 1
```
This error can occur in installations which have upgraded from a version of Paperless-ngx that used Django 4 (Paperless-ngx versions prior to v2.13.0) with a MariaDB/MySQL database. Due to the backawards-incompatible change in Django 5, the column "documents_document.transaction_id" will need to be re-created, which can be done with a one-time run of the following management command:
```shell-session
$ python3 manage.py convert_mariadb_uuid
```
## Platform-Specific Deployment Troubleshooting ## Platform-Specific Deployment Troubleshooting
A user-maintained wiki page is available to help troubleshoot issues that may arise when trying to deploy Paperless-ngx on specific platforms, for example SELinux. Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Platform%E2%80%90Specific-Troubleshooting). A user-maintained wiki page is available to help troubleshoot issues that may arise when trying to deploy Paperless-ngx on specific platforms, for example SELinux. Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Platform%E2%80%90Specific-Troubleshooting).

View File

@@ -10,37 +10,37 @@ and provides many utilities for finding and managing your documents.
Paperless essentially consists of two different parts for managing your Paperless essentially consists of two different parts for managing your
documents: documents:
- The _consumer_ watches a specified folder and adds all documents in - The _consumer_ watches a specified folder and adds all documents in
that folder to paperless. that folder to paperless.
- The _web server_ provides a UI that you use to manage and search for - The _web server_ provides a UI that you use to manage and search for
your scanned documents. your scanned documents.
Each document has a couple of fields that you can assign to them: Each document has a couple of fields that you can assign to them:
- A _Document_ is a piece of paper that sometimes contains valuable - A _Document_ is a piece of paper that sometimes contains valuable
information. information.
- The _correspondent_ of a document is the person, institution or - The _correspondent_ of a document is the person, institution or
company that a document either originates from, or is sent to. company that a document either originates from, or is sent to.
- A _tag_ is a label that you can assign to documents. Think of labels - A _tag_ is a label that you can assign to documents. Think of labels
as more powerful folders: Multiple documents can be grouped together as more powerful folders: Multiple documents can be grouped together
with a single tag, however, a single document can also have multiple with a single tag, however, a single document can also have multiple
tags. This is not possible with folders. The reason folders are not tags. This is not possible with folders. The reason folders are not
implemented in paperless is simply that tags are much more versatile implemented in paperless is simply that tags are much more versatile
than folders. than folders.
- A _document type_ is used to demarcate the type of a document such - A _document type_ is used to demarcate the type of a document such
as letter, bank statement, invoice, contract, etc. It is used to as letter, bank statement, invoice, contract, etc. It is used to
identify what a document is about. identify what a document is about.
- The _date added_ of a document is the date the document was scanned - The _date added_ of a document is the date the document was scanned
into paperless. You cannot and should not change this date. into paperless. You cannot and should not change this date.
- The _date created_ of a document is the date the document was - The _date created_ of a document is the date the document was
initially issued. This can be the date you bought a product, the initially issued. This can be the date you bought a product, the
date you signed a contract, or the date a letter was sent to you. date you signed a contract, or the date a letter was sent to you.
- The _archive serial number_ (short: ASN) of a document is the - The _archive serial number_ (short: ASN) of a document is the
identifier of the document in your physical document binders. See identifier of the document in your physical document binders. See
[recommended workflow](#usage-recommended-workflow) below. [recommended workflow](#usage-recommended-workflow) below.
- The _content_ of a document is the text that was OCR'ed from the - The _content_ of a document is the text that was OCR'ed from the
document. This text is fed into the search engine and is used for document. This text is fed into the search engine and is used for
matching tags, correspondents and document types. matching tags, correspondents and document types.
## Adding documents to paperless ## Adding documents to paperless
@@ -109,10 +109,10 @@ process.
### Mobile upload {#usage-mobile_upload} ### Mobile upload {#usage-mobile_upload}
Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Affiliated-Projects) for a user-maintained list of affiliated projects and Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Related-Projects) for a user-maintained list of related projects and
software (e.g. for mobile devices) that is compatible with Paperless-ngx. software (e.g. for mobile devices) that is compatible with Paperless-ngx.
### IMAP (Email) {#usage-email} ### Email {#usage-email}
You can tell paperless-ngx to consume documents from your email You can tell paperless-ngx to consume documents from your email
accounts. This is a very flexible and powerful feature, if you regularly accounts. This is a very flexible and powerful feature, if you regularly
@@ -136,26 +136,27 @@ These rules perform the following:
Paperless will check all emails only once and completely ignore messages Paperless will check all emails only once and completely ignore messages
that do not match your filters. It will also only perform the rule action that do not match your filters. It will also only perform the rule action
on e-mails that it has consumed documents from. on e-mails that it has consumed documents from. The filename attachment
patterns can include wildcards and multiple patterns separated by a comma.
The actions all ensure that the same mail is not consumed twice by The actions all ensure that the same mail is not consumed twice by
different means. These are as follows: different means. These are as follows:
- **Delete:** Immediately deletes mail that paperless has consumed - **Delete:** Immediately deletes mail that paperless has consumed
documents from. Use with caution. documents from. Use with caution.
- **Mark as read:** Mark consumed mail as read. Paperless will not - **Mark as read:** Mark consumed mail as read. Paperless will not
consume documents from already read mails. If you read a mail before consume documents from already read mails. If you read a mail before
paperless sees it, it will be ignored. paperless sees it, it will be ignored.
- **Flag:** Sets the 'important' flag on mails with consumed - **Flag:** Sets the 'important' flag on mails with consumed
documents. Paperless will not consume flagged mails. documents. Paperless will not consume flagged mails.
- **Move to folder:** Moves consumed mails out of the way so that - **Move to folder:** Moves consumed mails out of the way so that
paperless won't consume them again. paperless won't consume them again.
- **Add custom Tag:** Adds a custom tag to mails with consumed - **Add custom Tag:** Adds a custom tag to mails with consumed
documents (the IMAP standard calls these "keywords"). Paperless documents (the IMAP standard calls these "keywords"). Paperless
will not consume mails already tagged. Not all mail servers support will not consume mails already tagged. Not all mail servers support
this feature! this feature!
- **Apple Mail support:** Apple Mail clients allow differently colored tags. For this to work use `apple:<color>` (e.g. _apple:green_) as a custom tag. Available colors are _red_, _orange_, _yellow_, _blue_, _green_, _violet_ and _grey_. - **Apple Mail support:** Apple Mail clients allow differently colored tags. For this to work use `apple:<color>` (e.g. _apple:green_) as a custom tag. Available colors are _red_, _orange_, _yellow_, _blue_, _green_, _violet_ and _grey_.
!!! warning !!! warning
@@ -199,6 +200,14 @@ different means. These are as follows:
Paperless is set up to check your mails every 10 minutes. This can be Paperless is set up to check your mails every 10 minutes. This can be
configured via [`PAPERLESS_EMAIL_TASK_CRON`](configuration.md#PAPERLESS_EMAIL_TASK_CRON) configured via [`PAPERLESS_EMAIL_TASK_CRON`](configuration.md#PAPERLESS_EMAIL_TASK_CRON)
#### OAuth Email Setup
Paperless-ngx supports OAuth2 authentication for Gmail and Outlook email accounts. To set up an email account with OAuth2, you will need to create a 'developer' app with the respective provider and obtain the client ID and client secret and set the appropriate [configuration variables](configuration.md#email_oauth). You will also need to set either [`PAPERLESS_OAUTH_CALLBACK_BASE_URL`](configuration.md#PAPERLESS_OAUTH_CALLBACK_BASE_URL) or [`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) to the correct value for the OAuth2 flow to work correctly.
Specific instructions for setting up the required 'developer' app with Google or Microsoft are beyond the scope of this documentation, but you can find user-maintained instructions in [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Email-OAuth-App-Setup) or by searching the web.
Once setup, navigating to the email settings page in Paperless-ngx will allow you to add an email account for Gmail or Outlook using OAuth2. After authenticating, you will be presented with the newly-created account where you will need to enter and save your email address. After this, the account will work as any other email account in Paperless-ngx and refreshing tokens will be handled automatically.
### REST API ### REST API
You can also submit a document using the REST API, see [POSTing documents](api.md#file-uploads) You can also submit a document using the REST API, see [POSTing documents](api.md#file-uploads)
@@ -206,12 +215,12 @@ for details.
## Permissions ## Permissions
As of version 1.14.0 Paperless-ngx added core support for user / group permissions. Permissions is Permissions in Paperless-ngx are based around ['global' permissions](#global-permissions) as well as
based around 'global' permissions as well as 'object-level' permissions. Global permissions designate ['object-level' permissions](#object-permissions). Global permissions determine which parts of the
which parts of the application a user can access (e.g. Documents, Tags, Settings) and object-level application a user can access (e.g. Documents, Tags, Settings) and object-level determine which
determine which objects are visible or editable. All objects have an 'owner' and 'view' and 'edit' objects are visible or editable. All objects have an 'owner' and 'view' and 'edit' permissions which
permissions which can be granted to other users or groups. The paperless-ngx permissions system uses can be granted to other users or groups. The paperless-ngx permissions system uses the built-in user
the built-in user model of the backend framework, Django. model of the backend framework, Django.
!!! tip !!! tip
@@ -219,37 +228,71 @@ the built-in user model of the backend framework, Django.
for a Tag will _not_ affect the permissions of documents that have the Tag. for a Tag will _not_ affect the permissions of documents that have the Tag.
Permissions can be set using the new "Permissions" tab when editing documents, or bulk-applied Permissions can be set using the new "Permissions" tab when editing documents, or bulk-applied
in the UI by selecting documents and choosing the "Permissions" button. Owner can also optionally in the UI by selecting documents and choosing the "Permissions" button.
be set for documents uploaded via the API. Documents consumed via the consumption dir currently
do not have an owner set.
!!! note
After migration to version 1.14.0 all existing documents, tags etc. will have no explicit owner
set which means they will be visible / editable by all users. Once an object has an owner set,
only the owner can explicitly grant / revoke permissions.
!!! note
When first migrating to permissions it is recommended to use a 'superuser' account (which
would usually have been setup during installation) to ensure you have full permissions.
Note that superusers have access to all objects.
### Default permissions ### Default permissions
Default permissions for documents can be set using workflows. [Workflows](#workflows) provide advanced ways to control permissions.
For objects created via the web UI (tags, doc types, etc.) the default is to set the current user For objects created via the web UI (tags, doc types, etc.) the default is to set the current user
as owner and no extra permissions, but you explicitly set these under Settings > Permissions. as owner and no extra permissions, but you can explicitly set these under Settings > Permissions.
Documents consumed via the consumption directory do not have an owner or additional permissions set by default, but again, can be controlled with [Workflows](#workflows).
### Users and Groups ### Users and Groups
Paperless-ngx versions after 1.14.0 allow creating and editing users and groups via the 'frontend' UI. Paperless-ngx supports editing users and groups via the 'frontend' UI, which can be found under
These can be found under Settings > Users & Groups, assuming the user has access. If a user is designated Settings > Users & Groups, assuming the user has access. If a user is designated
as a member of a group those permissions will be inherited and this is reflected in the UI. Explicit as a member of a group those permissions will be inherited and this is reflected in the UI. Explicit
permissions can be granted to limit access to certain parts of the UI (and corresponding API endpoints). permissions can be granted to limit access to certain parts of the UI (and corresponding API endpoints).
!!! tip
By default, new users are not granted any permissions, except those inherited from any group(s) of which they are a member.
#### Superusers
Superusers can access all parts of the front and backend application as well as any and all objects.
#### Admin Status
Admin status (Django 'staff status') grants access to viewing the paperless logs and the system status dialog
as well as accessing the Django backend.
#### Detailed Explanation of Global Permissions {#global-permissions}
Global permissions define what areas of the app and API endpoints users can access. For example, they
determine if a user can create, edit, delete or view _any_ documents, but individual documents themselves
still have "object-level" permissions.
| Type | Details |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| AppConfig | _Change_ or higher permissions grants access to the "Application Configuration" area. |
| Correspondent | Add, edit, delete or view Correspondents. |
| CustomField | Add, edit, delete or view Custom Fields. |
| Document | Add, edit, delete or view Documents. |
| DocumentType | Add, edit, delete or view Document Types. |
| Group | Add, edit, delete or view Groups. |
| MailAccount | Add, edit, delete or view Mail Accounts. |
| MailRule | Add, edit, delete or view Mail Rules. |
| Note | Add, edit, delete or view Notes. |
| PaperlessTask | View or dismiss (_Change_) File Tasks. |
| SavedView | Add, edit, delete or view Saved Views. |
| ShareLink | Add, delete or view Share Links. |
| StoragePath | Add, edit, delete or view Storage Paths. |
| Tag | Add, edit, delete or view Tags. |
| UISettings | Add, edit, delete or view the UI settings that are used by the web app.<br/>:warning: **Users that will access the web UI must be granted at least _View_ permissions.** |
| User | Add, edit, delete or view Users. |
| Workflow | Add, edit, delete or view Workflows.<br/>Note that Workflows are global, in other words all users who can access workflows have access to the same set of them. |
#### Detailed Explanation of Object Permissions {#object-permissions}
| Type | Details |
| ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Owner | By default objects are only visible and editable by their owner.<br/>Only the object owner can grant permissions to other users or groups.<br/>Additionally, only document owners can create share links and add / remove custom fields.<br/>For backwards compatibility objects can have no owner which makes them visible to any user. |
| View | Confers the ability to view (not edit) a document, tag, etc.<br/>Users without 'view' (or higher) permissions will be shown _'Private'_ in place of the object name for example when viewing a document with a tag for which the user doesn't have permissions. |
| Edit | Confers the ability to edit (and view) a document, tag, etc. |
### Password reset ### Password reset
In order to enable the password reset feature you will need to setup an SMTP backend, see In order to enable the password reset feature you will need to setup an SMTP backend, see
@@ -317,32 +360,32 @@ flowchart TD
Workflows allow you to filter by: Workflows allow you to filter by:
- Source, e.g. documents uploaded via consume folder, API (& the web UI) and mail fetch - Source, e.g. documents uploaded via consume folder, API (& the web UI) and mail fetch
- File name, including wildcards e.g. \*.pdf will apply to all pdfs - File name, including wildcards e.g. \*.pdf will apply to all pdfs
- File path, including wildcards. Note that enabling `PAPERLESS_CONSUMER_RECURSIVE` would allow, for - File path, including wildcards. Note that enabling `PAPERLESS_CONSUMER_RECURSIVE` would allow, for
example, automatically assigning documents to different owners based on the upload directory. example, automatically assigning documents to different owners based on the upload directory.
- Mail rule. Choosing this option will force 'mail fetch' to be the workflow source. - Mail rule. Choosing this option will force 'mail fetch' to be the workflow source.
- Content matching (`Added` and `Updated` triggers only). Filter document content using the matching settings. - Content matching (`Added` and `Updated` triggers only). Filter document content using the matching settings.
- Tags (`Added` and `Updated` triggers only). Filter for documents with any of the specified tags - Tags (`Added` and `Updated` triggers only). Filter for documents with any of the specified tags
- Document type (`Added` and `Updated` triggers only). Filter documents with this doc type - Document type (`Added` and `Updated` triggers only). Filter documents with this doc type
- Correspondent (`Added` and `Updated` triggers only). Filter documents with this correspondent - Correspondent (`Added` and `Updated` triggers only). Filter documents with this correspondent
### Workflow Actions ### Workflow Actions
There are currently two types of workflow actions, "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 - Title, see [title placeholders](usage.md#title-placeholders) below
- Tags, correspondent, document type and storage path - Tags, correspondent, document type and storage path
- Document owner - Document owner
- View and / or edit permissions to users or groups - View and / or edit permissions to users or groups
- Custom fields. Note that no value for the field will be set - 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: and "Removal" actions, which can remove either all of or specific sets of the following:
- Tags, correspondents, document types or storage paths - Tags, correspondents, document types or storage paths
- Document owner - Document owner
- View and / or edit permissions - View and / or edit permissions
- Custom fields - Custom fields
#### Title placeholders #### Title placeholders
@@ -350,29 +393,29 @@ Workflow titles can include placeholders but the available options differ depend
workflow trigger. This is because at the time of consumption (when the title is to be set), no automatic tags etc. have been workflow trigger. This is because at the time of consumption (when the title is to be set), no automatic tags etc. have been
applied. You can use the following placeholders with any trigger type: applied. You can use the following placeholders with any trigger type:
- `{correspondent}`: assigned correspondent name - `{correspondent}`: assigned correspondent name
- `{document_type}`: assigned document type name - `{document_type}`: assigned document type name
- `{owner_username}`: assigned owner username - `{owner_username}`: assigned owner username
- `{added}`: added datetime - `{added}`: added datetime
- `{added_year}`: added year - `{added_year}`: added year
- `{added_year_short}`: added year - `{added_year_short}`: added year
- `{added_month}`: added month - `{added_month}`: added month
- `{added_month_name}`: added month name - `{added_month_name}`: added month name
- `{added_month_name_short}`: added month short name - `{added_month_name_short}`: added month short name
- `{added_day}`: added day - `{added_day}`: added day
- `{added_time}`: added time in HH:MM format - `{added_time}`: added time in HH:MM format
- `{original_filename}`: original file name without extension - `{original_filename}`: original file name without extension
The following placeholders are only available for "added" or "updated" triggers The following placeholders are only available for "added" or "updated" triggers
- `{created}`: created datetime - `{created}`: created datetime
- `{created_year}`: created year - `{created_year}`: created year
- `{created_year_short}`: created year - `{created_year_short}`: created year
- `{created_month}`: created month - `{created_month}`: created month
- `{created_month_name}`: created month name - `{created_month_name}`: created month name
- `{created_month_name_short}`: created month short name - `{created_month_name_short}`: created month short name
- `{created_day}`: created day - `{created_day}`: created day
- `{created_time}`: created time in HH:MM format - `{created_time}`: created time in HH:MM format
### Workflow permissions ### Workflow permissions
@@ -391,13 +434,12 @@ to optionally attach data to documents which does not fit in the existing set of
Paperless-ngx provides. Paperless-ngx provides.
1. First, create a custom field (under "Manage"), with a given name and data type. This could be something like "Invoice Number" or "Date Paid", with a data type of "Number", "Date", "String", etc. 1. First, create a custom field (under "Manage"), with a given name and data type. This could be something like "Invoice Number" or "Date Paid", with a data type of "Number", "Date", "String", etc.
2. Once created, a field can be used with documents and data stored. To do so, use the "Custom Fields" menu on the document detail page, choose your existing field and click "Add". Once the field is visible in the form you can enter the appropriate 2. Once created, a field can be used with documents and data stored. To do so, use the "Custom Fields" menu on the document detail page, choose your existing field from the dropdown. Once the field is visible in the form you can enter the appropriate data which will be validated according to the custom field "data type".
data which will be validated according to the custom field "data type".
3. Fields can be removed by hovering over the field name revealing a "Remove" button. 3. Fields can be removed by hovering over the field name revealing a "Remove" button.
!!! important !!! important
Added / removed fields, as well as any data is not saved to the document until you Added / removed fields, as well as any data, is not saved to the document until you
actually hit the "Save" button, similar to other changes on the document details page. actually hit the "Save" button, similar to other changes on the document details page.
!!! note !!! note
@@ -408,28 +450,57 @@ Multiple fields may be attached to a document but the same field name cannot be
The following custom field types are supported: The following custom field types are supported:
- `Text`: any text - `Text`: any text
- `Boolean`: true / false (check / unchecked) field - `Boolean`: true / false (check / unchecked) field
- `Date`: date - `Date`: date
- `URL`: a valid url - `URL`: a valid url
- `Integer`: integer number e.g. 12 - `Integer`: integer number e.g. 12
- `Number`: float number e.g. 12.3456 - `Number`: float number e.g. 12.3456
- `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 - `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 - `Document Link`: reference(s) to other document(s) displayed as links, automatically creates a symmetrical link in reverse
- `Select`: a pre-defined list of strings from which the user can choose
## Share Links ## Share Links
Paperless-ngx added the ability to create shareable links to files in version 2.0. You can find the button for this on the document detail screen. Paperless-ngx added the ability to create shareable links to files in version 2.0. You can find the button for this on the document detail screen.
- Share links do not require a user to login and thus link directly to a file. - Share links do not require a user to login and thus link directly to a file.
- Links are unique and are of the form `{paperless-url}/share/{randomly-generated-slug}`. - Links are unique and are of the form `{paperless-url}/share/{randomly-generated-slug}`.
- Links can optionally have an expiration time set. - Links can optionally have an expiration time set.
- After a link expires or is deleted users will be redirected to the regular paperless-ngx login. - After a link expires or is deleted users will be redirected to the regular paperless-ngx login.
!!! tip !!! tip
If your paperless-ngx instance is behind a reverse-proxy you may want to create an exception to bypass any authentication layers that are part of your setup in order to make links truly publicly-accessible. Of course, do so with caution. If your paperless-ngx instance is behind a reverse-proxy you may want to create an exception to bypass any authentication layers that are part of your setup in order to make links truly publicly-accessible. Of course, do so with caution.
## PDF Actions
Paperless-ngx supports four basic editing operations for PDFs (these operations currently cannot be performed on non-PDF files):
- Merging documents: available when selecting multiple documents for 'bulk editing'.
- Rotating documents: available when selecting multiple documents for 'bulk editing' and from an individual document's details page.
- Splitting documents: available from an individual document's details page.
- Deleting pages: available from an individual document's details page.
!!! important
Note that rotation and deleting pages alter the Paperless-ngx _original_ file, which would, for example, invalidate a digital signature.
## Document History
As of version 2.7, Paperless-ngx automatically records all changes to a document and records this in an audit log. The feature requires [`PAPERLESS_AUDIT_LOG_ENABLED`](configuration.md#PAPERLESS_AUDIT_LOG_ENABLED) be enabled, which it is by default as of version 2.7.
Changes to documents are visible under the "History" tab. Note that certain changes such as those made by workflows, record the 'actor'
as "System".
## Document Trash
When you first delete a document it is moved to the 'trash' until either it is explicitly deleted or it is automatically removed after a set amount of time has passed.
You can set how long documents remain in the trash before being automatically deleted with [`PAPERLESS_EMPTY_TRASH_DELAY`](configuration.md#PAPERLESS_EMPTY_TRASH_DELAY), which defaults
to 30 days. Until the file is actually deleted (e.g. the trash is emptied), all files and database content remains intact and can be restored at any point up until that time.
Additionally you may configure a directory where deleted files are moved to when they the trash is emptied with [`PAPERLESS_EMPTY_TRASH_DIR`](configuration.md#PAPERLESS_EMPTY_TRASH_DIR).
Note that the empty trash directory only stores the original file, the archive file and all database information is permanently removed once a document is fully deleted.
## Best practices {#basic-searching} ## Best practices {#basic-searching}
Paperless offers a couple tools that help you organize your document Paperless offers a couple tools that help you organize your document
@@ -487,21 +558,31 @@ the system.
Here are a couple examples of tags and types that you could use in your Here are a couple examples of tags and types that you could use in your
collection. collection.
- An `inbox` tag for newly added documents that you haven't manually - An `inbox` tag for newly added documents that you haven't manually
edited yet. edited yet.
- A tag `car` for everything car related (repairs, registration, - A tag `car` for everything car related (repairs, registration,
insurance, etc) insurance, etc)
- A tag `todo` for documents that you still need to do something with, - A tag `todo` for documents that you still need to do something with,
such as reply, or perform some task online. such as reply, or perform some task online.
- A tag `bank account x` for all bank statement related to that - A tag `bank account x` for all bank statement related to that
account. account.
- A tag `mail` for anything that you added to paperless via its mail - A tag `mail` for anything that you added to paperless via its mail
processing capabilities. processing capabilities.
- A tag `missing_metadata` when you still need to add some metadata to - A tag `missing_metadata` when you still need to add some metadata to
a document, but can't or don't want to do this right now. a document, but can't or don't want to do this right now.
## Searching {#basic-usage_searching} ## Searching {#basic-usage_searching}
### Global search
The top search bar in the web UI performs a "global" search of the various
objects Paperless-ngx uses, including documents, tags, workflows, etc. Only
objects for which the user has appropriate permissions are returned. For
documents, if there are < 3 results, "advanced" search results (which use
the document index) will also be included. This can be disabled under settings.
### Document searches
Paperless offers an extensive searching mechanism that is designed to Paperless offers an extensive searching mechanism that is designed to
allow you to quickly find a document you're looking for (for example, allow you to quickly find a document you're looking for (for example,
that thing that just broke and you bought a couple months ago, that that thing that just broke and you bought a couple months ago, that
@@ -557,6 +638,12 @@ language](https://whoosh.readthedocs.io/en/latest/querylang.html). For
details on what date parsing utilities are available, see [Date details on what date parsing utilities are available, see [Date
parsing](https://whoosh.readthedocs.io/en/latest/dates.html#parsing-date-queries). parsing](https://whoosh.readthedocs.io/en/latest/dates.html#parsing-date-queries).
## Keyboard shortcuts / hotkeys
A list of available hotkeys can be shown on any page using <kbd>Shift</kbd> +
<kbd>?</kbd>. The help dialog shows only the keys that are currently available
based on which area of Paperless-ngx you are using.
## The recommended workflow {#usage-recommended-workflow} ## The recommended workflow {#usage-recommended-workflow}
Once you have familiarized yourself with paperless and are ready to use Once you have familiarized yourself with paperless and are ready to use
@@ -571,8 +658,8 @@ The following diagram shows how easy it is to manage your documents.
### Preparations in paperless ### Preparations in paperless
- Create an inbox tag that gets assigned to all new documents. - Create an inbox tag that gets assigned to all new documents.
- Create a TODO tag. - Create a TODO tag.
### Processing of the physical documents ### Processing of the physical documents
@@ -646,78 +733,78 @@ Some documents require attention and require you to act on the document.
You may take two different approaches to handle these documents based on You may take two different approaches to handle these documents based on
how regularly you intend to scan documents and use paperless. how regularly you intend to scan documents and use paperless.
- If you scan and process your documents in paperless regularly, - If you scan and process your documents in paperless regularly,
assign a TODO tag to all scanned documents that you need to process. assign a TODO tag to all scanned documents that you need to process.
Create a saved view on the dashboard that shows all documents with Create a saved view on the dashboard that shows all documents with
this tag. this tag.
- If you do not scan documents regularly and use paperless solely for - If you do not scan documents regularly and use paperless solely for
archiving, create a physical todo box next to your physical inbox archiving, create a physical todo box next to your physical inbox
and put documents you need to process in the TODO box. When you and put documents you need to process in the TODO box. When you
performed the task associated with the document, move it to the performed the task associated with the document, move it to the
inbox. inbox.
## Architecture ## Architecture
Paperless-ngx consists of the following components: Paperless-ngx consists of the following components:
- **The webserver:** This serves the administration pages, the API, - **The webserver:** This serves the administration pages, the API,
and the new frontend. This is the main tool you'll be using to interact and the new frontend. This is the main tool you'll be using to interact
with paperless. You may start the webserver directly with with paperless. You may start the webserver directly with
```shell-session ```shell-session
$ cd /path/to/paperless/src/ $ cd /path/to/paperless/src/
$ gunicorn -c ../gunicorn.conf.py paperless.wsgi $ gunicorn -c ../gunicorn.conf.py paperless.wsgi
``` ```
or by any other means such as Apache `mod_wsgi`. or by any other means such as Apache `mod_wsgi`.
- **The consumer:** This is what watches your consumption folder for - **The consumer:** This is what watches your consumption folder for
documents. However, the consumer itself does not really consume your documents. However, the consumer itself does not really consume your
documents. Now it notifies a task processor that a new file is ready documents. Now it notifies a task processor that a new file is ready
for consumption. I suppose it should be named differently. This was for consumption. I suppose it should be named differently. This was
also used to check your emails, but that's now done elsewhere as also used to check your emails, but that's now done elsewhere as
well. well.
Start the consumer with the management command `document_consumer`: Start the consumer with the management command `document_consumer`:
```shell-session ```shell-session
$ cd /path/to/paperless/src/ $ cd /path/to/paperless/src/
$ python3 manage.py document_consumer $ python3 manage.py document_consumer
``` ```
- **The task processor:** Paperless relies on [Celery - Distributed - **The task processor:** Paperless relies on [Celery - Distributed
Task Queue](https://docs.celeryq.dev/en/stable/index.html) for doing Task Queue](https://docs.celeryq.dev/en/stable/index.html) for doing
most of the heavy lifting. This is a task queue that accepts tasks most of the heavy lifting. This is a task queue that accepts tasks
from multiple sources and processes these in parallel. It also comes from multiple sources and processes these in parallel. It also comes
with a scheduler that executes certain commands periodically. with a scheduler that executes certain commands periodically.
This task processor is responsible for: This task processor is responsible for:
- Consuming documents. When the consumer finds new documents, it - Consuming documents. When the consumer finds new documents, it
notifies the task processor to start a consumption task. notifies the task processor to start a consumption task.
- The task processor also performs the consumption of any - The task processor also performs the consumption of any
documents you upload through the web interface. documents you upload through the web interface.
- Consuming emails. It periodically checks your configured - Consuming emails. It periodically checks your configured
accounts for new emails and notifies the task processor to accounts for new emails and notifies the task processor to
consume the attachment of an email. consume the attachment of an email.
- Maintaining the search index and the automatic matching - Maintaining the search index and the automatic matching
algorithm. These are things that paperless needs to do from time algorithm. These are things that paperless needs to do from time
to time in order to operate properly. to time in order to operate properly.
This allows paperless to process multiple documents from your This allows paperless to process multiple documents from your
consumption folder in parallel! On a modern multi core system, this consumption folder in parallel! On a modern multi core system, this
makes the consumption process with full OCR blazingly fast. makes the consumption process with full OCR blazingly fast.
The task processor comes with a built-in admin interface that you The task processor comes with a built-in admin interface that you
can use to check whenever any of the tasks fail and inspect the can use to check whenever any of the tasks fail and inspect the
errors (i.e., wrong email credentials, errors during consuming a errors (i.e., wrong email credentials, errors during consuming a
specific file, etc). specific file, etc).
- A [redis](https://redis.io/) message broker: This is a really - A [redis](https://redis.io/) message broker: This is a really
lightweight service that is responsible for getting the tasks from lightweight service that is responsible for getting the tasks from
the webserver and the consumer to the task scheduler. These run in a the webserver and the consumer to the task scheduler. These run in a
different process (maybe even on different machines!), and different process (maybe even on different machines!), and
therefore, this is necessary. therefore, this is necessary.
- Optional: A database server. Paperless supports PostgreSQL, MariaDB - Optional: A database server. Paperless supports PostgreSQL, MariaDB
and SQLite for storing its data. and SQLite for storing its data.

View File

@@ -37,11 +37,11 @@ def worker_int(worker):
id2name = {th.ident: th.name for th in threading.enumerate()} id2name = {th.ident: th.name for th in threading.enumerate()}
code = [] code = []
for threadId, stack in sys._current_frames().items(): for threadId, stack in sys._current_frames().items():
code.append("\n# Thread: %s(%d)" % (id2name.get(threadId, ""), threadId)) code.append(f"\n# Thread: {id2name.get(threadId, '')}({threadId})")
for filename, lineno, name, line in traceback.extract_stack(stack): for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('File: "%s", line %d, in %s' % (filename, lineno, name)) code.append(f'File: "{filename}", line {lineno}, in {name}')
if line: if line:
code.append(" %s" % (line.strip())) code.append(f" {line.strip()}")
worker.log.debug("\n".join(code)) worker.log.debug("\n".join(code))

View File

@@ -71,7 +71,17 @@ if ! docker stats --no-stream &> /dev/null ; then
sleep 3 sleep 3
fi fi
default_time_zone=$(timedatectl show -p Timezone --value) # Added handling for timezone for busybox based linux, not having timedatectl available (i.e. QNAP QTS)
# if neither timedatectl nor /etc/TZ is succeeding, defaulting to GMT.
if command -v timedatectl &> /dev/null ; then
default_time_zone=$(timedatectl show -p Timezone --value)
elif [ -f /etc/TZ ] && [ -f /etc/tzlist ] ; then
TZ=$(cat /etc/TZ)
default_time_zone=$(grep -B 1 -m 1 "$TZ" /etc/tzlist | head -1 | cut -f 2 -d =)
else
echo "WARN: unable to detect timezone, defaulting to Etc/UTC"
default_time_zone="Etc/UTC"
fi
set -e set -e
@@ -335,7 +345,7 @@ read -r -a OCR_LANGUAGES_ARRAY <<< "${_split_langs}"
fi fi
echo "PAPERLESS_TIME_ZONE=$TIME_ZONE" echo "PAPERLESS_TIME_ZONE=$TIME_ZONE"
echo "PAPERLESS_OCR_LANGUAGE=$OCR_LANGUAGE" echo "PAPERLESS_OCR_LANGUAGE=$OCR_LANGUAGE"
echo "PAPERLESS_SECRET_KEY=$SECRET_KEY" echo "PAPERLESS_SECRET_KEY='$SECRET_KEY'"
if [[ ! ${DEFAULT_LANGUAGES[*]} =~ ${OCR_LANGUAGES_ARRAY[*]} ]] ; then if [[ ! ${DEFAULT_LANGUAGES[*]} =~ ${OCR_LANGUAGES_ARRAY[*]} ]] ; then
echo "PAPERLESS_OCR_LANGUAGES=${OCR_LANGUAGES_ARRAY[*]}" echo "PAPERLESS_OCR_LANGUAGES=${OCR_LANGUAGES_ARRAY[*]}"
fi fi

View File

@@ -6,6 +6,12 @@ theme:
text: Roboto text: Roboto
code: Roboto Mono code: Roboto Mono
palette: palette:
# Palette toggle for automatic mode
- media: "(prefers-color-scheme)"
toggle:
icon: material/brightness-auto
name: Switch to light mode
# Palette toggle for light mode # Palette toggle for light mode
- media: "(prefers-color-scheme: light)" - media: "(prefers-color-scheme: light)"
scheme: default scheme: default
@@ -18,7 +24,7 @@ theme:
scheme: slate scheme: slate
toggle: toggle:
icon: material/brightness-4 icon: material/brightness-4
name: Switch to light mode name: Switch to system preference
features: features:
- navigation.tabs - navigation.tabs
- navigation.top - navigation.top
@@ -49,6 +55,9 @@ markdown_extensions:
- name: mermaid - name: mermaid
class: mermaid class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
strict: true strict: true
nav: nav:
- index.md - index.md

View File

@@ -0,0 +1,41 @@
{
"folders": [
{
"path": "."
},
{
"path": "./src",
"name": "Backend"
},
{
"path": "./src-ui",
"name": "Frontend"
},
{
"path": "./.github",
"name": "CI/CD"
},
{
"path": "./docs",
"name": "Documentation"
}
],
"settings": {
"files.exclude": {
"**/__pycache__": true,
"**/.mypy_cache": true,
"**/.ruff_cache": true,
"**/.pytest_cache": true,
"**/.idea": true,
"**/.venv": true,
"**/.coverage": true,
"**/coverage.json": true
},
"python.defaultInterpreterPath": ".venv/bin/python3",
},
"extensions": {
"recommendations": ["ms-python.python", "charliermarsh.ruff", "editorconfig.editorconfig"],
"unwantedRecommendations": ["ms-python.black-formatter"]
}
}

View File

@@ -19,7 +19,7 @@
#PAPERLESS_CONSUMPTION_DIR=../consume #PAPERLESS_CONSUMPTION_DIR=../consume
#PAPERLESS_DATA_DIR=../data #PAPERLESS_DATA_DIR=../data
#PAPERLESS_TRASH_DIR= #PAPERLESS_EMPTY_TRASH_DIR=
#PAPERLESS_MEDIA_ROOT=../media #PAPERLESS_MEDIA_ROOT=../media
#PAPERLESS_STATICDIR=../static #PAPERLESS_STATICDIR=../static
#PAPERLESS_FILENAME_FORMAT= #PAPERLESS_FILENAME_FORMAT=

View File

@@ -14,6 +14,7 @@ following additional information about it:
* Thumbnail Path: ${DOCUMENT_THUMBNAIL_PATH} * Thumbnail Path: ${DOCUMENT_THUMBNAIL_PATH}
* Download URL: ${DOCUMENT_DOWNLOAD_URL} * Download URL: ${DOCUMENT_DOWNLOAD_URL}
* Thumbnail URL: ${DOCUMENT_THUMBNAIL_URL} * Thumbnail URL: ${DOCUMENT_THUMBNAIL_URL}
* Owner Name: ${DOCUMENT_OWNER}
* Correspondent: ${DOCUMENT_CORRESPONDENT} * Correspondent: ${DOCUMENT_CORRESPONDENT}
* Tags: ${DOCUMENT_TAGS} * Tags: ${DOCUMENT_TAGS}

View File

@@ -3,4 +3,4 @@
docker run -p 5432:5432 -e POSTGRES_PASSWORD=password -v paperless_pgdata:/var/lib/postgresql/data -d postgres:15 docker run -p 5432:5432 -e POSTGRES_PASSWORD=password -v paperless_pgdata:/var/lib/postgresql/data -d postgres:15
docker run -d -p 6379:6379 redis:latest docker run -d -p 6379:6379 redis:latest
docker run -p 3000:3000 -d gotenberg/gotenberg:7.8 gotenberg --chromium-disable-javascript=true --chromium-allow-list="file:///tmp/.*" docker run -p 3000:3000 -d gotenberg/gotenberg:7.8 gotenberg --chromium-disable-javascript=true --chromium-allow-list="file:///tmp/.*"
docker run -p 9998:9998 -d ghcr.io/paperless-ngx/tika:latest docker run -p 9998:9998 -d docker.io/apache/tika:latest

View File

@@ -33,6 +33,7 @@
"it-IT": "src/locale/messages.it_IT.xlf", "it-IT": "src/locale/messages.it_IT.xlf",
"ja-JP": "src/locale/messages.ja_JP.xlf", "ja-JP": "src/locale/messages.ja_JP.xlf",
"lb-LU": "src/locale/messages.lb_LU.xlf", "lb-LU": "src/locale/messages.lb_LU.xlf",
"ko-KR": "src/locale/messages.ko_KR.xlf",
"nl-NL": "src/locale/messages.nl_NL.xlf", "nl-NL": "src/locale/messages.nl_NL.xlf",
"no-NO": "src/locale/messages.no_NO.xlf", "no-NO": "src/locale/messages.no_NO.xlf",
"pl-PL": "src/locale/messages.pl_PL.xlf", "pl-PL": "src/locale/messages.pl_PL.xlf",
@@ -51,8 +52,11 @@
}, },
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-builders/custom-webpack:browser",
"options": { "options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.ts"
},
"outputPath": "dist/paperless-ui", "outputPath": "dist/paperless-ui",
"outputHashing": "none", "outputHashing": "none",
"index": "src/index.html", "index": "src/index.html",
@@ -66,8 +70,8 @@
"src/assets", "src/assets",
"src/manifest.webmanifest", "src/manifest.webmanifest",
{ {
"glob": "{pdf.worker.min.js,pdf.min.js}", "glob": "{pdf.worker.min.mjs,pdf.min.mjs}",
"input": "node_modules/pdfjs-dist/build/", "input": "node_modules/pdfjs-dist/legacy/build/",
"output": "/assets/js/" "output": "/assets/js/"
} }
], ],
@@ -76,9 +80,7 @@
], ],
"scripts": [], "scripts": [],
"allowedCommonJsDependencies": [ "allowedCommonJsDependencies": [
"pdfjs-dist", "ng2-pdf-viewer",
"pdfjs-dist/web/pdf_viewer",
"filesize",
"file-saver" "file-saver"
], ],
"vendorChunk": true, "vendorChunk": true,
@@ -126,7 +128,7 @@
"defaultConfiguration": "" "defaultConfiguration": ""
}, },
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-builders/custom-webpack:dev-server",
"options": { "options": {
"buildTarget": "paperless-ui:build:en-US" "buildTarget": "paperless-ui:build:en-US"
}, },
@@ -137,7 +139,7 @@
} }
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-builders/custom-webpack:extract-i18n",
"options": { "options": {
"buildTarget": "paperless-ui:build" "buildTarget": "paperless-ui:build"
} }

View File

@@ -9,7 +9,7 @@ test('dashboard inbox link', async ({ page }) => {
await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' }) await page.routeFromHAR(REQUESTS_HAR1, { notFound: 'fallback' })
await page.goto('/dashboard') await page.goto('/dashboard')
await page.getByRole('link', { name: 'Documents in inbox' }).click() await page.getByRole('link', { name: 'Documents in inbox' }).click()
await expect(page).toHaveURL(/tags__id__all=9/) await expect(page).toHaveURL(/tags__id__in=9/)
await expect(page.locator('pngx-document-list')).toHaveText(/8 documents/) await expect(page.locator('pngx-document-list')).toHaveText(/8 documents/)
}) })

View File

@@ -124,7 +124,7 @@
"content": { "content": {
"size": -1, "size": -1,
"mimeType": "application/json", "mimeType": "application/json",
"text": "{\"count\":6,\"next\":null,\"previous\":null,\"all\":[8,17,7,4,11,15],\"results\":[{\"id\":8,\"name\":\"Correspondent 2\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":3,\"value\":\"2\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":17,\"name\":\"In the Last Month\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":20,\"value\":\"created:[-1 month to now]\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":7,\"name\":\"Inbox\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"9\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":4,\"name\":\"Recently Added\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[],\"owner\":\"2\",\"user_can_change\":true},{\"id\":11,\"name\":\"Tag: Another Sample Tag\",\"show_on_dashboard\":false,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"4\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":15,\"name\":\"View ASN not empty\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":18,\"value\":\"false\"}],\"owner\":\"2\",\"user_can_change\":true}]}" "text": "{\"count\":6,\"next\":null,\"previous\":null,\"all\":[8,17,7,4,11,15],\"results\":[{\"id\":8,\"name\":\"Correspondent 2\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":3,\"value\":\"2\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":17,\"name\":\"In the Last Month\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":20,\"value\":\"created:[-1 month to now]\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":7,\"name\":\"Inbox\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"9\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":4,\"name\":\"Recently Added\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":11,\"name\":\"Tag: Another Sample Tag\",\"show_on_dashboard\":false,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"4\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":15,\"name\":\"View ASN not empty\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":18,\"value\":\"false\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]}]}"
}, },
"headersSize": -1, "headersSize": -1,
"bodySize": -1, "bodySize": -1,
@@ -236,7 +236,7 @@
"content": { "content": {
"size": -1, "size": -1,
"mimeType": "application/json", "mimeType": "application/json",
"text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tag\":9,\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}" "text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tags\":[9],\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}"
}, },
"headersSize": -1, "headersSize": -1,
"bodySize": -1, "bodySize": -1,
@@ -250,7 +250,7 @@
"time": 0.609, "time": 0.609,
"request": { "request": {
"method": "GET", "method": "GET",
"url": "http://localhost:8000/api/documents/?page=1&page_size=10&ordering=-created&truncate_content=true&tags__id__all=9", "url": "http://localhost:8000/api/documents/?page=1&page_size=10&ordering=-created&truncate_content=true&tags__id__in=9",
"httpVersion": "HTTP/1.1", "httpVersion": "HTTP/1.1",
"cookies": [], "cookies": [],
"headers": [ "headers": [
@@ -284,7 +284,7 @@
"value": "true" "value": "true"
}, },
{ {
"name": "tags__id__all", "name": "tags__id__in",
"value": "9" "value": "9"
} }
], ],
@@ -468,7 +468,7 @@
"time": 0.951, "time": 0.951,
"request": { "request": {
"method": "GET", "method": "GET",
"url": "http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&truncate_content=true&tags__id__all=9", "url": "http://localhost:8000/api/documents/?page=1&page_size=50&ordering=-created&truncate_content=true&tags__id__in=9",
"httpVersion": "HTTP/1.1", "httpVersion": "HTTP/1.1",
"cookies": [], "cookies": [],
"headers": [ "headers": [
@@ -502,7 +502,7 @@
"value": "true" "value": "true"
}, },
{ {
"name": "tags__id__all", "name": "tags__id__in",
"value": "9" "value": "9"
} }
], ],

View File

@@ -124,7 +124,7 @@
"content": { "content": {
"size": -1, "size": -1,
"mimeType": "application/json", "mimeType": "application/json",
"text": "{\"count\":6,\"next\":null,\"previous\":null,\"all\":[8,17,7,4,11,15],\"results\":[{\"id\":8,\"name\":\"Correspondent 2\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":3,\"value\":\"2\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":17,\"name\":\"In the Last Month\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":20,\"value\":\"created:[-1 month to now]\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":7,\"name\":\"Inbox\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"9\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":4,\"name\":\"Recently Added\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[],\"owner\":\"2\",\"user_can_change\":true},{\"id\":11,\"name\":\"Tag: Another Sample Tag\",\"show_on_dashboard\":false,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"4\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":15,\"name\":\"View ASN not empty\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":18,\"value\":\"false\"}],\"owner\":\"2\",\"user_can_change\":true}]}" "text": "{\"count\":6,\"next\":null,\"previous\":null,\"all\":[8,17,7,4,11,15],\"results\":[{\"id\":8,\"name\":\"Correspondent 2\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":3,\"value\":\"2\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":17,\"name\":\"In the Last Month\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":20,\"value\":\"created:[-1 month to now]\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":7,\"name\":\"Inbox\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"9\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":4,\"name\":\"Recently Added\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":11,\"name\":\"Tag: Another Sample Tag\",\"show_on_dashboard\":false,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"4\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":15,\"name\":\"View ASN not empty\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":18,\"value\":\"false\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]}]}"
}, },
"headersSize": -1, "headersSize": -1,
"bodySize": -1, "bodySize": -1,
@@ -236,7 +236,7 @@
"content": { "content": {
"size": -1, "size": -1,
"mimeType": "application/json", "mimeType": "application/json",
"text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tag\":9,\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}" "text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tags\":[9],\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}"
}, },
"headersSize": -1, "headersSize": -1,
"bodySize": -1, "bodySize": -1,
@@ -250,7 +250,7 @@
"time": 0.622, "time": 0.622,
"request": { "request": {
"method": "GET", "method": "GET",
"url": "http://localhost:8000/api/documents/?page=1&page_size=10&ordering=-created&truncate_content=true&tags__id__all=9", "url": "http://localhost:8000/api/documents/?page=1&page_size=10&ordering=-created&truncate_content=true&tags__id__in=9",
"httpVersion": "HTTP/1.1", "httpVersion": "HTTP/1.1",
"cookies": [], "cookies": [],
"headers": [ "headers": [
@@ -284,7 +284,7 @@
"value": "true" "value": "true"
}, },
{ {
"name": "tags__id__all", "name": "tags__id__in",
"value": "9" "value": "9"
} }
], ],

View File

@@ -124,7 +124,7 @@
"content": { "content": {
"size": -1, "size": -1,
"mimeType": "application/json", "mimeType": "application/json",
"text": "{\"count\":6,\"next\":null,\"previous\":null,\"all\":[8,17,7,4,11,15],\"results\":[{\"id\":8,\"name\":\"Correspondent 2\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":3,\"value\":\"2\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":17,\"name\":\"In the Last Month\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":20,\"value\":\"created:[-1 month to now]\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":7,\"name\":\"Inbox\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"9\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":4,\"name\":\"Recently Added\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[],\"owner\":\"2\",\"user_can_change\":true},{\"id\":11,\"name\":\"Tag: Another Sample Tag\",\"show_on_dashboard\":false,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"4\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":15,\"name\":\"View ASN not empty\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":18,\"value\":\"false\"}],\"owner\":\"2\",\"user_can_change\":true}]}" "text": "{\"count\":6,\"next\":null,\"previous\":null,\"all\":[8,17,7,4,11,15],\"results\":[{\"id\":8,\"name\":\"Correspondent 2\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":3,\"value\":\"2\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":17,\"name\":\"In the Last Month\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":20,\"value\":\"created:[-1 month to now]\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":7,\"name\":\"Inbox\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"9\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":4,\"name\":\"Recently Added\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":11,\"name\":\"Tag: Another Sample Tag\",\"show_on_dashboard\":false,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"4\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":15,\"name\":\"View ASN not empty\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":18,\"value\":\"false\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]}]}"
}, },
"headersSize": -1, "headersSize": -1,
"bodySize": -1, "bodySize": -1,
@@ -236,7 +236,7 @@
"content": { "content": {
"size": -1, "size": -1,
"mimeType": "application/json", "mimeType": "application/json",
"text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tag\":9,\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}" "text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tags\":[9],\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}"
}, },
"headersSize": -1, "headersSize": -1,
"bodySize": -1, "bodySize": -1,

View File

@@ -124,7 +124,7 @@
"content": { "content": {
"size": -1, "size": -1,
"mimeType": "application/json", "mimeType": "application/json",
"text": "{\"count\":6,\"next\":null,\"previous\":null,\"all\":[8,17,7,4,11,15],\"results\":[{\"id\":8,\"name\":\"Correspondent 2\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":3,\"value\":\"2\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":7,\"name\":\"Inbox\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"9\"}],\"owner\":\"2\",\"user_can_change\":true},{\"id\":11,\"name\":\"Tag: Another Sample Tag\",\"show_on_dashboard\":false,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"4\"}],\"owner\":\"2\",\"user_can_change\":true}]}" "text": "{\"count\":6,\"next\":null,\"previous\":null,\"all\":[8,17,7,4,11,15],\"results\":[{\"id\":8,\"name\":\"Correspondent 2\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":3,\"value\":\"2\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":17,\"name\":\"In the Last Month\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":20,\"value\":\"created:[-1 month to now]\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":7,\"name\":\"Inbox\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"9\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":4,\"name\":\"Recently Added\",\"show_on_dashboard\":true,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":11,\"name\":\"Tag: Another Sample Tag\",\"show_on_dashboard\":false,\"show_in_sidebar\":true,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":6,\"value\":\"4\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]},{\"id\":15,\"name\":\"View ASN not empty\",\"show_on_dashboard\":false,\"show_in_sidebar\":false,\"sort_field\":\"created\",\"sort_reverse\":true,\"filter_rules\":[{\"rule_type\":18,\"value\":\"false\"}],\"owner\":\"2\",\"user_can_change\":true,\"page_size\":10,\"display_mode\":\"table\",\"display_fields\":[\"created\",\"title\",\"tag\",\"documenttype\"]}]}"
}, },
"headersSize": -1, "headersSize": -1,
"bodySize": -1, "bodySize": -1,
@@ -236,7 +236,7 @@
"content": { "content": {
"size": -1, "size": -1,
"mimeType": "application/json", "mimeType": "application/json",
"text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tag\":9,\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}" "text": "{\"documents_total\":61,\"documents_inbox\":8,\"inbox_tags\":[9],\"document_file_type_counts\":[{\"mime_type\":\"application/pdf\",\"mime_type_count\":57},{\"mime_type\":\"text/plain\",\"mime_type_count\":3},{\"mime_type\":\"text/csv\",\"mime_type_count\":1}],\"character_count\":2407053}"
}, },
"headersSize": -1, "headersSize": -1,
"bodySize": -1, "bodySize": -1,

View File

@@ -71,7 +71,7 @@ test('should show a mobile preview', async ({ page }) => {
await page.setViewportSize({ width: 400, height: 1000 }) await page.setViewportSize({ width: 400, height: 1000 })
await expect(page.getByRole('tab', { name: 'Preview' })).toBeVisible() await expect(page.getByRole('tab', { name: 'Preview' })).toBeVisible()
await page.getByRole('tab', { name: 'Preview' }).click() await page.getByRole('tab', { name: 'Preview' }).click()
await page.waitForSelector('pngx-pdf-viewer') await page.waitForSelector('pdf-viewer')
}) })
test('should show a list of notes', async ({ page }) => { test('should show a list of notes', async ({ page }) => {

View File

@@ -45,8 +45,8 @@ test('basic filtering', async ({ page }) => {
test('text filtering', async ({ page }) => { test('text filtering', async ({ page }) => {
await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' }) await page.routeFromHAR(REQUESTS_HAR2, { notFound: 'fallback' })
await page.goto('/documents') await page.goto('/documents')
await page.getByRole('textbox').click() await page.getByRole('main').getByRole('combobox').click()
await page.getByRole('textbox').fill('test') await page.getByRole('main').getByRole('combobox').fill('test')
await expect(page.locator('pngx-document-list')).toHaveText(/32 documents/) await expect(page.locator('pngx-document-list')).toHaveText(/32 documents/)
await expect(page).toHaveURL(/title_content=test/) await expect(page).toHaveURL(/title_content=test/)
await page.getByRole('button', { name: 'Title & content' }).click() await page.getByRole('button', { name: 'Title & content' }).click()
@@ -59,12 +59,12 @@ test('text filtering', async ({ page }) => {
await expect(page.locator('pngx-document-list')).toHaveText(/26 documents/) await expect(page.locator('pngx-document-list')).toHaveText(/26 documents/)
await page.getByRole('button', { name: 'Advanced search' }).click() await page.getByRole('button', { name: 'Advanced search' }).click()
await page.getByRole('button', { name: 'ASN' }).click() await page.getByRole('button', { name: 'ASN' }).click()
await page.getByRole('textbox').fill('1123') await page.getByRole('main').getByRole('combobox').nth(1).fill('1123')
await expect(page).toHaveURL(/archive_serial_number=1123/) await expect(page).toHaveURL(/archive_serial_number=1123/)
await expect(page.locator('pngx-document-list')).toHaveText(/one document/i) await expect(page.locator('pngx-document-list')).toHaveText(/one document/i)
await page.locator('select').selectOption('greater') await page.locator('select').selectOption('greater')
await page.getByRole('textbox').click() await page.getByRole('main').getByRole('combobox').nth(1).click()
await page.getByRole('textbox').fill('1123') await page.getByRole('main').getByRole('combobox').nth(1).fill('1123')
await expect(page).toHaveURL(/archive_serial_number__gt=1123/) await expect(page).toHaveURL(/archive_serial_number__gt=1123/)
await expect(page.locator('pngx-document-list')).toHaveText(/5 documents/) await expect(page.locator('pngx-document-list')).toHaveText(/5 documents/)
await page.locator('select').selectOption('less') await page.locator('select').selectOption('less')
@@ -81,15 +81,11 @@ test('text filtering', async ({ page }) => {
test('date filtering', async ({ page }) => { test('date filtering', async ({ page }) => {
await page.routeFromHAR(REQUESTS_HAR3, { notFound: 'fallback' }) await page.routeFromHAR(REQUESTS_HAR3, { notFound: 'fallback' })
await page.goto('/documents') await page.goto('/documents')
await page.getByRole('button', { name: 'Created' }).click() await page.getByRole('button', { name: 'Dates' }).click()
await page.getByRole('menuitem', { name: 'Last 3 months' }).click() await page.getByRole('menuitem', { name: 'Last 3 months' }).first().click()
await expect(page.locator('pngx-document-list')).toHaveText(/one document/i) await expect(page.locator('pngx-document-list')).toHaveText(/one document/i)
await page.getByRole('button', { name: 'Created Clear selected' }).click() await page.getByRole('menuitem', { name: 'Last 3 months' }).first().click()
await page.getByRole('button', { name: 'Created' }).click() await page.getByLabel('Datesselected').getByRole('button').first().click()
await page
.getByRole('menuitem', { name: 'After mm/dd/yyyy' })
.getByRole('button')
.click()
await page.getByRole('combobox', { name: 'Select month' }).selectOption('12') await page.getByRole('combobox', { name: 'Select month' }).selectOption('12')
await page.getByRole('combobox', { name: 'Select year' }).selectOption('2022') await page.getByRole('combobox', { name: 'Select year' }).selectOption('2022')
await page.getByText('11', { exact: true }).click() await page.getByText('11', { exact: true }).click()
@@ -138,11 +134,11 @@ test('sorting', async ({ page }) => {
test('change views', async ({ page }) => { test('change views', async ({ page }) => {
await page.routeFromHAR(REQUESTS_HAR5, { notFound: 'fallback' }) await page.routeFromHAR(REQUESTS_HAR5, { notFound: 'fallback' })
await page.goto('/documents') await page.goto('/documents')
await page.locator('pngx-page-header label').first().click() await page.locator('.btn-group label').first().click()
await expect(page.locator('pngx-document-list table')).toBeVisible() await expect(page.locator('pngx-document-list table')).toBeVisible()
await page.locator('pngx-page-header label').nth(1).click() await page.locator('.btn-group label').nth(1).click()
await expect(page.locator('pngx-document-card-small').first()).toBeAttached() await expect(page.locator('pngx-document-card-small').first()).toBeAttached()
await page.locator('pngx-page-header label').nth(2).click() await page.locator('.btn-group label').nth(2).click()
await expect(page.locator('pngx-document-card-large').first()).toBeAttached() await expect(page.locator('pngx-document-card-large').first()).toBeAttached()
}) })

View File

@@ -0,0 +1,24 @@
import * as webpack from 'webpack'
import {
CustomWebpackBrowserSchema,
TargetOptions,
} from '@angular-builders/custom-webpack'
const { codecovWebpackPlugin } = require('@codecov/webpack-plugin')
export default (
config: webpack.Configuration,
options: CustomWebpackBrowserSchema,
targetOptions: TargetOptions
) => {
if (config.plugins) {
config.plugins.push(
codecovWebpackPlugin({
enableBundleAnalysis: process.env.CODECOV_TOKEN !== undefined,
bundleName: 'paperless-ngx',
uploadToken: process.env.CODECOV_TOKEN,
})
)
}
return config
}

View File

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

File diff suppressed because it is too large Load Diff

10470
src-ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,58 +11,60 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/cdk": "^17.2.1", "@angular/cdk": "^18.2.11",
"@angular/common": "~17.2.3", "@angular/common": "~18.2.10",
"@angular/compiler": "~17.2.3", "@angular/compiler": "~18.2.10",
"@angular/core": "~17.2.3", "@angular/core": "~18.2.10",
"@angular/forms": "~17.2.3", "@angular/forms": "~18.2.10",
"@angular/localize": "~17.2.3", "@angular/localize": "~18.2.10",
"@angular/platform-browser": "~17.2.3", "@angular/platform-browser": "~18.2.10",
"@angular/platform-browser-dynamic": "~17.2.3", "@angular/platform-browser-dynamic": "~18.2.10",
"@angular/router": "~17.2.3", "@angular/router": "~18.2.10",
"@ng-bootstrap/ng-bootstrap": "^16.0.0", "@ng-bootstrap/ng-bootstrap": "^17.0.1",
"@ng-select/ng-select": "^12.0.7", "@ng-select/ng-select": "^13.9.1",
"@ngneat/dirty-check-forms": "^3.0.3", "@ngneat/dirty-check-forms": "^3.0.3",
"@popperjs/core": "^2.11.8", "@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"mime-names": "^1.0.0", "mime-names": "^1.0.0",
"ng2-pdf-viewer": "^10.3.4",
"ngx-bootstrap-icons": "^1.9.3", "ngx-bootstrap-icons": "^1.9.3",
"ngx-color": "^9.0.0", "ngx-color": "^9.0.0",
"ngx-cookie-service": "^17.1.0", "ngx-cookie-service": "^18.0.0",
"ngx-file-drop": "^16.0.0", "ngx-file-drop": "^16.0.0",
"ngx-filesize": "^3.0.3", "ngx-ui-tour-ng-bootstrap": "^15.0.0",
"ngx-ui-tour-ng-bootstrap": "^14.0.2",
"pdfjs-dist": "^3.11.174",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"tslib": "^2.6.2", "tslib": "^2.8.1",
"uuid": "^9.0.1", "uuid": "^11.0.2",
"zone.js": "^0.14.4" "zone.js": "^0.14.8"
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/jest": "17.0.2", "@angular-builders/custom-webpack": "^18.0.0",
"@angular-devkit/build-angular": "~17.2.2", "@angular-builders/jest": "^18.0.0",
"@angular-eslint/builder": "17.2.1", "@angular-devkit/build-angular": "^18.2.2",
"@angular-eslint/eslint-plugin": "17.2.1", "@angular-devkit/core": "^18.2.11",
"@angular-eslint/eslint-plugin-template": "17.2.1", "@angular-devkit/schematics": "^18.2.11",
"@angular-eslint/schematics": "17.2.1", "@angular-eslint/builder": "18.4.0",
"@angular-eslint/template-parser": "17.2.1", "@angular-eslint/eslint-plugin": "18.4.0",
"@angular/cli": "~17.2.2", "@angular-eslint/eslint-plugin-template": "18.4.0",
"@angular/compiler-cli": "~17.2.2", "@angular-eslint/schematics": "18.4.0",
"@playwright/test": "^1.42.0", "@angular-eslint/template-parser": "18.4.0",
"@types/jest": "^29.5.12", "@angular/cli": "~18.2.11",
"@types/node": "^20.11.24", "@angular/compiler-cli": "~18.2.2",
"@typescript-eslint/eslint-plugin": "^7.1.0", "@codecov/webpack-plugin": "^1.2.1",
"@typescript-eslint/parser": "^7.1.0", "@playwright/test": "^1.48.2",
"concurrently": "^8.2.2", "@types/jest": "^29.5.14",
"eslint": "^8.57.0", "@types/node": "^22.8.6",
"@typescript-eslint/eslint-plugin": "^8.12.2",
"@typescript-eslint/parser": "^8.12.2",
"@typescript-eslint/utils": "^8.0.0",
"eslint": "^9.14.0",
"jest": "29.7.0", "jest": "29.7.0",
"jest-environment-jsdom": "^29.7.0", "jest-environment-jsdom": "^29.7.0",
"jest-preset-angular": "^14.0.0", "jest-preset-angular": "^14.2.4",
"jest-websocket-mock": "^2.5.0", "jest-websocket-mock": "^2.5.0",
"patch-package": "^8.0.0", "patch-package": "^8.0.0",
"ts-node": "~10.9.1", "ts-node": "~10.9.1",
"typescript": "^5.3.3", "typescript": "^5.5.4"
"wait-on": "^7.2.0"
} }
} }

View File

@@ -24,6 +24,7 @@ import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu' import localeHu from '@angular/common/locales/hu'
import localeIt from '@angular/common/locales/it' import localeIt from '@angular/common/locales/it'
import localeJa from '@angular/common/locales/ja' import localeJa from '@angular/common/locales/ja'
import localeKo from '@angular/common/locales/ko'
import localeLb from '@angular/common/locales/lb' import localeLb from '@angular/common/locales/lb'
import localeNl from '@angular/common/locales/nl' import localeNl from '@angular/common/locales/nl'
import localeNo from '@angular/common/locales/no' import localeNo from '@angular/common/locales/no'
@@ -55,6 +56,7 @@ registerLocaleData(localeFr)
registerLocaleData(localeHu) registerLocaleData(localeHu)
registerLocaleData(localeIt) registerLocaleData(localeIt)
registerLocaleData(localeJa) registerLocaleData(localeJa)
registerLocaleData(localeKo)
registerLocaleData(localeLb) registerLocaleData(localeLb)
registerLocaleData(localeNl) registerLocaleData(localeNl)
registerLocaleData(localeNo) registerLocaleData(localeNo)
@@ -76,12 +78,16 @@ const mock = () => {
let storage: { [key: string]: string } = {} let storage: { [key: string]: string } = {}
return { return {
getItem: (key: string) => (key in storage ? storage[key] : null), getItem: (key: string) => (key in storage ? storage[key] : null),
setItem: (key: string, value: string) => (storage[key] = value || ''), setItem: (key: string, value: string) => {
if (value.length > 1000000) throw new Error('localStorage overflow')
storage[key] = value || ''
},
removeItem: (key: string) => delete storage[key], removeItem: (key: string) => delete storage[key],
clear: () => (storage = {}), clear: () => (storage = {}),
} }
} }
Object.defineProperty(window, 'open', { value: jest.fn() })
Object.defineProperty(window, 'localStorage', { value: mock() }) Object.defineProperty(window, 'localStorage', { value: mock() })
Object.defineProperty(window, 'sessionStorage', { value: mock() }) Object.defineProperty(window, 'sessionStorage', { value: mock() })
Object.defineProperty(window, 'getComputedStyle', { Object.defineProperty(window, 'getComputedStyle', {

View File

@@ -26,6 +26,7 @@ import { MailComponent } from './components/manage/mail/mail.component'
import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component' import { UsersAndGroupsComponent } from './components/admin/users-groups/users-groups.component'
import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component' import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component'
import { ConfigComponent } from './components/admin/config/config.component' import { ConfigComponent } from './components/admin/config/config.component'
import { TrashComponent } from './components/admin/trash/trash.component'
export const routes: Routes = [ export const routes: Routes = [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' }, { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
@@ -140,10 +141,18 @@ export const routes: Routes = [
path: 'logs', path: 'logs',
component: LogsComponent, component: LogsComponent,
canActivate: [PermissionsGuard], canActivate: [PermissionsGuard],
data: {
requireAdmin: true,
},
},
{
path: 'trash',
component: TrashComponent,
canActivate: [PermissionsGuard],
data: { data: {
requiredPermission: { requiredPermission: {
action: PermissionAction.View, action: PermissionAction.Delete,
type: PermissionType.Admin, type: PermissionType.Document,
}, },
}, },
}, },

View File

@@ -1,12 +1,11 @@
import { HttpClientTestingModule } from '@angular/common/http/testing' import { provideHttpClientTesting } from '@angular/common/http/testing'
import { import {
ComponentFixture, ComponentFixture,
TestBed, TestBed,
fakeAsync, fakeAsync,
tick, tick,
} from '@angular/core/testing' } from '@angular/core/testing'
import { Router } from '@angular/router' import { Router, RouterModule } from '@angular/router'
import { RouterTestingModule } from '@angular/router/testing'
import { TourService, TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap' import { TourService, TourNgBootstrapModule } from 'ngx-ui-tour-ng-bootstrap'
import { Subject } from 'rxjs' import { Subject } from 'rxjs'
import { routes } from './app-routing.module' import { routes } from './app-routing.module'
@@ -21,6 +20,11 @@ import { ToastService, Toast } from './services/toast.service'
import { SettingsService } from './services/settings.service' import { SettingsService } from './services/settings.service'
import { FileDropComponent } from './components/file-drop/file-drop.component' import { FileDropComponent } from './components/file-drop/file-drop.component'
import { NgxFileDropModule } from 'ngx-file-drop' import { NgxFileDropModule } from 'ngx-file-drop'
import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap'
import { HotKeyService } from './services/hot-key.service'
import { PermissionsGuard } from './guards/permissions.guard'
import { DirtySavedViewGuard } from './guards/dirty-saved-view.guard'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
describe('AppComponent', () => { describe('AppComponent', () => {
let component: AppComponent let component: AppComponent
@@ -31,16 +35,22 @@ describe('AppComponent', () => {
let toastService: ToastService let toastService: ToastService
let router: Router let router: Router
let settingsService: SettingsService let settingsService: SettingsService
let hotKeyService: HotKeyService
beforeEach(async () => { beforeEach(async () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [AppComponent, ToastsComponent, FileDropComponent], declarations: [AppComponent, ToastsComponent, FileDropComponent],
providers: [],
imports: [ imports: [
HttpClientTestingModule,
TourNgBootstrapModule, TourNgBootstrapModule,
RouterTestingModule.withRoutes(routes), RouterModule.forRoot(routes),
NgxFileDropModule, NgxFileDropModule,
NgbModalModule,
],
providers: [
PermissionsGuard,
DirtySavedViewGuard,
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
], ],
}).compileComponents() }).compileComponents()
@@ -50,6 +60,7 @@ describe('AppComponent', () => {
settingsService = TestBed.inject(SettingsService) settingsService = TestBed.inject(SettingsService)
toastService = TestBed.inject(ToastService) toastService = TestBed.inject(ToastService)
router = TestBed.inject(Router) router = TestBed.inject(Router)
hotKeyService = TestBed.inject(HotKeyService)
fixture = TestBed.createComponent(AppComponent) fixture = TestBed.createComponent(AppComponent)
component = fixture.componentInstance component = fixture.componentInstance
}) })
@@ -139,4 +150,20 @@ describe('AppComponent', () => {
fileStatusSubject.next(new FileStatus()) fileStatusSubject.next(new FileStatus())
expect(toastSpy).toHaveBeenCalled() expect(toastSpy).toHaveBeenCalled()
}) })
it('should support hotkeys', () => {
const addShortcutSpy = jest.spyOn(hotKeyService, 'addShortcut')
const routerSpy = jest.spyOn(router, 'navigate')
// prevent actual navigation
routerSpy.mockReturnValue(new Promise(() => {}))
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
component.ngOnInit()
expect(addShortcutSpy).toHaveBeenCalled()
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'h' }))
expect(routerSpy).toHaveBeenCalledWith(['/dashboard'])
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'd' }))
expect(routerSpy).toHaveBeenCalledWith(['/documents'])
document.dispatchEvent(new KeyboardEvent('keydown', { key: 's' }))
expect(routerSpy).toHaveBeenCalledWith(['/settings'])
})
}) })

View File

@@ -12,6 +12,7 @@ import {
PermissionsService, PermissionsService,
PermissionType, PermissionType,
} from './services/permissions.service' } from './services/permissions.service'
import { HotKeyService } from './services/hot-key.service'
@Component({ @Component({
selector: 'pngx-root', selector: 'pngx-root',
@@ -31,8 +32,11 @@ export class AppComponent implements OnInit, OnDestroy {
private tasksService: TasksService, private tasksService: TasksService,
public tourService: TourService, public tourService: TourService,
private renderer: Renderer2, private renderer: Renderer2,
private permissionsService: PermissionsService private permissionsService: PermissionsService,
private hotKeyService: HotKeyService
) { ) {
let anyWindow = window as any
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.mjs'
this.settings.updateAppearanceSettings() this.settings.updateAppearanceSettings()
} }
@@ -123,6 +127,36 @@ export class AppComponent implements OnInit, OnDestroy {
} }
}) })
this.hotKeyService
.addShortcut({ keys: 'h', description: $localize`Dashboard` })
.subscribe(() => {
this.router.navigate(['/dashboard'])
})
if (
this.permissionsService.currentUserCan(
PermissionAction.View,
PermissionType.Document
)
) {
this.hotKeyService
.addShortcut({ keys: 'd', description: $localize`Documents` })
.subscribe(() => {
this.router.navigate(['/documents'])
})
}
if (
this.permissionsService.currentUserCan(
PermissionAction.Change,
PermissionType.UISettings
)
) {
this.hotKeyService
.addShortcut({ keys: 's', description: $localize`Settings` })
.subscribe(() => {
this.router.navigate(['/settings'])
})
}
const prevBtnTitle = $localize`Prev` const prevBtnTitle = $localize`Prev`
const nextBtnTitle = $localize`Next` const nextBtnTitle = $localize`Next`
const endBtnTitle = $localize`End` const endBtnTitle = $localize`End`

View File

@@ -7,7 +7,11 @@ import {
NgbDateParserFormatter, NgbDateParserFormatter,
NgbModule, NgbModule,
} from '@ng-bootstrap/ng-bootstrap' } from '@ng-bootstrap/ng-bootstrap'
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http' import {
HTTP_INTERCEPTORS,
provideHttpClient,
withInterceptorsFromDi,
} from '@angular/common/http'
import { DocumentListComponent } from './components/document-list/document-list.component' import { DocumentListComponent } from './components/document-list/document-list.component'
import { DocumentDetailComponent } from './components/document-detail/document-detail.component' import { DocumentDetailComponent } from './components/document-detail/document-detail.component'
import { DashboardComponent } from './components/dashboard/dashboard.component' import { DashboardComponent } from './components/dashboard/dashboard.component'
@@ -31,12 +35,13 @@ import { ToastsComponent } from './components/common/toasts/toasts.component'
import { FilterEditorComponent } from './components/document-list/filter-editor/filter-editor.component' import { FilterEditorComponent } from './components/document-list/filter-editor/filter-editor.component'
import { FilterableDropdownComponent } from './components/common/filterable-dropdown/filterable-dropdown.component' import { FilterableDropdownComponent } from './components/common/filterable-dropdown/filterable-dropdown.component'
import { ToggleableDropdownButtonComponent } from './components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component' import { ToggleableDropdownButtonComponent } from './components/common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component'
import { DateDropdownComponent } from './components/common/date-dropdown/date-dropdown.component' import { DatesDropdownComponent } from './components/common/dates-dropdown/dates-dropdown.component'
import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component' import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component'
import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component' import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component'
import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component' import { BulkEditorComponent } from './components/document-list/bulk-editor/bulk-editor.component'
import { NgxFileDropModule } from 'ngx-file-drop' import { NgxFileDropModule } from 'ngx-file-drop'
import { TextComponent } from './components/common/input/text/text.component' import { TextComponent } from './components/common/input/text/text.component'
import { TextAreaComponent } from './components/common/input/textarea/textarea.component'
import { SelectComponent } from './components/common/input/select/select.component' import { SelectComponent } from './components/common/input/select/select.component'
import { CheckComponent } from './components/common/input/check/check.component' import { CheckComponent } from './components/common/input/check/check.component'
import { UrlComponent } from './components/common/input/url/url.component' import { UrlComponent } from './components/common/input/url/url.component'
@@ -104,8 +109,9 @@ import { FileDropComponent } from './components/file-drop/file-drop.component'
import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component' import { CustomFieldsComponent } from './components/manage/custom-fields/custom-fields.component'
import { CustomFieldEditDialogComponent } from './components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component' import { CustomFieldEditDialogComponent } from './components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
import { CustomFieldsDropdownComponent } from './components/common/custom-fields-dropdown/custom-fields-dropdown.component' import { CustomFieldsDropdownComponent } from './components/common/custom-fields-dropdown/custom-fields-dropdown.component'
import { CustomFieldsQueryDropdownComponent } from './components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component'
import { ProfileEditDialogComponent } from './components/common/profile-edit-dialog/profile-edit-dialog.component' import { ProfileEditDialogComponent } from './components/common/profile-edit-dialog/profile-edit-dialog.component'
import { PdfViewerComponent } from './components/common/pdf-viewer/pdf-viewer.component' import { PdfViewerModule } from 'ng2-pdf-viewer'
import { DocumentLinkComponent } from './components/common/input/document-link/document-link.component' import { DocumentLinkComponent } from './components/common/input/document-link/document-link.component'
import { PreviewPopupComponent } from './components/common/preview-popup/preview-popup.component' import { PreviewPopupComponent } from './components/common/preview-popup/preview-popup.component'
import { SwitchComponent } from './components/common/input/switch/switch.component' import { SwitchComponent } from './components/common/input/switch/switch.component'
@@ -115,10 +121,20 @@ import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { ConfirmButtonComponent } from './components/common/confirm-button/confirm-button.component' import { ConfirmButtonComponent } from './components/common/confirm-button/confirm-button.component'
import { MonetaryComponent } from './components/common/input/monetary/monetary.component' import { MonetaryComponent } from './components/common/input/monetary/monetary.component'
import { SystemStatusDialogComponent } from './components/common/system-status-dialog/system-status-dialog.component' import { SystemStatusDialogComponent } from './components/common/system-status-dialog/system-status-dialog.component'
import { NgxFilesizeModule } from 'ngx-filesize' import { RotateConfirmDialogComponent } from './components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component'
import { MergeConfirmDialogComponent } from './components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component'
import { SplitConfirmDialogComponent } from './components/common/confirm-dialog/split-confirm-dialog/split-confirm-dialog.component'
import { DocumentHistoryComponent } from './components/document-history/document-history.component'
import { DragDropSelectComponent } from './components/common/input/drag-drop-select/drag-drop-select.component'
import { CustomFieldDisplayComponent } from './components/common/custom-field-display/custom-field-display.component'
import { GlobalSearchComponent } from './components/app-frame/global-search/global-search.component'
import { HotkeyDialogComponent } from './components/common/hotkey-dialog/hotkey-dialog.component'
import { DeletePagesConfirmDialogComponent } from './components/common/confirm-dialog/delete-pages-confirm-dialog/delete-pages-confirm-dialog.component'
import { TrashComponent } from './components/admin/trash/trash.component'
import { import {
airplane, airplane,
archive, archive,
arrowClockwise,
arrowCounterclockwise, arrowCounterclockwise,
arrowDown, arrowDown,
arrowLeft, arrowLeft,
@@ -127,12 +143,17 @@ import {
arrowRightShort, arrowRightShort,
arrowUpRight, arrowUpRight,
asterisk, asterisk,
braces,
bodyText,
boxArrowInRight,
boxArrowUp, boxArrowUp,
boxArrowUpRight, boxArrowUpRight,
boxes, boxes,
calendar, calendar,
calendarEvent, calendarEvent,
calendarEventFill,
cardChecklist, cardChecklist,
cardHeading,
caretDown, caretDown,
caretUp, caretUp,
chatLeftText, chatLeftText,
@@ -148,11 +169,14 @@ import {
clipboardCheckFill, clipboardCheckFill,
clipboardFill, clipboardFill,
dash, dash,
dashCircle,
diagram3, diagram3,
dice5, dice5,
doorOpen, doorOpen,
download, download,
envelope, envelope,
envelopeAt,
envelopeAtFill,
exclamationCircleFill, exclamationCircleFill,
exclamationTriangle, exclamationTriangle,
exclamationTriangleFill, exclamationTriangleFill,
@@ -161,6 +185,7 @@ import {
fileEarmarkCheck, fileEarmarkCheck,
fileEarmarkFill, fileEarmarkFill,
fileEarmarkLock, fileEarmarkLock,
fileEarmarkMinus,
files, files,
fileText, fileText,
filter, filter,
@@ -168,15 +193,19 @@ import {
folderFill, folderFill,
funnel, funnel,
gear, gear,
google,
grid, grid,
gripVertical, gripVertical,
hash, hash,
hddStack, hddStack,
house, house,
infoCircle, infoCircle,
journals,
link, link,
listTask, listTask,
listUl, listUl,
microsoft,
nodePlus,
pencil, pencil,
people, people,
peopleFill, peopleFill,
@@ -185,15 +214,18 @@ import {
personFill, personFill,
personFillLock, personFillLock,
personLock, personLock,
personSquare,
plus, plus,
plusCircle, plusCircle,
questionCircle, questionCircle,
scissors,
search, search,
slashCircle, slashCircle,
sliders2Vertical, sliders2Vertical,
sortAlphaDown, sortAlphaDown,
sortAlphaUpAlt, sortAlphaUpAlt,
tagFill, tagFill,
tag,
tags, tags,
textIndentLeft, textIndentLeft,
textLeft, textLeft,
@@ -203,12 +235,14 @@ import {
uiRadios, uiRadios,
upcScan, upcScan,
x, x,
xCircle,
xLg, xLg,
} from 'ngx-bootstrap-icons' } from 'ngx-bootstrap-icons'
const icons = { const icons = {
airplane, airplane,
archive, archive,
arrowClockwise,
arrowCounterclockwise, arrowCounterclockwise,
arrowDown, arrowDown,
arrowLeft, arrowLeft,
@@ -217,12 +251,17 @@ const icons = {
arrowRightShort, arrowRightShort,
arrowUpRight, arrowUpRight,
asterisk, asterisk,
braces,
bodyText,
boxArrowInRight,
boxArrowUp, boxArrowUp,
boxArrowUpRight, boxArrowUpRight,
boxes, boxes,
calendar, calendar,
calendarEvent, calendarEvent,
calendarEventFill,
cardChecklist, cardChecklist,
cardHeading,
caretDown, caretDown,
caretUp, caretUp,
chatLeftText, chatLeftText,
@@ -238,11 +277,14 @@ const icons = {
clipboardCheckFill, clipboardCheckFill,
clipboardFill, clipboardFill,
dash, dash,
dashCircle,
diagram3, diagram3,
dice5, dice5,
doorOpen, doorOpen,
download, download,
envelope, envelope,
envelopeAt,
envelopeAtFill,
exclamationCircleFill, exclamationCircleFill,
exclamationTriangle, exclamationTriangle,
exclamationTriangleFill, exclamationTriangleFill,
@@ -251,6 +293,7 @@ const icons = {
fileEarmarkCheck, fileEarmarkCheck,
fileEarmarkFill, fileEarmarkFill,
fileEarmarkLock, fileEarmarkLock,
fileEarmarkMinus,
files, files,
fileText, fileText,
filter, filter,
@@ -258,15 +301,19 @@ const icons = {
folderFill, folderFill,
funnel, funnel,
gear, gear,
google,
grid, grid,
gripVertical, gripVertical,
hash, hash,
hddStack, hddStack,
house, house,
infoCircle, infoCircle,
journals,
link, link,
listTask, listTask,
listUl, listUl,
microsoft,
nodePlus,
pencil, pencil,
people, people,
peopleFill, peopleFill,
@@ -275,15 +322,18 @@ const icons = {
personFill, personFill,
personFillLock, personFillLock,
personLock, personLock,
personSquare,
plus, plus,
plusCircle, plusCircle,
questionCircle, questionCircle,
scissors,
search, search,
slashCircle, slashCircle,
sliders2Vertical, sliders2Vertical,
sortAlphaDown, sortAlphaDown,
sortAlphaUpAlt, sortAlphaUpAlt,
tagFill, tagFill,
tag,
tags, tags,
textIndentLeft, textIndentLeft,
textLeft, textLeft,
@@ -293,6 +343,7 @@ const icons = {
uiRadios, uiRadios,
upcScan, upcScan,
x, x,
xCircle,
xLg, xLg,
} }
@@ -312,6 +363,7 @@ import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu' import localeHu from '@angular/common/locales/hu'
import localeIt from '@angular/common/locales/it' import localeIt from '@angular/common/locales/it'
import localeJa from '@angular/common/locales/ja' import localeJa from '@angular/common/locales/ja'
import localeKo from '@angular/common/locales/ko'
import localeLb from '@angular/common/locales/lb' import localeLb from '@angular/common/locales/lb'
import localeNl from '@angular/common/locales/nl' import localeNl from '@angular/common/locales/nl'
import localeNo from '@angular/common/locales/no' import localeNo from '@angular/common/locales/no'
@@ -343,6 +395,7 @@ registerLocaleData(localeFr)
registerLocaleData(localeHu) registerLocaleData(localeHu)
registerLocaleData(localeIt) registerLocaleData(localeIt)
registerLocaleData(localeJa) registerLocaleData(localeJa)
registerLocaleData(localeKo)
registerLocaleData(localeLb) registerLocaleData(localeLb)
registerLocaleData(localeNl) registerLocaleData(localeNl)
registerLocaleData(localeNo) registerLocaleData(localeNo)
@@ -391,11 +444,12 @@ function initializeApp(settings: SettingsService) {
FilterEditorComponent, FilterEditorComponent,
FilterableDropdownComponent, FilterableDropdownComponent,
ToggleableDropdownButtonComponent, ToggleableDropdownButtonComponent,
DateDropdownComponent, DatesDropdownComponent,
DocumentCardLargeComponent, DocumentCardLargeComponent,
DocumentCardSmallComponent, DocumentCardSmallComponent,
BulkEditorComponent, BulkEditorComponent,
TextComponent, TextComponent,
TextAreaComponent,
SelectComponent, SelectComponent,
CheckComponent, CheckComponent,
UrlComponent, UrlComponent,
@@ -448,8 +502,8 @@ function initializeApp(settings: SettingsService) {
CustomFieldsComponent, CustomFieldsComponent,
CustomFieldEditDialogComponent, CustomFieldEditDialogComponent,
CustomFieldsDropdownComponent, CustomFieldsDropdownComponent,
CustomFieldsQueryDropdownComponent,
ProfileEditDialogComponent, ProfileEditDialogComponent,
PdfViewerComponent,
DocumentLinkComponent, DocumentLinkComponent,
PreviewPopupComponent, PreviewPopupComponent,
SwitchComponent, SwitchComponent,
@@ -458,21 +512,31 @@ function initializeApp(settings: SettingsService) {
ConfirmButtonComponent, ConfirmButtonComponent,
MonetaryComponent, MonetaryComponent,
SystemStatusDialogComponent, SystemStatusDialogComponent,
RotateConfirmDialogComponent,
MergeConfirmDialogComponent,
SplitConfirmDialogComponent,
DocumentHistoryComponent,
DragDropSelectComponent,
CustomFieldDisplayComponent,
GlobalSearchComponent,
HotkeyDialogComponent,
DeletePagesConfirmDialogComponent,
TrashComponent,
], ],
bootstrap: [AppComponent],
imports: [ imports: [
BrowserModule, BrowserModule,
AppRoutingModule, AppRoutingModule,
NgbModule, NgbModule,
HttpClientModule,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
PdfViewerModule,
NgxFileDropModule, NgxFileDropModule,
NgSelectModule, NgSelectModule,
ColorSliderModule, ColorSliderModule,
TourNgBootstrapModule, TourNgBootstrapModule,
DragDropModule, DragDropModule,
NgxBootstrapIconsModule.pick(icons), NgxBootstrapIconsModule.pick(icons),
NgxFilesizeModule,
], ],
providers: [ providers: [
{ {
@@ -501,7 +565,7 @@ function initializeApp(settings: SettingsService) {
DirtyDocGuard, DirtyDocGuard,
DirtySavedViewGuard, DirtySavedViewGuard,
UsernamePipe, UsernamePipe,
provideHttpClient(withInterceptorsFromDi()),
], ],
bootstrap: [AppComponent],
}) })
export class AppModule {} export class AppModule {}

View File

@@ -11,7 +11,7 @@
<ul ngbNav #nav="ngbNav" class="nav-tabs"> <ul ngbNav #nav="ngbNav" class="nav-tabs">
@for (category of optionCategories; track category) { @for (category of optionCategories; track category) {
<li [ngbNavItem]="category"> <li [ngbNavItem]="category">
<a ngbNavLink i18n>{{category}}</a> <a ngbNavLink>{{category}}</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
<div class="p-3"> <div class="p-3">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-2"> <div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-2">

View File

@@ -5,7 +5,7 @@ import { ConfigService } from 'src/app/services/config.service'
import { ToastService } from 'src/app/services/toast.service' import { ToastService } from 'src/app/services/toast.service'
import { of, throwError } from 'rxjs' import { of, throwError } from 'rxjs'
import { OutputTypeConfig } from 'src/app/data/paperless-config' import { OutputTypeConfig } from 'src/app/data/paperless-config'
import { HttpClientTestingModule } from '@angular/common/http/testing' import { provideHttpClientTesting } from '@angular/common/http/testing'
import { BrowserModule } from '@angular/platform-browser' import { BrowserModule } from '@angular/platform-browser'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
import { NgSelectModule } from '@ng-select/ng-select' import { NgSelectModule } from '@ng-select/ng-select'
@@ -18,6 +18,7 @@ import { SelectComponent } from '../../common/input/select/select.component'
import { FileComponent } from '../../common/input/file/file.component' import { FileComponent } from '../../common/input/file/file.component'
import { SettingsService } from 'src/app/services/settings.service' import { SettingsService } from 'src/app/services/settings.service'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
describe('ConfigComponent', () => { describe('ConfigComponent', () => {
let component: ConfigComponent let component: ConfigComponent
@@ -38,7 +39,6 @@ describe('ConfigComponent', () => {
PageHeaderComponent, PageHeaderComponent,
], ],
imports: [ imports: [
HttpClientTestingModule,
BrowserModule, BrowserModule,
NgbModule, NgbModule,
NgSelectModule, NgSelectModule,
@@ -46,6 +46,10 @@ describe('ConfigComponent', () => {
ReactiveFormsModule, ReactiveFormsModule,
NgxBootstrapIconsModule.pick(allIcons), NgxBootstrapIconsModule.pick(allIcons),
], ],
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
],
}).compileComponents() }).compileComponents()
configService = TestBed.inject(ConfigService) configService = TestBed.inject(ConfigService)

View File

@@ -8,10 +8,11 @@ import { LogService } from 'src/app/services/rest/log.service'
import { PageHeaderComponent } from '../../common/page-header/page-header.component' import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { LogsComponent } from './logs.component' import { LogsComponent } from './logs.component'
import { of, throwError } from 'rxjs' import { of, throwError } from 'rxjs'
import { HttpClientTestingModule } from '@angular/common/http/testing' import { provideHttpClientTesting } from '@angular/common/http/testing'
import { NgbModule, NgbNavLink } from '@ng-bootstrap/ng-bootstrap' import { NgbModule, NgbNavLink } from '@ng-bootstrap/ng-bootstrap'
import { BrowserModule, By } from '@angular/platform-browser' import { BrowserModule, By } from '@angular/platform-browser'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
const paperless_logs = [ const paperless_logs = [
'[2023-05-29 03:05:01,224] [DEBUG] [paperless.tasks] Training data unchanged.', '[2023-05-29 03:05:01,224] [DEBUG] [paperless.tasks] Training data unchanged.',
@@ -37,13 +38,15 @@ describe('LogsComponent', () => {
beforeEach(async () => { beforeEach(async () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [LogsComponent, PageHeaderComponent], declarations: [LogsComponent, PageHeaderComponent],
providers: [],
imports: [ imports: [
HttpClientTestingModule,
BrowserModule, BrowserModule,
NgbModule, NgbModule,
NgxBootstrapIconsModule.pick(allIcons), NgxBootstrapIconsModule.pick(allIcons),
], ],
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
],
}).compileComponents() }).compileComponents()
logService = TestBed.inject(LogService) logService = TestBed.inject(LogService)

View File

@@ -7,29 +7,30 @@
<button class="btn btn-sm btn-outline-primary" (click)="tourService.start()"> <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> <i-bs class="me-1" name="airplane"></i-bs>&nbsp;<ng-container i18n>Start tour</ng-container>
</button> </button>
<button class="btn btn-sm btn-outline-primary position-relative ms-md-5 me-1" (click)="showSystemStatus()" @if (permissionsService.isAdmin()) {
[disabled]="!systemStatus" <button class="btn btn-sm btn-outline-primary position-relative ms-md-5 me-1" (click)="showSystemStatus()"
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }"> [disabled]="!systemStatus">
@if (!systemStatus) { @if (!systemStatus) {
<div class="spinner-border spinner-border-sm me-1 h-75" role="status"></div> <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 { } @else {
<span class="badge bg-body position-absolute top-0 start-100 translate-middle rounded-pill p-0"> <i-bs class="me-2" name="card-checklist"></i-bs>
<i-bs name="check-circle-fill" class="text-primary" width="1.75em" height="1.75em"></i-bs> @if (systemStatusHasErrors) {
</span> <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>
<ng-container i18n>System Status</ng-container> </button>
</button> <a class="btn btn-sm btn-primary" href="admin/" target="_blank">
<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>
<ng-container i18n>Open Django Admin</ng-container> &nbsp;<i-bs name="arrow-up-right"></i-bs>
&nbsp;<i-bs name="arrow-up-right"></i-bs> </a>
</a> }
</pngx-page-header> </pngx-page-header>
<form [formGroup]="settingsForm" (ngSubmit)="saveSettings()"> <form [formGroup]="settingsForm" (ngSubmit)="saveSettings()">
@@ -191,11 +192,35 @@
<div class="row mb-3"> <div class="row mb-3">
<div class="offset-md-3 col"> <div class="offset-md-3 col">
<pngx-input-check i18n-title title="Show confirmation dialogs" formControlName="bulkEditConfirmationDialogs" i18n-hint hint="Deleting documents will always ask for confirmation."></pngx-input-check> <pngx-input-check i18n-title title="Show confirmation dialogs" formControlName="bulkEditConfirmationDialogs"></pngx-input-check>
<pngx-input-check i18n-title title="Apply on close" formControlName="bulkEditApplyOnClose"></pngx-input-check> <pngx-input-check i18n-title title="Apply on close" formControlName="bulkEditApplyOnClose"></pngx-input-check>
</div> </div>
</div> </div>
<h4 class="mt-4" i18n>Global search</h4>
<div class="row mb-3">
<div class="offset-md-3 col">
<pngx-input-check i18n-title title="Do not include advanced search results" formControlName="searchDbOnly"></pngx-input-check>
</div>
</div>
<div class="row mb-3">
<div class="offset-md-3 col">
<div class="row">
<div class="col-md-2 col-form-label pt-0">
<span i18n>Full search links to</span>
</div>
<div class="col">
<select class="form-select" formControlName="searchLink">
<option [ngValue]="GlobalSearchType.TITLE_CONTENT" i18n>Title and content search</option>
<option [ngValue]="GlobalSearchType.ADVANCED" i18n>Advanced search</option>
</select>
</div>
</div>
</div>
</div>
<h4 class="mt-4" i18n>Notes</h4> <h4 class="mt-4" i18n>Notes</h4>
<div class="row mb-3"> <div class="row mb-3">
@@ -307,7 +332,7 @@
</ng-template> </ng-template>
</li> </li>
<li [ngbNavItem]="SettingsNavIDs.SavedViews"> <li [ngbNavItem]="SettingsNavIDs.SavedViews" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.SavedView }">
<a ngbNavLink i18n>Saved views</a> <a ngbNavLink i18n>Saved views</a>
<ng-template ngbNavContent> <ng-template ngbNavContent>
@@ -319,52 +344,71 @@
</div> </div>
<h4 i18n>Views</h4> <h4 i18n>Views</h4>
<div formGroupName="savedViews"> <ul class="list-group" formGroupName="savedViews">
@for (view of savedViews; track view) { @for (view of savedViews; track view) {
<div [formGroupName]="view.id" class="row"> <li class="list-group-item py-3">
<div class="mb-3 col"> <div [formGroupName]="view.id">
<label class="form-label" for="name_{{view.id}}" i18n>Name</label> <div class="row">
<input type="text" class="form-control" formControlName="name" id="name_{{view.id}}"> <div class="col">
</div> <pngx-input-text title="Name" formControlName="name"></pngx-input-text>
<div class="mb-2 col">
<label class="form-label" for="show_on_dashboard_{{view.id}}" i18n>&nbsp;<span class="visually-hidden">Appears on</span></label>
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input" id="show_on_dashboard_{{view.id}}" formControlName="show_on_dashboard">
<label class="form-check-label" for="show_on_dashboard_{{view.id}}" i18n>Show on dashboard</label>
</div> </div>
<div class="form-check form-switch"> <div class="col">
<input type="checkbox" class="form-check-input" id="show_in_sidebar_{{view.id}}" formControlName="show_in_sidebar"> <div class="form-check form-switch mt-3">
<label class="form-check-label" for="show_in_sidebar_{{view.id}}" i18n>Show in sidebar</label> <input type="checkbox" class="form-check-input" id="show_on_dashboard_{{view.id}}" formControlName="show_on_dashboard">
<label class="form-check-label" for="show_on_dashboard_{{view.id}}" i18n>Show on dashboard</label>
</div>
<div class="form-check form-switch">
<input type="checkbox" class="form-check-input" id="show_in_sidebar_{{view.id}}" formControlName="show_in_sidebar">
<label class="form-check-label" for="show_in_sidebar_{{view.id}}" i18n>Show in sidebar</label>
</div>
</div>
<div class="col-auto">
<label class="form-label" for="name_{{view.id}}" i18n>Actions</label>
<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>
</div> </div>
<div class="mb-2 col-auto"> <div class="row">
<label class="form-label" for="name_{{view.id}}" i18n>Actions</label> <div class="col">
<pngx-input-number i18n-title title="Documents page size" [showAdd]="false" formControlName="page_size"></pngx-input-number>
<pngx-confirm-button </div>
label="Delete" <div class="col">
i18n-label <label class="form-label" for="display_mode_{{view.id}}" i18n>Display as</label>
(confirm)="deleteSavedView(view)" <select class="form-select" formControlName="display_mode">
*pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.SavedView }" <option [ngValue]="DisplayMode.TABLE" i18n>Table</option>
buttonClasses="btn-sm btn-outline-danger form-control" <option [ngValue]="DisplayMode.SMALL_CARDS" i18n>Small Cards</option>
iconName="trash"> <option [ngValue]="DisplayMode.LARGE_CARDS" i18n>Large Cards</option>
</pngx-confirm-button> </select>
</div> </div>
@if (displayFields) {
<pngx-input-drag-drop-select i18n-title title="Show" i18n-emptyText emptyText="Default" [items]="displayFields" formControlName="display_fields"></pngx-input-drag-drop-select>
}
</div>
</div> </div>
</li>
} }
@if (savedViews && savedViews.length === 0) { @if (savedViews && savedViews.length === 0) {
<div i18n>No saved views defined.</div> <li class="list-group-item">
<div i18n>No saved views defined.</div>
</li>
} }
@if (!savedViews) { @if (!savedViews) {
<div> <li class="list-group-item">
<div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></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 class="visually-hidden" i18n>Loading...</div>
</div> </li>
} }
</div> </ul>
</ng-template> </ng-template>
</li> </li>
@@ -373,4 +417,5 @@
<div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div> <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" [disabled]="(isDirty$ | async) === false" i18n>Save</button> <button type="submit" class="btn btn-primary mb-2" [disabled]="(isDirty$ | async) === false" i18n>Save</button>
<button type="button" (click)="reset()" class="btn btn-secondary ms-2 mb-2" [disabled]="(isDirty$ | async) === false" i18n>Cancel</button>
</form> </form>

View File

@@ -1,5 +1,5 @@
import { ViewportScroller, DatePipe } from '@angular/common' import { ViewportScroller, DatePipe } from '@angular/common'
import { HttpClientTestingModule } from '@angular/common/http/testing' import { provideHttpClientTesting } from '@angular/common/http/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing' import { ComponentFixture, TestBed } from '@angular/core/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { By } from '@angular/platform-browser' import { By } from '@angular/platform-browser'
@@ -48,6 +48,9 @@ import {
InstallType, InstallType,
SystemStatusItemStatus, SystemStatusItemStatus,
} from 'src/app/data/system-status' } from 'src/app/data/system-status'
import { DragDropSelectComponent } from '../../common/input/drag-drop-select/drag-drop-select.component'
import { DragDropModule } from '@angular/cdk/drag-drop'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
const savedViews = [ const savedViews = [
{ id: 1, name: 'view1', show_in_sidebar: true, show_on_dashboard: true }, { id: 1, name: 'view1', show_in_sidebar: true, show_on_dashboard: true },
@@ -96,11 +99,10 @@ describe('SettingsComponent', () => {
PermissionsGroupComponent, PermissionsGroupComponent,
IfOwnerDirective, IfOwnerDirective,
ConfirmButtonComponent, ConfirmButtonComponent,
DragDropSelectComponent,
], ],
providers: [CustomDatePipe, DatePipe, PermissionsGuard],
imports: [ imports: [
NgbModule, NgbModule,
HttpClientTestingModule,
RouterTestingModule.withRoutes(routes), RouterTestingModule.withRoutes(routes),
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
@@ -108,6 +110,14 @@ describe('SettingsComponent', () => {
NgSelectModule, NgSelectModule,
NgxBootstrapIconsModule.pick(allIcons), NgxBootstrapIconsModule.pick(allIcons),
NgbModalModule, NgbModalModule,
DragDropModule,
],
providers: [
CustomDatePipe,
DatePipe,
PermissionsGuard,
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
], ],
}).compileComponents() }).compileComponents()
@@ -305,7 +315,7 @@ describe('SettingsComponent', () => {
expect(toastErrorSpy).toHaveBeenCalled() expect(toastErrorSpy).toHaveBeenCalled()
expect(storeSpy).toHaveBeenCalled() expect(storeSpy).toHaveBeenCalled()
expect(appearanceSettingsSpy).not.toHaveBeenCalled() expect(appearanceSettingsSpy).not.toHaveBeenCalled()
expect(setSpy).toHaveBeenCalledTimes(25) expect(setSpy).toHaveBeenCalledTimes(27)
// succeed // succeed
storeSpy.mockReturnValueOnce(of(true)) storeSpy.mockReturnValueOnce(of(true))
@@ -418,6 +428,7 @@ describe('SettingsComponent', () => {
}, },
} }
jest.spyOn(systemStatusService, 'get').mockReturnValue(of(status)) jest.spyOn(systemStatusService, 'get').mockReturnValue(of(status))
jest.spyOn(permissionsService, 'isAdmin').mockReturnValue(true)
completeSetup() completeSetup()
expect(component['systemStatus']).toEqual(status) // private expect(component['systemStatus']).toEqual(status) // private
expect(component.systemStatusHasErrors).toBeTruthy() expect(component.systemStatusHasErrors).toBeTruthy()
@@ -436,4 +447,11 @@ describe('SettingsComponent', () => {
size: 'xl', size: 'xl',
}) })
}) })
it('should support reset', () => {
completeSetup()
component.settingsForm.get('themeColor').setValue('#ff0000')
component.reset()
expect(component.settingsForm.get('themeColor').value).toEqual('')
})
}) })

View File

@@ -27,7 +27,7 @@ import {
} from 'rxjs' } from 'rxjs'
import { Group } from 'src/app/data/group' import { Group } from 'src/app/data/group'
import { SavedView } from 'src/app/data/saved-view' import { SavedView } from 'src/app/data/saved-view'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings' import { GlobalSearchType, SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { User } from 'src/app/data/user' import { User } from 'src/app/data/user'
import { DocumentListViewService } from 'src/app/services/document-list-view.service' import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { import {
@@ -50,6 +50,7 @@ import {
SystemStatusItemStatus, SystemStatusItemStatus,
SystemStatus, SystemStatus,
} from 'src/app/data/system-status' } from 'src/app/data/system-status'
import { DisplayMode } from 'src/app/data/document'
enum SettingsNavIDs { enum SettingsNavIDs {
General = 1, General = 1,
@@ -73,8 +74,8 @@ export class SettingsComponent
extends ComponentWithPermissions extends ComponentWithPermissions
implements OnInit, AfterViewInit, OnDestroy, DirtyComponent implements OnInit, AfterViewInit, OnDestroy, DirtyComponent
{ {
SettingsNavIDs = SettingsNavIDs
activeNavID: number activeNavID: number
DisplayMode = DisplayMode
savedViewGroup = new FormGroup({}) savedViewGroup = new FormGroup({})
@@ -99,6 +100,8 @@ export class SettingsComponent
defaultPermsEditUsers: new FormControl(null), defaultPermsEditUsers: new FormControl(null),
defaultPermsEditGroups: new FormControl(null), defaultPermsEditGroups: new FormControl(null),
documentEditingRemoveInboxTags: new FormControl(null), documentEditingRemoveInboxTags: new FormControl(null),
searchDbOnly: new FormControl(null),
searchLink: new FormControl(null),
notificationsConsumerNewDocument: new FormControl(null), notificationsConsumerNewDocument: new FormControl(null),
notificationsConsumerSuccess: new FormControl(null), notificationsConsumerSuccess: new FormControl(null),
@@ -110,6 +113,10 @@ export class SettingsComponent
}) })
savedViews: SavedView[] savedViews: SavedView[]
SettingsNavIDs = SettingsNavIDs
get displayFields() {
return this.settings.allDisplayFields
}
store: BehaviorSubject<any> store: BehaviorSubject<any>
storeSub: Subscription storeSub: Subscription
@@ -121,7 +128,9 @@ export class SettingsComponent
users: User[] users: User[]
groups: Group[] groups: Group[]
private systemStatus: SystemStatus public systemStatus: SystemStatus
public readonly GlobalSearchType = GlobalSearchType
get systemStatusHasErrors(): boolean { get systemStatusHasErrors(): boolean {
return ( return (
@@ -299,6 +308,8 @@ export class SettingsComponent
documentEditingRemoveInboxTags: this.settings.get( documentEditingRemoveInboxTags: this.settings.get(
SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS
), ),
searchDbOnly: this.settings.get(SETTINGS_KEYS.SEARCH_DB_ONLY),
searchLink: this.settings.get(SETTINGS_KEYS.SEARCH_FULL_TYPE),
savedViews: {}, savedViews: {},
} }
} }
@@ -340,6 +351,9 @@ export class SettingsComponent
name: view.name, name: view.name,
show_on_dashboard: view.show_on_dashboard, show_on_dashboard: view.show_on_dashboard,
show_in_sidebar: view.show_in_sidebar, show_in_sidebar: view.show_in_sidebar,
page_size: view.page_size,
display_mode: view.display_mode,
display_fields: view.display_fields,
} }
this.savedViewGroup.addControl( this.savedViewGroup.addControl(
view.id.toString(), view.id.toString(),
@@ -348,6 +362,9 @@ export class SettingsComponent
name: new FormControl(null), name: new FormControl(null),
show_on_dashboard: new FormControl(null), show_on_dashboard: new FormControl(null),
show_in_sidebar: new FormControl(null), show_in_sidebar: new FormControl(null),
page_size: new FormControl(null),
display_mode: new FormControl(null),
display_fields: new FormControl([]),
}) })
) )
} }
@@ -385,12 +402,7 @@ export class SettingsComponent
this.settingsForm.patchValue(currentFormValue) this.settingsForm.patchValue(currentFormValue)
} }
if ( if (this.permissionsService.isAdmin()) {
this.permissionsService.currentUserCan(
PermissionAction.View,
PermissionType.Admin
)
) {
this.systemStatusService.get().subscribe((status) => { this.systemStatusService.get().subscribe((status) => {
this.systemStatus = status this.systemStatus = status
}) })
@@ -527,6 +539,14 @@ export class SettingsComponent
SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS, SETTINGS_KEYS.DOCUMENT_EDITING_REMOVE_INBOX_TAGS,
this.settingsForm.value.documentEditingRemoveInboxTags this.settingsForm.value.documentEditingRemoveInboxTags
) )
this.settings.set(
SETTINGS_KEYS.SEARCH_DB_ONLY,
this.settingsForm.value.searchDbOnly
)
this.settings.set(
SETTINGS_KEYS.SEARCH_FULL_TYPE,
this.settingsForm.value.searchLink
)
this.settings.setLanguage(this.settingsForm.value.displayLanguage) this.settings.setLanguage(this.settingsForm.value.displayLanguage)
this.settings this.settings
.storeSettings() .storeSettings()
@@ -535,8 +555,8 @@ export class SettingsComponent
.subscribe({ .subscribe({
next: () => { next: () => {
this.store.next(this.settingsForm.value) this.store.next(this.settingsForm.value)
this.documentListViewService.updatePageSize()
this.settings.updateAppearanceSettings() this.settings.updateAppearanceSettings()
this.settings.initializeDisplayFields()
let savedToast: Toast = { let savedToast: Toast = {
content: $localize`Settings were saved successfully.`, content: $localize`Settings were saved successfully.`,
delay: 5000, delay: 5000,
@@ -597,6 +617,10 @@ export class SettingsComponent
} }
} }
reset() {
this.settingsForm.patchValue(this.store.getValue())
}
clearThemeColor() { clearThemeColor() {
this.settingsForm.get('themeColor').patchValue('') this.settingsForm.get('themeColor').patchValue('')
} }

View File

@@ -29,7 +29,7 @@
<tr> <tr>
<th scope="col"> <th scope="col">
<div class="form-check"> <div class="form-check">
<input type="checkbox" class="form-check-input" id="all-tasks" [disabled]="currentTasks.length === 0" (click)="toggleAll($event); $event.stopPropagation();"> <input type="checkbox" class="form-check-input" id="all-tasks" [disabled]="currentTasks.length === 0" [(ngModel)]="togggleAll" (click)="toggleAll($event); $event.stopPropagation();">
<label class="form-check-label" for="all-tasks"></label> <label class="form-check-label" for="all-tasks"></label>
</div> </div>
</th> </th>

View File

@@ -1,7 +1,7 @@
import { DatePipe } from '@angular/common' import { DatePipe } from '@angular/common'
import { import {
HttpTestingController, HttpTestingController,
HttpClientTestingModule, provideHttpClientTesting,
} from '@angular/common/http/testing' } from '@angular/common/http/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing' import { ComponentFixture, TestBed } from '@angular/core/testing'
import { By } from '@angular/platform-browser' import { By } from '@angular/platform-browser'
@@ -29,6 +29,8 @@ import { PageHeaderComponent } from '../../common/page-header/page-header.compon
import { TasksComponent } from './tasks.component' import { TasksComponent } from './tasks.component'
import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { FormsModule } from '@angular/forms'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
const tasks: PaperlessTask[] = [ const tasks: PaperlessTask[] = [
{ {
@@ -124,6 +126,12 @@ describe('TasksComponent', () => {
CustomDatePipe, CustomDatePipe,
ConfirmDialogComponent, ConfirmDialogComponent,
], ],
imports: [
NgbModule,
RouterTestingModule.withRoutes(routes),
NgxBootstrapIconsModule.pick(allIcons),
FormsModule,
],
providers: [ providers: [
{ {
provide: PermissionsService, provide: PermissionsService,
@@ -134,12 +142,8 @@ describe('TasksComponent', () => {
CustomDatePipe, CustomDatePipe,
DatePipe, DatePipe,
PermissionsGuard, PermissionsGuard,
], provideHttpClient(withInterceptorsFromDi()),
imports: [ provideHttpClientTesting(),
NgbModule,
HttpClientTestingModule,
RouterTestingModule.withRoutes(routes),
NgxBootstrapIconsModule.pick(allIcons),
], ],
}).compileComponents() }).compileComponents()

View File

@@ -18,6 +18,7 @@ export class TasksComponent
{ {
public activeTab: string public activeTab: string
public selectedTasks: Set<number> = new Set() public selectedTasks: Set<number> = new Set()
public togggleAll: boolean = false
public expandedTask: number public expandedTask: number
public pageSize: number = 25 public pageSize: number = 25
@@ -69,11 +70,11 @@ export class TasksComponent
modal.componentInstance.buttonsEnabled = false modal.componentInstance.buttonsEnabled = false
modal.close() modal.close()
this.tasksService.dismissTasks(tasks) this.tasksService.dismissTasks(tasks)
this.selectedTasks.clear() this.clearSelection()
}) })
} else { } else {
this.tasksService.dismissTasks(tasks) this.tasksService.dismissTasks(tasks)
this.selectedTasks.clear() this.clearSelection()
} }
} }
@@ -120,6 +121,7 @@ export class TasksComponent
} }
clearSelection() { clearSelection() {
this.togggleAll = false
this.selectedTasks.clear() this.selectedTasks.clear()
} }

View File

@@ -0,0 +1,98 @@
<pngx-page-header
title="Trash"
i18n-title
info="Manage trashed documents that are pending deletion."
i18n-info
infoLink="usage/#document-trash">
<button class="btn btn-sm btn-outline-secondary" (click)="clearSelection()" [hidden]="selectedDocuments.size === 0">
<i-bs name="x"></i-bs>&nbsp;<ng-container i18n>Clear selection</ng-container>
</button>
<button type="button" class="btn btn-sm btn-outline-primary" (click)="restoreAll(selectedDocuments)" [disabled]="selectedDocuments.size === 0">
<i-bs name="arrow-counterclockwise"></i-bs>&nbsp;<ng-container i18n>Restore selected</ng-container>
</button>
<button type="button" class="btn btn-sm btn-outline-danger" (click)="emptyTrash(selectedDocuments)" [disabled]="selectedDocuments.size === 0">
<i-bs name="trash"></i-bs>&nbsp;<ng-container i18n>Delete selected</ng-container>
</button>
<button type="button" class="btn btn-sm btn-outline-danger" (click)="emptyTrash()" [disabled]="documentsInTrash.length === 0">
<i-bs name="trash"></i-bs>&nbsp;<ng-container i18n>Empty trash</ng-container>
</button>
</pngx-page-header>
<div class="row mb-3">
<ngb-pagination class="col-auto" [pageSize]="25" [collectionSize]="totalDocuments" [(page)]="page" [maxSize]="5" (pageChange)="reload()" size="sm" aria-label="Pagination"></ngb-pagination>
</div>
<div class="card border table-responsive mb-3">
<table class="table table-striped align-middle shadow-sm mb-0">
<thead>
<tr>
<th scope="col">
<div class="form-check m-0 ms-2 me-n2">
<input type="checkbox" class="form-check-input" id="all-objects" [(ngModel)]="allToggled" [disabled]="documentsInTrash.length === 0" (click)="toggleAll($event); $event.stopPropagation();">
<label class="form-check-label" for="all-objects"></label>
</div>
</th>
<th scope="col" class="fw-normal" i18n>Name</th>
<th scope="col" class="fw-normal d-none d-sm-table-cell" i18n>Remaining</th>
<th scope="col" class="fw-normal" i18n>Actions</th>
</tr>
</thead>
<tbody>
@if (isLoading) {
<tr>
<td colspan="5">
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
<ng-container i18n>Loading...</ng-container>
</td>
</tr>
}
@for (document of documentsInTrash; track document.id) {
<tr (click)="toggleSelected(document); $event.stopPropagation();">
<td>
<div class="form-check m-0 ms-2 me-n2">
<input type="checkbox" class="form-check-input" id="{{document.id}}" [checked]="selectedDocuments.has(document.id)" (click)="toggleSelected(document); $event.stopPropagation();">
<label class="form-check-label" for="{{document.id}}"></label>
</div>
</td>
<td scope="row">{{ document.title }}</td>
<td scope="row" i18n>{{ getDaysRemaining(document) }} days</td>
<td scope="row">
<div class="btn-group d-block d-sm-none">
<div ngbDropdown container="body" class="d-inline-block">
<button type="button" class="btn btn-link" id="actionsMenuMobile" (click)="$event.stopPropagation()" ngbDropdownToggle>
<i-bs name="three-dots-vertical"></i-bs>
</button>
<div ngbDropdownMenu aria-labelledby="actionsMenuMobile">
<button (click)="restore(document)" ngbDropdownItem i18n>Restore</button>
<button (click)="delete(document)" ngbDropdownItem i18n>Delete</button>
</div>
</div>
</div>
<div class="btn-group d-none d-sm-block">
<button class="btn btn-sm btn-outline-secondary" (click)="restore(document); $event.stopPropagation();">
<i-bs width="1em" height="1em" name="arrow-counterclockwise"></i-bs>&nbsp;<ng-container i18n>Restore</ng-container>
</button>
<button class="btn btn-sm btn-outline-danger" (click)="delete(document); $event.stopPropagation();">
<i-bs width="1em" height="1em" name="trash"></i-bs>&nbsp;<ng-container i18n>Delete</ng-container>
</button>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
@if (!isLoading) {
<div class="d-flex mb-2">
<div>
<ng-container i18n>{totalDocuments, plural, =1 {One document in trash} other {{{totalDocuments || 0}} total documents in trash}}</ng-container>
@if (selectedDocuments.size > 0) {
&nbsp;({{selectedDocuments.size}} selected)
}
</div>
@if (documentsInTrash.length > 20) {
<ngb-pagination class="ms-auto" [pageSize]="25" [collectionSize]="totalDocuments" [(page)]="page" [maxSize]="5" (pageChange)="reload()" size="sm" aria-label="Pagination"></ngb-pagination>
}
</div>
}

View File

@@ -0,0 +1,200 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { TrashComponent } from './trash.component'
import { HttpClientTestingModule } from '@angular/common/http/testing'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import {
NgbModal,
NgbPaginationModule,
NgbPopoverModule,
} from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { TrashService } from 'src/app/services/trash.service'
import { of, throwError } from 'rxjs'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { By } from '@angular/platform-browser'
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
import { ToastService } from 'src/app/services/toast.service'
const documentsInTrash = [
{
id: 1,
name: 'test1',
created: new Date('2023-03-01T10:26:03.093116Z'),
deleted_at: new Date('2023-03-01T10:26:03.093116Z'),
},
{
id: 2,
name: 'test2',
created: new Date('2023-03-01T10:26:03.093116Z'),
deleted_at: new Date('2023-03-01T10:26:03.093116Z'),
},
]
describe('TrashComponent', () => {
let component: TrashComponent
let fixture: ComponentFixture<TrashComponent>
let trashService: TrashService
let modalService: NgbModal
let toastService: ToastService
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
TrashComponent,
PageHeaderComponent,
ConfirmDialogComponent,
SafeHtmlPipe,
],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
NgbPopoverModule,
NgbPaginationModule,
NgxBootstrapIconsModule.pick(allIcons),
],
}).compileComponents()
fixture = TestBed.createComponent(TrashComponent)
trashService = TestBed.inject(TrashService)
modalService = TestBed.inject(NgbModal)
toastService = TestBed.inject(ToastService)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should call correct service method on reload', () => {
const trashSpy = jest.spyOn(trashService, 'getTrash')
trashSpy.mockReturnValue(
of({
count: 2,
all: documentsInTrash.map((d) => d.id),
results: documentsInTrash,
})
)
component.reload()
expect(trashSpy).toHaveBeenCalled()
expect(component.documentsInTrash).toEqual(documentsInTrash)
})
it('should support delete document, show error if needed', () => {
const trashSpy = jest.spyOn(trashService, 'emptyTrash')
let modal
modalService.activeInstances.subscribe((instances) => {
modal = instances[0]
})
const toastErrorSpy = jest.spyOn(toastService, 'showError')
// fail first
trashSpy.mockReturnValue(throwError(() => 'Error'))
component.delete(documentsInTrash[0])
modal.componentInstance.confirmClicked.next()
expect(toastErrorSpy).toHaveBeenCalled()
trashSpy.mockReturnValue(of('OK'))
component.delete(documentsInTrash[0])
expect(modal).toBeDefined()
modal.componentInstance.confirmClicked.next()
expect(trashSpy).toHaveBeenCalled()
})
it('should support empty trash, show error if needed', () => {
const trashSpy = jest.spyOn(trashService, 'emptyTrash')
let modal
modalService.activeInstances.subscribe((instances) => {
modal = instances[instances.length - 1]
})
const toastErrorSpy = jest.spyOn(toastService, 'showError')
// fail first
trashSpy.mockReturnValue(throwError(() => 'Error'))
component.emptyTrash()
modal.componentInstance.confirmClicked.next()
expect(toastErrorSpy).toHaveBeenCalled()
trashSpy.mockReturnValue(of('OK'))
component.emptyTrash()
expect(modal).toBeDefined()
modal.componentInstance.confirmClicked.next()
expect(trashSpy).toHaveBeenCalled()
modal.close()
component.emptyTrash(new Set([1, 2]))
modal.componentInstance.confirmClicked.next()
expect(trashSpy).toHaveBeenCalledWith([1, 2])
})
it('should support restore document, show error if needed', () => {
const restoreSpy = jest.spyOn(trashService, 'restoreDocuments')
const reloadSpy = jest.spyOn(component, 'reload')
const toastErrorSpy = jest.spyOn(toastService, 'showError')
// fail first
restoreSpy.mockReturnValue(throwError(() => 'Error'))
component.restore(documentsInTrash[0])
expect(toastErrorSpy).toHaveBeenCalled()
expect(reloadSpy).not.toHaveBeenCalled()
restoreSpy.mockReturnValue(of('OK'))
component.restore(documentsInTrash[0])
expect(restoreSpy).toHaveBeenCalledWith([documentsInTrash[0].id])
expect(reloadSpy).toHaveBeenCalled()
})
it('should support restore all documents, show error if needed', () => {
const restoreSpy = jest.spyOn(trashService, 'restoreDocuments')
const reloadSpy = jest.spyOn(component, 'reload')
const toastErrorSpy = jest.spyOn(toastService, 'showError')
// fail first
restoreSpy.mockReturnValue(throwError(() => 'Error'))
component.restoreAll()
expect(toastErrorSpy).toHaveBeenCalled()
expect(reloadSpy).not.toHaveBeenCalled()
restoreSpy.mockReturnValue(of('OK'))
component.restoreAll()
expect(restoreSpy).toHaveBeenCalled()
expect(reloadSpy).toHaveBeenCalled()
component.restoreAll(new Set([1, 2]))
expect(restoreSpy).toHaveBeenCalledWith([1, 2])
})
it('should support toggle all items in view', () => {
component.documentsInTrash = documentsInTrash
expect(component.selectedDocuments.size).toEqual(0)
const toggleAllSpy = jest.spyOn(component, 'toggleAll')
const checkButton = fixture.debugElement.queryAll(
By.css('input.form-check-input')
)[0]
checkButton.nativeElement.dispatchEvent(new Event('click'))
checkButton.nativeElement.checked = true
checkButton.nativeElement.dispatchEvent(new Event('click'))
expect(toggleAllSpy).toHaveBeenCalled()
expect(component.selectedDocuments.size).toEqual(documentsInTrash.length)
})
it('should support toggle item', () => {
component.selectedDocuments = new Set([1])
component.toggleSelected(documentsInTrash[0])
expect(component.selectedDocuments.size).toEqual(0)
component.toggleSelected(documentsInTrash[0])
expect(component.selectedDocuments.size).toEqual(1)
})
it('should support clear selection', () => {
component.selectedDocuments = new Set([1])
component.clearSelection()
expect(component.selectedDocuments.size).toEqual(0)
})
it('should correctly display days remaining', () => {
expect(component.getDaysRemaining(documentsInTrash[0])).toBeLessThan(0)
const tenDaysAgo = new Date()
tenDaysAgo.setDate(tenDaysAgo.getDate() - 10)
expect(
component.getDaysRemaining({ deleted_at: tenDaysAgo })
).toBeGreaterThan(0) // 10 days ago but depends on month
})
})

View File

@@ -0,0 +1,165 @@
import { Component, OnDestroy } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { Document } from 'src/app/data/document'
import { ToastService } from 'src/app/services/toast.service'
import { TrashService } from 'src/app/services/trash.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { Subject, takeUntil } from 'rxjs'
import { SettingsService } from 'src/app/services/settings.service'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
@Component({
selector: 'pngx-trash',
templateUrl: './trash.component.html',
styleUrl: './trash.component.scss',
})
export class TrashComponent implements OnDestroy {
public documentsInTrash: Document[] = []
public selectedDocuments: Set<number> = new Set()
public allToggled: boolean = false
public page: number = 1
public totalDocuments: number
public isLoading: boolean = false
unsubscribeNotifier: Subject<void> = new Subject()
constructor(
private trashService: TrashService,
private toastService: ToastService,
private modalService: NgbModal,
private settingsService: SettingsService
) {
this.reload()
}
ngOnDestroy() {
this.unsubscribeNotifier.next()
this.unsubscribeNotifier.complete()
}
reload() {
this.isLoading = true
this.trashService.getTrash(this.page).subscribe((r) => {
this.documentsInTrash = r.results
this.totalDocuments = r.count
this.isLoading = false
this.selectedDocuments.clear()
})
}
delete(document: Document) {
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Confirm delete`
modal.componentInstance.messageBold = $localize`This operation will permanently delete this document.`
modal.componentInstance.message = $localize`This operation cannot be undone.`
modal.componentInstance.btnClass = 'btn-danger'
modal.componentInstance.btnCaption = $localize`Delete`
modal.componentInstance.confirmClicked
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(() => {
modal.componentInstance.buttonsEnabled = false
this.trashService.emptyTrash([document.id]).subscribe({
next: () => {
this.toastService.showInfo($localize`Document deleted`)
modal.close()
this.reload()
},
error: (err) => {
this.toastService.showError($localize`Error deleting document`, err)
modal.close()
},
})
})
}
emptyTrash(documents?: Set<number>) {
let modal = this.modalService.open(ConfirmDialogComponent, {
backdrop: 'static',
})
modal.componentInstance.title = $localize`Confirm delete`
modal.componentInstance.messageBold = documents
? $localize`This operation will permanently delete the selected documents.`
: $localize`This operation will permanently delete all documents in the trash.`
modal.componentInstance.message = $localize`This operation cannot be undone.`
modal.componentInstance.btnClass = 'btn-danger'
modal.componentInstance.btnCaption = $localize`Delete`
modal.componentInstance.confirmClicked
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe(() => {
this.trashService
.emptyTrash(documents ? Array.from(documents) : null)
.subscribe({
next: () => {
this.toastService.showInfo($localize`Document(s) deleted`)
this.allToggled = false
modal.close()
this.reload()
},
error: (err) => {
this.toastService.showError(
$localize`Error deleting document(s)`,
err
)
modal.close()
},
})
})
}
restore(document: Document) {
this.trashService.restoreDocuments([document.id]).subscribe({
next: () => {
this.toastService.showInfo($localize`Document restored`)
this.reload()
},
error: (err) => {
this.toastService.showError($localize`Error restoring document`, err)
},
})
}
restoreAll(documents: Set<number> = null) {
this.trashService
.restoreDocuments(documents ? Array.from(documents) : null)
.subscribe({
next: () => {
this.toastService.showInfo($localize`Document(s) restored`)
this.allToggled = false
this.reload()
},
error: (err) => {
this.toastService.showError(
$localize`Error restoring document(s)`,
err
)
},
})
}
toggleAll(event: PointerEvent) {
if ((event.target as HTMLInputElement).checked) {
this.selectedDocuments = new Set(this.documentsInTrash.map((t) => t.id))
} else {
this.clearSelection()
}
}
toggleSelected(object: Document) {
this.selectedDocuments.has(object.id)
? this.selectedDocuments.delete(object.id)
: this.selectedDocuments.add(object.id)
}
clearSelection() {
this.allToggled = false
this.selectedDocuments.clear()
}
getDaysRemaining(document: Document): number {
const delay = this.settingsService.get(SETTINGS_KEYS.EMPTY_TRASH_DELAY)
const diff = new Date().getTime() - new Date(document.deleted_at).getTime()
const days = Math.ceil(diff / (1000 * 3600 * 24))
return delay - days
}
}

View File

@@ -26,7 +26,7 @@
@for (user of users; track user) { @for (user of users; track user) {
<li class="list-group-item"> <li class="list-group-item">
<div class="row"> <div class="row">
<div class="col d-flex align-items-center"><button class="btn btn-link p-0" type="button" (click)="editUser(user)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.User)">{{user.username}}</button></div> <div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" type="button" (click)="editUser(user)" [disabled]="!permissionsService.currentUserCan(PermissionAction.Change, PermissionType.User)">{{user.username}}</button></div>
<div class="col d-flex align-items-center">{{user.first_name}} {{user.last_name}}</div> <div class="col d-flex align-items-center">{{user.first_name}} {{user.last_name}}</div>
<div class="col d-flex align-items-center">{{user.groups?.map(getGroupName, this).join(', ')}}</div> <div class="col d-flex align-items-center">{{user.groups?.map(getGroupName, this).join(', ')}}</div>
<div class="col"> <div class="col">
@@ -52,40 +52,38 @@
<i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Group</ng-container> <i-bs name="plus-circle"></i-bs>&nbsp;<ng-container i18n>Add Group</ng-container>
</button> </button>
</h4> </h4>
@if (groups.length > 0) { <ul class="list-group">
<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"> <li class="list-group-item">
<div class="row"> <div class="row">
<div class="col" i18n>Name</div> <div class="col d-flex align-items-center"><button class="btn btn-link p-0 text-start" 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> <div class="col"></div>
<div class="col" i18n>Actions</div> <div class="col">
</div> <div class="btn-group">
</li> <button class="btn btn-sm btn-outline-secondary" type="button" (click)="editGroup(group)" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Group }">
@for (group of groups; track group) { <i-bs width="1em" height="1em" name="pencil"></i-bs>&nbsp;<ng-container i18n>Edit</ng-container>
<li class="list-group-item"> </button>
<div class="row"> <button class="btn btn-sm btn-outline-danger" type="button" (click)="deleteGroup(group)" *pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.Group }">
<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> <i-bs width="1em" height="1em" name="trash"></i-bs>&nbsp;<ng-container i18n>Delete</ng-container>
<div class="col"></div> </button>
<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>
</div> </div>
</li> </div>
} </li>
@if (groups.length === 0) { }
<li class="list-group-item" i18n>No groups defined</li> @if (groups.length === 0) {
} <li class="list-group-item" i18n>No groups defined</li>
</ul> }
} </ul>
} }
@if (!users || !groups) { @if (!users || !groups) {

View File

@@ -1,5 +1,5 @@
import { DatePipe } from '@angular/common' import { DatePipe } from '@angular/common'
import { HttpClientTestingModule } from '@angular/common/http/testing' import { provideHttpClientTesting } from '@angular/common/http/testing'
import { import {
ComponentFixture, ComponentFixture,
TestBed, TestBed,
@@ -44,6 +44,7 @@ import { UsersAndGroupsComponent } from './users-groups.component'
import { User } from 'src/app/data/user' import { User } from 'src/app/data/user'
import { Group } from 'src/app/data/group' import { Group } from 'src/app/data/group'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
const users = [ const users = [
{ id: 1, username: 'user1', is_superuser: false }, { id: 1, username: 'user1', is_superuser: false },
@@ -84,10 +85,8 @@ describe('UsersAndGroupsComponent', () => {
PermissionsGroupComponent, PermissionsGroupComponent,
IfOwnerDirective, IfOwnerDirective,
], ],
providers: [CustomDatePipe, DatePipe, PermissionsGuard],
imports: [ imports: [
NgbModule, NgbModule,
HttpClientTestingModule,
RouterTestingModule.withRoutes(routes), RouterTestingModule.withRoutes(routes),
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
@@ -95,6 +94,13 @@ describe('UsersAndGroupsComponent', () => {
NgSelectModule, NgSelectModule,
NgxBootstrapIconsModule.pick(allIcons), NgxBootstrapIconsModule.pick(allIcons),
], ],
providers: [
CustomDatePipe,
DatePipe,
PermissionsGuard,
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
],
}).compileComponents() }).compileComponents()
fixture = TestBed.createComponent(UsersAndGroupsComponent) fixture = TestBed.createComponent(UsersAndGroupsComponent)
settingsService = TestBed.inject(SettingsService) settingsService = TestBed.inject(SettingsService)

View File

@@ -5,7 +5,7 @@
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<a class="navbar-brand d-flex align-items-center me-0 px-3 py-3 order-sm-0" <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 }" [ngClass]="{ 'slim': slimSidebarEnabled, 'col-auto col-md-3 col-lg-2 col-xxxl-1' : !slimSidebarEnabled, 'py-3' : !customAppTitle?.length || slimSidebarEnabled, 'py-2': customAppTitle?.length }"
routerLink="/dashboard" routerLink="/dashboard"
tourAnchor="tour.intro"> tourAnchor="tour.intro">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 198.43 238.91" width="1em" height="1.5em" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 198.43 238.91" width="1em" height="1.5em" fill="currentColor">
@@ -24,19 +24,10 @@
} }
</div> </div>
</a> </a>
<div class="search-form-container flex-grow-1 py-2 pb-3 pb-sm-2 px-3 ps-md-4 me-sm-auto order-3 order-sm-1" <div class="search-container flex-grow-1 py-2 pb-3 pb-sm-2 px-3 ps-md-4 me-sm-auto order-3 order-sm-1">
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"> <div class="col-12 col-md-7">
<form (ngSubmit)="search()" class="form-inline flex-grow-1"> <pngx-global-search></pngx-global-search>
<i-bs width="1em" height="1em" name="search"></i-bs> </div>
<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 ps-0 pe-1 position-absolute top-0 end-0" (click)="resetSearchField()">
<i-bs width="1em" height="1em" name="x"></i-bs>
</button>
}
</form>
</div> </div>
<ul ngbNav class="order-sm-3"> <ul ngbNav class="order-sm-3">
<li ngbDropdown class="nav-item dropdown"> <li ngbDropdown class="nav-item dropdown">
@@ -52,7 +43,7 @@
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
</div> </div>
<button ngbDropdownItem class="nav-link" (click)="editProfile()"> <button ngbDropdownItem class="nav-link" (click)="editProfile()">
<i-bs class="me-2" name="person"></i-bs>&nbsp;<ng-container i18n>My Profile</ng-container> <i-bs class="me-2" name="person"></i-bs><ng-container i18n>My Profile</ng-container>
</button> </button>
<a ngbDropdownItem class="nav-link" routerLink="settings" (click)="closeMenu()" <a ngbDropdownItem class="nav-link" routerLink="settings" (click)="closeMenu()"
*pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }"> *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }">
@@ -85,14 +76,14 @@
</button> </button>
<div class="sidebar-sticky pt-3 d-flex flex-column justify-space-around"> <div class="sidebar-sticky pt-3 d-flex flex-column justify-space-around">
<ul class="nav flex-column"> <ul class="nav flex-column">
<li class="nav-item"> <li class="nav-item app-link">
<a class="nav-link" routerLink="dashboard" routerLinkActive="active" (click)="closeMenu()" <a class="nav-link" routerLink="dashboard" routerLinkActive="active" (click)="closeMenu()"
ngbPopover="Dashboard" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" ngbPopover="Dashboard" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="me-1" name="house"></i-bs><span>&nbsp;<ng-container i18n>Dashboard</ng-container></span> <i-bs class="me-1" name="house"></i-bs><span>&nbsp;<ng-container i18n>Dashboard</ng-container></span>
</a> </a>
</li> </li>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"> <li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
<a class="nav-link" routerLink="documents" routerLinkActive="active" (click)="closeMenu()" <a class="nav-link" routerLink="documents" routerLinkActive="active" (click)="closeMenu()"
ngbPopover="Documents" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" ngbPopover="Documents" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
@@ -100,46 +91,49 @@
</a> </a>
</li> </li>
</ul> </ul>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }">
@if (savedViewService.loading || savedViewService.sidebarViews?.length > 0) {
<h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted">
<span i18n>Saved views</span>
@if (savedViewService.loading) {
<div class="spinner-border spinner-border-sm fw-normal ms-2" role="status"></div>
}
</h6>
}
<ul class="nav flex-column mb-2" cdkDropList (cdkDropListDropped)="onDrop($event)">
@for (view of savedViewService.sidebarViews; track view) {
<li class="nav-item w-100" cdkDrag [cdkDragDisabled]="!settingsService.organizingSidebarSavedViews"
cdkDragPreviewContainer="parent" cdkDragPreviewClass="navItemDrag" (cdkDragStarted)="onDragStart($event)"
(cdkDragEnded)="onDragEnd($event)">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="view/{{view.id}}"
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name"
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
popoverClass="popover-slim">
<i-bs class="me-1" name="funnel"></i-bs><span>&nbsp;{{view.name}}</span>
</a>
@if (settingsService.organizingSidebarSavedViews) {
<div class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
<i-bs name="grip-vertical"></i-bs>
</div>
}
</li>
}
</ul>
</ng-container>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"> <div class="nav-group mt-3 mb-1" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }">
@if (savedViewService.loading) {
<h6 class="sidebar-heading px-3 text-muted">
<span i18n>Saved views</span>
<div class="spinner-border spinner-border-sm fw-normal ms-2" role="status"></div>
</h6>
} @else if (savedViewService.sidebarViews?.length > 0) {
<h6 class="sidebar-heading px-3 text-muted">
<span i18n>Saved views</span>
</h6>
<ul class="nav flex-column mb-2" cdkDropList (cdkDropListDropped)="onDrop($event)">
@for (view of savedViewService.sidebarViews; track view.id) {
<li class="nav-item w-100 app-link" cdkDrag [cdkDragDisabled]="!settingsService.organizingSidebarSavedViews"
cdkDragPreviewContainer="parent" cdkDragPreviewClass="navItemDrag" (cdkDragStarted)="onDragStart($event)"
(cdkDragEnded)="onDragEnd($event)">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="view/{{view.id}}"
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name"
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
popoverClass="popover-slim">
<i-bs class="me-1" name="funnel"></i-bs><span>&nbsp;{{view.name}}</span>
</a>
@if (settingsService.organizingSidebarSavedViews) {
<div class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
<i-bs name="grip-vertical"></i-bs>
</div>
}
</li>
}
</ul>
}
</div>
<div class="nav-group mt-3 mb-1" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }">
@if (openDocuments.length > 0) { @if (openDocuments.length > 0) {
<h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted"> <h6 class="sidebar-heading px-3 text-muted">
<span i18n>Open documents</span> <span i18n>Open documents</span>
</h6> </h6>
} }
<ul class="nav flex-column mb-2"> <ul class="nav flex-column mb-2">
@for (d of openDocuments; track d) { @for (d of openDocuments; track d) {
<li class="nav-item w-100"> <li class="nav-item w-100 app-link">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="documents/{{d.id}}" <a class="nav-link app-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="documents/{{d.id}}"
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="d.title | documentTitle" routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="d.title | documentTitle"
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
popoverClass="popover-slim"> popoverClass="popover-slim">
@@ -151,8 +145,8 @@
</li> </li>
} }
@if (openDocuments.length >= 1) { @if (openDocuments.length >= 1) {
<li class="nav-item w-100"> <li class="nav-item w-100 app-link">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" [routerLink]="[]" (click)="closeAll()" <a class="nav-link app-link" [class.text-truncate]="!slimSidebarEnabled" [routerLink]="[]" (click)="closeAll()"
ngbPopover="Close all" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" ngbPopover="Close all" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="me-1" name="x"></i-bs><span>&nbsp;<ng-container i18n>Close all</ng-container></span> <i-bs class="me-1" name="x"></i-bs><span>&nbsp;<ng-container i18n>Close all</ng-container></span>
@@ -160,178 +154,191 @@
</li> </li>
} }
</ul> </ul>
</ng-container> </div>
<h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted"> <div class="nav-group mt-3 mb-1">
<span i18n>Manage</span> <h6 class="sidebar-heading px-3 text-muted">
</h6> <span i18n>Manage</span>
<ul class="nav flex-column mb-2"> </h6>
<li class="nav-item" <ul class="nav flex-column mb-2">
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }"> <li class="nav-item app-link"
<a class="nav-link" routerLink="correspondents" routerLinkActive="active" (click)="closeMenu()" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Correspondent }">
ngbPopover="Correspondents" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" <a class="nav-link" routerLink="correspondents" routerLinkActive="active" (click)="closeMenu()"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> ngbPopover="Correspondents" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
<i-bs class="me-1" name="person"></i-bs><span>&nbsp;<ng-container i18n>Correspondents</ng-container></span> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="person"></i-bs><span>&nbsp;<ng-container i18n>Correspondents</ng-container></span>
</li> </a>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }" </li>
tourAnchor="tour.tags"> <li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Tag }"
<a class="nav-link" routerLink="tags" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Tags" tourAnchor="tour.tags">
i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" <a class="nav-link" routerLink="tags" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Tags"
triggers="mouseenter:mouseleave" popoverClass="popover-slim"> i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body"
<i-bs class="me-1" name="tags"></i-bs><span>&nbsp;<ng-container i18n>Tags</ng-container></span> triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="tags"></i-bs><span>&nbsp;<ng-container i18n>Tags</ng-container></span>
</li> </a>
<li class="nav-item" </li>
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }"> <li class="nav-item app-link"
<a class="nav-link" routerLink="documenttypes" routerLinkActive="active" (click)="closeMenu()" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.DocumentType }">
ngbPopover="Document Types" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" <a class="nav-link" routerLink="documenttypes" routerLinkActive="active" (click)="closeMenu()"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> ngbPopover="Document Types" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
<i-bs class="me-1" name="hash"></i-bs><span>&nbsp;<ng-container i18n>Document Types</ng-container></span> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="hash"></i-bs><span>&nbsp;<ng-container i18n>Document Types</ng-container></span>
</li> </a>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }"> </li>
<a class="nav-link" routerLink="storagepaths" routerLinkActive="active" (click)="closeMenu()" <li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.StoragePath }">
ngbPopover="Storage Paths" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" <a class="nav-link" routerLink="storagepaths" routerLinkActive="active" (click)="closeMenu()"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> ngbPopover="Storage Paths" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
<i-bs class="me-1" name="folder"></i-bs><span>&nbsp;<ng-container i18n>Storage Paths</ng-container></span> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="folder"></i-bs><span>&nbsp;<ng-container i18n>Storage Paths</ng-container></span>
</li> </a>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.CustomField }"> </li>
<a class="nav-link" routerLink="customfields" routerLinkActive="active" (click)="closeMenu()" <li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.CustomField }">
ngbPopover="Custom Fields" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" <a class="nav-link" routerLink="customfields" routerLinkActive="active" (click)="closeMenu()"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> ngbPopover="Custom Fields" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
<i-bs class="me-1" name="ui-radios"></i-bs><span>&nbsp;<ng-container i18n>Custom Fields</ng-container></span> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="ui-radios"></i-bs><span>&nbsp;<ng-container i18n>Custom Fields</ng-container></span>
</li> </a>
<li class="nav-item" </li>
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Workflow }" <li class="nav-item app-link"
tourAnchor="tour.workflows"> *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Workflow }"
<a class="nav-link" routerLink="workflows" routerLinkActive="active" (click)="closeMenu()" tourAnchor="tour.workflows">
ngbPopover="Workflows" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" <a class="nav-link" routerLink="workflows" routerLinkActive="active" (click)="closeMenu()"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> ngbPopover="Workflows" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
<i-bs class="me-1" name="boxes"></i-bs><span>&nbsp;<ng-container i18n>Workflows</ng-container></span> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="boxes"></i-bs><span>&nbsp;<ng-container i18n>Workflows</ng-container></span>
</li> </a>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.MailAccount }" </li>
tourAnchor="tour.mail"> <li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.MailAccount }"
<a class="nav-link" routerLink="mail" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Mail" tourAnchor="tour.mail">
i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" <a class="nav-link" routerLink="mail" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Mail"
triggers="mouseenter:mouseleave" popoverClass="popover-slim"> i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body"
<i-bs class="me-1" name="envelope"></i-bs><span>&nbsp;<ng-container i18n>Mail</ng-container></span> triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="envelope"></i-bs><span>&nbsp;<ng-container i18n>Mail</ng-container></span>
</li> </a>
</ul> </li>
<li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.Delete, type: PermissionType.Document }">
<a class="nav-link" routerLink="trash" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Trash"
i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body"
triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="me-1" name="trash"></i-bs><span>&nbsp;<ng-container i18n>Trash</ng-container></span>
</a>
</li>
</ul>
</div>
<h6 class="sidebar-heading px-3 mt-auto pt-4 mb-1 text-muted"> <div class="nav-group mt-auto mb-1">
<span i18n>Administration</span> <h6 class="sidebar-heading px-3 pt-4 text-muted">
</h6> <span i18n>Administration</span>
<ul class="nav flex-column mb-2"> </h6>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }" <ul class="nav flex-column mb-2">
tourAnchor="tour.settings"> <li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }"
<a class="nav-link" routerLink="settings" routerLinkActive="active" (click)="closeMenu()" tourAnchor="tour.settings">
ngbPopover="Settings" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" <a class="nav-link" routerLink="settings" routerLinkActive="active" (click)="closeMenu()"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> ngbPopover="Settings" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
<i-bs class="me-1" name="gear"></i-bs><span>&nbsp;<ng-container i18n>Settings</ng-container></span> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="gear"></i-bs><span>&nbsp;<ng-container i18n>Settings</ng-container></span>
</li> </a>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.AppConfig }"> </li>
<a class="nav-link" routerLink="config" routerLinkActive="active" (click)="closeMenu()" <li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.AppConfig }">
ngbPopover="Configuration" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" <a class="nav-link" routerLink="config" routerLinkActive="active" (click)="closeMenu()"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> ngbPopover="Configuration" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
<i-bs class="me-1" name="sliders2-vertical"></i-bs><span>&nbsp;<ng-container i18n>Configuration</ng-container></span> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="sliders2-vertical"></i-bs><span>&nbsp;<ng-container i18n>Configuration</ng-container></span>
</li> </a>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.User }"> </li>
<a class="nav-link" routerLink="usersgroups" routerLinkActive="active" (click)="closeMenu()" <li class="nav-item app-link" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.User }">
ngbPopover="Users & Groups" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" <a class="nav-link" routerLink="usersgroups" routerLinkActive="active" (click)="closeMenu()"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> ngbPopover="Users & Groups" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
<i-bs class="me-1" name="people"></i-bs><span>&nbsp;<ng-container i18n>Users & Groups</ng-container></span> container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
</a> <i-bs class="me-1" name="people"></i-bs><span>&nbsp;<ng-container i18n>Users & Groups</ng-container></span>
</li> </a>
<li class="nav-item" </li>
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.PaperlessTask }" <li class="nav-item app-link"
tourAnchor="tour.file-tasks"> *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.PaperlessTask }"
<a class="nav-link" routerLink="tasks" routerLinkActive="active" (click)="closeMenu()" tourAnchor="tour.file-tasks">
ngbPopover="File Tasks" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" <a class="nav-link" routerLink="tasks" routerLinkActive="active" (click)="closeMenu()"
container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> ngbPopover="File Tasks" i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end"
<i-bs class="me-1" name="list-task"></i-bs><span>&nbsp;<ng-container i18n>File Tasks</ng-container>@if (tasksService.failedFileTasks.length > 0) { container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<span><span class="badge bg-danger ms-2 d-inline">{{tasksService.failedFileTasks.length}}</span></span> <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><span class="badge bg-danger ms-2 d-inline">{{tasksService.failedFileTasks.length}}</span></span>
@if (tasksService.failedFileTasks.length > 0 && slimSidebarEnabled) { }</span>
<span class="badge bg-danger position-absolute top-0 end-0 d-none d-md-block">{{tasksService.failedFileTasks.length}}</span> @if (tasksService.failedFileTasks.length > 0 && slimSidebarEnabled) {
} <span class="badge bg-danger position-absolute top-0 end-0 d-none d-md-block">{{tasksService.failedFileTasks.length}}</span>
</a> }
</li> </a>
<li class="nav-item" *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Admin }"> </li>
<a class="nav-link" routerLink="logs" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Logs" @if (permissionsService.isAdmin()) {
i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body" <li class="nav-item app-link">
triggers="mouseenter:mouseleave" popoverClass="popover-slim"> <a class="nav-link" routerLink="logs" routerLinkActive="active" (click)="closeMenu()" ngbPopover="Logs"
<i-bs class="me-1" name="text-left"></i-bs><span>&nbsp;<ng-container i18n>Logs</ng-container></span> i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body"
</a>
</li>
<li class="nav-item mt-2" tourAnchor="tour.outro">
<a class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap text-decoration-none"
target="_blank" rel="noopener noreferrer" href="https://docs.paperless-ngx.com" ngbPopover="Documentation"
i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body"
triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<i-bs class="d-flex" name="question-circle"></i-bs><span class="ms-1">&nbsp;<ng-container i18n>Documentation</ng-container></span>
</a>
</li>
<li class="nav-item" [class.visually-hidden]="slimSidebarEnabled">
<div class="px-3 py-0 text-muted small d-flex align-items-center flex-wrap">
<div class="me-3">
<a class="text-muted text-decoration-none" target="_blank" rel="noopener noreferrer"
href="https://github.com/paperless-ngx/paperless-ngx" ngbPopover="GitHub" i18n-ngbPopover
[disablePopover]="!slimSidebarEnabled" placement="end" container="body"
triggers="mouseenter:mouseleave" popoverClass="popover-slim"> triggers="mouseenter:mouseleave" popoverClass="popover-slim">
{{ versionString }} <i-bs class="me-1" name="text-left"></i-bs><span>&nbsp;<ng-container i18n>Logs</ng-container></span>
</a> </a>
</div> </li>
@if (!settingsService.updateCheckingIsSet || appRemoteVersion) { }
<div class="version-check"> <li class="nav-item mt-2" tourAnchor="tour.outro">
<ng-template #updateAvailablePopContent> <a class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap text-decoration-none"
<span class="small">Paperless-ngx {{ appRemoteVersion.version }} <ng-container i18n>is target="_blank" rel="noopener noreferrer" href="https://docs.paperless-ngx.com" ngbPopover="Documentation"
available.</ng-container><br /><ng-container i18n>Click to view.</ng-container></span> i18n-ngbPopover [disablePopover]="!slimSidebarEnabled" placement="end" container="body"
</ng-template> triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<ng-template #updateCheckingNotEnabledPopContent> <i-bs class="d-flex" name="question-circle"></i-bs><span class="ms-1">&nbsp;<ng-container i18n>Documentation</ng-container></span>
<p class="small mb-2"> </a>
<ng-container i18n>Paperless-ngx can automatically check for updates</ng-container> </li>
</p> <li class="nav-item" [class.visually-hidden]="slimSidebarEnabled">
<div class="btn-group btn-group-xs flex-fill w-100"> <div class="px-3 py-0 text-muted small d-flex align-items-center flex-wrap">
<button class="btn btn-outline-primary" (click)="setUpdateChecking(true)">Enable</button> <div class="me-3">
<button class="btn btn-outline-secondary" (click)="setUpdateChecking(false)">Disable</button> <a class="text-muted text-decoration-none" target="_blank" rel="noopener noreferrer"
</div> href="https://github.com/paperless-ngx/paperless-ngx" ngbPopover="GitHub" i18n-ngbPopover
<p class="small mb-0 mt-2"> [disablePopover]="!slimSidebarEnabled" placement="end" container="body"
<a class="small text-decoration-none fst-italic" routerLink="/settings" fragment="update-checking" i18n> triggers="mouseenter:mouseleave" popoverClass="popover-slim">
How does this work? {{ versionString }}
</a> </a>
</p> </div>
</ng-template> @if (!settingsService.updateCheckingIsSet || appRemoteVersion) {
@if (settingsService.updateCheckingIsSet) { <div class="version-check">
@if (appRemoteVersion.update_available) { <ng-template #updateAvailablePopContent>
<a class="small text-decoration-none" target="_blank" rel="noopener noreferrer" <span class="small">Paperless-ngx {{ appRemoteVersion.version }} <ng-container i18n>is
href="https://github.com/paperless-ngx/paperless-ngx/releases" available.</ng-container><br /><ng-container i18n>Click to view.</ng-container></span>
[ngbPopover]="updateAvailablePopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" </ng-template>
<ng-template #updateCheckingNotEnabledPopContent>
<p class="small mb-2">
<ng-container i18n>Paperless-ngx can automatically check for updates</ng-container>
</p>
<div class="btn-group btn-group-xs flex-fill w-100">
<button class="btn btn-outline-primary" (click)="setUpdateChecking(true)">Enable</button>
<button class="btn btn-outline-secondary" (click)="setUpdateChecking(false)">Disable</button>
</div>
<p class="small mb-0 mt-2">
<a class="small text-decoration-none fst-italic" routerLink="/settings" fragment="update-checking" i18n>
How does this work?
</a>
</p>
</ng-template>
@if (settingsService.updateCheckingIsSet) {
@if (appRemoteVersion.update_available) {
<a class="small text-decoration-none" target="_blank" rel="noopener noreferrer"
href="https://github.com/paperless-ngx/paperless-ngx/releases"
[ngbPopover]="updateAvailablePopContent" popoverClass="shadow" triggers="mouseenter:mouseleave"
container="body">
<i-bs width="1.2em" height="1.2em" name="info-circle"></i-bs>
@if (appRemoteVersion?.update_available) {
&nbsp;<ng-container i18n>Update available</ng-container>
}
</a>
}
} @else {
<a class="small text-decoration-none" routerLink="/settings" fragment="update-checking"
[ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter"
container="body"> container="body">
<i-bs width="1.2em" height="1.2em" name="info-circle"></i-bs> <i-bs width="1.2em" height="1.2em" name="info-circle"></i-bs>
@if (appRemoteVersion?.update_available) {
<ng-container i18n>Update available</ng-container>
}
</a> </a>
} }
} @else { </div>
<a class="small text-decoration-none" routerLink="/settings" fragment="update-checking" }
[ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter" </div>
container="body"> </li>
<i-bs width="1.2em" height="1.2em" name="info-circle"></i-bs> </ul>
</a> </div>
}
</div>
}
</div>
</li>
</ul>
</div> </div>
</nav> </nav>

View File

@@ -12,23 +12,30 @@
z-index: 995; /* Behind the navbar */ z-index: 995; /* Behind the navbar */
padding: 50px 0 0; /* Height of navbar */ padding: 50px 0 0; /* Height of navbar */
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
overflow-y: auto;
--pngx-sidebar-width: 100%;
max-width: var(--pngx-sidebar-width);
.sidebar-heading .spinner-border { .sidebar-heading .spinner-border {
width: 0.8em; width: 0.8em;
height: 0.8em; height: 0.8em;
} }
.nav-group:not(:has(.app-link)) .sidebar-heading {
display: none !important;
}
// These come from the col-* classes for non-slim sidebar, needed for animation // These come from the col-* classes for non-slim sidebar, needed for animation
@media (min-width: 768px) { @media (min-width: 768px) {
max-width: 25%; --pngx-sidebar-width: 25%;
} }
@media (min-width: 992px) { @media (min-width: 992px) {
max-width: 16.66666667%; --pngx-sidebar-width: 16.66666667%;
} }
@media (min-width: 2400px) { @media (min-width: 2400px) {
max-width: 8.33333333%; --pngx-sidebar-width: 8.33333333%;
} }
transition: all .2s ease; transition: all .2s ease;
@@ -105,12 +112,17 @@ main {
.sidebar-slim-toggler { .sidebar-slim-toggler {
display: block; display: block;
position: absolute; position: fixed;
right: -12px; left: calc(var(--pngx-sidebar-width) - 12px);
top: 60px; top: 60px;
z-index: 996; z-index: 996;
--bs-btn-padding-x: 0.35rem; --bs-btn-padding-x: 0.35rem;
--bs-btn-padding-y: 0.125rem; --bs-btn-padding-y: 0.125rem;
transition: all .2s ease;
}
.sidebar.slim .sidebar-slim-toggler {
--pngx-sidebar-width: 50px !important;
} }
} }
@@ -253,59 +265,6 @@ main {
} }
} }
.navbar .search-form-container {
max-width: 550px;
form {
position: relative;
> i-bs {
position: absolute;
left: 0.6rem;
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;
}
}
}
&:focus-within {
form > i-bs {
display: none;
}
.form-control::placeholder {
color: rgba(255, 255, 255, 0);
}
}
.form-control {
color: rgba(255, 255, 255, 0.3);
background-color: rgba(0, 0, 0, 0.15);
padding-left: 1.8rem;
border-color: rgba(255, 255, 255, 0.2);
transition: all .3s ease, padding-left 0s ease, background-color 0s ease; // Safari requires all
max-width: 600px;
min-width: 300px; // 1/2 max
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
&:focus {
background-color: rgba(0, 0, 0, 0.3);
color: var(--bs-light);
flex-grow: 1;
padding-left: 0.5rem;
}
}
}
.version-check { .version-check {
animation: pulse 2s ease-in-out 0s 1; animation: pulse 2s ease-in-out 0s 1;
} }

View File

@@ -1,6 +1,6 @@
import { import {
HttpClientTestingModule,
HttpTestingController, HttpTestingController,
provideHttpClientTesting,
} from '@angular/common/http/testing' } from '@angular/common/http/testing'
import { AppFrameComponent } from './app-frame.component' import { AppFrameComponent } from './app-frame.component'
import { import {
@@ -30,14 +30,14 @@ import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { DocumentDetailComponent } from '../document-detail/document-detail.component' import { DocumentDetailComponent } from '../document-detail/document-detail.component'
import { SearchService } from 'src/app/services/rest/search.service' import { SearchService } from 'src/app/services/rest/search.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
import { routes } from 'src/app/app-routing.module' import { routes } from 'src/app/app-routing.module'
import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { CdkDragDrop, DragDropModule } from '@angular/cdk/drag-drop' import { CdkDragDrop, DragDropModule } from '@angular/cdk/drag-drop'
import { SavedView } from 'src/app/data/saved-view' import { SavedView } from 'src/app/data/saved-view'
import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component' import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { GlobalSearchComponent } from './global-search/global-search.component'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
const saved_views = [ const saved_views = [
{ {
@@ -89,17 +89,18 @@ describe('AppFrameComponent', () => {
let toastService: ToastService let toastService: ToastService
let messagesService: DjangoMessagesService let messagesService: DjangoMessagesService
let openDocumentsService: OpenDocumentsService let openDocumentsService: OpenDocumentsService
let searchService: SearchService
let documentListViewService: DocumentListViewService
let router: Router let router: Router
let savedViewSpy let savedViewSpy
let modalService: NgbModal let modalService: NgbModal
beforeEach(async () => { beforeEach(async () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [AppFrameComponent, IfPermissionsDirective], declarations: [
AppFrameComponent,
IfPermissionsDirective,
GlobalSearchComponent,
],
imports: [ imports: [
HttpClientTestingModule,
BrowserModule, BrowserModule,
RouterTestingModule.withRoutes(routes), RouterTestingModule.withRoutes(routes),
NgbModule, NgbModule,
@@ -114,7 +115,7 @@ describe('AppFrameComponent', () => {
{ {
provide: SavedViewService, provide: SavedViewService,
useValue: { useValue: {
initialize: () => {}, reload: () => {},
listAll: () => listAll: () =>
of({ of({
all: [saved_views.map((v) => v.id)], all: [saved_views.map((v) => v.id)],
@@ -149,6 +150,8 @@ describe('AppFrameComponent', () => {
}, },
}, },
PermissionsGuard, PermissionsGuard,
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
], ],
}).compileComponents() }).compileComponents()
@@ -159,8 +162,6 @@ describe('AppFrameComponent', () => {
toastService = TestBed.inject(ToastService) toastService = TestBed.inject(ToastService)
messagesService = TestBed.inject(DjangoMessagesService) messagesService = TestBed.inject(DjangoMessagesService)
openDocumentsService = TestBed.inject(OpenDocumentsService) openDocumentsService = TestBed.inject(OpenDocumentsService)
searchService = TestBed.inject(SearchService)
documentListViewService = TestBed.inject(DocumentListViewService)
modalService = TestBed.inject(NgbModal) modalService = TestBed.inject(NgbModal)
router = TestBed.inject(Router) router = TestBed.inject(Router)
@@ -169,7 +170,7 @@ describe('AppFrameComponent', () => {
.mockReturnValue('Hello World') .mockReturnValue('Hello World')
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
savedViewSpy = jest.spyOn(savedViewService, 'initialize') savedViewSpy = jest.spyOn(savedViewService, 'reload')
fixture = TestBed.createComponent(AppFrameComponent) fixture = TestBed.createComponent(AppFrameComponent)
component = fixture.componentInstance component = fixture.componentInstance
@@ -296,62 +297,6 @@ describe('AppFrameComponent', () => {
expect(component.canDeactivate()).toBeFalsy() expect(component.canDeactivate()).toBeFalsy()
}) })
it('should call autocomplete endpoint on input', fakeAsync(() => {
const autocompleteSpy = jest.spyOn(searchService, 'autocomplete')
component.searchAutoComplete(of('hello')).subscribe()
tick(250)
expect(autocompleteSpy).toHaveBeenCalled()
component.searchAutoComplete(of('hello world 1')).subscribe()
tick(250)
expect(autocompleteSpy).toHaveBeenCalled()
}))
it('should handle autocomplete backend failure gracefully', fakeAsync(() => {
const serviceAutocompleteSpy = jest.spyOn(searchService, 'autocomplete')
serviceAutocompleteSpy.mockReturnValue(
throwError(() => new Error('autcomplete failed'))
)
// serviceAutocompleteSpy.mockReturnValue(of([' world']))
let result
component.searchAutoComplete(of('hello')).subscribe((res) => {
result = res
})
tick(250)
expect(serviceAutocompleteSpy).toHaveBeenCalled()
expect(result).toEqual([])
}))
it('should support reset search field', () => {
const resetSpy = jest.spyOn(component, 'resetSearchField')
const input = (fixture.nativeElement as HTMLDivElement).querySelector(
'input'
) as HTMLInputElement
input.dispatchEvent(new KeyboardEvent('keyup', { key: 'Escape' }))
expect(resetSpy).toHaveBeenCalled()
})
it('should support choosing a search item', () => {
expect(component.searchField.value).toEqual('')
component.itemSelected({ item: 'hello', preventDefault: () => true })
expect(component.searchField.value).toEqual('hello ')
component.itemSelected({ item: 'world', preventDefault: () => true })
expect(component.searchField.value).toEqual('hello world ')
})
it('should navigate via quickFilter on search', () => {
const str = 'hello world '
component.searchField.patchValue(str)
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
component.search()
expect(qfSpy).toHaveBeenCalledWith([
{
rule_type: FILTER_FULLTEXT_QUERY,
value: str.trim(),
},
])
})
it('should disable global dropzone on start drag + drop, re-enable after', () => { it('should disable global dropzone on start drag + drop, re-enable after', () => {
expect(settingsService.globalDropzoneEnabled).toBeTruthy() expect(settingsService.globalDropzoneEnabled).toBeTruthy()
component.onDragStart(null) component.onDragStart(null)

View File

@@ -1,15 +1,7 @@
import { Component, HostListener, OnInit } from '@angular/core' import { Component, HostListener, OnInit } from '@angular/core'
import { FormControl } from '@angular/forms'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { from, Observable } from 'rxjs' import { Observable } from 'rxjs'
import { import { first } from 'rxjs/operators'
debounceTime,
distinctUntilChanged,
map,
switchMap,
first,
catchError,
} from 'rxjs/operators'
import { Document } from 'src/app/data/document' import { Document } from 'src/app/data/document'
import { OpenDocumentsService } from 'src/app/services/open-documents.service' import { OpenDocumentsService } from 'src/app/services/open-documents.service'
import { import {
@@ -17,11 +9,8 @@ import {
DjangoMessagesService, DjangoMessagesService,
} from 'src/app/services/django-messages.service' } from 'src/app/services/django-messages.service'
import { SavedViewService } from 'src/app/services/rest/saved-view.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' import { environment } from 'src/environments/environment'
import { DocumentDetailComponent } from '../document-detail/document-detail.component' import { DocumentDetailComponent } from '../document-detail/document-detail.component'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
import { import {
RemoteVersionService, RemoteVersionService,
AppRemoteVersion, AppRemoteVersion,
@@ -63,16 +52,12 @@ export class AppFrameComponent
slimSidebarAnimating: boolean = false slimSidebarAnimating: boolean = false
searchField = new FormControl('')
constructor( constructor(
public router: Router, public router: Router,
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
private openDocumentsService: OpenDocumentsService, private openDocumentsService: OpenDocumentsService,
private searchService: SearchService,
public savedViewService: SavedViewService, public savedViewService: SavedViewService,
private remoteVersionService: RemoteVersionService, private remoteVersionService: RemoteVersionService,
private list: DocumentListViewService,
public settingsService: SettingsService, public settingsService: SettingsService,
public tasksService: TasksService, public tasksService: TasksService,
private readonly toastService: ToastService, private readonly toastService: ToastService,
@@ -88,7 +73,7 @@ export class AppFrameComponent
PermissionType.SavedView PermissionType.SavedView
) )
) { ) {
this.savedViewService.initialize() this.savedViewService.reload()
} }
} }
@@ -164,65 +149,6 @@ export class AppFrameComponent
return !this.openDocumentsService.hasDirty() return !this.openDocumentsService.hasDirty()
} }
get searchFieldEmpty(): boolean {
return this.searchField.value.trim().length == 0
}
resetSearchField() {
this.searchField.reset('')
}
searchFieldKeyup(event: KeyboardEvent) {
if (event.key == 'Escape') {
this.resetSearchField()
}
}
searchAutoComplete = (text$: Observable<string>) =>
text$.pipe(
debounceTime(200),
distinctUntilChanged(),
map((term) => {
if (term.lastIndexOf(' ') != -1) {
return term.substring(term.lastIndexOf(' ') + 1)
} else {
return term
}
}),
switchMap((term) =>
term.length < 2
? from([[]])
: this.searchService.autocomplete(term).pipe(
catchError(() => {
return from([[]])
})
)
)
)
itemSelected(event) {
event.preventDefault()
let currentSearch: string = this.searchField.value
let lastSpaceIndex = currentSearch.lastIndexOf(' ')
if (lastSpaceIndex != -1) {
currentSearch = currentSearch.substring(0, lastSpaceIndex + 1)
currentSearch += event.item + ' '
} else {
currentSearch = event.item + ' '
}
this.searchField.patchValue(currentSearch)
}
search() {
this.closeMenu()
this.list.quickFilter([
{
rule_type: FILTER_FULLTEXT_QUERY,
value: (this.searchField.value as string).trim(),
},
])
}
closeDocument(d: Document) { closeDocument(d: Document) {
this.openDocumentsService this.openDocumentsService
.closeDocument(d) .closeDocument(d)

View File

@@ -0,0 +1,174 @@
<div ngbDropdown #resultsDropdown="ngbDropdown" (openChange)="onDropdownOpenChange">
<form class="form-inline position-relative">
<i-bs width="1em" height="1em" name="search"></i-bs>
<div class="input-group">
<div class="form-control form-control-sm">
<input class="bg-transparent border-0 w-100 h-100" #searchInput type="text" name="query"
placeholder="Search" aria-label="Search" i18n-placeholder
autocomplete="off"
spellcheck="false"
[(ngModel)]="query"
(ngModelChange)="this.queryDebounce.next($event)"
(keydown)="searchInputKeyDown($event)"
ngbDropdownAnchor>
<div class="position-absolute top-50 end-0 translate-middle">
@if (loading) {
<div class="spinner-border spinner-border-sm text-muted mt-1"></div>
}
</div>
</div>
@if (query) {
<button class="btn btn-sm btn-outline-secondary" type="button" (click)="runFullSearch()">
@if (useAdvancedForFullSearch) {
<ng-container i18n>Advanced search</ng-container>
} @else {
<ng-container i18n>Search</ng-container>
}
<i-bs width="1em" height="1em" name="arrow-right-short"></i-bs>
</button>
}
</div>
</form>
<ng-template #resultItemTemplate let-item="item" let-nameProp="nameProp" let-type="type" let-icon="icon" let-date="date">
<div #resultItem ngbDropdownItem class="py-2 d-flex align-items-center focus-ring border-0 cursor-pointer" tabindex="-1"
(click)="primaryAction(type, item, $event)"
(mouseenter)="onItemHover($event)">
<i-bs width="1.2em" height="1.2em" name="{{icon}}" class="me-2 text-muted"></i-bs>
<div class="text-truncate">
{{item[nameProp]}}
@if (date) {
<small class="small text-muted">{{date | customDate}}</small>
}
</div>
<div class="btn-group ms-auto">
<button #primaryButton type="button" class="btn btn-sm btn-outline-primary d-flex"
(click)="primaryAction(type, item, $event); $event.stopImmediatePropagation()"
(keydown)="onButtonKeyDown($event)"
[disabled]="disablePrimaryButton(type, item)"
(mouseenter)="onButtonHover($event)">
@if (type === DataType.Document) {
<i-bs width="1em" height="1em" name="box-arrow-in-right"></i-bs>
<span>&nbsp;<ng-container i18n>Open</ng-container></span>
} @else if (type === DataType.SavedView) {
<i-bs width="1em" height="1em" name="eye"></i-bs>
<span>&nbsp;<ng-container i18n>Open</ng-container></span>
} @else if (type === DataType.Workflow || type === DataType.CustomField || type === DataType.Group || type === DataType.User || type === DataType.MailAccount || type === DataType.MailRule) {
<i-bs width="1em" height="1em" name="pencil"></i-bs>
<span>&nbsp;<ng-container i18n>Open</ng-container></span>
} @else {
<i-bs width="1em" height="1em" name="filter"></i-bs>
<span>&nbsp;<ng-container i18n>Filter documents</ng-container></span>
}
</button>
@if (type !== DataType.SavedView && type !== DataType.Workflow && type !== DataType.CustomField && type !== DataType.Group && type !== DataType.User && type !== DataType.MailAccount && type !== DataType.MailRule) {
<button #secondaryButton type="button" class="btn btn-sm btn-outline-primary d-flex"
(click)="secondaryAction(type, item, $event); $event.stopImmediatePropagation()"
(keydown)="onButtonKeyDown($event)"
[disabled]="disableSecondaryButton(type, item)"
(mouseenter)="onButtonHover($event)">
@if (type === DataType.Document) {
<i-bs width="1em" height="1em" name="download"></i-bs>
<span>&nbsp;<ng-container i18n>Download</ng-container></span>
} @else {
<i-bs width="1em" height="1em" name="box-arrow-in-right"></i-bs>
<span>&nbsp;<ng-container i18n>Open</ng-container></span>
}
</button>
}
</div>
</div>
</ng-template>
<div ngbDropdownMenu class="w-100 mh-75 overflow-y-scroll shadow-lg">
<div (keydown)="dropdownKeyDown($event)">
@if (searchResults?.total === 0) {
<h6 class="dropdown-header" i18n="@@searchResults.noResults">No results</h6>
} @else {
@if (searchResults?.documents.length) {
<h6 class="dropdown-header" i18n="@@searchResults.documents">Documents</h6>
@for (document of searchResults.documents; track document.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: document, nameProp: 'title', type: DataType.Document, icon: 'file-text', date: document.added}"></ng-container>
}
}
@if (searchResults?.saved_views.length) {
<h6 class="dropdown-header" i18n="@@searchResults.saved_views">Saved Views</h6>
@for (saved_view of searchResults.saved_views; track saved_view.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: saved_view, nameProp: 'name', type: DataType.SavedView, icon: 'funnel'}"></ng-container>
}
}
@if (searchResults?.tags.length) {
<h6 class="dropdown-header" i18n="@@searchResults.tags">Tags</h6>
@for (tag of searchResults.tags; track tag.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: tag, nameProp: 'name', type: DataType.Tag, icon: 'tag'}"></ng-container>
}
}
@if (searchResults?.correspondents.length) {
<h6 class="dropdown-header" i18n="@@searchResults.correspondents">Correspondents</h6>
@for (correspondent of searchResults.correspondents; track correspondent.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: correspondent, nameProp: 'name', type: DataType.Correspondent, icon: 'person'}"></ng-container>
}
}
@if (searchResults?.document_types.length) {
<h6 class="dropdown-header" i18n="@@searchResults.documentTypes">Document types</h6>
@for (documentType of searchResults.document_types; track documentType.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: documentType, nameProp: 'name', type: DataType.DocumentType, icon: 'file-earmark'}"></ng-container>
}
}
@if (searchResults?.storage_paths.length) {
<h6 class="dropdown-header" i18n="@@searchResults.storagePaths">Storage paths</h6>
@for (storagePath of searchResults.storage_paths; track storagePath.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: storagePath, nameProp: 'name', type: DataType.StoragePath, icon: 'folder'}"></ng-container>
}
}
@if (searchResults?.users.length) {
<h6 class="dropdown-header" i18n="@@searchResults.users">Users</h6>
@for (user of searchResults.users; track user.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: user, nameProp: 'username', type: DataType.User, icon: 'person-square'}"></ng-container>
}
}
@if (searchResults?.groups.length) {
<h6 class="dropdown-header" i18n="@@searchResults.groups">Groups</h6>
@for (group of searchResults.groups; track group.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: group, nameProp: 'name', type: DataType.Group, icon: 'people'}"></ng-container>
}
}
@if (searchResults?.custom_fields.length) {
<h6 class="dropdown-header" i18n="@@searchResults.customFields">Custom fields</h6>
@for (customField of searchResults.custom_fields; track customField.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: customField, nameProp: 'name', type: DataType.CustomField, icon: 'ui-radios'}"></ng-container>
}
}
@if (searchResults?.mail_accounts.length) {
<h6 class="dropdown-header" i18n="@@searchResults.mailAccounts">Mail accounts</h6>
@for (mailAccount of searchResults.mail_accounts; track mailAccount.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: mailAccount, nameProp: 'name', type: DataType.MailAccount, icon: 'envelope-at'}"></ng-container>
}
}
@if (searchResults?.mail_rules.length) {
<h6 class="dropdown-header" i18n="@@searchResults.mailRules">Mail rules</h6>
@for (mailRule of searchResults.mail_rules; track mailRule.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: mailRule, nameProp: 'name', type: DataType.MailRule, icon: 'envelope'}"></ng-container>
}
}
@if (searchResults?.workflows.length) {
<h6 class="dropdown-header" i18n="@@searchResults.workflows">Workflows</h6>
@for (workflow of searchResults.workflows; track workflow.id) {
<ng-container *ngTemplateOutlet="resultItemTemplate; context: {item: workflow, nameProp: 'name', type: DataType.Workflow, icon: 'boxes'}"></ng-container>
}
}
}
</div>
</div>
</div>

View File

@@ -0,0 +1,97 @@
form {
position: relative;
> i-bs[name="search"] {
position: absolute;
left: 0.6rem;
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;
}
}
&:focus-within {
i-bs[name="search"],
.badge {
display: none !important;
}
.form-control::placeholder {
color: rgba(255, 255, 255, 0);
}
}
.badge {
font-size: 0.8rem;
}
.input-group .btn {
border-color: rgba(255, 255, 255, 0.2);
color: var(--pngx-primary-text-contrast);
padding-top: .15rem;
padding-bottom: .15rem;
min-height: calc(1.3em + 0.5rem + calc(var(--bs-border-width) * 2)) !important;
}
.form-control {
color: rgba(255, 255, 255, 0.3);
background-color: rgba(0, 0, 0, 0.15);
padding-left: 1.8rem;
border-color: rgba(255, 255, 255, 0.2);
transition: all .3s ease, padding-left 0s ease, background-color 0s ease; // Safari requires all
> input {
outline: none;
color: var(--pngx-primary-text-contrast);
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
&:focus-within {
background-color: rgba(0, 0, 0, 0.3);
color: var(--bs-light);
flex-grow: 1;
padding-left: 0.5rem;
}
}
}
* {
--pngx-focus-alpha: 0;
}
.mh-75 {
max-height: 75vh;
}
.dropdown-item {
&:has(button:focus) {
background-color: var(--pngx-bg-darker);
}
& button {
transition: all 0.3s ease, color 0.15s ease;
max-width: 2rem;
overflow: hidden;
}
& button span {
opacity: 0;
transition: inherit;
}
&:hover button,
&:has(button:focus) button {
max-width: 10rem;
}
&:hover button span,
&:has(button:focus) span {
opacity: 1;
}
}

View File

@@ -0,0 +1,550 @@
import {
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing'
import { GlobalSearchComponent } from './global-search.component'
import { of } from 'rxjs'
import { SearchService } from 'src/app/services/rest/search.service'
import { Router } from '@angular/router'
import {
NgbDropdownModule,
NgbModal,
NgbModalModule,
NgbModalRef,
} from '@ng-bootstrap/ng-bootstrap'
import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component'
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import {
FILTER_FULLTEXT_QUERY,
FILTER_HAS_CORRESPONDENT_ANY,
FILTER_HAS_DOCUMENT_TYPE_ANY,
FILTER_HAS_STORAGE_PATH_ANY,
FILTER_HAS_TAGS_ALL,
FILTER_TITLE_CONTENT,
} from 'src/app/data/filter-rule-type'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { DocumentService } from 'src/app/services/rest/document.service'
import { MailRuleEditDialogComponent } from '../../common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component'
import { MailAccountEditDialogComponent } from '../../common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component'
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component'
import { ElementRef } from '@angular/core'
import { ToastService } from 'src/app/services/toast.service'
import { DataType } from 'src/app/data/datatype'
import { queryParamsFromFilterRules } from 'src/app/utils/query-params'
import { SettingsService } from 'src/app/services/settings.service'
import { GlobalSearchType, SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
const searchResults = {
total: 11,
documents: [
{
id: 1,
title: 'Test',
created_at: new Date(),
updated_at: new Date(),
document_type: { id: 1, name: 'Test' },
storage_path: { id: 1, path: 'Test' },
tags: [],
correspondents: [],
custom_fields: [],
},
],
saved_views: [
{
id: 1,
name: 'TestSavedView',
},
],
correspondents: [
{
id: 1,
name: 'TestCorrespondent',
},
],
document_types: [
{
id: 1,
name: 'TestDocumentType',
},
],
storage_paths: [
{
id: 1,
name: 'TestStoragePath',
},
],
tags: [
{
id: 1,
name: 'TestTag',
},
],
users: [
{
id: 1,
username: 'TestUser',
},
],
groups: [
{
id: 1,
name: 'TestGroup',
},
],
mail_accounts: [
{
id: 1,
name: 'TestMailAccount',
},
],
mail_rules: [
{
id: 1,
name: 'TestMailRule',
},
],
custom_fields: [
{
id: 1,
name: 'TestCustomField',
},
],
workflows: [
{
id: 1,
name: 'TestWorkflow',
},
],
}
describe('GlobalSearchComponent', () => {
let component: GlobalSearchComponent
let fixture: ComponentFixture<GlobalSearchComponent>
let searchService: SearchService
let router: Router
let modalService: NgbModal
let documentService: DocumentService
let documentListViewService: DocumentListViewService
let toastService: ToastService
let settingsService: SettingsService
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GlobalSearchComponent],
imports: [
NgbModalModule,
NgbDropdownModule,
FormsModule,
ReactiveFormsModule,
NgxBootstrapIconsModule.pick(allIcons),
],
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
],
}).compileComponents()
searchService = TestBed.inject(SearchService)
router = TestBed.inject(Router)
modalService = TestBed.inject(NgbModal)
documentService = TestBed.inject(DocumentService)
documentListViewService = TestBed.inject(DocumentListViewService)
toastService = TestBed.inject(ToastService)
settingsService = TestBed.inject(SettingsService)
fixture = TestBed.createComponent(GlobalSearchComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should handle keyboard nav', () => {
const focusSpy = jest.spyOn(component.searchInput.nativeElement, 'focus')
document.dispatchEvent(new KeyboardEvent('keydown', { key: '/' }))
expect(focusSpy).toHaveBeenCalled()
component.searchResults = searchResults as any
component.resultsDropdown.open()
fixture.detectChanges()
component['currentItemIndex'] = 0
component['setCurrentItem']()
const firstItemFocusSpy = jest.spyOn(
component.primaryButtons.get(1).nativeElement,
'focus'
)
component.dropdownKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowDown' })
)
expect(component['currentItemIndex']).toBe(1)
expect(firstItemFocusSpy).toHaveBeenCalled()
const secondaryItemFocusSpy = jest.spyOn(
component.secondaryButtons.get(1).nativeElement,
'focus'
)
component.dropdownKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowRight' })
)
expect(secondaryItemFocusSpy).toHaveBeenCalled()
component.dropdownKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowLeft' })
)
expect(firstItemFocusSpy).toHaveBeenCalled()
const zeroItemSpy = jest.spyOn(
component.primaryButtons.get(0).nativeElement,
'focus'
)
component.dropdownKeyDown(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
expect(component['currentItemIndex']).toBe(0)
expect(zeroItemSpy).toHaveBeenCalled()
const inputFocusSpy = jest.spyOn(
component.searchInput.nativeElement,
'focus'
)
component.dropdownKeyDown(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
expect(component['currentItemIndex']).toBe(-1)
expect(inputFocusSpy).toHaveBeenCalled()
component.dropdownKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowDown' })
)
component['currentItemIndex'] = searchResults.total - 1
component['setCurrentItem']()
component.dropdownKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowDown' })
)
expect(component['currentItemIndex']).toBe(-1)
// Search input
component.searchInputKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowUp' })
)
expect(component['currentItemIndex']).toBe(searchResults.total - 1)
component.searchInputKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowDown' })
)
expect(component['currentItemIndex']).toBe(0)
component.searchResults = { total: 1 } as any
const primaryActionSpy = jest.spyOn(component, 'primaryAction')
component.searchInputKeyDown(new KeyboardEvent('keydown', { key: 'Enter' }))
expect(primaryActionSpy).toHaveBeenCalled()
component.query = 'test'
const resetSpy = jest.spyOn(GlobalSearchComponent.prototype as any, 'reset')
component.searchInputKeyDown(
new KeyboardEvent('keydown', { key: 'Escape' })
)
expect(resetSpy).toHaveBeenCalled()
component.query = ''
const blurSpy = jest.spyOn(component.searchInput.nativeElement, 'blur')
component.searchInputKeyDown(
new KeyboardEvent('keydown', { key: 'Escape' })
)
expect(blurSpy).toHaveBeenCalled()
component.searchResults = { total: 1 } as any
component.resultsDropdown.open()
component.searchInputKeyDown(
new KeyboardEvent('keydown', { key: 'ArrowDown' })
)
expect(component['currentItemIndex']).toBe(0)
const closeSpy = jest.spyOn(component.resultsDropdown, 'close')
component.dropdownKeyDown(new KeyboardEvent('keydown', { key: 'Escape' }))
expect(closeSpy).toHaveBeenCalled()
component.searchResults = searchResults as any
component.resultsDropdown.open()
component.query = 'test'
const advancedSearchSpy = jest.spyOn(component, 'runFullSearch')
component.searchInputKeyDown(new KeyboardEvent('keydown', { key: 'Enter' }))
expect(advancedSearchSpy).toHaveBeenCalled()
})
it('should search on query debounce', fakeAsync(() => {
const query = 'test'
const searchSpy = jest.spyOn(searchService, 'globalSearch')
searchSpy.mockReturnValue(of({} as any))
const dropdownOpenSpy = jest.spyOn(component.resultsDropdown, 'open')
component.queryDebounce.next(query)
tick(401)
expect(searchSpy).toHaveBeenCalledWith(query)
expect(dropdownOpenSpy).toHaveBeenCalled()
}))
it('should support primary action', () => {
const object = { id: 1 }
const routerSpy = jest.spyOn(router, 'navigate')
const modalSpy = jest.spyOn(modalService, 'open')
let modal: NgbModalRef
modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1]))
component.primaryAction(DataType.Document, object)
expect(routerSpy).toHaveBeenCalledWith(['/documents', object.id], {})
component.primaryAction(DataType.SavedView, object)
expect(routerSpy).toHaveBeenCalledWith(['/view', object.id], {})
component.primaryAction(DataType.Correspondent, object)
expect(routerSpy).toHaveBeenCalledWith(['/documents'], {
queryParams: Object.assign(
{
page: 1,
reverse: 1,
sort: 'created',
},
queryParamsFromFilterRules([
{
rule_type: FILTER_HAS_CORRESPONDENT_ANY,
value: object.id.toString(),
},
])
),
})
component.primaryAction(DataType.DocumentType, object)
expect(routerSpy).toHaveBeenCalledWith(['/documents'], {
queryParams: Object.assign(
{
page: 1,
reverse: 1,
sort: 'created',
},
queryParamsFromFilterRules([
{
rule_type: FILTER_HAS_DOCUMENT_TYPE_ANY,
value: object.id.toString(),
},
])
),
})
component.primaryAction(DataType.StoragePath, object)
expect(routerSpy).toHaveBeenCalledWith(['/documents'], {
queryParams: Object.assign(
{
page: 1,
reverse: 1,
sort: 'created',
},
queryParamsFromFilterRules([
{
rule_type: FILTER_HAS_STORAGE_PATH_ANY,
value: object.id.toString(),
},
])
),
})
component.primaryAction(DataType.Tag, object)
expect(routerSpy).toHaveBeenCalledWith(['/documents'], {
queryParams: Object.assign(
{
page: 1,
reverse: 1,
sort: 'created',
},
queryParamsFromFilterRules([
{ rule_type: FILTER_HAS_TAGS_ALL, value: object.id.toString() },
])
),
})
component.primaryAction(DataType.User, object)
expect(modalSpy).toHaveBeenCalledWith(UserEditDialogComponent, {
size: 'lg',
})
component.primaryAction(DataType.Group, object)
expect(modalSpy).toHaveBeenCalledWith(GroupEditDialogComponent, {
size: 'lg',
})
component.primaryAction(DataType.MailAccount, object)
expect(modalSpy).toHaveBeenCalledWith(MailAccountEditDialogComponent, {
size: 'xl',
})
component.primaryAction(DataType.MailRule, object)
expect(modalSpy).toHaveBeenCalledWith(MailRuleEditDialogComponent, {
size: 'xl',
})
component.primaryAction(DataType.CustomField, object)
expect(modalSpy).toHaveBeenCalledWith(CustomFieldEditDialogComponent, {
size: 'md',
})
component.primaryAction(DataType.Workflow, object)
expect(modalSpy).toHaveBeenCalledWith(WorkflowEditDialogComponent, {
size: 'xl',
})
const editDialog = modal.componentInstance as CustomFieldEditDialogComponent
const toastErrorSpy = jest.spyOn(toastService, 'showError')
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
// fail first
editDialog.failed.emit({ error: 'error creating item' })
expect(toastErrorSpy).toHaveBeenCalled()
// succeed
editDialog.succeeded.emit(true)
expect(toastInfoSpy).toHaveBeenCalled()
})
it('should support secondary action', () => {
const doc = searchResults.documents[0]
const openSpy = jest.spyOn(window, 'open')
component.secondaryAction('document', doc)
expect(openSpy).toHaveBeenCalledWith(documentService.getDownloadUrl(doc.id))
const correspondent = searchResults.correspondents[0]
const modalSpy = jest.spyOn(modalService, 'open')
let modal: NgbModalRef
modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1]))
component.secondaryAction(DataType.Correspondent, correspondent)
expect(modalSpy).toHaveBeenCalledWith(CorrespondentEditDialogComponent, {
size: 'md',
})
component.secondaryAction(
DataType.DocumentType,
searchResults.document_types[0]
)
expect(modalSpy).toHaveBeenCalledWith(CorrespondentEditDialogComponent, {
size: 'md',
})
component.secondaryAction(
DataType.StoragePath,
searchResults.storage_paths[0]
)
expect(modalSpy).toHaveBeenCalledWith(CorrespondentEditDialogComponent, {
size: 'md',
})
component.secondaryAction(DataType.Tag, searchResults.tags[0])
expect(modalSpy).toHaveBeenCalledWith(CorrespondentEditDialogComponent, {
size: 'md',
})
const editDialog = modal.componentInstance as CustomFieldEditDialogComponent
const toastErrorSpy = jest.spyOn(toastService, 'showError')
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
// fail first
editDialog.failed.emit({ error: 'error creating item' })
expect(toastErrorSpy).toHaveBeenCalled()
// succeed
editDialog.succeeded.emit(true)
expect(toastInfoSpy).toHaveBeenCalled()
})
it('should support reset', () => {
const debounce = jest.spyOn(component.queryDebounce, 'next')
const closeSpy = jest.spyOn(component.resultsDropdown, 'close')
component['reset'](true)
expect(debounce).toHaveBeenCalledWith(null)
expect(component.searchResults).toBeNull()
expect(component['currentItemIndex']).toBe(-1)
expect(closeSpy).toHaveBeenCalled()
})
it('should support focus current item', () => {
component.searchResults = searchResults as any
fixture.detectChanges()
const focusSpy = jest.spyOn(
component.primaryButtons.get(0).nativeElement,
'focus'
)
component['currentItemIndex'] = 0
component['setCurrentItem']()
expect(focusSpy).toHaveBeenCalled()
})
it('should reset on dropdown close', () => {
const resetSpy = jest.spyOn(GlobalSearchComponent.prototype as any, 'reset')
component.onDropdownOpenChange(false)
expect(resetSpy).toHaveBeenCalled()
})
it('should focus button on dropdown item hover', () => {
component.searchResults = searchResults as any
fixture.detectChanges()
const item: ElementRef = component.resultItems.first
const focusSpy = jest.spyOn(
component.primaryButtons.first.nativeElement,
'focus'
)
component.onItemHover({ currentTarget: item.nativeElement } as any)
expect(component['currentItemIndex']).toBe(0)
expect(focusSpy).toHaveBeenCalled()
})
it('should focus on button hover', () => {
const event = { currentTarget: { focus: jest.fn() } }
const focusSpy = jest.spyOn(event.currentTarget, 'focus')
component.onButtonHover(event as any)
expect(focusSpy).toHaveBeenCalled()
})
it('should support open in new window', () => {
const openSpy = jest.spyOn(window, 'open')
const event = new Event('click')
event['ctrlKey'] = true
component.primaryAction(DataType.Document, { id: 2 }, event as any)
expect(openSpy).toHaveBeenCalledWith('/documents/2', '_blank')
component.searchResults = searchResults as any
component.resultsDropdown.open()
fixture.detectChanges()
const button = component.primaryButtons.get(0).nativeElement
const keyboardEvent = new KeyboardEvent('keydown', {
key: 'Enter',
ctrlKey: true,
})
const dispatchSpy = jest.spyOn(button, 'dispatchEvent')
button.dispatchEvent(keyboardEvent)
expect(dispatchSpy).toHaveBeenCalledTimes(2) // once for keydown, second for click
})
it('should support title content search and advanced search', () => {
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
component.query = 'test'
component.runFullSearch()
expect(qfSpy).toHaveBeenCalledWith([
{ rule_type: FILTER_TITLE_CONTENT, value: 'test' },
])
settingsService.set(
SETTINGS_KEYS.SEARCH_FULL_TYPE,
GlobalSearchType.ADVANCED
)
component.query = 'test'
component.runFullSearch()
expect(qfSpy).toHaveBeenCalledWith([
{ rule_type: FILTER_FULLTEXT_QUERY, value: 'test' },
])
})
})

View File

@@ -0,0 +1,416 @@
import {
Component,
ViewChild,
ElementRef,
ViewChildren,
QueryList,
OnInit,
} from '@angular/core'
import { Router } from '@angular/router'
import { NgbDropdown, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
import { Subject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs'
import {
FILTER_FULLTEXT_QUERY,
FILTER_HAS_CORRESPONDENT_ANY,
FILTER_HAS_DOCUMENT_TYPE_ANY,
FILTER_HAS_STORAGE_PATH_ANY,
FILTER_HAS_TAGS_ALL,
FILTER_TITLE_CONTENT,
} from 'src/app/data/filter-rule-type'
import { DataType } from 'src/app/data/datatype'
import { ObjectWithId } from 'src/app/data/object-with-id'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import {
PermissionsService,
PermissionAction,
} from 'src/app/services/permissions.service'
import { DocumentService } from 'src/app/services/rest/document.service'
import {
GlobalSearchResult,
SearchService,
} from 'src/app/services/rest/search.service'
import { ToastService } from 'src/app/services/toast.service'
import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component'
import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component'
import { DocumentTypeEditDialogComponent } from '../../common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component'
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
import { MailAccountEditDialogComponent } from '../../common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component'
import { MailRuleEditDialogComponent } from '../../common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component'
import { StoragePathEditDialogComponent } from '../../common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component'
import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component'
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component'
import { HotKeyService } from 'src/app/services/hot-key.service'
import { paramsFromViewState } from 'src/app/utils/query-params'
import { SettingsService } from 'src/app/services/settings.service'
import { GlobalSearchType, SETTINGS_KEYS } from 'src/app/data/ui-settings'
@Component({
selector: 'pngx-global-search',
templateUrl: './global-search.component.html',
styleUrl: './global-search.component.scss',
})
export class GlobalSearchComponent implements OnInit {
public DataType = DataType
public query: string
public queryDebounce: Subject<string>
public searchResults: GlobalSearchResult
private currentItemIndex: number = -1
private domIndex: number = -1
public loading: boolean = false
@ViewChild('searchInput') searchInput: ElementRef
@ViewChild('resultsDropdown') resultsDropdown: NgbDropdown
@ViewChildren('resultItem') resultItems: QueryList<ElementRef>
@ViewChildren('primaryButton') primaryButtons: QueryList<ElementRef>
@ViewChildren('secondaryButton') secondaryButtons: QueryList<ElementRef>
get useAdvancedForFullSearch(): boolean {
return (
this.settingsService.get(SETTINGS_KEYS.SEARCH_FULL_TYPE) ===
GlobalSearchType.ADVANCED
)
}
constructor(
public searchService: SearchService,
private router: Router,
private modalService: NgbModal,
private documentService: DocumentService,
private documentListViewService: DocumentListViewService,
private permissionsService: PermissionsService,
private toastService: ToastService,
private hotkeyService: HotKeyService,
private settingsService: SettingsService
) {
this.queryDebounce = new Subject<string>()
this.queryDebounce
.pipe(
debounceTime(400),
map((query) => query?.trim()),
filter((query) => !query?.length || query?.length > 2),
distinctUntilChanged()
)
.subscribe((text) => {
this.query = text
if (text) this.search(text)
})
}
public ngOnInit() {
this.hotkeyService
.addShortcut({ keys: '/', description: $localize`Global search` })
.subscribe(() => {
this.searchInput.nativeElement.focus()
})
}
private search(query: string) {
this.loading = true
this.searchService.globalSearch(query).subscribe((results) => {
this.searchResults = results
this.loading = false
this.resultsDropdown.open()
})
}
public primaryAction(
type: string,
object: ObjectWithId,
event: PointerEvent = null
) {
const newWindow = event?.metaKey || event?.ctrlKey
this.reset(true)
let filterRuleType: number
let editDialogComponent: any
let size: string = 'md'
switch (type) {
case DataType.Document:
this.navigateOrOpenInNewWindow(['/documents', object.id], newWindow)
return
case DataType.SavedView:
this.navigateOrOpenInNewWindow(['/view', object.id], newWindow)
return
case DataType.Correspondent:
filterRuleType = FILTER_HAS_CORRESPONDENT_ANY
break
case DataType.DocumentType:
filterRuleType = FILTER_HAS_DOCUMENT_TYPE_ANY
break
case DataType.StoragePath:
filterRuleType = FILTER_HAS_STORAGE_PATH_ANY
break
case DataType.Tag:
filterRuleType = FILTER_HAS_TAGS_ALL
break
case DataType.User:
editDialogComponent = UserEditDialogComponent
size = 'lg'
break
case DataType.Group:
editDialogComponent = GroupEditDialogComponent
size = 'lg'
break
case DataType.MailAccount:
editDialogComponent = MailAccountEditDialogComponent
size = 'xl'
break
case DataType.MailRule:
editDialogComponent = MailRuleEditDialogComponent
size = 'xl'
break
case DataType.CustomField:
editDialogComponent = CustomFieldEditDialogComponent
break
case DataType.Workflow:
editDialogComponent = WorkflowEditDialogComponent
size = 'xl'
break
}
if (filterRuleType) {
let params = paramsFromViewState({
filterRules: [
{ rule_type: filterRuleType, value: object.id.toString() },
],
currentPage: 1,
sortField: this.documentListViewService.sortField ?? 'created',
sortReverse: this.documentListViewService.sortReverse,
})
this.navigateOrOpenInNewWindow(['/documents'], newWindow, {
queryParams: params,
})
} else if (editDialogComponent) {
const modalRef: NgbModalRef = this.modalService.open(
editDialogComponent,
{ size }
)
modalRef.componentInstance.dialogMode = EditDialogMode.EDIT
modalRef.componentInstance.object = object
modalRef.componentInstance.succeeded.subscribe(() => {
this.toastService.showInfo($localize`Successfully updated object.`)
})
modalRef.componentInstance.failed.subscribe((e) => {
this.toastService.showError($localize`Error occurred saving object.`, e)
})
}
}
public secondaryAction(type: string, object: ObjectWithId) {
this.reset(true)
let editDialogComponent: any
let size: string = 'md'
switch (type) {
case DataType.Document:
window.open(this.documentService.getDownloadUrl(object.id))
break
case DataType.Correspondent:
editDialogComponent = CorrespondentEditDialogComponent
break
case DataType.DocumentType:
editDialogComponent = DocumentTypeEditDialogComponent
break
case DataType.StoragePath:
editDialogComponent = StoragePathEditDialogComponent
break
case DataType.Tag:
editDialogComponent = TagEditDialogComponent
break
}
if (editDialogComponent) {
const modalRef: NgbModalRef = this.modalService.open(
editDialogComponent,
{ size }
)
modalRef.componentInstance.dialogMode = EditDialogMode.EDIT
modalRef.componentInstance.object = object
modalRef.componentInstance.succeeded.subscribe(() => {
this.toastService.showInfo($localize`Successfully updated object.`)
})
modalRef.componentInstance.failed.subscribe((e) => {
this.toastService.showError($localize`Error occurred saving object.`, e)
})
}
}
private reset(close: boolean = false) {
this.queryDebounce.next(null)
this.query = null
this.searchResults = null
this.currentItemIndex = -1
if (close) {
this.resultsDropdown.close()
}
}
private setCurrentItem() {
// QueryLists do not always reflect the current DOM order, so we need to find the actual element
// Yes, using some vanilla JS
const result: HTMLElement = this.resultItems.first.nativeElement.parentNode
.querySelectorAll('.dropdown-item')
.item(this.currentItemIndex)
this.domIndex = this.resultItems
.toArray()
.indexOf(this.resultItems.find((item) => item.nativeElement === result))
const item: ElementRef = this.primaryButtons.get(this.domIndex)
item.nativeElement.focus()
}
public onItemHover(event: MouseEvent) {
const item: ElementRef = this.resultItems
.toArray()
.find((item) => item.nativeElement === event.currentTarget)
this.currentItemIndex = this.resultItems.toArray().indexOf(item)
this.setCurrentItem()
}
public onButtonHover(event: MouseEvent) {
;(event.currentTarget as HTMLElement).focus()
}
public searchInputKeyDown(event: KeyboardEvent) {
if (
event.key === 'ArrowDown' &&
this.searchResults?.total &&
this.resultsDropdown.isOpen()
) {
event.preventDefault()
this.currentItemIndex = 0
this.setCurrentItem()
} else if (
event.key === 'ArrowUp' &&
this.searchResults?.total &&
this.resultsDropdown.isOpen()
) {
event.preventDefault()
this.currentItemIndex = this.searchResults.total - 1
this.setCurrentItem()
} else if (event.key === 'Enter') {
if (this.searchResults?.total === 1 && this.resultsDropdown.isOpen()) {
this.primaryButtons.first.nativeElement.click()
this.searchInput.nativeElement.blur()
} else if (this.query?.length) {
this.runFullSearch()
this.reset(true)
}
} else if (event.key === 'Escape' && !this.resultsDropdown.isOpen()) {
if (this.query?.length) {
this.reset(true)
} else {
this.searchInput.nativeElement.blur()
}
}
}
public dropdownKeyDown(event: KeyboardEvent) {
if (
this.searchResults?.total &&
this.resultsDropdown.isOpen() &&
document.activeElement !== this.searchInput.nativeElement
) {
if (event.key === 'ArrowDown') {
event.preventDefault()
event.stopImmediatePropagation()
if (this.currentItemIndex < this.searchResults.total - 1) {
this.currentItemIndex++
this.setCurrentItem()
} else {
this.searchInput.nativeElement.focus()
this.currentItemIndex = -1
}
} else if (event.key === 'ArrowUp') {
event.preventDefault()
event.stopImmediatePropagation()
if (this.currentItemIndex > 0) {
this.currentItemIndex--
this.setCurrentItem()
} else {
this.searchInput.nativeElement.focus()
this.currentItemIndex = -1
}
} else if (event.key === 'ArrowRight') {
event.preventDefault()
event.stopImmediatePropagation()
this.secondaryButtons.get(this.domIndex)?.nativeElement.focus()
} else if (event.key === 'ArrowLeft') {
event.preventDefault()
event.stopImmediatePropagation()
this.primaryButtons.get(this.domIndex).nativeElement.focus()
} else if (event.key === 'Escape') {
event.preventDefault()
event.stopImmediatePropagation()
this.reset(true)
this.searchInput.nativeElement.focus()
}
}
}
public onButtonKeyDown(event: KeyboardEvent) {
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
event.target.dispatchEvent(new MouseEvent('click', { ctrlKey: true }))
}
}
public onDropdownOpenChange(open: boolean) {
if (!open) {
this.reset()
}
}
public disablePrimaryButton(type: DataType, object: ObjectWithId): boolean {
if (
[
DataType.Workflow,
DataType.CustomField,
DataType.Group,
DataType.User,
].includes(type)
) {
return !this.permissionsService.currentUserHasObjectPermissions(
PermissionAction.Change,
object
)
}
return false
}
public disableSecondaryButton(type: DataType, object: ObjectWithId): boolean {
if (DataType.Document === type) {
return false
}
return !this.permissionsService.currentUserHasObjectPermissions(
PermissionAction.Change,
object
)
}
public runFullSearch() {
const ruleType = this.useAdvancedForFullSearch
? FILTER_FULLTEXT_QUERY
: FILTER_TITLE_CONTENT
this.documentListViewService.quickFilter([
{ rule_type: ruleType, value: this.query },
])
this.reset(true)
}
private navigateOrOpenInNewWindow(
commands: any,
newWindow: boolean = false,
extras: Object = {}
) {
if (newWindow) {
const url = this.router.serializeUrl(
this.router.createUrlTree(commands, extras)
)
window.open(url, '_blank')
} else {
this.router.navigate(commands, extras)
}
}
}

View File

@@ -86,14 +86,4 @@ describe('ConfirmDialogComponent', () => {
expect(closeModalSpy).toHaveBeenCalled() expect(closeModalSpy).toHaveBeenCalled()
expect(confirmSubjectResult).toBeFalsy() expect(confirmSubjectResult).toBeFalsy()
}) })
it('should support delay confirm', fakeAsync(() => {
component.confirmButtonEnabled = false
component.delayConfirm(1)
expect(component.confirmButtonEnabled).toBeFalsy()
tick(1500)
fixture.detectChanges()
expect(component.confirmButtonEnabled).toBeTruthy()
discardPeriodicTasks()
}))
}) })

View File

@@ -54,26 +54,6 @@ export class ConfirmDialogComponent {
confirmSubject: Subject<boolean> confirmSubject: Subject<boolean>
alternativeSubject: Subject<boolean> alternativeSubject: Subject<boolean>
delayConfirm(seconds: number) {
const refreshInterval = 0.15 // s
this.secondsTotal = seconds
this.seconds = seconds
interval(refreshInterval * 1000)
.pipe(
take(this.secondsTotal / refreshInterval + 2) // need 2 more for animation to complete after 0
)
.subscribe((count) => {
this.seconds = Math.max(
0,
this.secondsTotal - refreshInterval * (count + 1)
)
this.confirmButtonEnabled =
this.secondsTotal - refreshInterval * count < 0
})
}
cancel() { cancel() {
this.confirmSubject?.next(false) this.confirmSubject?.next(false)
this.confirmSubject?.complete() this.confirmSubject?.complete()

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