mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-09-10 21:33:48 -05:00
Compare commits
10 Commits
v2.17.1
...
2db4c4c698
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2db4c4c698 | ||
![]() |
88c6be5940 | ||
![]() |
fa1c01a732 | ||
![]() |
37267f3f04 | ||
![]() |
b34538d991 | ||
![]() |
fc97bd1315 | ||
![]() |
dbf3721ec2 | ||
![]() |
59afbe09b1 | ||
![]() |
bfeaa1b119 | ||
![]() |
e1c3124698 |
55
.github/DISCUSSION_TEMPLATE/support.yml
vendored
Normal file
55
.github/DISCUSSION_TEMPLATE/support.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
title: "[Support] "
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: What's your question or issue?
|
||||||
|
description: Provide a clear and concise description of what you're trying to do, and what's going wrong.
|
||||||
|
placeholder: |
|
||||||
|
I'm trying to...
|
||||||
|
|
||||||
|
[Include screenshots if helpful]
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: What have you tried?
|
||||||
|
description: Describe any steps you've already taken to troubleshoot or solve the issue.
|
||||||
|
placeholder: |
|
||||||
|
- I checked the logs and saw...
|
||||||
|
- I followed the install guide and tried...
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: Paperless-ngx version
|
||||||
|
placeholder: e.g. 1.14.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: host-os
|
||||||
|
attributes:
|
||||||
|
label: Host OS
|
||||||
|
description: Include architecture if relevant.
|
||||||
|
placeholder: e.g. Ubuntu 22.04 / Raspberry Pi arm64
|
||||||
|
- type: dropdown
|
||||||
|
id: install-method
|
||||||
|
attributes:
|
||||||
|
label: Installation method
|
||||||
|
options:
|
||||||
|
- Docker - official image
|
||||||
|
- Docker - linuxserver.io image
|
||||||
|
- Bare metal
|
||||||
|
- Other (please describe above)
|
||||||
|
- 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: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Relevant logs or output
|
||||||
|
description: If you have logs, errors that might help, paste it here.
|
||||||
|
render: bash
|
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## paperless-ngx 2.17.1
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- Fix: correct PAPERLESS_EMPTY_TRASH_DIR to Path [@shamoon](https://github.com/shamoon) ([#10227](https://github.com/paperless-ngx/paperless-ngx/pull/10227))
|
||||||
|
|
||||||
|
### All App Changes
|
||||||
|
|
||||||
|
- Fix: correct PAPERLESS_EMPTY_TRASH_DIR to Path [@shamoon](https://github.com/shamoon) ([#10227](https://github.com/paperless-ngx/paperless-ngx/pull/10227))
|
||||||
|
|
||||||
## paperless-ngx 2.17.0
|
## paperless-ngx 2.17.0
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
@@ -5997,7 +6007,6 @@ primarily.
|
|||||||
a very good job at ocr'ing a document with the default
|
a very good job at ocr'ing a document with the default
|
||||||
language. Certain language specifics such as umlauts may not get
|
language. Certain language specifics such as umlauts may not get
|
||||||
picked up properly.
|
picked up properly.
|
||||||
- `PAPERLESS_DEBUG` defaults to `false`.
|
|
||||||
- The presence of `PAPERLESS_DBHOST` now determines whether to use
|
- The presence of `PAPERLESS_DBHOST` now determines whether to use
|
||||||
PostgreSQL or SQLite.
|
PostgreSQL or SQLite.
|
||||||
- `PAPERLESS_OCR_THREADS` is gone and replaced with
|
- `PAPERLESS_OCR_THREADS` is gone and replaced with
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
# Have a look at the docs for documentation.
|
# Have a look at the docs for documentation.
|
||||||
# https://docs.paperless-ngx.com/configuration/
|
# https://docs.paperless-ngx.com/configuration/
|
||||||
|
|
||||||
# Debug. Only enable this for development.
|
|
||||||
|
|
||||||
#PAPERLESS_DEBUG=false
|
|
||||||
|
|
||||||
# Required services
|
# Required services
|
||||||
|
|
||||||
#PAPERLESS_REDIS=redis://localhost:6379
|
#PAPERLESS_REDIS=redis://localhost:6379
|
||||||
|
@@ -221,9 +221,6 @@ lint.per-file-ignores."src/documents/parsers.py" = [
|
|||||||
lint.per-file-ignores."src/documents/signals/handlers.py" = [
|
lint.per-file-ignores."src/documents/signals/handlers.py" = [
|
||||||
"PTH",
|
"PTH",
|
||||||
] # TODO Enable & remove
|
] # TODO Enable & remove
|
||||||
lint.per-file-ignores."src/paperless/settings.py" = [
|
|
||||||
"PTH",
|
|
||||||
] # TODO Enable & remove
|
|
||||||
lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [
|
lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [
|
||||||
"RUF001",
|
"RUF001",
|
||||||
]
|
]
|
||||||
|
@@ -67,7 +67,8 @@
|
|||||||
"jest-websocket-mock": "^2.5.0",
|
"jest-websocket-mock": "^2.5.0",
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
"prettier-plugin-organize-imports": "^4.1.0",
|
||||||
"ts-node": "~10.9.1",
|
"ts-node": "~10.9.1",
|
||||||
"typescript": "^5.5.4"
|
"typescript": "^5.5.4",
|
||||||
|
"webpack": "^5.98.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
|
67
src-ui/pnpm-lock.yaml
generated
67
src-ui/pnpm-lock.yaml
generated
@@ -174,6 +174,9 @@ importers:
|
|||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.5.4
|
specifier: ^5.5.4
|
||||||
version: 5.5.4
|
version: 5.5.4
|
||||||
|
webpack:
|
||||||
|
specifier: ^5.98.0
|
||||||
|
version: 5.98.0(esbuild@0.25.4)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -6187,7 +6190,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@ampproject/remapping': 2.3.0
|
'@ampproject/remapping': 2.3.0
|
||||||
'@angular-devkit/architect': 0.1902.14(chokidar@4.0.3)
|
'@angular-devkit/architect': 0.1902.14(chokidar@4.0.3)
|
||||||
'@angular-devkit/build-webpack': 0.1902.14(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.4)))(webpack@5.98.0(esbuild@0.25.4))
|
'@angular-devkit/build-webpack': 0.1902.14(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0)
|
||||||
'@angular-devkit/core': 19.2.14(chokidar@4.0.3)
|
'@angular-devkit/core': 19.2.14(chokidar@4.0.3)
|
||||||
'@angular/build': 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@types/node@22.15.29)(chokidar@4.0.3)(jiti@1.21.7)(less@4.2.2)(postcss@8.5.2)(terser@5.39.0)(typescript@5.5.4)(yaml@2.7.0)
|
'@angular/build': 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)(@angular/localize@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14))(@types/node@22.15.29)(chokidar@4.0.3)(jiti@1.21.7)(less@4.2.2)(postcss@8.5.2)(terser@5.39.0)(typescript@5.5.4)(yaml@2.7.0)
|
||||||
'@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4)
|
'@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4)
|
||||||
@@ -6201,14 +6204,14 @@ snapshots:
|
|||||||
'@babel/preset-env': 7.26.9(@babel/core@7.26.10)
|
'@babel/preset-env': 7.26.9(@babel/core@7.26.10)
|
||||||
'@babel/runtime': 7.26.10
|
'@babel/runtime': 7.26.10
|
||||||
'@discoveryjs/json-ext': 0.6.3
|
'@discoveryjs/json-ext': 0.6.3
|
||||||
'@ngtools/webpack': 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(typescript@5.5.4)(webpack@5.98.0(esbuild@0.25.4))
|
'@ngtools/webpack': 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(typescript@5.5.4)(webpack@5.98.0)
|
||||||
'@vitejs/plugin-basic-ssl': 1.2.0(vite@6.2.7(@types/node@22.15.29)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.7.0))
|
'@vitejs/plugin-basic-ssl': 1.2.0(vite@6.2.7(@types/node@22.15.29)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.7.0))
|
||||||
ansi-colors: 4.1.3
|
ansi-colors: 4.1.3
|
||||||
autoprefixer: 10.4.20(postcss@8.5.2)
|
autoprefixer: 10.4.20(postcss@8.5.2)
|
||||||
babel-loader: 9.2.1(@babel/core@7.26.10)(webpack@5.98.0(esbuild@0.25.4))
|
babel-loader: 9.2.1(@babel/core@7.26.10)(webpack@5.98.0)
|
||||||
browserslist: 4.25.0
|
browserslist: 4.25.0
|
||||||
copy-webpack-plugin: 12.0.2(webpack@5.98.0(esbuild@0.25.4))
|
copy-webpack-plugin: 12.0.2(webpack@5.98.0)
|
||||||
css-loader: 7.1.2(webpack@5.98.0(esbuild@0.25.4))
|
css-loader: 7.1.2(webpack@5.98.0)
|
||||||
esbuild-wasm: 0.25.4
|
esbuild-wasm: 0.25.4
|
||||||
fast-glob: 3.3.3
|
fast-glob: 3.3.3
|
||||||
http-proxy-middleware: 3.0.5
|
http-proxy-middleware: 3.0.5
|
||||||
@@ -6216,32 +6219,32 @@ snapshots:
|
|||||||
jsonc-parser: 3.3.1
|
jsonc-parser: 3.3.1
|
||||||
karma-source-map-support: 1.4.0
|
karma-source-map-support: 1.4.0
|
||||||
less: 4.2.2
|
less: 4.2.2
|
||||||
less-loader: 12.2.0(less@4.2.2)(webpack@5.98.0(esbuild@0.25.4))
|
less-loader: 12.2.0(less@4.2.2)(webpack@5.98.0)
|
||||||
license-webpack-plugin: 4.0.2(webpack@5.98.0(esbuild@0.25.4))
|
license-webpack-plugin: 4.0.2(webpack@5.98.0)
|
||||||
loader-utils: 3.3.1
|
loader-utils: 3.3.1
|
||||||
mini-css-extract-plugin: 2.9.2(webpack@5.98.0(esbuild@0.25.4))
|
mini-css-extract-plugin: 2.9.2(webpack@5.98.0)
|
||||||
open: 10.1.0
|
open: 10.1.0
|
||||||
ora: 5.4.1
|
ora: 5.4.1
|
||||||
picomatch: 4.0.2
|
picomatch: 4.0.2
|
||||||
piscina: 4.8.0
|
piscina: 4.8.0
|
||||||
postcss: 8.5.2
|
postcss: 8.5.2
|
||||||
postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.5.4)(webpack@5.98.0(esbuild@0.25.4))
|
postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.5.4)(webpack@5.98.0)
|
||||||
resolve-url-loader: 5.0.0
|
resolve-url-loader: 5.0.0
|
||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
sass: 1.85.0
|
sass: 1.85.0
|
||||||
sass-loader: 16.0.5(sass@1.85.0)(webpack@5.98.0(esbuild@0.25.4))
|
sass-loader: 16.0.5(sass@1.85.0)(webpack@5.98.0)
|
||||||
semver: 7.7.1
|
semver: 7.7.1
|
||||||
source-map-loader: 5.0.0(webpack@5.98.0(esbuild@0.25.4))
|
source-map-loader: 5.0.0(webpack@5.98.0)
|
||||||
source-map-support: 0.5.21
|
source-map-support: 0.5.21
|
||||||
terser: 5.39.0
|
terser: 5.39.0
|
||||||
tree-kill: 1.2.2
|
tree-kill: 1.2.2
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
typescript: 5.5.4
|
typescript: 5.5.4
|
||||||
webpack: 5.98.0(esbuild@0.25.4)
|
webpack: 5.98.0(esbuild@0.25.4)
|
||||||
webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.4))
|
webpack-dev-middleware: 7.4.2(webpack@5.98.0)
|
||||||
webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.4))
|
webpack-dev-server: 5.2.0(webpack@5.98.0)
|
||||||
webpack-merge: 6.0.1
|
webpack-merge: 6.0.1
|
||||||
webpack-subresource-integrity: 5.1.0(webpack@5.98.0(esbuild@0.25.4))
|
webpack-subresource-integrity: 5.1.0(webpack@5.98.0)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@angular/localize': 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)
|
'@angular/localize': 19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(@angular/compiler@19.2.14)
|
||||||
esbuild: 0.25.4
|
esbuild: 0.25.4
|
||||||
@@ -6270,12 +6273,12 @@ snapshots:
|
|||||||
- webpack-cli
|
- webpack-cli
|
||||||
- yaml
|
- yaml
|
||||||
|
|
||||||
'@angular-devkit/build-webpack@0.1902.14(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.4)))(webpack@5.98.0(esbuild@0.25.4))':
|
'@angular-devkit/build-webpack@0.1902.14(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@angular-devkit/architect': 0.1902.14(chokidar@4.0.3)
|
'@angular-devkit/architect': 0.1902.14(chokidar@4.0.3)
|
||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
webpack: 5.98.0(esbuild@0.25.4)
|
webpack: 5.98.0(esbuild@0.25.4)
|
||||||
webpack-dev-server: 5.2.0(webpack@5.98.0(esbuild@0.25.4))
|
webpack-dev-server: 5.2.0(webpack@5.98.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- chokidar
|
- chokidar
|
||||||
|
|
||||||
@@ -8129,7 +8132,7 @@ snapshots:
|
|||||||
rxjs: 7.8.2
|
rxjs: 7.8.2
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@ngtools/webpack@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(typescript@5.5.4)(webpack@5.98.0(esbuild@0.25.4))':
|
'@ngtools/webpack@19.2.14(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4))(typescript@5.5.4)(webpack@5.98.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4)
|
'@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.5.4)
|
||||||
typescript: 5.5.4
|
typescript: 5.5.4
|
||||||
@@ -8928,7 +8931,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
babel-loader@9.2.1(@babel/core@7.26.10)(webpack@5.98.0(esbuild@0.25.4)):
|
babel-loader@9.2.1(@babel/core@7.26.10)(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.26.10
|
'@babel/core': 7.26.10
|
||||||
find-cache-dir: 4.0.0
|
find-cache-dir: 4.0.0
|
||||||
@@ -9263,7 +9266,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-what: 3.14.1
|
is-what: 3.14.1
|
||||||
|
|
||||||
copy-webpack-plugin@12.0.2(webpack@5.98.0(esbuild@0.25.4)):
|
copy-webpack-plugin@12.0.2(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-glob: 3.3.3
|
fast-glob: 3.3.3
|
||||||
glob-parent: 6.0.2
|
glob-parent: 6.0.2
|
||||||
@@ -9311,7 +9314,7 @@ snapshots:
|
|||||||
shebang-command: 2.0.0
|
shebang-command: 2.0.0
|
||||||
which: 2.0.2
|
which: 2.0.2
|
||||||
|
|
||||||
css-loader@7.1.2(webpack@5.98.0(esbuild@0.25.4)):
|
css-loader@7.1.2(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
icss-utils: 5.1.0(postcss@8.5.2)
|
icss-utils: 5.1.0(postcss@8.5.2)
|
||||||
postcss: 8.5.2
|
postcss: 8.5.2
|
||||||
@@ -10768,7 +10771,7 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
shell-quote: 1.8.3
|
shell-quote: 1.8.3
|
||||||
|
|
||||||
less-loader@12.2.0(less@4.2.2)(webpack@5.98.0(esbuild@0.25.4)):
|
less-loader@12.2.0(less@4.2.2)(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
less: 4.2.2
|
less: 4.2.2
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -10795,7 +10798,7 @@ snapshots:
|
|||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
|
|
||||||
license-webpack-plugin@4.0.2(webpack@5.98.0(esbuild@0.25.4)):
|
license-webpack-plugin@4.0.2(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
webpack-sources: 3.3.2
|
webpack-sources: 3.3.2
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -10963,7 +10966,7 @@ snapshots:
|
|||||||
mimic-response@3.1.0:
|
mimic-response@3.1.0:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
mini-css-extract-plugin@2.9.2(webpack@5.98.0(esbuild@0.25.4)):
|
mini-css-extract-plugin@2.9.2(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
schema-utils: 4.3.2
|
schema-utils: 4.3.2
|
||||||
tapable: 2.2.2
|
tapable: 2.2.2
|
||||||
@@ -11436,7 +11439,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
|
|
||||||
postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.5.4)(webpack@5.98.0(esbuild@0.25.4)):
|
postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.5.4)(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
cosmiconfig: 9.0.0(typescript@5.5.4)
|
cosmiconfig: 9.0.0(typescript@5.5.4)
|
||||||
jiti: 1.21.7
|
jiti: 1.21.7
|
||||||
@@ -11727,7 +11730,7 @@ snapshots:
|
|||||||
|
|
||||||
safer-buffer@2.1.2: {}
|
safer-buffer@2.1.2: {}
|
||||||
|
|
||||||
sass-loader@16.0.5(sass@1.85.0)(webpack@5.98.0(esbuild@0.25.4)):
|
sass-loader@16.0.5(sass@1.85.0)(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
neo-async: 2.6.2
|
neo-async: 2.6.2
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -11930,7 +11933,7 @@ snapshots:
|
|||||||
|
|
||||||
source-map-js@1.2.1: {}
|
source-map-js@1.2.1: {}
|
||||||
|
|
||||||
source-map-loader@5.0.0(webpack@5.98.0(esbuild@0.25.4)):
|
source-map-loader@5.0.0(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
iconv-lite: 0.6.3
|
iconv-lite: 0.6.3
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
@@ -12102,7 +12105,7 @@ snapshots:
|
|||||||
mkdirp: 3.0.1
|
mkdirp: 3.0.1
|
||||||
yallist: 5.0.0
|
yallist: 5.0.0
|
||||||
|
|
||||||
terser-webpack-plugin@5.3.14(esbuild@0.25.4)(webpack@5.98.0(esbuild@0.25.4)):
|
terser-webpack-plugin@5.3.14(esbuild@0.25.4)(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/trace-mapping': 0.3.25
|
'@jridgewell/trace-mapping': 0.3.25
|
||||||
jest-worker: 27.5.1
|
jest-worker: 27.5.1
|
||||||
@@ -12399,7 +12402,7 @@ snapshots:
|
|||||||
|
|
||||||
webidl-conversions@7.0.0: {}
|
webidl-conversions@7.0.0: {}
|
||||||
|
|
||||||
webpack-dev-middleware@7.4.2(webpack@5.98.0(esbuild@0.25.4)):
|
webpack-dev-middleware@7.4.2(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
colorette: 2.0.20
|
colorette: 2.0.20
|
||||||
memfs: 4.17.2
|
memfs: 4.17.2
|
||||||
@@ -12410,7 +12413,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
webpack: 5.98.0(esbuild@0.25.4)
|
webpack: 5.98.0(esbuild@0.25.4)
|
||||||
|
|
||||||
webpack-dev-server@5.2.0(webpack@5.98.0(esbuild@0.25.4)):
|
webpack-dev-server@5.2.0(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/bonjour': 3.5.13
|
'@types/bonjour': 3.5.13
|
||||||
'@types/connect-history-api-fallback': 1.5.4
|
'@types/connect-history-api-fallback': 1.5.4
|
||||||
@@ -12437,7 +12440,7 @@ snapshots:
|
|||||||
serve-index: 1.9.1
|
serve-index: 1.9.1
|
||||||
sockjs: 0.3.24
|
sockjs: 0.3.24
|
||||||
spdy: 4.0.2
|
spdy: 4.0.2
|
||||||
webpack-dev-middleware: 7.4.2(webpack@5.98.0(esbuild@0.25.4))
|
webpack-dev-middleware: 7.4.2(webpack@5.98.0)
|
||||||
ws: 8.18.2
|
ws: 8.18.2
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
webpack: 5.98.0(esbuild@0.25.4)
|
webpack: 5.98.0(esbuild@0.25.4)
|
||||||
@@ -12455,7 +12458,7 @@ snapshots:
|
|||||||
|
|
||||||
webpack-sources@3.3.2: {}
|
webpack-sources@3.3.2: {}
|
||||||
|
|
||||||
webpack-subresource-integrity@5.1.0(webpack@5.98.0(esbuild@0.25.4)):
|
webpack-subresource-integrity@5.1.0(webpack@5.98.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
typed-assert: 1.0.9
|
typed-assert: 1.0.9
|
||||||
webpack: 5.98.0(esbuild@0.25.4)
|
webpack: 5.98.0(esbuild@0.25.4)
|
||||||
@@ -12484,7 +12487,7 @@ snapshots:
|
|||||||
neo-async: 2.6.2
|
neo-async: 2.6.2
|
||||||
schema-utils: 4.3.2
|
schema-utils: 4.3.2
|
||||||
tapable: 2.2.2
|
tapable: 2.2.2
|
||||||
terser-webpack-plugin: 5.3.14(esbuild@0.25.4)(webpack@5.98.0(esbuild@0.25.4))
|
terser-webpack-plugin: 5.3.14(esbuild@0.25.4)(webpack@5.98.0)
|
||||||
watchpack: 2.4.4
|
watchpack: 2.4.4
|
||||||
webpack-sources: 3.3.2
|
webpack-sources: 3.3.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
@@ -176,6 +176,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
||||||
|
<pngx-input-check i18n-title title="Show document counts in sidebar saved views" formControlName="sidebarViewsShowCount"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -212,7 +212,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(29)
|
expect(setSpy).toHaveBeenCalledTimes(30)
|
||||||
|
|
||||||
// succeed
|
// succeed
|
||||||
storeSpy.mockReturnValueOnce(of(true))
|
storeSpy.mockReturnValueOnce(of(true))
|
||||||
|
@@ -49,6 +49,7 @@ import {
|
|||||||
PermissionsService,
|
PermissionsService,
|
||||||
} from 'src/app/services/permissions.service'
|
} from 'src/app/services/permissions.service'
|
||||||
import { GroupService } from 'src/app/services/rest/group.service'
|
import { GroupService } from 'src/app/services/rest/group.service'
|
||||||
|
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import {
|
import {
|
||||||
LanguageOption,
|
LanguageOption,
|
||||||
@@ -138,6 +139,7 @@ export class SettingsComponent
|
|||||||
notificationsConsumerSuppressOnDashboard: new FormControl(null),
|
notificationsConsumerSuppressOnDashboard: new FormControl(null),
|
||||||
|
|
||||||
savedViewsWarnOnUnsavedChange: new FormControl(null),
|
savedViewsWarnOnUnsavedChange: new FormControl(null),
|
||||||
|
sidebarViewsShowCount: new FormControl(null),
|
||||||
})
|
})
|
||||||
|
|
||||||
SettingsNavIDs = SettingsNavIDs
|
SettingsNavIDs = SettingsNavIDs
|
||||||
@@ -192,11 +194,13 @@ export class SettingsComponent
|
|||||||
private router: Router,
|
private router: Router,
|
||||||
public permissionsService: PermissionsService,
|
public permissionsService: PermissionsService,
|
||||||
private modalService: NgbModal,
|
private modalService: NgbModal,
|
||||||
private systemStatusService: SystemStatusService
|
private systemStatusService: SystemStatusService,
|
||||||
|
private savedViewsService: SavedViewService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.settings.settingsSaved.subscribe(() => {
|
this.settings.settingsSaved.subscribe(() => {
|
||||||
if (!this.savePending) this.initialize()
|
if (!this.savePending) this.initialize()
|
||||||
|
this.savedViewsService.maybeRefreshDocumentCounts()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +312,9 @@ export class SettingsComponent
|
|||||||
savedViewsWarnOnUnsavedChange: this.settings.get(
|
savedViewsWarnOnUnsavedChange: this.settings.get(
|
||||||
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE
|
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE
|
||||||
),
|
),
|
||||||
|
sidebarViewsShowCount: this.settings.get(
|
||||||
|
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT
|
||||||
|
),
|
||||||
defaultPermsOwner: this.settings.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER),
|
defaultPermsOwner: this.settings.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER),
|
||||||
defaultPermsViewUsers: this.settings.get(
|
defaultPermsViewUsers: this.settings.get(
|
||||||
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS
|
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS
|
||||||
@@ -485,6 +492,10 @@ export class SettingsComponent
|
|||||||
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE,
|
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE,
|
||||||
this.settingsForm.value.savedViewsWarnOnUnsavedChange
|
this.settingsForm.value.savedViewsWarnOnUnsavedChange
|
||||||
)
|
)
|
||||||
|
this.settings.set(
|
||||||
|
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT,
|
||||||
|
this.settingsForm.value.sidebarViewsShowCount
|
||||||
|
)
|
||||||
this.settings.set(
|
this.settings.set(
|
||||||
SETTINGS_KEYS.DEFAULT_PERMS_OWNER,
|
SETTINGS_KEYS.DEFAULT_PERMS_OWNER,
|
||||||
this.settingsForm.value.defaultPermsOwner
|
this.settingsForm.value.defaultPermsOwner
|
||||||
|
@@ -112,7 +112,14 @@
|
|||||||
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name"
|
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name"
|
||||||
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
|
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
|
||||||
popoverClass="popover-slim">
|
popoverClass="popover-slim">
|
||||||
<i-bs class="me-1" name="funnel"></i-bs><span> {{view.name}}</span>
|
<i-bs class="me-1" name="funnel"></i-bs><span> {{view.name}}
|
||||||
|
@if (showSidebarCounts && !slimSidebarEnabled) {
|
||||||
|
<span><span class="badge bg-info text-dark ms-2 d-inline">{{ savedViewService.getDocumentCount(view) }}</span></span>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
@if (showSidebarCounts && slimSidebarEnabled) {
|
||||||
|
<span class="badge bg-info text-dark position-absolute top-0 end-0 d-none d-md-block">{{ savedViewService.getDocumentCount(view) }}</span>
|
||||||
|
}
|
||||||
</a>
|
</a>
|
||||||
@if (settingsService.organizingSidebarSavedViews) {
|
@if (settingsService.organizingSidebarSavedViews) {
|
||||||
<div class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
|
<div class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
|
||||||
|
@@ -121,6 +121,7 @@ describe('AppFrameComponent', () => {
|
|||||||
results: saved_views,
|
results: saved_views,
|
||||||
}),
|
}),
|
||||||
sidebarViews: saved_views.filter((v) => v.show_in_sidebar),
|
sidebarViews: saved_views.filter((v) => v.show_in_sidebar),
|
||||||
|
getDocumentCount: (view: SavedView) => 5,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PermissionsService,
|
PermissionsService,
|
||||||
|
@@ -35,6 +35,7 @@ import {
|
|||||||
PermissionsService,
|
PermissionsService,
|
||||||
PermissionType,
|
PermissionType,
|
||||||
} from 'src/app/services/permissions.service'
|
} from 'src/app/services/permissions.service'
|
||||||
|
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||||
import {
|
import {
|
||||||
AppRemoteVersion,
|
AppRemoteVersion,
|
||||||
RemoteVersionService,
|
RemoteVersionService,
|
||||||
@@ -91,7 +92,8 @@ export class AppFrameComponent
|
|||||||
private readonly toastService: ToastService,
|
private readonly toastService: ToastService,
|
||||||
private modalService: NgbModal,
|
private modalService: NgbModal,
|
||||||
public permissionsService: PermissionsService,
|
public permissionsService: PermissionsService,
|
||||||
private djangoMessagesService: DjangoMessagesService
|
private djangoMessagesService: DjangoMessagesService,
|
||||||
|
private documentService: DocumentService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
@@ -101,7 +103,9 @@ export class AppFrameComponent
|
|||||||
PermissionType.SavedView
|
PermissionType.SavedView
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
this.savedViewService.reload()
|
this.savedViewService.reload(() => {
|
||||||
|
this.savedViewService.maybeRefreshDocumentCounts()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,4 +286,8 @@ export class AppFrameComponent
|
|||||||
onLogout() {
|
onLogout() {
|
||||||
this.openDocumentsService.closeAll()
|
this.openDocumentsService.closeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get showSidebarCounts(): boolean {
|
||||||
|
return this.settingsService.get(SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
<pngx-widget-frame
|
<pngx-widget-frame
|
||||||
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"
|
*pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.Document }"
|
||||||
[title]="savedView.name"
|
[title]="savedView.name"
|
||||||
|
[badge]="count"
|
||||||
[loading]="loading"
|
[loading]="loading"
|
||||||
[draggable]="savedView"
|
[draggable]="savedView"
|
||||||
>
|
>
|
||||||
|
@@ -121,6 +121,8 @@ export class SavedViewWidgetComponent
|
|||||||
|
|
||||||
displayFields: DisplayField[] = DEFAULT_DASHBOARD_DISPLAY_FIELDS
|
displayFields: DisplayField[] = DEFAULT_DASHBOARD_DISPLAY_FIELDS
|
||||||
|
|
||||||
|
count: number
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.reload()
|
this.reload()
|
||||||
this.displayMode = this.savedView.display_mode ?? DisplayMode.TABLE
|
this.displayMode = this.savedView.display_mode ?? DisplayMode.TABLE
|
||||||
@@ -181,6 +183,7 @@ export class SavedViewWidgetComponent
|
|||||||
tap((result) => {
|
tap((result) => {
|
||||||
this.show = true
|
this.show = true
|
||||||
this.documents = result.results
|
this.documents = result.results
|
||||||
|
this.count = result.count
|
||||||
}),
|
}),
|
||||||
delay(500)
|
delay(500)
|
||||||
)
|
)
|
||||||
|
@@ -2,13 +2,16 @@
|
|||||||
<div class="card shadow-sm bg-light fade" [class.show]="show" cdkDrag [cdkDragDisabled]="!draggable" cdkDragPreviewContainer="parent">
|
<div class="card shadow-sm bg-light fade" [class.show]="show" cdkDrag [cdkDragDisabled]="!draggable" cdkDragPreviewContainer="parent">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="d-flex justify-content-between align-items-center">
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<div class="d-flex">
|
<div class="d-flex align-items-center">
|
||||||
@if (draggable) {
|
@if (draggable) {
|
||||||
<div class="ms-n2 me-1" cdkDragHandle>
|
<div class="ms-n2 me-1" cdkDragHandle>
|
||||||
<i-bs name="grip-vertical"></i-bs>
|
<i-bs name="grip-vertical"></i-bs>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<h6 class="card-title mb-0">{{title}}</h6>
|
<h6 class="card-title mb-0">{{title}}</h6>
|
||||||
|
@if (badge) {
|
||||||
|
<span class="badge bg-info text-dark ms-2">{{badge}}</span>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
@if (loading) {
|
@if (loading) {
|
||||||
<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>
|
||||||
|
@@ -30,6 +30,9 @@ export class WidgetFrameComponent
|
|||||||
@Input()
|
@Input()
|
||||||
cardless: boolean = false
|
cardless: boolean = false
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
badge: string
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.show = true
|
this.show = true
|
||||||
|
@@ -73,6 +73,7 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic
|
|||||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||||
|
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||||
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
@@ -278,7 +279,8 @@ export class DocumentDetailComponent
|
|||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private hotKeyService: HotKeyService,
|
private hotKeyService: HotKeyService,
|
||||||
private componentRouterService: ComponentRouterService,
|
private componentRouterService: ComponentRouterService,
|
||||||
private deviceDetectorService: DeviceDetectorService
|
private deviceDetectorService: DeviceDetectorService,
|
||||||
|
private savedViewService: SavedViewService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@@ -845,6 +847,7 @@ export class DocumentDetailComponent
|
|||||||
} else {
|
} else {
|
||||||
this.openDocumentService.refreshDocument(this.documentId)
|
this.openDocumentService.refreshDocument(this.documentId)
|
||||||
}
|
}
|
||||||
|
this.savedViewService.maybeRefreshDocumentCounts()
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: (error) => {
|
||||||
this.networkActive = false
|
this.networkActive = false
|
||||||
@@ -1192,6 +1195,7 @@ export class DocumentDetailComponent
|
|||||||
notesUpdated(notes: DocumentNote[]) {
|
notesUpdated(notes: DocumentNote[]) {
|
||||||
this.document.notes = notes
|
this.document.notes = notes
|
||||||
this.openDocumentService.refreshDocument(this.documentId)
|
this.openDocumentService.refreshDocument(this.documentId)
|
||||||
|
this.savedViewService.maybeRefreshDocumentCounts()
|
||||||
}
|
}
|
||||||
|
|
||||||
get userIsOwner(): boolean {
|
get userIsOwner(): boolean {
|
||||||
|
@@ -32,6 +32,7 @@ import {
|
|||||||
DocumentService,
|
DocumentService,
|
||||||
SelectionDataItem,
|
SelectionDataItem,
|
||||||
} from 'src/app/services/rest/document.service'
|
} from 'src/app/services/rest/document.service'
|
||||||
|
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||||
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
||||||
import { TagService } from 'src/app/services/rest/tag.service'
|
import { TagService } from 'src/app/services/rest/tag.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
@@ -106,7 +107,8 @@ export class BulkEditorComponent
|
|||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private storagePathService: StoragePathService,
|
private storagePathService: StoragePathService,
|
||||||
private customFieldService: CustomFieldsService,
|
private customFieldService: CustomFieldsService,
|
||||||
private permissionService: PermissionsService
|
private permissionService: PermissionsService,
|
||||||
|
public savedViewService: SavedViewService
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
@@ -274,6 +276,7 @@ export class BulkEditorComponent
|
|||||||
this.list.selected.forEach((id) => {
|
this.list.selected.forEach((id) => {
|
||||||
this.openDocumentService.refreshDocument(id)
|
this.openDocumentService.refreshDocument(id)
|
||||||
})
|
})
|
||||||
|
this.savedViewService.maybeRefreshDocumentCounts()
|
||||||
if (modal) {
|
if (modal) {
|
||||||
modal.close()
|
modal.close()
|
||||||
}
|
}
|
||||||
|
@@ -58,6 +58,8 @@ export const SETTINGS_KEYS = {
|
|||||||
'general-settings:saved-views:dashboard-views-sort-order',
|
'general-settings:saved-views:dashboard-views-sort-order',
|
||||||
SIDEBAR_VIEWS_SORT_ORDER:
|
SIDEBAR_VIEWS_SORT_ORDER:
|
||||||
'general-settings:saved-views:sidebar-views-sort-order',
|
'general-settings:saved-views:sidebar-views-sort-order',
|
||||||
|
SIDEBAR_VIEWS_SHOW_COUNT:
|
||||||
|
'general-settings:saved-views:sidebar-views-show-count',
|
||||||
TOUR_COMPLETE: 'general-settings:tour-complete',
|
TOUR_COMPLETE: 'general-settings:tour-complete',
|
||||||
DEFAULT_PERMS_OWNER: 'general-settings:permissions:default-owner',
|
DEFAULT_PERMS_OWNER: 'general-settings:permissions:default-owner',
|
||||||
DEFAULT_PERMS_VIEW_USERS: 'general-settings:permissions:default-view-users',
|
DEFAULT_PERMS_VIEW_USERS: 'general-settings:permissions:default-view-users',
|
||||||
@@ -227,6 +229,11 @@ export const SETTINGS: UiSetting[] = [
|
|||||||
type: 'array',
|
type: 'array',
|
||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT,
|
||||||
|
type: 'boolean',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: SETTINGS_KEYS.APP_LOGO,
|
key: SETTINGS_KEYS.APP_LOGO,
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@@ -17,7 +17,7 @@ const saved_views = [
|
|||||||
id: 1,
|
id: 1,
|
||||||
show_on_dashboard: true,
|
show_on_dashboard: true,
|
||||||
show_in_sidebar: true,
|
show_in_sidebar: true,
|
||||||
sort_field: 'name',
|
sort_field: 'title',
|
||||||
sort_reverse: true,
|
sort_reverse: true,
|
||||||
filter_rules: [],
|
filter_rules: [],
|
||||||
},
|
},
|
||||||
@@ -26,7 +26,7 @@ const saved_views = [
|
|||||||
id: 2,
|
id: 2,
|
||||||
show_on_dashboard: true,
|
show_on_dashboard: true,
|
||||||
show_in_sidebar: true,
|
show_in_sidebar: true,
|
||||||
sort_field: 'name',
|
sort_field: 'created',
|
||||||
sort_reverse: true,
|
sort_reverse: true,
|
||||||
filter_rules: [],
|
filter_rules: [],
|
||||||
},
|
},
|
||||||
@@ -35,7 +35,7 @@ const saved_views = [
|
|||||||
id: 3,
|
id: 3,
|
||||||
show_on_dashboard: true,
|
show_on_dashboard: true,
|
||||||
show_in_sidebar: true,
|
show_in_sidebar: true,
|
||||||
sort_field: 'name',
|
sort_field: 'added',
|
||||||
sort_reverse: true,
|
sort_reverse: true,
|
||||||
filter_rules: [],
|
filter_rules: [],
|
||||||
},
|
},
|
||||||
@@ -44,7 +44,7 @@ const saved_views = [
|
|||||||
id: 4,
|
id: 4,
|
||||||
show_on_dashboard: false,
|
show_on_dashboard: false,
|
||||||
show_in_sidebar: false,
|
show_in_sidebar: false,
|
||||||
sort_field: 'name',
|
sort_field: 'owner',
|
||||||
sort_reverse: true,
|
sort_reverse: true,
|
||||||
filter_rules: [],
|
filter_rules: [],
|
||||||
},
|
},
|
||||||
@@ -222,6 +222,33 @@ describe(`Additional service tests for SavedViewService`, () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should accept a callback for reload', () => {
|
||||||
|
const reloadSpy = jest.fn()
|
||||||
|
service.reload(reloadSpy)
|
||||||
|
const req = httpTestingController.expectOne(
|
||||||
|
`${environment.apiBaseUrl}${endpoint}/?page=1&page_size=100000`
|
||||||
|
)
|
||||||
|
req.flush({
|
||||||
|
results: saved_views,
|
||||||
|
})
|
||||||
|
expect(reloadSpy).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support getting document counts for views', () => {
|
||||||
|
service.maybeRefreshDocumentCounts(saved_views)
|
||||||
|
saved_views.forEach((saved_view) => {
|
||||||
|
const req = httpTestingController.expectOne(
|
||||||
|
`${environment.apiBaseUrl}documents/?page=1&page_size=1&ordering=-${saved_view.sort_field}&fields=id&truncate_content=true`
|
||||||
|
)
|
||||||
|
req.flush({
|
||||||
|
all: [],
|
||||||
|
count: 1,
|
||||||
|
results: [{ id: 1 }],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
expect(service.getDocumentCount(saved_views[0])).toEqual(1)
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Dont need to setup again
|
// Dont need to setup again
|
||||||
|
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
import { HttpClient } from '@angular/common/http'
|
import { HttpClient } from '@angular/common/http'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { combineLatest, Observable } from 'rxjs'
|
import { combineLatest, Observable, Subject } from 'rxjs'
|
||||||
import { tap } from 'rxjs/operators'
|
import { takeUntil, tap } from 'rxjs/operators'
|
||||||
import { Results } from 'src/app/data/results'
|
import { Results } from 'src/app/data/results'
|
||||||
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 { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import { SettingsService } from '../settings.service'
|
import { SettingsService } from '../settings.service'
|
||||||
import { AbstractPaperlessService } from './abstract-paperless-service'
|
import { AbstractPaperlessService } from './abstract-paperless-service'
|
||||||
|
import { DocumentService } from './document.service'
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -14,10 +15,13 @@ import { AbstractPaperlessService } from './abstract-paperless-service'
|
|||||||
export class SavedViewService extends AbstractPaperlessService<SavedView> {
|
export class SavedViewService extends AbstractPaperlessService<SavedView> {
|
||||||
public loading: boolean = true
|
public loading: boolean = true
|
||||||
private savedViews: SavedView[] = []
|
private savedViews: SavedView[] = []
|
||||||
|
private savedViewDocumentCounts: Map<number, number> = new Map()
|
||||||
|
private unsubscribeNotifier: Subject<void> = new Subject<void>()
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected http: HttpClient,
|
protected http: HttpClient,
|
||||||
private settingsService: SettingsService
|
private settingsService: SettingsService,
|
||||||
|
private documentService: DocumentService
|
||||||
) {
|
) {
|
||||||
super(http, 'saved_views')
|
super(http, 'saved_views')
|
||||||
}
|
}
|
||||||
@@ -45,8 +49,16 @@ export class SavedViewService extends AbstractPaperlessService<SavedView> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public reload() {
|
public reload(callback: any = null) {
|
||||||
this.listAll().subscribe()
|
this.listAll()
|
||||||
|
.pipe(
|
||||||
|
tap((r) => {
|
||||||
|
if (callback) {
|
||||||
|
callback(r)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
get allViews() {
|
get allViews() {
|
||||||
@@ -109,4 +121,30 @@ export class SavedViewService extends AbstractPaperlessService<SavedView> {
|
|||||||
delete(o: SavedView) {
|
delete(o: SavedView) {
|
||||||
return super.delete(o).pipe(tap(() => this.reload()))
|
return super.delete(o).pipe(tap(() => this.reload()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public maybeRefreshDocumentCounts(views: SavedView[] = this.sidebarViews) {
|
||||||
|
if (!this.settingsService.get(SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.unsubscribeNotifier.next() // clear previous subscriptions
|
||||||
|
views.forEach((view) => {
|
||||||
|
this.documentService
|
||||||
|
.listFiltered(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
view.sort_field,
|
||||||
|
view.sort_reverse,
|
||||||
|
view.filter_rules,
|
||||||
|
{ fields: 'id', truncate_content: true }
|
||||||
|
)
|
||||||
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
|
.subscribe((results: Results<Document>) => {
|
||||||
|
this.savedViewDocumentCounts.set(view.id, results.count)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDocumentCount(view: SavedView): number {
|
||||||
|
return this.savedViewDocumentCounts.get(view.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,15 +16,15 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
# Tap paperless.conf if it's available
|
# Tap paperless.conf if it's available
|
||||||
configuration_path = os.getenv("PAPERLESS_CONFIGURATION_PATH")
|
for path in [
|
||||||
if configuration_path and os.path.exists(configuration_path):
|
os.getenv("PAPERLESS_CONFIGURATION_PATH"),
|
||||||
load_dotenv(configuration_path)
|
"../paperless.conf",
|
||||||
elif os.path.exists("../paperless.conf"):
|
"/etc/paperless.conf",
|
||||||
load_dotenv("../paperless.conf")
|
"/usr/local/etc/paperless.conf",
|
||||||
elif os.path.exists("/etc/paperless.conf"):
|
]:
|
||||||
load_dotenv("/etc/paperless.conf")
|
if path and Path(path).exists():
|
||||||
elif os.path.exists("/usr/local/etc/paperless.conf"):
|
load_dotenv(path)
|
||||||
load_dotenv("/usr/local/etc/paperless.conf")
|
break
|
||||||
|
|
||||||
# There are multiple levels of concurrency in paperless:
|
# There are multiple levels of concurrency in paperless:
|
||||||
# - Multiple consumers may be run in parallel.
|
# - Multiple consumers may be run in parallel.
|
||||||
@@ -674,7 +674,7 @@ def _parse_db_settings() -> dict:
|
|||||||
databases = {
|
databases = {
|
||||||
"default": {
|
"default": {
|
||||||
"ENGINE": "django.db.backends.sqlite3",
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
"NAME": os.path.join(DATA_DIR, "db.sqlite3"),
|
"NAME": str(DATA_DIR / "db.sqlite3"),
|
||||||
"OPTIONS": {},
|
"OPTIONS": {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -789,7 +789,7 @@ LANGUAGES = [
|
|||||||
("zh-tw", _("Chinese Traditional")),
|
("zh-tw", _("Chinese Traditional")),
|
||||||
]
|
]
|
||||||
|
|
||||||
LOCALE_PATHS = [os.path.join(BASE_DIR, "locale")]
|
LOCALE_PATHS = [str(BASE_DIR / "locale")]
|
||||||
|
|
||||||
TIME_ZONE = os.getenv("PAPERLESS_TIME_ZONE", "UTC")
|
TIME_ZONE = os.getenv("PAPERLESS_TIME_ZONE", "UTC")
|
||||||
|
|
||||||
@@ -832,21 +832,21 @@ LOGGING = {
|
|||||||
"file_paperless": {
|
"file_paperless": {
|
||||||
"class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
|
"class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
|
||||||
"formatter": "verbose",
|
"formatter": "verbose",
|
||||||
"filename": os.path.join(LOGGING_DIR, "paperless.log"),
|
"filename": str(LOGGING_DIR / "paperless.log"),
|
||||||
"maxBytes": LOGROTATE_MAX_SIZE,
|
"maxBytes": LOGROTATE_MAX_SIZE,
|
||||||
"backupCount": LOGROTATE_MAX_BACKUPS,
|
"backupCount": LOGROTATE_MAX_BACKUPS,
|
||||||
},
|
},
|
||||||
"file_mail": {
|
"file_mail": {
|
||||||
"class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
|
"class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
|
||||||
"formatter": "verbose",
|
"formatter": "verbose",
|
||||||
"filename": os.path.join(LOGGING_DIR, "mail.log"),
|
"filename": str(LOGGING_DIR / "mail.log"),
|
||||||
"maxBytes": LOGROTATE_MAX_SIZE,
|
"maxBytes": LOGROTATE_MAX_SIZE,
|
||||||
"backupCount": LOGROTATE_MAX_BACKUPS,
|
"backupCount": LOGROTATE_MAX_BACKUPS,
|
||||||
},
|
},
|
||||||
"file_celery": {
|
"file_celery": {
|
||||||
"class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
|
"class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
|
||||||
"formatter": "verbose",
|
"formatter": "verbose",
|
||||||
"filename": os.path.join(LOGGING_DIR, "celery.log"),
|
"filename": str(LOGGING_DIR / "celery.log"),
|
||||||
"maxBytes": LOGROTATE_MAX_SIZE,
|
"maxBytes": LOGROTATE_MAX_SIZE,
|
||||||
"backupCount": LOGROTATE_MAX_BACKUPS,
|
"backupCount": LOGROTATE_MAX_BACKUPS,
|
||||||
},
|
},
|
||||||
@@ -901,7 +901,7 @@ CELERY_ACCEPT_CONTENT = ["application/json", "application/x-python-serialize"]
|
|||||||
CELERY_BEAT_SCHEDULE = _parse_beat_schedule()
|
CELERY_BEAT_SCHEDULE = _parse_beat_schedule()
|
||||||
|
|
||||||
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-schedule-filename
|
# https://docs.celeryq.dev/en/stable/userguide/configuration.html#beat-schedule-filename
|
||||||
CELERY_BEAT_SCHEDULE_FILENAME = os.path.join(DATA_DIR, "celerybeat-schedule.db")
|
CELERY_BEAT_SCHEDULE_FILENAME = str(DATA_DIR / "celerybeat-schedule.db")
|
||||||
|
|
||||||
# django setting.
|
# django setting.
|
||||||
CACHES = {
|
CACHES = {
|
||||||
|
@@ -125,3 +125,8 @@ class MailRuleSerializer(OwnedObjectSerializer):
|
|||||||
raise serializers.ValidationError("An action parameter is required.")
|
raise serializers.ValidationError("An action parameter is required.")
|
||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def validate_maximum_age(self, value):
|
||||||
|
if value > 36500: # ~100 years
|
||||||
|
raise serializers.ValidationError("Maximum mail age is unreasonably large.")
|
||||||
|
return value
|
||||||
|
@@ -680,3 +680,44 @@ class TestAPIMailRules(DirectoriesMixin, APITestCase):
|
|||||||
self.assertEqual(response.data["results"][0]["name"], rule1.name)
|
self.assertEqual(response.data["results"][0]["name"], rule1.name)
|
||||||
self.assertEqual(response.data["results"][1]["name"], rule2.name)
|
self.assertEqual(response.data["results"][1]["name"], rule2.name)
|
||||||
self.assertEqual(response.data["results"][2]["name"], rule4.name)
|
self.assertEqual(response.data["results"][2]["name"], rule4.name)
|
||||||
|
|
||||||
|
def test_mailrule_maxage_validation(self):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- An existing mail account
|
||||||
|
WHEN:
|
||||||
|
- The user submits a mail rule with an excessively large maximum_age
|
||||||
|
THEN:
|
||||||
|
- The API should reject the request
|
||||||
|
"""
|
||||||
|
account = MailAccount.objects.create(
|
||||||
|
name="Email1",
|
||||||
|
username="username1",
|
||||||
|
password="password1",
|
||||||
|
imap_server="server.example.com",
|
||||||
|
imap_port=443,
|
||||||
|
imap_security=MailAccount.ImapSecurity.SSL,
|
||||||
|
character_set="UTF-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
rule_data = {
|
||||||
|
"name": "Rule1",
|
||||||
|
"account": account.pk,
|
||||||
|
"folder": "INBOX",
|
||||||
|
"filter_from": "from@example.com",
|
||||||
|
"filter_to": "aperson@aplace.com",
|
||||||
|
"filter_subject": "subject",
|
||||||
|
"filter_body": "body",
|
||||||
|
"filter_attachment_filename_include": "file.pdf",
|
||||||
|
"maximum_age": 9000000,
|
||||||
|
"action": MailRule.MailAction.MARK_READ,
|
||||||
|
"assign_title_from": MailRule.TitleSource.FROM_SUBJECT,
|
||||||
|
"assign_correspondent_from": MailRule.CorrespondentSource.FROM_NOTHING,
|
||||||
|
"order": 0,
|
||||||
|
"attachment_type": MailRule.AttachmentProcessing.ATTACHMENTS_ONLY,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(self.ENDPOINT, data=rule_data, format="json")
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertIn("maximum_age", response.data)
|
||||||
|
Reference in New Issue
Block a user