Compare commits

..

8 Commits

Author SHA1 Message Date
shamoon
ec12e71487 Basic option selection 2024-11-08 20:36:58 -08:00
shamoon
62b470f691 Retry action, basic frontend, cleanup handler 2024-11-07 18:47:48 -08:00
shamoon
a2e4977201 Fix for ci 2024-11-07 13:29:15 -08:00
shamoon
0fcd69b739 Move it out of consumer 2024-11-07 13:19:06 -08:00
shamoon
af1c64e969 Try this 2024-11-07 12:28:20 -08:00
shamoon
85c661dff2 Update consumer.py 2024-11-07 10:44:58 -08:00
shamoon
3a7eee2c2e Fix tests 2024-11-07 10:32:15 -08:00
shamoon
bc4d3925cc Messing around 2024-10-30 01:04:14 -07:00
54 changed files with 3874 additions and 1855 deletions

View File

@@ -98,7 +98,7 @@ body:
label: Browser
description: Which browser you are using, if relevant.
placeholder: e.g. Chrome, Safari
- type: textarea
- type: input
id: config-changes
attributes:
label: Configuration changes

View File

@@ -1,52 +1,5 @@
# Changelog
## paperless-ngx 2.13.3
### Bug Fixes
- Fix: fix auto-clean PDFs, create parent dir for storing unmodified original [@shamoon](https://github.com/shamoon) ([#8157](https://github.com/paperless-ngx/paperless-ngx/pull/8157))
- Fix: correctly handle exists, false in custom field query filter @yichi-yang ([#8158](https://github.com/paperless-ngx/paperless-ngx/pull/8158))
- Fix: dont use filters for inverted thumbnails in Safari [@shamoon](https://github.com/shamoon) ([#8121](https://github.com/paperless-ngx/paperless-ngx/pull/8121))
- Fix: use static object for activedisplayfields to prevent changes [@shamoon](https://github.com/shamoon) ([#8120](https://github.com/paperless-ngx/paperless-ngx/pull/8120))
- Fix: dont invert pdf colors in FF [@shamoon](https://github.com/shamoon) ([#8110](https://github.com/paperless-ngx/paperless-ngx/pull/8110))
- Fix: make mail account password and refresh token text fields [@shamoon](https://github.com/shamoon) ([#8107](https://github.com/paperless-ngx/paperless-ngx/pull/8107))
### Dependencies
<details>
<summary>8 changes</summary>
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates [@dependabot](https://github.com/dependabot) ([#8145](https://github.com/paperless-ngx/paperless-ngx/pull/8145))
- Chore(deps-dev): Bump @types/node from 22.7.4 to 22.8.6 in /src-ui [@dependabot](https://github.com/dependabot) ([#8148](https://github.com/paperless-ngx/paperless-ngx/pull/8148))
- Chore(deps-dev): Bump @playwright/test from 1.47.2 to 1.48.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#8147](https://github.com/paperless-ngx/paperless-ngx/pull/8147))
- Chore(deps): Bump uuid from 10.0.0 to 11.0.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#8146](https://github.com/paperless-ngx/paperless-ngx/pull/8146))
- Chore(deps): Bump tslib from 2.7.0 to 2.8.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#8149](https://github.com/paperless-ngx/paperless-ngx/pull/8149))
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.2.0 to 1.2.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#8150](https://github.com/paperless-ngx/paperless-ngx/pull/8150))
- Chore(deps-dev): Bump @types/jest from 29.5.13 to 29.5.14 in /src-ui in the frontend-jest-dependencies group [@dependabot](https://github.com/dependabot) ([#8144](https://github.com/paperless-ngx/paperless-ngx/pull/8144))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 21 updates [@dependabot](https://github.com/dependabot) ([#8143](https://github.com/paperless-ngx/paperless-ngx/pull/8143))
</details>
### All App Changes
<details>
<summary>14 changes</summary>
- Fix: fix auto-clean PDFs, create parent dir for storing unmodified original [@shamoon](https://github.com/shamoon) ([#8157](https://github.com/paperless-ngx/paperless-ngx/pull/8157))
- Fix: correctly handle exists, false in custom field query filter @yichi-yang ([#8158](https://github.com/paperless-ngx/paperless-ngx/pull/8158))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates [@dependabot](https://github.com/dependabot) ([#8145](https://github.com/paperless-ngx/paperless-ngx/pull/8145))
- Chore(deps-dev): Bump @types/node from 22.7.4 to 22.8.6 in /src-ui [@dependabot](https://github.com/dependabot) ([#8148](https://github.com/paperless-ngx/paperless-ngx/pull/8148))
- Chore(deps-dev): Bump @playwright/test from 1.47.2 to 1.48.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#8147](https://github.com/paperless-ngx/paperless-ngx/pull/8147))
- Chore(deps): Bump uuid from 10.0.0 to 11.0.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#8146](https://github.com/paperless-ngx/paperless-ngx/pull/8146))
- Chore(deps): Bump tslib from 2.7.0 to 2.8.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#8149](https://github.com/paperless-ngx/paperless-ngx/pull/8149))
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.2.0 to 1.2.1 in /src-ui [@dependabot](https://github.com/dependabot) ([#8150](https://github.com/paperless-ngx/paperless-ngx/pull/8150))
- Chore(deps-dev): Bump @types/jest from 29.5.13 to 29.5.14 in /src-ui in the frontend-jest-dependencies group [@dependabot](https://github.com/dependabot) ([#8144](https://github.com/paperless-ngx/paperless-ngx/pull/8144))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 21 updates [@dependabot](https://github.com/dependabot) ([#8143](https://github.com/paperless-ngx/paperless-ngx/pull/8143))
- Fix: dont use filters for inverted thumbnails in Safari [@shamoon](https://github.com/shamoon) ([#8121](https://github.com/paperless-ngx/paperless-ngx/pull/8121))
- Fix: use static object for activedisplayfields to prevent changes [@shamoon](https://github.com/shamoon) ([#8120](https://github.com/paperless-ngx/paperless-ngx/pull/8120))
- Fix: dont invert pdf colors in FF [@shamoon](https://github.com/shamoon) ([#8110](https://github.com/paperless-ngx/paperless-ngx/pull/8110))
- Fix: make mail account password and refresh token text fields [@shamoon](https://github.com/shamoon) ([#8107](https://github.com/paperless-ngx/paperless-ngx/pull/8107))
</details>
## paperless-ngx 2.13.2
### Bug Fixes
@@ -105,120 +58,6 @@
- Fix: oauth settings without base url [@shamoon](https://github.com/shamoon) ([#8020](https://github.com/paperless-ngx/paperless-ngx/pull/8020))
</details>
## paperless-ngx 2.13.0
### Notable Changes
- Feature: OAuth2 Gmail and Outlook email support [@shamoon](https://github.com/shamoon) ([#7866](https://github.com/paperless-ngx/paperless-ngx/pull/7866))
- Feature: Enhanced templating for filename format [@stumpylog](https://github.com/stumpylog) ([#7836](https://github.com/paperless-ngx/paperless-ngx/pull/7836))
- Feature: custom fields queries [@shamoon](https://github.com/shamoon) ([#7761](https://github.com/paperless-ngx/paperless-ngx/pull/7761))
- Chore: Drop Python 3.9 support [@stumpylog](https://github.com/stumpylog) ([#7774](https://github.com/paperless-ngx/paperless-ngx/pull/7774))
### Features
- Enhancement: QoL, auto-focus default select field in custom field dropdown [@shamoon](https://github.com/shamoon) ([#7961](https://github.com/paperless-ngx/paperless-ngx/pull/7961))
- Change: open not edit [@shamoon](https://github.com/shamoon) ([#7942](https://github.com/paperless-ngx/paperless-ngx/pull/7942))
- Enhancement: support retain barcode split pages [@shamoon](https://github.com/shamoon) ([#7912](https://github.com/paperless-ngx/paperless-ngx/pull/7912))
- Enhancement: don't wait for doc API to load preview [@shamoon](https://github.com/shamoon) ([#7894](https://github.com/paperless-ngx/paperless-ngx/pull/7894))
- Feature: OAuth2 Gmail and Outlook email support [@shamoon](https://github.com/shamoon) ([#7866](https://github.com/paperless-ngx/paperless-ngx/pull/7866))
- Enhancement: live preview of storage path [@shamoon](https://github.com/shamoon) ([#7870](https://github.com/paperless-ngx/paperless-ngx/pull/7870))
- Enhancement: management list button improvements [@shamoon](https://github.com/shamoon) ([#7848](https://github.com/paperless-ngx/paperless-ngx/pull/7848))
- Enhancement: check for mail destination directory, log post-consume errors [@mrichtarsky](https://github.com/mrichtarsky) ([#7808](https://github.com/paperless-ngx/paperless-ngx/pull/7808))
- Enhancement: workflow overview toggle enable button [@shamoon](https://github.com/shamoon) ([#7818](https://github.com/paperless-ngx/paperless-ngx/pull/7818))
- Enhancement: disable-able mail rules, add toggle to overview [@shamoon](https://github.com/shamoon) ([#7810](https://github.com/paperless-ngx/paperless-ngx/pull/7810))
- Feature: auto-clean some invalid pdfs [@shamoon](https://github.com/shamoon) ([#7651](https://github.com/paperless-ngx/paperless-ngx/pull/7651))
- Feature: page count [@s0llvan](https://github.com/s0llvan) ([#7750](https://github.com/paperless-ngx/paperless-ngx/pull/7750))
- Enhancement: use apt only when needed docker-entrypoint.sh [@gawa971](https://github.com/gawa971) ([#7756](https://github.com/paperless-ngx/paperless-ngx/pull/7756))
- Enhancement: set Django SESSION_EXPIRE_AT_BROWSER_CLOSE from PAPERLESS_ACCOUNT_SESSION_REMEMBER [@shamoon](https://github.com/shamoon) ([#7748](https://github.com/paperless-ngx/paperless-ngx/pull/7748))
- Enhancement: allow setting session cookie age [@shamoon](https://github.com/shamoon) ([#7743](https://github.com/paperless-ngx/paperless-ngx/pull/7743))
- Feature: copy workflows and mail rules, improve layout [@shamoon](https://github.com/shamoon) ([#7727](https://github.com/paperless-ngx/paperless-ngx/pull/7727))
### Bug Fixes
- Fix: remove space before my profile button in dropdown [@tooomm](https://github.com/tooomm) ([#7963](https://github.com/paperless-ngx/paperless-ngx/pull/7963))
- Fix: v2.13.0 RC1 - Handling of Nones when using custom fields in filepath templating [@stumpylog](https://github.com/stumpylog) ([#7933](https://github.com/paperless-ngx/paperless-ngx/pull/7933))
- Fix: v2.13.0 RC1 - trigger move and rename after CustomFieldInstance saved [@shamoon](https://github.com/shamoon) ([#7927](https://github.com/paperless-ngx/paperless-ngx/pull/7927))
- Fix: v2.13.0 RC1 - increase field max lengths to accommodate larger tokens [@shamoon](https://github.com/shamoon) ([#7916](https://github.com/paperless-ngx/paperless-ngx/pull/7916))
- Fix: preserve text linebreaks in doc edit [@shamoon](https://github.com/shamoon) ([#7908](https://github.com/paperless-ngx/paperless-ngx/pull/7908))
- Fix: only show colon on cards if correspondent and title shown [@shamoon](https://github.com/shamoon) ([#7893](https://github.com/paperless-ngx/paperless-ngx/pull/7893))
- Fix: Allow ASN values of 0 from barcodes [@stumpylog](https://github.com/stumpylog) ([#7878](https://github.com/paperless-ngx/paperless-ngx/pull/7878))
- Fix: fix auto-dismiss completed tasks on open document [@shamoon](https://github.com/shamoon) ([#7869](https://github.com/paperless-ngx/paperless-ngx/pull/7869))
- Fix: trigger change warning for saved views with default fields if changed [@shamoon](https://github.com/shamoon) ([#7865](https://github.com/paperless-ngx/paperless-ngx/pull/7865))
- Fix: hidden canvas element causes scroll bug [@shamoon](https://github.com/shamoon) ([#7770](https://github.com/paperless-ngx/paperless-ngx/pull/7770))
- Fix: handle overflowing dropdowns on mobile [@shamoon](https://github.com/shamoon) ([#7758](https://github.com/paperless-ngx/paperless-ngx/pull/7758))
- Fix: chrome scrolling in >= 129 [@shamoon](https://github.com/shamoon) ([#7738](https://github.com/paperless-ngx/paperless-ngx/pull/7738))
### Maintenance
- Enhancement: use apt only when needed docker-entrypoint.sh [@gawa971](https://github.com/gawa971) ([#7756](https://github.com/paperless-ngx/paperless-ngx/pull/7756))
### Dependencies
<details>
<summary>10 changes</summary>
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.0.1 to 1.2.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#7830](https://github.com/paperless-ngx/paperless-ngx/pull/7830))
- Chore(deps-dev): Bump @types/node from 22.5.2 to 22.7.4 in /src-ui [@dependabot](https://github.com/dependabot) ([#7829](https://github.com/paperless-ngx/paperless-ngx/pull/7829))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates [@dependabot](https://github.com/dependabot) ([#7827](https://github.com/paperless-ngx/paperless-ngx/pull/7827))
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#7826](https://github.com/paperless-ngx/paperless-ngx/pull/7826))
- Chore(deps-dev): Bump @playwright/test from 1.46.1 to 1.47.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#7828](https://github.com/paperless-ngx/paperless-ngx/pull/7828))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 21 updates [@dependabot](https://github.com/dependabot) ([#7825](https://github.com/paperless-ngx/paperless-ngx/pull/7825))
- Chore: Upgrades OCRMyPDF to v16 [@stumpylog](https://github.com/stumpylog) ([#7815](https://github.com/paperless-ngx/paperless-ngx/pull/7815))
- Chore: Upgrades the Docker image to use Python 3.12 [@stumpylog](https://github.com/stumpylog) ([#7796](https://github.com/paperless-ngx/paperless-ngx/pull/7796))
- Chore: Upgrade Django to 5.1 [@stumpylog](https://github.com/stumpylog) ([#7795](https://github.com/paperless-ngx/paperless-ngx/pull/7795))
- Chore(deps-dev): Bump the development group with 2 updates [@dependabot](https://github.com/dependabot) ([#7723](https://github.com/paperless-ngx/paperless-ngx/pull/7723))
</details>
### All App Changes
<details>
<summary>43 changes</summary>
- Change: Use a TextField for the storage path field [@stumpylog](https://github.com/stumpylog) ([#7967](https://github.com/paperless-ngx/paperless-ngx/pull/7967))
- Fix: remove space before my profile button in dropdown [@tooomm](https://github.com/tooomm) ([#7963](https://github.com/paperless-ngx/paperless-ngx/pull/7963))
- Enhancement: QoL, auto-focus default select field in custom field dropdown [@shamoon](https://github.com/shamoon) ([#7961](https://github.com/paperless-ngx/paperless-ngx/pull/7961))
- Change: open not edit [@shamoon](https://github.com/shamoon) ([#7942](https://github.com/paperless-ngx/paperless-ngx/pull/7942))
- Fix: v2.13.0 RC1 - Handling of Nones when using custom fields in filepath templating [@stumpylog](https://github.com/stumpylog) ([#7933](https://github.com/paperless-ngx/paperless-ngx/pull/7933))
- Fix: v2.13.0 RC1 - trigger move and rename after CustomFieldInstance saved [@shamoon](https://github.com/shamoon) ([#7927](https://github.com/paperless-ngx/paperless-ngx/pull/7927))
- Fix: v2.13.0 RC1 - increase field max lengths to accommodate larger tokens [@shamoon](https://github.com/shamoon) ([#7916](https://github.com/paperless-ngx/paperless-ngx/pull/7916))
- Enhancement: support retain barcode split pages [@shamoon](https://github.com/shamoon) ([#7912](https://github.com/paperless-ngx/paperless-ngx/pull/7912))
- Fix: preserve text linebreaks in doc edit [@shamoon](https://github.com/shamoon) ([#7908](https://github.com/paperless-ngx/paperless-ngx/pull/7908))
- Enhancement: don't wait for doc API to load preview [@shamoon](https://github.com/shamoon) ([#7894](https://github.com/paperless-ngx/paperless-ngx/pull/7894))
- Fix: only show colon on cards if correspondent and title shown [@shamoon](https://github.com/shamoon) ([#7893](https://github.com/paperless-ngx/paperless-ngx/pull/7893))
- Feature: OAuth2 Gmail and Outlook email support [@shamoon](https://github.com/shamoon) ([#7866](https://github.com/paperless-ngx/paperless-ngx/pull/7866))
- Chore: Consolidate workflow logic [@shamoon](https://github.com/shamoon) ([#7880](https://github.com/paperless-ngx/paperless-ngx/pull/7880))
- Enhancement: live preview of storage path [@shamoon](https://github.com/shamoon) ([#7870](https://github.com/paperless-ngx/paperless-ngx/pull/7870))
- Fix: Allow ASN values of 0 from barcodes [@stumpylog](https://github.com/stumpylog) ([#7878](https://github.com/paperless-ngx/paperless-ngx/pull/7878))
- Fix: fix auto-dismiss completed tasks on open document [@shamoon](https://github.com/shamoon) ([#7869](https://github.com/paperless-ngx/paperless-ngx/pull/7869))
- Fix: trigger change warning for saved views with default fields if changed [@shamoon](https://github.com/shamoon) ([#7865](https://github.com/paperless-ngx/paperless-ngx/pull/7865))
- Feature: Enhanced templating for filename format [@stumpylog](https://github.com/stumpylog) ([#7836](https://github.com/paperless-ngx/paperless-ngx/pull/7836))
- Enhancement: management list button improvements [@shamoon](https://github.com/shamoon) ([#7848](https://github.com/paperless-ngx/paperless-ngx/pull/7848))
- Enhancement: check for mail destination directory, log post-consume errors [@mrichtarsky](https://github.com/mrichtarsky) ([#7808](https://github.com/paperless-ngx/paperless-ngx/pull/7808))
- Feature: custom fields queries [@shamoon](https://github.com/shamoon) ([#7761](https://github.com/paperless-ngx/paperless-ngx/pull/7761))
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.0.1 to 1.2.0 in /src-ui [@dependabot](https://github.com/dependabot) ([#7830](https://github.com/paperless-ngx/paperless-ngx/pull/7830))
- Chore(deps-dev): Bump @types/node from 22.5.2 to 22.7.4 in /src-ui [@dependabot](https://github.com/dependabot) ([#7829](https://github.com/paperless-ngx/paperless-ngx/pull/7829))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates [@dependabot](https://github.com/dependabot) ([#7827](https://github.com/paperless-ngx/paperless-ngx/pull/7827))
- Chore(deps-dev): Bump the frontend-jest-dependencies group in /src-ui with 2 updates [@dependabot](https://github.com/dependabot) ([#7826](https://github.com/paperless-ngx/paperless-ngx/pull/7826))
- Chore(deps-dev): Bump @playwright/test from 1.46.1 to 1.47.2 in /src-ui [@dependabot](https://github.com/dependabot) ([#7828](https://github.com/paperless-ngx/paperless-ngx/pull/7828))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 21 updates [@dependabot](https://github.com/dependabot) ([#7825](https://github.com/paperless-ngx/paperless-ngx/pull/7825))
- Chore: Upgrades OCRMyPDF to v16 [@stumpylog](https://github.com/stumpylog) ([#7815](https://github.com/paperless-ngx/paperless-ngx/pull/7815))
- Enhancement: workflow overview toggle enable button [@shamoon](https://github.com/shamoon) ([#7818](https://github.com/paperless-ngx/paperless-ngx/pull/7818))
- Enhancement: disable-able mail rules, add toggle to overview [@shamoon](https://github.com/shamoon) ([#7810](https://github.com/paperless-ngx/paperless-ngx/pull/7810))
- Chore: Upgrades the Docker image to use Python 3.12 [@stumpylog](https://github.com/stumpylog) ([#7796](https://github.com/paperless-ngx/paperless-ngx/pull/7796))
- Chore: Upgrade Django to 5.1 [@stumpylog](https://github.com/stumpylog) ([#7795](https://github.com/paperless-ngx/paperless-ngx/pull/7795))
- Chore: Drop Python 3.9 support [@stumpylog](https://github.com/stumpylog) ([#7774](https://github.com/paperless-ngx/paperless-ngx/pull/7774))
- Feature: auto-clean some invalid pdfs [@shamoon](https://github.com/shamoon) ([#7651](https://github.com/paperless-ngx/paperless-ngx/pull/7651))
- Feature: page count [@s0llvan](https://github.com/s0llvan) ([#7750](https://github.com/paperless-ngx/paperless-ngx/pull/7750))
- Fix: hidden canvas element causes scroll bug [@shamoon](https://github.com/shamoon) ([#7770](https://github.com/paperless-ngx/paperless-ngx/pull/7770))
- Enhancement: compactify dates dropdown [@shamoon](https://github.com/shamoon) ([#7759](https://github.com/paperless-ngx/paperless-ngx/pull/7759))
- Fix: handle overflowing dropdowns on mobile [@shamoon](https://github.com/shamoon) ([#7758](https://github.com/paperless-ngx/paperless-ngx/pull/7758))
- Enhancement: set Django SESSION_EXPIRE_AT_BROWSER_CLOSE from PAPERLESS_ACCOUNT_SESSION_REMEMBER [@shamoon](https://github.com/shamoon) ([#7748](https://github.com/paperless-ngx/paperless-ngx/pull/7748))
- Enhancement: allow setting session cookie age [@shamoon](https://github.com/shamoon) ([#7743](https://github.com/paperless-ngx/paperless-ngx/pull/7743))
- Fix: chrome scrolling in >= 129 [@shamoon](https://github.com/shamoon) ([#7738](https://github.com/paperless-ngx/paperless-ngx/pull/7738))
- Feature: copy workflows and mail rules, improve layout [@shamoon](https://github.com/shamoon) ([#7727](https://github.com/paperless-ngx/paperless-ngx/pull/7727))
- Chore(deps-dev): Bump the development group with 2 updates [@dependabot](https://github.com/dependabot) ([#7723](https://github.com/paperless-ngx/paperless-ngx/pull/7723))
</details>
## paperless-ngx 2.12.1
### Bug Fixes

View File

@@ -18,6 +18,7 @@
# Paths and folders
#PAPERLESS_CONSUMPTION_DIR=../consume
#PAPERLESS_CONSUMPTION_FAILED_DIR=../consume/failed
#PAPERLESS_DATA_DIR=../data
#PAPERLESS_EMPTY_TRASH_DIR=
#PAPERLESS_MEDIA_ROOT=../media

View File

@@ -1994,120 +1994,141 @@
<context context-type="linenumber">72</context>
</context-group>
</trans-unit>
<trans-unit id="7934833136974560675" datatype="html">
<source>Retry</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">86</context>
</context-group>
</trans-unit>
<trans-unit id="1536087519743707362" datatype="html">
<source>Dismiss</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">85</context>
<context context-type="linenumber">90</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">68</context>
<context context-type="linenumber">71</context>
</context-group>
</trans-unit>
<trans-unit id="2134950584701094962" datatype="html">
<source>Open Document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">90</context>
<context context-type="linenumber">95</context>
</context-group>
</trans-unit>
<trans-unit id="428536141871853903" datatype="html">
<source>{VAR_PLURAL, plural, =1 {One <x id="INTERPOLATION"/> task} other {<x id="INTERPOLATION_1"/> total <x id="INTERPOLATION"/> tasks}}</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">109</context>
<context context-type="linenumber">114</context>
</context-group>
</trans-unit>
<trans-unit id="1943508481059904274" datatype="html">
<source> (<x id="INTERPOLATION" equiv-text="{{selectedTasks.size}}"/> selected)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">111</context>
<context context-type="linenumber">116</context>
</context-group>
</trans-unit>
<trans-unit id="5639839509673911668" datatype="html">
<source>Failed<x id="START_BLOCK_IF" equiv-text="@if (tasksService.failedFileTasks.length &gt; 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="&lt;span class=&quot;badge bg-danger ms-2&quot;&gt;"/><x id="INTERPOLATION" equiv-text="{{tasksService.failedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="&lt;/span&gt;"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">123,125</context>
<context context-type="linenumber">128,130</context>
</context-group>
</trans-unit>
<trans-unit id="8210778930307085868" datatype="html">
<source>Complete<x id="START_BLOCK_IF" equiv-text="@if (tasksService.completedFileTasks.length &gt; 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="&lt;span class=&quot;badge bg-secondary ms-2&quot;&gt;"/><x id="INTERPOLATION" equiv-text="{{tasksService.completedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="&lt;/span&gt;"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">131,133</context>
<context context-type="linenumber">136,138</context>
</context-group>
</trans-unit>
<trans-unit id="3522801015717851360" datatype="html">
<source>Started<x id="START_BLOCK_IF" equiv-text="@if (tasksService.startedFileTasks.length &gt; 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="&lt;span class=&quot;badge bg-secondary ms-2&quot;&gt;"/><x id="INTERPOLATION" equiv-text="{{tasksService.startedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="&lt;/span&gt;"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">139,141</context>
<context context-type="linenumber">144,146</context>
</context-group>
</trans-unit>
<trans-unit id="2341807459308874922" datatype="html">
<source>Queued<x id="START_BLOCK_IF" equiv-text="@if (tasksService.queuedFileTasks.length &gt; 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="&lt;span class=&quot;badge bg-secondary ms-2&quot;&gt;"/><x id="INTERPOLATION" equiv-text="{{tasksService.queuedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="&lt;/span&gt;"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">147,149</context>
<context context-type="linenumber">152,154</context>
</context-group>
</trans-unit>
<trans-unit id="5404910960991552159" datatype="html">
<source>Dismiss selected</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">31</context>
<context context-type="linenumber">33</context>
</context-group>
</trans-unit>
<trans-unit id="8829078752502782653" datatype="html">
<source>Dismiss all</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">32</context>
<context context-type="linenumber">34</context>
</context-group>
</trans-unit>
<trans-unit id="1323591410517879795" datatype="html">
<source>Confirm Dismiss All</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">65</context>
<context context-type="linenumber">68</context>
</context-group>
</trans-unit>
<trans-unit id="4157200209636243740" datatype="html">
<source>Dismiss all <x id="PH" equiv-text="tasks.size"/> tasks?</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">66</context>
<context context-type="linenumber">69</context>
</context-group>
</trans-unit>
<trans-unit id="7611027432301841688" datatype="html">
<source>Retrying task...</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">92</context>
</context-group>
</trans-unit>
<trans-unit id="5445438607105804721" datatype="html">
<source>Failed to retry task</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">95</context>
</context-group>
</trans-unit>
<trans-unit id="9011556615675272238" datatype="html">
<source>queued</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">135</context>
<context context-type="linenumber">149</context>
</context-group>
</trans-unit>
<trans-unit id="6415892379431855826" datatype="html">
<source>started</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">137</context>
<context context-type="linenumber">151</context>
</context-group>
</trans-unit>
<trans-unit id="7510279840486540181" datatype="html">
<source>completed</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">139</context>
<context context-type="linenumber">153</context>
</context-group>
</trans-unit>
<trans-unit id="4083337005045748464" datatype="html">
<source>failed</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">141</context>
<context context-type="linenumber">155</context>
</context-group>
</trans-unit>
<trans-unit id="3418677553313974490" datatype="html">

4079
src-ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,54 +11,54 @@
},
"private": true,
"dependencies": {
"@angular/cdk": "^18.2.11",
"@angular/common": "~18.2.10",
"@angular/compiler": "~18.2.10",
"@angular/core": "~18.2.10",
"@angular/forms": "~18.2.10",
"@angular/localize": "~18.2.10",
"@angular/platform-browser": "~18.2.10",
"@angular/platform-browser-dynamic": "~18.2.10",
"@angular/router": "~18.2.10",
"@angular/cdk": "^18.2.6",
"@angular/common": "~18.2.6",
"@angular/compiler": "~18.2.6",
"@angular/core": "~18.2.6",
"@angular/forms": "~18.2.6",
"@angular/localize": "~18.2.6",
"@angular/platform-browser": "~18.2.6",
"@angular/platform-browser-dynamic": "~18.2.6",
"@angular/router": "~18.2.6",
"@ng-bootstrap/ng-bootstrap": "^17.0.1",
"@ng-select/ng-select": "^13.9.1",
"@ng-select/ng-select": "^13.9.0",
"@ngneat/dirty-check-forms": "^3.0.3",
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.3",
"file-saver": "^2.0.5",
"mime-names": "^1.0.0",
"ng2-pdf-viewer": "^10.3.4",
"ng2-pdf-viewer": "^10.3.1",
"ngx-bootstrap-icons": "^1.9.3",
"ngx-color": "^9.0.0",
"ngx-cookie-service": "^18.0.0",
"ngx-file-drop": "^16.0.0",
"ngx-ui-tour-ng-bootstrap": "^15.0.0",
"rxjs": "^7.8.1",
"tslib": "^2.8.1",
"uuid": "^11.0.2",
"tslib": "^2.7.0",
"uuid": "^10.0.0",
"zone.js": "^0.14.8"
},
"devDependencies": {
"@angular-builders/custom-webpack": "^18.0.0",
"@angular-builders/jest": "^18.0.0",
"@angular-devkit/build-angular": "^18.2.2",
"@angular-devkit/core": "^18.2.11",
"@angular-devkit/schematics": "^18.2.11",
"@angular-eslint/builder": "18.4.0",
"@angular-eslint/eslint-plugin": "18.4.0",
"@angular-eslint/eslint-plugin-template": "18.4.0",
"@angular-eslint/schematics": "18.4.0",
"@angular-eslint/template-parser": "18.4.0",
"@angular/cli": "~18.2.11",
"@angular-devkit/core": "^18.2.6",
"@angular-devkit/schematics": "^18.2.6",
"@angular-eslint/builder": "18.3.1",
"@angular-eslint/eslint-plugin": "18.3.1",
"@angular-eslint/eslint-plugin-template": "18.3.1",
"@angular-eslint/schematics": "18.3.1",
"@angular-eslint/template-parser": "18.3.1",
"@angular/cli": "~18.2.6",
"@angular/compiler-cli": "~18.2.2",
"@codecov/webpack-plugin": "^1.2.1",
"@playwright/test": "^1.48.2",
"@types/jest": "^29.5.14",
"@types/node": "^22.8.6",
"@typescript-eslint/eslint-plugin": "^8.12.2",
"@typescript-eslint/parser": "^8.12.2",
"@codecov/webpack-plugin": "^1.2.0",
"@playwright/test": "^1.47.2",
"@types/jest": "^29.5.13",
"@types/node": "^22.7.4",
"@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0",
"@typescript-eslint/utils": "^8.0.0",
"eslint": "^9.14.0",
"eslint": "^9.11.1",
"jest": "29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest-preset-angular": "^14.2.4",

View File

@@ -81,6 +81,9 @@
</td>
<td scope="row">
<div class="btn-group" role="group">
@if (task.status === PaperlessTaskStatus.Failed) {
<ng-container *ngTemplateOutlet="retryDropdown; context: { task: task }"></ng-container>
}
<button class="btn btn-sm btn-outline-secondary" (click)="dismissTask(task); $event.stopPropagation();" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }">
<i-bs name="check"></i-bs>&nbsp;<ng-container i18n>Dismiss</ng-container>
</button>
@@ -153,3 +156,25 @@
</li>
</ul>
<div [ngbNavOutlet]="nav"></div>
<ng-template #retryDropdown let-task="task">
<div ngbDropdown>
<button class="btn btn-sm btn-outline-primary" (click)="$event.stopImmediatePropagation()" ngbDropdownToggle>
<i-bs name="arrow-repeat"></i-bs>&nbsp;<ng-container i18n>Retry</ng-container>
</button>
<div ngbDropdownMenu class="shadow retry-dropdown">
<div class="p-2">
<ul class="list-group list-group-flush">
<li class="list-group-item small" i18n>
<pngx-input-check [(ngModel)]="retryClean" i18n-title title="Attempt to clean pdf"></pngx-input-check>
</li>
</ul>
<div class="d-flex justify-content-end">
<button class="btn btn-sm btn-outline-primary" (click)="retryTask(task); $event.stopPropagation();">
<ng-container i18n>Proceed</ng-container>
</button>
</div>
</div>
</div>
</div>
</ng-template>

View File

@@ -26,3 +26,7 @@ pre {
max-width: 150px;
}
}
.retry-dropdown {
width: 300px;
}

View File

@@ -31,6 +31,9 @@ import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { FormsModule } from '@angular/forms'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { ToastService } from 'src/app/services/toast.service'
import { of, throwError } from 'rxjs'
import { CheckComponent } from '../../common/input/check/check.component'
const tasks: PaperlessTask[] = [
{
@@ -115,6 +118,7 @@ describe('TasksComponent', () => {
let modalService: NgbModal
let router: Router
let httpTestingController: HttpTestingController
let toastService: ToastService
let reloadSpy
beforeEach(async () => {
@@ -125,6 +129,7 @@ describe('TasksComponent', () => {
IfPermissionsDirective,
CustomDatePipe,
ConfirmDialogComponent,
CheckComponent,
],
imports: [
NgbModule,
@@ -152,6 +157,7 @@ describe('TasksComponent', () => {
httpTestingController = TestBed.inject(HttpTestingController)
modalService = TestBed.inject(NgbModal)
router = TestBed.inject(Router)
toastService = TestBed.inject(ToastService)
fixture = TestBed.createComponent(TasksComponent)
component = fixture.componentInstance
jest.useFakeTimers()
@@ -173,8 +179,10 @@ describe('TasksComponent', () => {
`Failed${currentTasksLength}`
)
expect(
fixture.debugElement.queryAll(By.css('table input[type="checkbox"]'))
).toHaveLength(currentTasksLength + 1)
fixture.debugElement.queryAll(
By.css('table td > .form-check input[type="checkbox"]')
)
).toHaveLength(currentTasksLength)
currentTasksLength = tasks.filter(
(t) => t.status === PaperlessTaskStatus.Complete
@@ -289,4 +297,20 @@ describe('TasksComponent', () => {
jest.advanceTimersByTime(6000)
expect(reloadSpy).toHaveBeenCalledTimes(2)
})
it('should retry a task, show toast on error or success', () => {
const retrySpy = jest.spyOn(tasksService, 'retryTask')
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
const toastErrorSpy = jest.spyOn(toastService, 'showError')
retrySpy.mockReturnValueOnce(of({ task_id: '123' }))
component.retryTask(tasks[0])
expect(retrySpy).toHaveBeenCalledWith(tasks[0], false)
expect(toastInfoSpy).toHaveBeenCalledWith('Retrying task...')
retrySpy.mockReturnValueOnce(throwError(() => new Error('test')))
component.retryTask(tasks[0])
expect(toastErrorSpy).toHaveBeenCalledWith(
'Failed to retry task',
new Error('test')
)
})
})

View File

@@ -2,10 +2,11 @@ import { Component, OnInit, OnDestroy } from '@angular/core'
import { Router } from '@angular/router'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { first } from 'rxjs'
import { PaperlessTask } from 'src/app/data/paperless-task'
import { PaperlessTask, PaperlessTaskStatus } from 'src/app/data/paperless-task'
import { TasksService } from 'src/app/services/tasks.service'
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component'
import { ToastService } from 'src/app/services/toast.service'
@Component({
selector: 'pngx-tasks',
@@ -16,6 +17,7 @@ export class TasksComponent
extends ComponentWithPermissions
implements OnInit, OnDestroy
{
public PaperlessTaskStatus = PaperlessTaskStatus
public activeTab: string
public selectedTasks: Set<number> = new Set()
public togggleAll: boolean = false
@@ -26,6 +28,8 @@ export class TasksComponent
public autoRefreshInterval: any
public retryClean: boolean = false
get dismissButtonText(): string {
return this.selectedTasks.size > 0
? $localize`Dismiss selected`
@@ -35,6 +39,7 @@ export class TasksComponent
constructor(
public tasksService: TasksService,
private modalService: NgbModal,
private toastService: ToastService,
private readonly router: Router
) {
super()
@@ -83,6 +88,17 @@ export class TasksComponent
this.router.navigate(['documents', task.related_document])
}
retryTask(task: PaperlessTask) {
this.tasksService.retryTask(task, this.retryClean).subscribe({
next: () => {
this.toastService.showInfo($localize`Retrying task...`)
},
error: (e) => {
this.toastService.showError($localize`Failed to retry task`, e)
},
})
}
expandTask(task: PaperlessTask) {
this.expandedTask = this.expandedTask == task.id ? undefined : task.id
}

View File

@@ -38,7 +38,7 @@
@for (item of selectionModel.itemsSorted | filter: filterText:'name'; track item; let i = $index) {
@if (allowSelectNone || item.id) {
<pngx-toggleable-dropdown-button
[item]="item" [hideCount]="hideCount(item)" [state]="selectionModel.get(item.id)" [count]="getUpdatedDocumentCount(item.id)" (toggled)="selectionModel.toggle(item.id)" (exclude)="excludeClicked(item.id)" (click)="setButtonItemIndex(i - 1)" [disabled]="disabled">
[item]="item" [hideCount]="hideCount(item)" [state]="selectionModel.get(item.id)" [count]="getUpdatedDocumentCount(item.id)" (toggle)="selectionModel.toggle(item.id)" (exclude)="excludeClicked(item.id)" (click)="setButtonItemIndex(i - 1)" [disabled]="disabled">
</pngx-toggleable-dropdown-button>
}
}

View File

@@ -58,7 +58,7 @@ describe('ToggleableDropdownButtonComponent', () => {
let toggleResult
component.state = ToggleableItemState.Selected
component.exclude.subscribe(() => (excludeResult = true))
component.toggled.subscribe(() => (toggleResult = true))
component.toggle.subscribe(() => (toggleResult = true))
const button = fixture.nativeElement.querySelector('button')
button.dispatchEvent(new MouseEvent('click'))
expect(excludeResult).toBeTruthy()
@@ -70,7 +70,7 @@ describe('ToggleableDropdownButtonComponent', () => {
let toggleResult
component.state = ToggleableItemState.Excluded
component.exclude.subscribe(() => (excludeResult = true))
component.toggled.subscribe(() => (toggleResult = true))
component.toggle.subscribe(() => (toggleResult = true))
const button = fixture.nativeElement.querySelector('button')
button.dispatchEvent(new MouseEvent('click'))
expect(excludeResult).toBeFalsy()

View File

@@ -30,7 +30,7 @@ export class ToggleableDropdownButtonComponent {
hideCount: boolean = false
@Output()
toggled = new EventEmitter()
toggle = new EventEmitter()
@Output()
exclude = new EventEmitter()
@@ -43,7 +43,7 @@ export class ToggleableDropdownButtonComponent {
if (this.state == ToggleableItemState.Selected) {
this.exclude.emit()
} else {
this.toggled.emit()
this.toggle.emit()
}
}

View File

@@ -5,7 +5,7 @@
</div>
} @else {
@if (renderAsObject) {
<object [data]="previewURL | safeUrl" width="100%" class="bg-light" [class.p-2]="!isPdf" [class.pdf]="isPdf"></object>
<object [data]="previewURL | safeUrl" width="100%" class="bg-light" [class.p-2]="!isPdf"></object>
} @else {
@if (requiresPassword) {
<div class="w-100 h-100 position-relative">

View File

@@ -17,7 +17,7 @@
background-color: #e7e7e7;
}
object:not(.pdf) {
object {
mix-blend-mode: difference;
&.p-2 {
padding: 0 !important;

View File

@@ -1,6 +1,6 @@
<div class="card mb-3 shadow-sm bg-light" [class.card-selected]="selected" [class.document-card]="selectable" [class.popover-hidden]="popoverHidden" (mouseleave)="mouseLeaveCard()">
<div class="row g-0">
<div class="col-md-2 doc-img-container rounded-start" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit()">
<div class="col-md-2 doc-img-background rounded-start" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit()">
<img [src]="getThumbUrl()" class="card-img doc-img border-end rounded-start" [class.inverted]="getIsThumbInverted()">
<div class="border-end border-bottom bg-light document-card-check">

View File

@@ -2,7 +2,7 @@
overflow-wrap: anywhere;
}
.doc-img-container {
.doc-img-background {
position: relative;
}
@@ -11,6 +11,7 @@
object-position: top left;
height: 100%;
position: absolute;
mix-blend-mode: multiply;
}
.card-title {
@@ -48,6 +49,18 @@
display: block;
}
.card-selected {
border-color: var(--bs-primary);
.document-card-check {
display: block;
}
}
.doc-img-background-selected {
background-color: var(--pngx-primary-faded);
}
.card-info {
line-height: 1;

View File

@@ -1,7 +1,7 @@
<div class="col p-2 h-100">
<div class="card h-100 shadow-sm document-card" [class.card-selected]="selected" [class.popover-hidden]="popoverHidden" (mouseleave)="mouseLeaveCard()">
<div class="border-bottom doc-img-container" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit(this)">
<img class="card-img doc-img" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
<div class="border-bottom doc-img-container" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)" (dblclick)="dblClickDocument.emit(this)">
<img class="card-img doc-img rounded-top" [class.inverted]="getIsThumbInverted()" [src]="getThumbUrl()">
<div class="border-end border-bottom bg-light py-1 px-2 document-card-check">
<div class="form-check">

View File

@@ -6,6 +6,7 @@
object-fit: cover;
object-position: top left;
height: 180px;
mix-blend-mode: multiply;
}
.document-card-check {
@@ -39,6 +40,18 @@
top: 142px;
}
.card-selected {
border-color:var(--bs-primary);
.document-card-check {
display: block;
}
}
.doc-img-background-selected {
background-color: var(--pngx-primary-faded);
}
.card-info {
line-height: 1;

View File

@@ -1204,7 +1204,7 @@ describe('FilterEditorComponent', () => {
const tagButton = tagsFilterableDropdown.queryAll(
By.directive(ToggleableDropdownButtonComponent)
)[0]
tagButton.triggerEventHandler('toggled')
tagButton.triggerEventHandler('toggle')
fixture.detectChanges()
expect(component.filterRules).toEqual([
{
@@ -1222,8 +1222,8 @@ describe('FilterEditorComponent', () => {
const tagButtons = tagsFilterableDropdown.queryAll(
By.directive(ToggleableDropdownButtonComponent)
)
tagButtons[1].triggerEventHandler('toggled')
tagButtons[2].triggerEventHandler('toggled')
tagButtons[1].triggerEventHandler('toggle')
tagButtons[2].triggerEventHandler('toggle')
fixture.detectChanges()
expect(component.filterRules).toEqual([
{
@@ -1273,8 +1273,8 @@ describe('FilterEditorComponent', () => {
const correspondentButtons = correspondentsFilterableDropdown.queryAll(
By.directive(ToggleableDropdownButtonComponent)
)
correspondentButtons[1].triggerEventHandler('toggled')
correspondentButtons[2].triggerEventHandler('toggled')
correspondentButtons[1].triggerEventHandler('toggle')
correspondentButtons[2].triggerEventHandler('toggle')
fixture.detectChanges()
expect(component.filterRules).toEqual([
{
@@ -1312,7 +1312,7 @@ describe('FilterEditorComponent', () => {
const notAssignedButton = correspondentsFilterableDropdown.queryAll(
By.directive(ToggleableDropdownButtonComponent)
)[0]
notAssignedButton.triggerEventHandler('toggled')
notAssignedButton.triggerEventHandler('toggle')
fixture.detectChanges()
expect(component.filterRules).toEqual([
{
@@ -1330,8 +1330,8 @@ describe('FilterEditorComponent', () => {
const documentTypeButtons = documentTypesFilterableDropdown.queryAll(
By.directive(ToggleableDropdownButtonComponent)
)
documentTypeButtons[1].triggerEventHandler('toggled')
documentTypeButtons[2].triggerEventHandler('toggled')
documentTypeButtons[1].triggerEventHandler('toggle')
documentTypeButtons[2].triggerEventHandler('toggle')
fixture.detectChanges()
expect(component.filterRules).toEqual([
{
@@ -1369,7 +1369,7 @@ describe('FilterEditorComponent', () => {
const notAssignedButton = docTypesFilterableDropdown.queryAll(
By.directive(ToggleableDropdownButtonComponent)
)[0]
notAssignedButton.triggerEventHandler('toggled')
notAssignedButton.triggerEventHandler('toggle')
fixture.detectChanges()
expect(component.filterRules).toEqual([
{
@@ -1387,8 +1387,8 @@ describe('FilterEditorComponent', () => {
const storagePathButtons = storagePathFilterableDropdown.queryAll(
By.directive(ToggleableDropdownButtonComponent)
)
storagePathButtons[1].triggerEventHandler('toggled')
storagePathButtons[2].triggerEventHandler('toggled')
storagePathButtons[1].triggerEventHandler('toggle')
storagePathButtons[2].triggerEventHandler('toggle')
fixture.detectChanges()
expect(component.filterRules).toEqual([
{
@@ -1426,7 +1426,7 @@ describe('FilterEditorComponent', () => {
const notAssignedButton = storagePathsFilterableDropdown.queryAll(
By.directive(ToggleableDropdownButtonComponent)
)[0]
notAssignedButton.triggerEventHandler('toggled')
notAssignedButton.triggerEventHandler('toggle')
fixture.detectChanges()
expect(component.filterRules).toEqual([
{

View File

@@ -599,17 +599,14 @@ describe('DocumentListViewService', () => {
it('should not filter out custom fields if settings not initialized', () => {
const customFields = ['custom_field_1', 'custom_field_2']
documentListViewService.displayFields = customFields as any
settingsService.displayFieldsInitialized = false
expect(documentListViewService.displayFields).toEqual(customFields)
jest.spyOn(settingsService, 'allDisplayFields', 'get').mockReturnValue([
{ id: DisplayField.ADDED, name: 'Added' },
{ id: DisplayField.TITLE, name: 'Title' },
{ id: 'custom_field_1', name: 'Custom Field 1' },
] as any)
settingsService.displayFieldsInit.emit(true)
expect(documentListViewService.displayFields).toEqual(['custom_field_1'])
// will now filter on set
documentListViewService.displayFields = customFields as any
settingsService.displayFieldsInitialized = true
expect(documentListViewService.displayFields).toEqual(['custom_field_1'])
})
})

View File

@@ -20,10 +20,6 @@ import { paramsFromViewState, paramsToViewState } from '../utils/query-params'
import { DocumentService, SelectionData } from './rest/document.service'
import { SettingsService } from './settings.service'
const LIST_DEFAULT_DISPLAY_FIELDS: DisplayField[] = DEFAULT_DISPLAY_FIELDS.map(
(f) => f.id
).filter((f) => f !== DisplayField.ADDED)
/**
* Captures the current state of the list view.
*/
@@ -106,8 +102,6 @@ export class DocumentListViewService {
private _activeSavedViewId: number = null
private displayFieldsInitialized: boolean = false
get activeSavedViewId() {
return this._activeSavedViewId
}
@@ -140,19 +134,6 @@ export class DocumentListViewService {
localStorage.removeItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG)
}
}
this.settings.displayFieldsInit.subscribe(() => {
this.displayFieldsInitialized = true
if (this.activeListViewState.displayFields) {
this.activeListViewState.displayFields =
this.activeListViewState.displayFields.filter(
(field) =>
this.settings.allDisplayFields.find((f) => f.id === field) !==
undefined
)
this.saveDocumentListView()
}
})
}
private defaultListViewState(): ListViewState {
@@ -434,17 +415,23 @@ export class DocumentListViewService {
}
get displayFields(): DisplayField[] {
return this.activeListViewState.displayFields ?? LIST_DEFAULT_DISPLAY_FIELDS
}
set displayFields(fields: DisplayField[]) {
this.activeListViewState.displayFields = this.displayFieldsInitialized
? fields?.filter(
let fields =
this.activeListViewState.displayFields ??
DEFAULT_DISPLAY_FIELDS.map((f) => f.id)
if (!this.activeListViewState.displayFields) {
fields = fields.filter((f) => f !== DisplayField.ADDED)
}
return this.settings.displayFieldsInitialized
? fields.filter(
(field) =>
this.settings.allDisplayFields.find((f) => f.id === field) !==
undefined
)
: fields
}
set displayFields(fields: DisplayField[]) {
this.activeListViewState.displayFields = fields
this.saveDocumentListView()
}

View File

@@ -274,7 +274,7 @@ export class SettingsService {
public get allDisplayFields(): Array<{ id: DisplayField; name: string }> {
return this._allDisplayFields
}
public displayFieldsInit: EventEmitter<boolean> = new EventEmitter()
public displayFieldsInitialized: boolean = false
constructor(
rendererFactory: RendererFactory2,
@@ -382,10 +382,10 @@ export class SettingsService {
}
})
)
this.displayFieldsInit.emit(true)
this.displayFieldsInitialized = true
})
} else {
this.displayFieldsInit.emit(true)
this.displayFieldsInitialized = true
}
}

View File

@@ -118,4 +118,29 @@ describe('TasksService', () => {
expect(tasksService.queuedFileTasks).toHaveLength(1)
expect(tasksService.startedFileTasks).toHaveLength(1)
})
it('should call retry task api endpoint', () => {
const task = {
id: 1,
type: PaperlessTaskType.File,
status: PaperlessTaskStatus.Failed,
acknowledged: false,
task_id: '1234',
task_file_name: 'file1.pdf',
date_created: new Date(),
}
tasksService.retryTask(task, true).subscribe()
const reloadSpy = jest.spyOn(tasksService, 'reload')
const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}tasks/${task.id}/retry/`
)
expect(req.request.method).toEqual('POST')
expect(req.request.body).toEqual({
clean: true,
})
req.flush({ task_id: 12345 })
expect(reloadSpy).toHaveBeenCalled()
httpTestingController.expectOne(`${environment.apiBaseUrl}tasks/`).flush([])
})
})

View File

@@ -1,7 +1,7 @@
import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { Subject } from 'rxjs'
import { first, takeUntil } from 'rxjs/operators'
import { Observable, Subject } from 'rxjs'
import { first, takeUntil, tap } from 'rxjs/operators'
import {
PaperlessTask,
PaperlessTaskStatus,
@@ -73,6 +73,20 @@ export class TasksService {
})
}
public retryTask(task: PaperlessTask, clean: boolean): Observable<any> {
return this.http
.post(`${this.baseUrl}tasks/${task.id}/retry/`, {
clean,
})
.pipe(
takeUntil(this.unsubscribeNotifer),
first(),
tap(() => {
this.reload()
})
)
}
public cancelPending(): void {
this.unsubscribeNotifer.next(true)
}

View File

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

View File

@@ -3605,7 +3605,7 @@
</context-group>
<target state="final">Fehler beim Speichern des Feldes.</target>
</trans-unit>
<trans-unit id="4465085913683915434" datatype="html" approved="yes">
<trans-unit id="4465085913683915434" datatype="html">
<source>True</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
@@ -3619,9 +3619,9 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">79</context>
</context-group>
<target state="final">Wahr</target>
<target state="translated">Wahr</target>
</trans-unit>
<trans-unit id="3800326155195149498" datatype="html" approved="yes">
<trans-unit id="3800326155195149498" datatype="html">
<source>False</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
@@ -3635,15 +3635,15 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">80</context>
</context-group>
<target state="final">Falsch</target>
<target state="translated">Falsch</target>
</trans-unit>
<trans-unit id="7551700625201096185" datatype="html" approved="yes">
<trans-unit id="7551700625201096185" datatype="html">
<source>Search docs...</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">96</context>
</context-group>
<target state="final">Suche Dokumente...</target>
<target state="translated">Suche Dokumente...</target>
</trans-unit>
<trans-unit id="3184700926171002527" datatype="html" approved="yes">
<source>Any</source>
@@ -3685,29 +3685,29 @@
</context-group>
<target state="final">Alle</target>
</trans-unit>
<trans-unit id="1496549861742963591" datatype="html" approved="yes">
<trans-unit id="1496549861742963591" datatype="html">
<source>Not</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">131</context>
</context-group>
<target state="final">Nicht</target>
<target state="translated">Nicht</target>
</trans-unit>
<trans-unit id="6548676277933116532" datatype="html" approved="yes">
<trans-unit id="6548676277933116532" datatype="html">
<source>Add query</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">150</context>
</context-group>
<target state="final">Abfrage hinzufügen</target>
<target state="translated">Abfrage hinzufügen</target>
</trans-unit>
<trans-unit id="5599577087865387184" datatype="html" approved="yes">
<trans-unit id="5599577087865387184" datatype="html">
<source>Add expression</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">153</context>
</context-group>
<target state="final">Ausdruck hinzufügen</target>
<target state="translated">Ausdruck hinzufügen</target>
</trans-unit>
<trans-unit id="6052766076365105714" datatype="html" approved="yes">
<source>now</source>
@@ -4513,13 +4513,13 @@
</context-group>
<target state="final">Pfad</target>
</trans-unit>
<trans-unit id="2816147949408898105" datatype="html" approved="yes">
<trans-unit id="2816147949408898105" datatype="html">
<source>See &lt;a target=&apos;_blank&apos; href=&apos;https://docs.paperless-ngx.com/advanced_usage/#file-name-handling&apos;&gt;the documentation&lt;/a&gt;.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="final">Siehe &lt;a target='_blank' href='https://docs.paperless-ngx.com/advanced_usage/#file-name-handling'&gt;Dokumentation&lt;/a&gt;.</target>
<target state="translated">Siehe &lt;a target='_blank' href='https://docs.paperless-ngx.com/advanced_usage/#file-name-handling'&gt;Dokumentation&lt;/a&gt;.</target>
</trans-unit>
<trans-unit id="1295614462098694869" datatype="html" approved="yes">
<source>Preview</source>
@@ -4533,29 +4533,29 @@
</context-group>
<target state="final">Vorschau</target>
</trans-unit>
<trans-unit id="8057014866157903311" datatype="html" approved="yes">
<trans-unit id="8057014866157903311" datatype="html">
<source>Path test failed</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
<target state="final">Pfadüberprüfung fehlgeschlagen</target>
<target state="translated">Pfadüberprüfung fehlgeschlagen</target>
</trans-unit>
<trans-unit id="9116034231465034307" datatype="html" approved="yes">
<trans-unit id="9116034231465034307" datatype="html">
<source>No document selected</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context>
<context context-type="linenumber">32</context>
</context-group>
<target state="final">Kein Dokument ausgewählt</target>
<target state="translated">Kein Dokument ausgewählt</target>
</trans-unit>
<trans-unit id="2083498114116917092" datatype="html" approved="yes">
<trans-unit id="2083498114116917092" datatype="html">
<source>Search for a document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context>
<context context-type="linenumber">38</context>
</context-group>
<target state="final">Ein Dokument suchen</target>
<target state="translated">Ein Dokument suchen</target>
</trans-unit>
<trans-unit id="6423278459497515329" datatype="html" approved="yes">
<source>No documents found</source>
@@ -5342,21 +5342,21 @@
</context-group>
<target state="final">Link öffnen</target>
</trans-unit>
<trans-unit id="6595008830732269870" datatype="html" approved="yes">
<trans-unit id="6595008830732269870" datatype="html">
<source>Not found</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/document-link/document-link.component.html</context>
<context context-type="linenumber">50</context>
</context-group>
<target state="final">Nicht gefunden</target>
<target state="needs-translation">Not found</target>
</trans-unit>
<trans-unit id="5676637575587497817" datatype="html" approved="yes">
<trans-unit id="5676637575587497817" datatype="html">
<source>Search for documents</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/document-link/document-link.component.ts</context>
<context context-type="linenumber">53</context>
</context-group>
<target state="final">Nach Dokumenten suchen</target>
<target state="translated">Nach Dokumenten suchen</target>
</trans-unit>
<trans-unit id="8627133593113147800" datatype="html" approved="yes">
<source>Selected items</source>
@@ -8100,13 +8100,13 @@
</context-group>
<target state="final">Ohne Tag</target>
</trans-unit>
<trans-unit id="8644099678903817943" datatype="html" approved="yes">
<trans-unit id="8644099678903817943" datatype="html">
<source>Custom fields query</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">238</context>
</context-group>
<target state="final">Benutzerdefinierte Feldabfrage</target>
<target state="translated">Benutzerdefinierte Feldabfrage</target>
</trans-unit>
<trans-unit id="6523384805359286307" datatype="html" approved="yes">
<source>Title: <x id="PH" equiv-text="rule.value"/></source>
@@ -8296,7 +8296,7 @@
</context-group>
<target state="final">Datentyp</target>
</trans-unit>
<trans-unit id="6209318295562170730" datatype="html" approved="yes">
<trans-unit id="6209318295562170730" datatype="html">
<source>Filter Documents (<x id="INTERPOLATION" equiv-text="{{ field.document_count }}"/>)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
@@ -8318,7 +8318,7 @@
<context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context>
<context context-type="linenumber">85</context>
</context-group>
<target state="final">Dokumente filtern (<x id="INTERPOLATION" equiv-text="{{ field.document_count }}"/>)</target>
<target state="translated">Dokumente filtern (<x id="INTERPOLATION" equiv-text="{{ field.document_count }}"/>)</target>
</trans-unit>
<trans-unit id="651372623796033489" datatype="html" approved="yes">
<source>No fields defined.</source>
@@ -8408,21 +8408,21 @@
</context-group>
<target state="final">Konto hinzufügen</target>
</trans-unit>
<trans-unit id="5088684330574277786" datatype="html" approved="yes">
<trans-unit id="5088684330574277786" datatype="html">
<source>Connect Gmail Account</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
<context context-type="linenumber">18</context>
</context-group>
<target state="final">Gmail-Konto verbinden</target>
<target state="translated">Gmail-Konto verbinden</target>
</trans-unit>
<trans-unit id="6630732552154686829" datatype="html" approved="yes">
<trans-unit id="6630732552154686829" datatype="html">
<source>Connect Outlook Account</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/mail/mail.component.html</context>
<context context-type="linenumber">23</context>
</context-group>
<target state="final">Outlook-Konto verbinden</target>
<target state="translated">Outlook-Konto verbinden</target>
</trans-unit>
<trans-unit id="2188854519574316630" datatype="html" approved="yes">
<source>Server</source>
@@ -8500,21 +8500,21 @@
</context-group>
<target state="final">Fehler beim Abrufen der E-Mail-Regeln</target>
</trans-unit>
<trans-unit id="763945516325093575" datatype="html" approved="yes">
<trans-unit id="763945516325093575" datatype="html">
<source>OAuth2 authentication success</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
<context context-type="linenumber">101</context>
</context-group>
<target state="final">OAuth2-Authentifizierung erfolgreich</target>
<target state="translated">OAuth2-Authentifizierung erfolgreich</target>
</trans-unit>
<trans-unit id="9022978370268070156" datatype="html" approved="yes">
<trans-unit id="9022978370268070156" datatype="html">
<source>OAuth2 authentication failed, see logs for details</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/mail/mail.component.ts</context>
<context context-type="linenumber">112</context>
</context-group>
<target state="final">OAuth2-Authentifizierung fehlgeschlagen, siehe Protokolle für Details</target>
<target state="translated">OAuth2-Authentifizierung fehlgeschlagen, siehe Protokolle für Details</target>
</trans-unit>
<trans-unit id="6327501535846658797" datatype="html" approved="yes">
<source>Saved account &quot;<x id="PH" equiv-text="newMailAccount.name"/>&quot;.</source>
@@ -9000,93 +9000,93 @@
</context-group>
<target state="final">Zur Startseite</target>
</trans-unit>
<trans-unit id="7088714514100361567" datatype="html" approved="yes">
<trans-unit id="7088714514100361567" datatype="html">
<source>Equal to</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">24</context>
</context-group>
<target state="final">Ist gleich</target>
<target state="translated">Ist gleich</target>
</trans-unit>
<trans-unit id="2841739558138901231" datatype="html" approved="yes">
<trans-unit id="2841739558138901231" datatype="html">
<source>In</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">25</context>
</context-group>
<target state="final">In</target>
<target state="translated">In</target>
</trans-unit>
<trans-unit id="6504828068656625171" datatype="html" approved="yes">
<trans-unit id="6504828068656625171" datatype="html">
<source>Is null</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">26</context>
</context-group>
<target state="final">Ist null</target>
<target state="translated">Ist null</target>
</trans-unit>
<trans-unit id="4112599358351148632" datatype="html" approved="yes">
<trans-unit id="4112599358351148632" datatype="html">
<source>Exists</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">27</context>
</context-group>
<target state="final">Existiert</target>
<target state="translated">Existiert</target>
</trans-unit>
<trans-unit id="6238291467288576076" datatype="html" approved="yes">
<trans-unit id="6238291467288576076" datatype="html">
<source>Contains</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">28</context>
</context-group>
<target state="final">Enthält</target>
<target state="translated">Enthält</target>
</trans-unit>
<trans-unit id="870133374397538941" datatype="html" approved="yes">
<trans-unit id="870133374397538941" datatype="html">
<source>Contains (case-insensitive)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">29</context>
</context-group>
<target state="final">Enthält (beachte Groß- und Kleinschreibung)</target>
<target state="translated">Enthält (beachte Groß- und Kleinschreibung)</target>
</trans-unit>
<trans-unit id="7732309408488818531" datatype="html" approved="yes">
<trans-unit id="7732309408488818531" datatype="html">
<source>Greater than</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">30</context>
</context-group>
<target state="final">Größer als</target>
<target state="translated">Größer als</target>
</trans-unit>
<trans-unit id="9087788064443057357" datatype="html" approved="yes">
<trans-unit id="9087788064443057357" datatype="html">
<source>Greater than or equal to</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">31</context>
</context-group>
<target state="final">Größer oder gleich</target>
<target state="translated">Größer oder gleich</target>
</trans-unit>
<trans-unit id="5995604223909447366" datatype="html" approved="yes">
<trans-unit id="5995604223909447366" datatype="html">
<source>Less than</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">32</context>
</context-group>
<target state="final">Kleiner als</target>
<target state="translated">Kleiner als</target>
</trans-unit>
<trans-unit id="6989379963430864867" datatype="html" approved="yes">
<trans-unit id="6989379963430864867" datatype="html">
<source>Less than or equal to</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">33</context>
</context-group>
<target state="final">Kleiner oder gleich</target>
<target state="translated">Kleiner oder gleich</target>
</trans-unit>
<trans-unit id="2348971518300945764" datatype="html" approved="yes">
<trans-unit id="2348971518300945764" datatype="html">
<source>Range</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/custom-field-query.ts</context>
<context context-type="linenumber">34</context>
</context-group>
<target state="final">Bereich</target>
<target state="translated">Bereich</target>
</trans-unit>
<trans-unit id="969459137986754249" datatype="html" approved="yes">
<source>Boolean</source>

View File

@@ -1743,7 +1743,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">385</context>
</context-group>
<target state="translated">Tabell</target>
<target state="needs-translation">Table</target>
</trans-unit>
<trans-unit id="4236040382842528005" datatype="html">
<source>Small Cards</source>
@@ -1771,7 +1771,7 @@
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">17</context>
</context-group>
<target state="translated">Visa</target>
<target state="needs-translation">Show</target>
</trans-unit>
<trans-unit id="5607669932062416162" datatype="html">
<source>Default</source>
@@ -2303,7 +2303,7 @@
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">223</context>
</context-group>
<target state="translated">Papperskorg</target>
<target state="needs-translation">Trash</target>
</trans-unit>
<trans-unit id="3818027200170621545" datatype="html">
<source>Manage trashed documents that are pending deletion.</source>
@@ -3307,7 +3307,7 @@
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.html</context>
<context context-type="linenumber">131</context>
</context-group>
<target state="translated">Användare</target>
<target state="needs-translation">Users</target>
</trans-unit>
<trans-unit id="searchResults.groups" datatype="html">
<source>Groups</source>
@@ -3315,7 +3315,7 @@
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.html</context>
<context context-type="linenumber">138</context>
</context-group>
<target state="translated">Grupper</target>
<target state="needs-translation">Groups</target>
</trans-unit>
<trans-unit id="searchResults.customFields" datatype="html">
<source>Custom fields</source>
@@ -3471,7 +3471,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">7,8</context>
</context-group>
<target state="translated">av <x id="INTERPOLATION" equiv-text="{{totalPages}}"/></target>
<target state="needs-translation">of <x id="INTERPOLATION" equiv-text="{{totalPages}}"/></target>
</trans-unit>
<trans-unit id="6903610408081711391" datatype="html">
<source>Pages to remove</source>
@@ -3487,7 +3487,7 @@
<context context-type="sourcefile">src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html</context>
<context context-type="linenumber">9</context>
</context-group>
<target state="translated">Dokument:</target>
<target state="needs-translation">Documents:</target>
</trans-unit>
<trans-unit id="7508164375697837821" datatype="html">
<source>Use metadata from:</source>
@@ -3579,7 +3579,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.html</context>
<context context-type="linenumber">21</context>
</context-group>
<target state="translated">Skapa nytt fält</target>
<target state="needs-translation">Create new field</target>
</trans-unit>
<trans-unit id="6973528734330066202" datatype="html">
<source>Saved field &quot;<x id="PH" equiv-text="newField.name"/>&quot;.</source>
@@ -3939,7 +3939,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts</context>
<context context-type="linenumber">84</context>
</context-group>
<target state="translated">Redigera anpassat fält</target>
<target state="needs-translation">Edit custom field</target>
</trans-unit>
<trans-unit id="6672809941092516947" datatype="html" approved="yes">
<source>Create new document type</source>
@@ -5356,7 +5356,7 @@
<context context-type="sourcefile">src/app/components/common/input/document-link/document-link.component.ts</context>
<context context-type="linenumber">53</context>
</context-group>
<target state="translated">Sök efter dokument</target>
<target state="needs-translation">Search for documents</target>
</trans-unit>
<trans-unit id="8627133593113147800" datatype="html">
<source>Selected items</source>
@@ -5653,7 +5653,7 @@
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
<context context-type="linenumber">23</context>
</context-group>
<target state="translated">Bekräfta lösenord</target>
<target state="needs-translation">Confirm Password</target>
</trans-unit>
<trans-unit id="7554924397178347823" datatype="html">
<source>API Auth Token</source>
@@ -5905,7 +5905,7 @@
<context context-type="sourcefile">src/app/components/common/share-links-dropdown/share-links-dropdown.component.ts</context>
<context context-type="linenumber">94</context>
</context-group>
<target state="translated">1 dag</target>
<target state="needs-translation">1 day</target>
</trans-unit>
<trans-unit id="8542568275115626925" datatype="html">
<source>7 days</source>
@@ -5945,7 +5945,7 @@
<context context-type="sourcefile">src/app/components/common/share-links-dropdown/share-links-dropdown.component.ts</context>
<context context-type="linenumber">94</context>
</context-group>
<target state="translated"><x id="PH" equiv-text="days"/> dagar</target>
<target state="needs-translation"><x id="PH" equiv-text="days"/> days</target>
</trans-unit>
<trans-unit id="2897042887615940599" datatype="html">
<source>Error deleting link</source>
@@ -6025,7 +6025,7 @@
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
<context context-type="linenumber">41</context>
</context-group>
<target state="translated">Databas</target>
<target state="needs-translation">Database</target>
</trans-unit>
<trans-unit id="5611592591303869712" datatype="html">
<source>Status</source>
@@ -6045,7 +6045,7 @@
<context context-type="sourcefile">src/app/components/manage/workflows/workflows.component.html</context>
<context context-type="linenumber">19</context>
</context-group>
<target state="translated">Status</target>
<target state="needs-translation">Status</target>
</trans-unit>
<trans-unit id="2256165083739630668" datatype="html">
<source>Migration Status</source>
@@ -6085,7 +6085,7 @@
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
<context context-type="linenumber">83</context>
</context-group>
<target state="translated">Uppgifter</target>
<target state="needs-translation">Tasks</target>
</trans-unit>
<trans-unit id="6911698235105017958" datatype="html">
<source>Redis Status</source>
@@ -6157,7 +6157,7 @@
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
<context context-type="linenumber">40</context>
</context-group>
<target state="translated">Välkommen till <x id="PH" equiv-text="environment.appTitle"/></target>
<target state="needs-translation">Welcome to <x id="PH" equiv-text="environment.appTitle"/></target>
</trans-unit>
<trans-unit id="1325877348738783391" datatype="html">
<source>Dashboard updated</source>
@@ -6892,7 +6892,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">502</context>
</context-group>
<target state="translated">Spara dokument</target>
<target state="needs-translation">Save document</target>
</trans-unit>
<trans-unit id="5758784066858623886" datatype="html">
<source>Error retrieving metadata</source>
@@ -9142,7 +9142,7 @@
<context context-type="sourcefile">src/app/data/custom-field.ts</context>
<context context-type="linenumber">42</context>
</context-group>
<target state="translated">URL</target>
<target state="needs-translation">Url</target>
</trans-unit>
<trans-unit id="3650316326183661476" datatype="html">
<source>Document Link</source>
@@ -9295,7 +9295,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">83</context>
</context-group>
<target state="translated">Språk</target>
<target state="needs-translation">Language</target>
</trans-unit>
<trans-unit id="1713271461473302108" datatype="html">
<source>Mode</source>
@@ -9487,7 +9487,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">14</context>
</context-group>
<target state="translated">%s år sedan</target>
<target state="needs-translation">%s year ago</target>
</trans-unit>
<trans-unit id="3393387677918927062" datatype="html">
<source>%s years ago</source>
@@ -9495,7 +9495,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">15</context>
</context-group>
<target state="translated">%s år sedan</target>
<target state="needs-translation">%s years ago</target>
</trans-unit>
<trans-unit id="3053246523831285824" datatype="html">
<source>%s month ago</source>
@@ -9503,7 +9503,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">19</context>
</context-group>
<target state="translated">%s månad sedan</target>
<target state="needs-translation">%s month ago</target>
</trans-unit>
<trans-unit id="1158628882375251480" datatype="html">
<source>%s months ago</source>
@@ -9511,7 +9511,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">20</context>
</context-group>
<target state="translated">%s månader sedan</target>
<target state="needs-translation">%s months ago</target>
</trans-unit>
<trans-unit id="7039133412826927309" datatype="html">
<source>%s week ago</source>
@@ -9519,7 +9519,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">24</context>
</context-group>
<target state="translated">%s vecka sedan</target>
<target state="needs-translation">%s week ago</target>
</trans-unit>
<trans-unit id="2896962543647781653" datatype="html">
<source>%s weeks ago</source>
@@ -9527,7 +9527,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">25</context>
</context-group>
<target state="translated">%s veckor sedan</target>
<target state="needs-translation">%s weeks ago</target>
</trans-unit>
<trans-unit id="91416192007234700" datatype="html">
<source>%s day ago</source>
@@ -9535,7 +9535,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">29</context>
</context-group>
<target state="translated">%s dag sedan</target>
<target state="needs-translation">%s day ago</target>
</trans-unit>
<trans-unit id="5601594741748068208" datatype="html">
<source>%s days ago</source>
@@ -9543,7 +9543,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">30</context>
</context-group>
<target state="translated">%s dagar sedan</target>
<target state="needs-translation">%s days ago</target>
</trans-unit>
<trans-unit id="8387405724402999437" datatype="html">
<source>%s hour ago</source>
@@ -9551,7 +9551,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">34</context>
</context-group>
<target state="translated">%s timme sedan</target>
<target state="needs-translation">%s hour ago</target>
</trans-unit>
<trans-unit id="2008395012733474465" datatype="html">
<source>%s hours ago</source>
@@ -9559,7 +9559,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">35</context>
</context-group>
<target state="translated">%s timmar sedan</target>
<target state="needs-translation">%s hours ago</target>
</trans-unit>
<trans-unit id="5782387980670840884" datatype="html">
<source>%s minute ago</source>
@@ -9567,7 +9567,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">39</context>
</context-group>
<target state="translated">%s minut sedan</target>
<target state="needs-translation">%s minute ago</target>
</trans-unit>
<trans-unit id="7573942914011074807" datatype="html">
<source>%s minutes ago</source>
@@ -9575,7 +9575,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">40</context>
</context-group>
<target state="translated">%s minuter sedan</target>
<target state="needs-translation">%s minutes ago</target>
</trans-unit>
<trans-unit id="4272436583644511364" datatype="html">
<source>Just now</source>
@@ -9583,7 +9583,7 @@
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
<context context-type="linenumber">72</context>
</context-group>
<target state="translated">Just nu</target>
<target state="needs-translation">Just now</target>
</trans-unit>
<trans-unit id="7536524521722799066" datatype="html" approved="yes">
<source>(no title)</source>
@@ -9923,7 +9923,7 @@
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">166</context>
</context-group>
<target state="translated">Norska</target>
<target state="needs-translation">Norwegian</target>
</trans-unit>
<trans-unit id="792060551707690640" datatype="html" approved="yes">
<source>Polish</source>

View File

@@ -1743,7 +1743,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">385</context>
</context-group>
<target state="translated">表格</target>
<target state="needs-translation">Table</target>
</trans-unit>
<trans-unit id="4236040382842528005" datatype="html">
<source>Small Cards</source>
@@ -2371,7 +2371,7 @@
<context context-type="sourcefile">src/app/components/admin/trash/trash.component.html</context>
<context context-type="linenumber">89</context>
</context-group>
<target state="translated">{VAR_PLURAL, plural, =1 {垃圾桶中有一份文件} other {垃圾桶中共有 <x id="INTERPOLATION"/> 份文件}}</target>
<target state="needs-translation">{VAR_PLURAL, plural, =1 {One document in trash} other {<x id="INTERPOLATION"/> total documents in trash}}</target>
</trans-unit>
<trans-unit id="9021887951960049161" datatype="html">
<source>Confirm delete</source>
@@ -3619,7 +3619,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">79</context>
</context-group>
<target state="translated">真</target>
<target state="needs-translation">True</target>
</trans-unit>
<trans-unit id="3800326155195149498" datatype="html">
<source>False</source>
@@ -3635,7 +3635,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">80</context>
</context-group>
<target state="translated">假</target>
<target state="needs-translation">False</target>
</trans-unit>
<trans-unit id="7551700625201096185" datatype="html">
<source>Search docs...</source>
@@ -3643,7 +3643,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">96</context>
</context-group>
<target state="translated">搜索文档...</target>
<target state="needs-translation">Search docs...</target>
</trans-unit>
<trans-unit id="3184700926171002527" datatype="html">
<source>Any</source>
@@ -3691,7 +3691,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">131</context>
</context-group>
<target state="translated">否</target>
<target state="needs-translation">Not</target>
</trans-unit>
<trans-unit id="6548676277933116532" datatype="html">
<source>Add query</source>
@@ -3699,7 +3699,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">150</context>
</context-group>
<target state="translated">添加查询</target>
<target state="needs-translation">Add query</target>
</trans-unit>
<trans-unit id="5599577087865387184" datatype="html">
<source>Add expression</source>
@@ -3707,7 +3707,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">153</context>
</context-group>
<target state="translated">添加表达式</target>
<target state="needs-translation">Add expression</target>
</trans-unit>
<trans-unit id="6052766076365105714" datatype="html">
<source>now</source>
@@ -4135,7 +4135,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html</context>
<context context-type="linenumber">19</context>
</context-group>
<target state="translated">顺序</target>
<target state="needs-translation">Order</target>
</trans-unit>
<trans-unit id="4816216590591222133" datatype="html">
<source>Enabled</source>
@@ -4519,7 +4519,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">请参阅 &lt;a target='_blank' href='https://docs.paperless-ngx.com/advanced_usage/#file-name-handling'&gt;文档&lt;/a&gt;</target>
<target state="needs-translation">See &lt;a target='_blank' href='https://docs.paperless-ngx.com/advanced_usage/#file-name-handling'&gt;the documentation&lt;/a&gt;.</target>
</trans-unit>
<trans-unit id="1295614462098694869" datatype="html">
<source>Preview</source>
@@ -4539,7 +4539,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
<target state="translated">路径测试失败</target>
<target state="needs-translation">Path test failed</target>
</trans-unit>
<trans-unit id="9116034231465034307" datatype="html">
<source>No document selected</source>
@@ -4547,7 +4547,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context>
<context context-type="linenumber">32</context>
</context-group>
<target state="translated">没有选择文档</target>
<target state="needs-translation">No document selected</target>
</trans-unit>
<trans-unit id="2083498114116917092" datatype="html">
<source>Search for a document</source>
@@ -4555,7 +4555,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context>
<context context-type="linenumber">38</context>
</context-group>
<target state="translated">搜索文档</target>
<target state="needs-translation">Search for a document</target>
</trans-unit>
<trans-unit id="6423278459497515329" datatype="html">
<source>No documents found</source>
@@ -4759,7 +4759,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">33</context>
</context-group>
<target state="translated">添加触发器</target>
<target state="needs-translation">Add Trigger</target>
</trans-unit>
<trans-unit id="6882912390704300247" datatype="html">
<source>Apply Actions:</source>
@@ -4767,7 +4767,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">73</context>
</context-group>
<target state="translated">应用操作:</target>
<target state="needs-translation">Apply Actions:</target>
</trans-unit>
<trans-unit id="51883444329775670" datatype="html">
<source>Add Action</source>
@@ -4775,7 +4775,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">75</context>
</context-group>
<target state="translated">添加动作</target>
<target state="needs-translation">Add Action</target>
</trans-unit>
<trans-unit id="3288318211116868972" datatype="html">
<source>Trigger type</source>
@@ -4783,7 +4783,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">121</context>
</context-group>
<target state="translated">触发类型</target>
<target state="needs-translation">Trigger type</target>
</trans-unit>
<trans-unit id="8727727835543352574" datatype="html">
<source>Trigger for documents that match <x id="START_EMPHASISED_TEXT" ctype="x-em" equiv-text="&lt;em&gt;"/>all<x id="CLOSE_EMPHASISED_TEXT" ctype="x-em" equiv-text="&lt;/em&gt;"/> filters specified below.</source>
@@ -4791,7 +4791,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">122</context>
</context-group>
<target state="translated">触发匹配下面指定的 <x id="START_EMPHASISED_TEXT" ctype="x-em" equiv-text="&lt;em&gt;"/>所有<x id="CLOSE_EMPHASISED_TEXT" ctype="x-em" equiv-text="&lt;/em&gt;"/> 过滤器的文档。</target>
<target state="needs-translation">Trigger for documents that match <x id="START_EMPHASISED_TEXT" ctype="x-em" equiv-text="&lt;em&gt;"/>all<x id="CLOSE_EMPHASISED_TEXT" ctype="x-em" equiv-text="&lt;/em&gt;"/> filters specified below.</target>
</trans-unit>
<trans-unit id="7467799586957602479" datatype="html">
<source>Filter filename</source>
@@ -4831,7 +4831,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">128</context>
</context-group>
<target state="translated">应用于匹配此路径的文档。允许指定为*的通配符。不区分大小写。&lt;/a&gt;</target>
<target state="needs-translation">Apply to documents that match this path. Wildcards specified as * are allowed. Case-normalized.&lt;/a&gt;</target>
</trans-unit>
<trans-unit id="7468453896129193641" datatype="html">
<source>Filter mail rule</source>
@@ -4847,7 +4847,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">129</context>
</context-group>
<target state="translated">应用于通过此邮件规则消费的文档。</target>
<target state="needs-translation">Apply to documents consumed via this mail rule.</target>
</trans-unit>
<trans-unit id="6840369584127435743" datatype="html">
<source>Content matching algorithm</source>
@@ -4855,7 +4855,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">132</context>
</context-group>
<target state="translated">内容匹配算法</target>
<target state="needs-translation">Content matching algorithm</target>
</trans-unit>
<trans-unit id="510635115034690805" datatype="html">
<source>Content matching pattern</source>
@@ -4863,7 +4863,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">134</context>
</context-group>
<target state="translated">内容匹配模式</target>
<target state="needs-translation">Content matching pattern</target>
</trans-unit>
<trans-unit id="3484236514968690689" datatype="html">
<source>Has any of tags</source>
@@ -4871,7 +4871,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">143</context>
</context-group>
<target state="translated">有任何标签</target>
<target state="needs-translation">Has any of tags</target>
</trans-unit>
<trans-unit id="5281365940563983618" datatype="html">
<source>Has correspondent</source>
@@ -4879,7 +4879,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">144</context>
</context-group>
<target state="translated">已有联系人</target>
<target state="needs-translation">Has correspondent</target>
</trans-unit>
<trans-unit id="4806713133917046341" datatype="html">
<source>Has document type</source>
@@ -4887,7 +4887,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">145</context>
</context-group>
<target state="translated">有文档类型</target>
<target state="needs-translation">Has document type</target>
</trans-unit>
<trans-unit id="6417103744331194518" datatype="html">
<source>Action type</source>
@@ -4895,7 +4895,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">155</context>
</context-group>
<target state="translated">操作类型</target>
<target state="needs-translation">Action type</target>
</trans-unit>
<trans-unit id="6019822389883736115" datatype="html">
<source>Assign title</source>
@@ -4911,7 +4911,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">160</context>
</context-group>
<target state="translated">可以包含一些占位符,请参阅 &lt;a target='_blank' href='https://docs.paperless-ngx.com/usage/#workflows'&gt;文档&lt;/a&gt;</target>
<target state="needs-translation">Can include some placeholders, see &lt;a target='_blank' href='https://docs.paperless-ngx.com/usage/#workflows'&gt;documentation&lt;/a&gt;.</target>
</trans-unit>
<trans-unit id="6528897010417701530" datatype="html">
<source>Assign tags</source>
@@ -4967,7 +4967,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">216</context>
</context-group>
<target state="translated">移除标签</target>
<target state="needs-translation">Remove tags</target>
</trans-unit>
<trans-unit id="7890599006071681081" datatype="html">
<source>Remove all</source>
@@ -4999,7 +4999,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">254</context>
</context-group>
<target state="translated">移除全部</target>
<target state="needs-translation">Remove all</target>
</trans-unit>
<trans-unit id="8636414563726517994" datatype="html">
<source>Remove correspondents</source>
@@ -5007,7 +5007,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">222</context>
</context-group>
<target state="translated">移除所有通讯员</target>
<target state="needs-translation">Remove correspondents</target>
</trans-unit>
<trans-unit id="5305293055593064952" datatype="html">
<source>Remove document types</source>
@@ -5015,7 +5015,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">228</context>
</context-group>
<target state="translated">移除所有文档类型</target>
<target state="needs-translation">Remove document types</target>
</trans-unit>
<trans-unit id="2400388879708187" datatype="html">
<source>Remove storage paths</source>

View File

@@ -8,7 +8,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">關閉</target>
<target state="needs-translation">Close</target>
</trans-unit>
<trans-unit id="ngb.timepicker.HH" datatype="html">
<source>HH</source>
@@ -16,7 +16,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">HH</target>
<target state="needs-translation">HH</target>
</trans-unit>
<trans-unit id="ngb.toast.close-aria" datatype="html">
<source>Close</source>
@@ -24,7 +24,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">關閉</target>
<target state="needs-translation">Close</target>
</trans-unit>
<trans-unit id="ngb.datepicker.select-month" datatype="html">
<source>Select month</source>
@@ -36,7 +36,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">選取月份</target>
<target state="needs-translation">Select month</target>
</trans-unit>
<trans-unit id="ngb.pagination.first" datatype="html">
<source>««</source>
@@ -56,7 +56,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">上個月</target>
<target state="needs-translation">Previous month</target>
</trans-unit>
<trans-unit id="ngb.progressbar.value" datatype="html">
<source>
@@ -82,7 +82,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">小時</target>
<target state="needs-translation">Hours</target>
</trans-unit>
<trans-unit id="ngb.pagination.previous" datatype="html">
<source>«</source>
@@ -98,7 +98,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">上一個</target>
<target state="needs-translation">Previous</target>
</trans-unit>
<trans-unit id="ngb.timepicker.MM" datatype="html">
<source>MM</source>
@@ -106,7 +106,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">MM</target>
<target state="needs-translation">MM</target>
</trans-unit>
<trans-unit id="ngb.pagination.next" datatype="html">
<source>»</source>
@@ -126,7 +126,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">選擇年份</target>
<target state="needs-translation">Select year</target>
</trans-unit>
<trans-unit id="ngb.datepicker.next-month" datatype="html">
<source>Next month</source>
@@ -138,7 +138,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">下一個月</target>
<target state="needs-translation">Next month</target>
</trans-unit>
<trans-unit id="ngb.carousel.next" datatype="html">
<source>Next</source>
@@ -146,7 +146,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">下一個</target>
<target state="needs-translation">Next</target>
</trans-unit>
<trans-unit id="ngb.timepicker.minutes" datatype="html">
<source>Minutes</source>
@@ -154,7 +154,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">分鐘</target>
<target state="needs-translation">Minutes</target>
</trans-unit>
<trans-unit id="ngb.pagination.last" datatype="html">
<source>»»</source>
@@ -170,7 +170,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">第一頁</target>
<target state="needs-translation">First</target>
</trans-unit>
<trans-unit id="ngb.timepicker.increment-hours" datatype="html">
<source>Increment hours</source>
@@ -186,7 +186,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">上一頁</target>
<target state="needs-translation">Previous</target>
</trans-unit>
<trans-unit id="ngb.timepicker.decrement-hours" datatype="html">
<source>Decrement hours</source>
@@ -202,7 +202,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">下一頁</target>
<target state="needs-translation">Next</target>
</trans-unit>
<trans-unit id="ngb.timepicker.increment-minutes" datatype="html">
<source>Increment minutes</source>
@@ -218,7 +218,7 @@
<context context-type="sourcefile">node_modules/src/ngb-config.ts</context>
<context context-type="linenumber">13</context>
</context-group>
<target state="translated">最後一頁</target>
<target state="needs-translation">Last</target>
</trans-unit>
<trans-unit id="ngb.timepicker.decrement-minutes" datatype="html">
<source>Decrement minutes</source>

View File

@@ -490,23 +490,9 @@ ul.pagination {
.doc-img-container {
border: none !important;
border-top-left-radius: .25rem;
border-top-right-radius: .25rem;
overflow: hidden;
.doc-img {
overflow: visible;
}
}
.card-selected {
border-color:var(--bs-primary);
.document-card-check {
display: block;
}
.doc-img-container {
background-color: var(--pngx-primary-faded);
}
}
table.table {
@@ -719,8 +705,6 @@ i-bs svg {
vertical-align: text-bottom;
}
.document-card {
.card-footer i-bs svg {
vertical-align: middle;
}
.document-card .card-footer i-bs svg {
vertical-align: middle;
}

View File

@@ -183,7 +183,7 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
}
.doc-img {
mix-blend-mode: normal;
mix-blend-mode: normal !important;
border-radius: 0;
border-color: var(--bs-border-color);
filter: invert(10%);
@@ -201,24 +201,6 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
mix-blend-mode: luminosity;
}
@supports (hanging-punctuation: first) and (font: -apple-system-body) and (-webkit-appearance: none) {
// Safari does not like the filters on the image, see https://github.com/paperless-ngx/paperless-ngx/pull/8121
.doc-img-container {
background-color: #ffffff;
}
.doc-img {
filter: none !important;
box-shadow: inset 0px 0px 0px 10px rgba(0,0,0,1);
}
.doc-img.inverted {
filter: none !important;
mix-blend-mode: difference;
opacity: 0.95;
}
}
.paperless-input-select .ng-select .ng-dropdown-panel .ng-dropdown-panel-items .ng-option:not(.ng-option-selected):hover,
.paperless-input-select .ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked {
background-color: var(--bs-light) !important;

View File

@@ -148,6 +148,17 @@ class ConsumerPlugin(
):
self._send_progress(100, 100, ProgressStatusOptions.FAILED, message)
self.log.error(log_message or message, exc_info=exc_info)
# Move the file to the failed directory
if (
self.input_doc.original_file.exists()
and not Path(
settings.CONSUMPTION_FAILED_DIR / self.input_doc.original_file.name,
).exists()
):
copy_file_with_basic_stats(
self.input_doc.original_file,
settings.CONSUMPTION_FAILED_DIR / self.input_doc.original_file.name,
)
raise ConsumerError(f"{self.filename}: {log_message or message}") from exception
def pre_check_file_exists(self):
@@ -411,7 +422,6 @@ class ConsumerPlugin(
self.unmodified_original = (
Path(tempdir.name) / Path("uo") / Path(self.filename)
)
self.unmodified_original.parent.mkdir(exist_ok=True)
copy_file_with_basic_stats(
self.input_doc.original_file,
self.unmodified_original,

View File

@@ -424,28 +424,20 @@ class CustomFieldQueryParser:
value_field_name = "value_monetary_amount"
has_field = Q(custom_fields__field=custom_field)
# We need to use an annotation here because different atoms
# might be referring to different instances of custom fields.
annotation_name = f"_custom_field_filter_{len(self._annotations)}"
# Our special exists operator.
if op == "exists":
annotation = Count("custom_fields", filter=has_field)
# A Document should have > 0 match if it has this field, or 0 if doesn't.
query_op = "gt" if value else "exact"
query = Q(**{f"{annotation_name}__{query_op}": 0})
field_filter = has_field if value else ~has_field
else:
# Check if 1) custom field name matches, and 2) value satisfies condition
field_filter = has_field & Q(
**{f"custom_fields__{value_field_name}__{op}": value},
)
# Annotate how many matching custom fields each document has
annotation = Count("custom_fields", filter=field_filter)
# Filter document by count
query = Q(**{f"{annotation_name}__gt": 0})
self._annotations[annotation_name] = annotation
return query
# We need to use an annotation here because different atoms
# might be referring to different instances of custom fields.
annotation_name = f"_custom_field_filter_{len(self._annotations)}"
self._annotations[annotation_name] = Count("custom_fields", filter=field_filter)
return Q(**{f"{annotation_name}__gt": 0})
@handle_validation_prefix
def _get_custom_field(self, id_or_name):

View File

@@ -679,24 +679,28 @@ class PaperlessTask(models.Model):
verbose_name=_("Task State"),
help_text=_("Current state of the task being run"),
)
date_created = models.DateTimeField(
null=True,
default=timezone.now,
verbose_name=_("Created DateTime"),
help_text=_("Datetime field when the task result was created in UTC"),
)
date_started = models.DateTimeField(
null=True,
default=None,
verbose_name=_("Started DateTime"),
help_text=_("Datetime field when the task was started in UTC"),
)
date_done = models.DateTimeField(
null=True,
default=None,
verbose_name=_("Completed DateTime"),
help_text=_("Datetime field when the task was completed in UTC"),
)
result = models.TextField(
null=True,
default=None,

View File

@@ -1585,6 +1585,14 @@ class TasksViewSerializer(serializers.ModelSerializer):
return result
class RetryTaskSerializer(serializers.Serializer):
clean = serializers.BooleanField(
default=False,
write_only=True,
required=False,
)
class AcknowledgeTasksViewSerializer(serializers.Serializer):
tasks = serializers.ListField(
required=True,

View File

@@ -1,6 +1,7 @@
import logging
import os
import shutil
from pathlib import Path
from celery import states
from celery.signals import before_task_publish
@@ -520,6 +521,19 @@ def update_filename_and_move_files(
)
@receiver(models.signals.post_save, sender=PaperlessTask)
def cleanup_failed_documents(sender, instance: PaperlessTask, **kwargs):
if instance.status != states.FAILURE or not instance.acknowledged:
return
if instance.task_file_name:
try:
Path(settings.CONSUMPTION_FAILED_DIR / instance.task_file_name).unlink()
logger.debug(f"Cleaned up failed file {instance.task_file_name}")
except FileNotFoundError:
logger.warning(f"Failed to clean up failed file {instance.task_file_name}")
def set_log_entry(sender, document: Document, logging_group=None, **kwargs):
ct = ContentType.objects.get(model="document")
user = User.objects.get(username="consumer")

View File

@@ -9,6 +9,7 @@ from tempfile import TemporaryDirectory
import tqdm
from celery import Task
from celery import shared_task
from celery import states
from django.conf import settings
from django.db import models
from django.db import transaction
@@ -27,12 +28,14 @@ from documents.consumer import ConsumerPlugin
from documents.consumer import WorkflowTriggerPlugin
from documents.data_models import ConsumableDocument
from documents.data_models import DocumentMetadataOverrides
from documents.data_models import DocumentSource
from documents.double_sided import CollatePlugin
from documents.file_handling import create_source_path_directory
from documents.file_handling import generate_unique_filename
from documents.models import Correspondent
from documents.models import Document
from documents.models import DocumentType
from documents.models import PaperlessTask
from documents.models import StoragePath
from documents.models import Tag
from documents.parsers import DocumentParser
@@ -44,6 +47,8 @@ from documents.plugins.helpers import ProgressStatusOptions
from documents.sanity_checker import SanityCheckFailedException
from documents.signals import document_updated
from documents.signals.handlers import cleanup_document_deletion
from documents.utils import copy_file_with_basic_stats
from documents.utils import run_subprocess
if settings.AUDIT_LOG_ENABLED:
from auditlog.models import LogEntry
@@ -169,6 +174,48 @@ def consume_file(
return msg
@shared_task
def retry_failed_file(task_id: str, clean: bool = False, skip_ocr: bool = False):
task = PaperlessTask.objects.get(task_id=task_id, status=states.FAILURE)
if task:
failed_file = settings.CONSUMPTION_FAILED_DIR / task.task_file_name
if not failed_file.exists():
logger.error(f"File {failed_file} not found")
raise FileNotFoundError(f"File {failed_file} not found")
working_copy = settings.SCRATCH_DIR / failed_file.name
copy_file_with_basic_stats(failed_file, working_copy)
if clean:
try:
result = run_subprocess(
[
"qpdf",
"--replace-input",
"--warning-exit-0",
working_copy,
],
logger=logger,
)
if result.returncode != 0:
raise Exception(
f"qpdf failed with exit code {result.returncode}, error: {result.stderr}",
)
else:
logger.debug("PDF cleaned successfully")
except Exception as e:
logger.error(f"Error while cleaning PDF: {e}")
raise e
task = consume_file.delay(
ConsumableDocument(
source=DocumentSource.ConsumeFolder,
original_file=working_copy,
),
)
return task.id
@shared_task
def sanity_check():
messages = sanity_checker.check_sanity()

Binary file not shown.

View File

@@ -289,12 +289,6 @@ class TestCustomFieldsSearch(DirectoriesMixin, APITestCase):
lambda document: "string_field" in document,
)
def test_exists_false(self):
self._assert_query_match_predicate(
["string_field", "exists", False],
lambda document: "string_field" not in document,
)
def test_select(self):
# For select fields, you can either specify the index
# or the name of the option. They function exactly the same.

View File

@@ -1,5 +1,8 @@
import os
import shutil
import uuid
from datetime import timedelta
from pathlib import Path
from unittest import mock
from django.conf import settings
@@ -7,15 +10,22 @@ from django.test import TestCase
from django.utils import timezone
from documents import tasks
from documents.data_models import ConsumableDocument
from documents.data_models import DocumentSource
from documents.models import Correspondent
from documents.models import Document
from documents.models import DocumentType
from documents.models import PaperlessTask
from documents.models import Tag
from documents.sanity_checker import SanityCheckFailedException
from documents.sanity_checker import SanityCheckMessages
from documents.signals.handlers import before_task_publish_handler
from documents.signals.handlers import task_failure_handler
from documents.tests.test_classifier import dummy_preprocess
from documents.tests.utils import DirectoriesMixin
from documents.tests.utils import DummyProgressManager
from documents.tests.utils import FileSystemAssertsMixin
from documents.tests.utils import SampleDirMixin
class TestIndexReindex(DirectoriesMixin, TestCase):
@@ -184,3 +194,68 @@ class TestEmptyTrashTask(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
tasks.empty_trash()
self.assertEqual(Document.global_objects.count(), 0)
class TestRetryConsumeTask(
DirectoriesMixin,
SampleDirMixin,
FileSystemAssertsMixin,
TestCase,
):
def do_failed_task(self, test_file: Path) -> PaperlessTask:
temp_copy = self.dirs.scratch_dir / test_file.name
shutil.copy(test_file, temp_copy)
headers = {
"id": str(uuid.uuid4()),
"task": "documents.tasks.consume_file",
}
body = (
# args
(
ConsumableDocument(
source=DocumentSource.ConsumeFolder,
original_file=str(temp_copy),
),
None,
),
# kwargs
{},
# celery stuff
{"callbacks": None, "errbacks": None, "chain": None, "chord": None},
)
before_task_publish_handler(headers=headers, body=body)
with mock.patch("documents.tasks.ProgressManager", DummyProgressManager):
with self.assertRaises(Exception):
tasks.consume_file(
ConsumableDocument(
source=DocumentSource.ConsumeFolder,
original_file=temp_copy,
),
)
task_failure_handler(
task_id=headers["id"],
exception="Example failure",
)
task = PaperlessTask.objects.first()
# Ensure the file is moved to the failed dir
self.assertIsFile(settings.CONSUMPTION_FAILED_DIR / task.task_file_name)
return task
@mock.patch("documents.tasks.consume_file.delay")
@mock.patch("documents.tasks.run_subprocess")
def test_retry_consume_clean(self, m_subprocess, m_consume_file):
task = self.do_failed_task(self.SAMPLE_DIR / "corrupted.pdf")
m_subprocess.return_value.returncode = 0
task_id = tasks.retry_failed_file(task_id=task.task_id, clean=True)
self.assertIsNotNone(task_id)
m_consume_file.assert_called_once()
def test_cleanup(self):
task = self.do_failed_task(self.SAMPLE_DIR / "corrupted.pdf")
task.acknowledged = True
task.save() # simulate the task being acknowledged
self.assertIsNotFile(settings.CONSUMPTION_FAILED_DIR / task.task_file_name)

View File

@@ -35,6 +35,7 @@ def setup_directories():
dirs.scratch_dir = Path(tempfile.mkdtemp())
dirs.media_dir = Path(tempfile.mkdtemp())
dirs.consumption_dir = Path(tempfile.mkdtemp())
dirs.consumption_failed_dir = Path(tempfile.mkdtemp("failed"))
dirs.static_dir = Path(tempfile.mkdtemp())
dirs.index_dir = dirs.data_dir / "index"
dirs.originals_dir = dirs.media_dir / "documents" / "originals"
@@ -56,6 +57,7 @@ def setup_directories():
THUMBNAIL_DIR=dirs.thumbnail_dir,
ARCHIVE_DIR=dirs.archive_dir,
CONSUMPTION_DIR=dirs.consumption_dir,
CONSUMPTION_FAILED_DIR=dirs.consumption_failed_dir,
LOGGING_DIR=dirs.logging_dir,
INDEX_DIR=dirs.index_dir,
STATIC_ROOT=dirs.static_dir,
@@ -72,6 +74,7 @@ def remove_dirs(dirs):
shutil.rmtree(dirs.data_dir, ignore_errors=True)
shutil.rmtree(dirs.scratch_dir, ignore_errors=True)
shutil.rmtree(dirs.consumption_dir, ignore_errors=True)
shutil.rmtree(dirs.consumption_failed_dir, ignore_errors=True)
shutil.rmtree(dirs.static_dir, ignore_errors=True)
dirs.settings_override.disable()

View File

@@ -136,6 +136,7 @@ from documents.serialisers import DocumentListSerializer
from documents.serialisers import DocumentSerializer
from documents.serialisers import DocumentTypeSerializer
from documents.serialisers import PostDocumentSerializer
from documents.serialisers import RetryTaskSerializer
from documents.serialisers import SavedViewSerializer
from documents.serialisers import SearchResultSerializer
from documents.serialisers import ShareLinkSerializer
@@ -152,6 +153,7 @@ from documents.serialisers import WorkflowTriggerSerializer
from documents.signals import document_updated
from documents.tasks import consume_file
from documents.tasks import empty_trash
from documents.tasks import retry_failed_file
from documents.templating.filepath import validate_filepath_template_and_render
from paperless import version
from paperless.celery import app as celery_app
@@ -1718,6 +1720,25 @@ class TasksViewSet(ReadOnlyModelViewSet):
queryset = PaperlessTask.objects.filter(task_id=task_id)
return queryset
@action(methods=["post"], detail=True)
def retry(self, request, pk=None):
task = self.get_object()
serializer = RetryTaskSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
clean = serializer.validated_data.get("clean")
try:
new_task_id = retry_failed_file(task.task_id, clean)
return Response({"task_id": new_task_id})
except FileNotFoundError:
return HttpResponseBadRequest("Original file not found")
except Exception as e:
logger.warning(f"An error occurred retrying task: {e!s}")
return HttpResponseBadRequest(
"Error retrying task, check logs for more detail.",
)
class AcknowledgeTasksView(GenericAPIView):
permission_classes = (IsAuthenticated,)

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-19 22:56-0700\n"
"PO-Revision-Date: 2024-11-02 00:29\n"
"PO-Revision-Date: 2024-10-20 12:11\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -47,7 +47,7 @@ msgstr "{name!r} ist kein gültiges benutzerdefiniertes Feld."
#: documents/filters.py:492
msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} unterstützt den Abfrageausdruck {expr!r} nicht."
msgstr "{data_type} unterstützt Abfrage expr {expr!r} nicht."
#: documents/filters.py:600
msgid "Maximum nesting depth exceeded."
@@ -1547,11 +1547,11 @@ msgstr "IMAP"
#: paperless_mail/models.py:20
msgid "Gmail OAuth"
msgstr "Gmail-OAuth"
msgstr "Gmail OAuth"
#: paperless_mail/models.py:21
msgid "Outlook OAuth"
msgstr "Outlook-OAuth"
msgstr "Outlook OAuh"
#: paperless_mail/models.py:25
msgid "IMAP server"
@@ -1599,7 +1599,7 @@ msgstr "Aktualisierungstoken"
#: paperless_mail/models.py:71
msgid "The refresh token to use for token authentication e.g. with oauth2."
msgstr "Das Aktualisierungstoken, das für die Tokenauthentifizierung (z.B. mit OAuth2) verwendet werden soll."
msgstr "Das Aktualisierungstoken für die Tokenauthentifizierung z.B. mit OAuth2."
#: paperless_mail/models.py:80
msgid "The expiration date of the refresh token. "

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-19 22:56-0700\n"
"PO-Revision-Date: 2024-11-03 04:57\n"
"PO-Revision-Date: 2024-10-20 05:58\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
@@ -23,35 +23,35 @@ msgstr "Documentos"
#: documents/filters.py:334
msgid "Value must be valid JSON."
msgstr "El valor debe ser JSON válido."
msgstr ""
#: documents/filters.py:353
msgid "Invalid custom field query expression"
msgstr "Expresión de consulta de campo personalizado no válida"
msgstr ""
#: documents/filters.py:363
msgid "Invalid expression list. Must be nonempty."
msgstr "Lista de expresiones no válida. Debe ser no vacía."
msgstr ""
#: documents/filters.py:384
msgid "Invalid logical operator {op!r}"
msgstr "Operador lógico inválido {op!r}"
msgstr ""
#: documents/filters.py:398
msgid "Maximum number of query conditions exceeded."
msgstr "Se ha superado el número máximo de condiciones de consulta."
msgstr ""
#: documents/filters.py:455
msgid "{name!r} is not a valid custom field."
msgstr "{nombre!r} no es un campo personalizado válido."
msgstr ""
#: documents/filters.py:492
msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} no admite la consulta expr {expr!r}."
msgstr ""
#: documents/filters.py:600
msgid "Maximum nesting depth exceeded."
msgstr "Profundidad máxima de nidificación superada."
msgstr ""
#: documents/models.py:41 documents/models.py:802
msgid "owner"
@@ -192,11 +192,11 @@ msgstr "La cadena de verificación del documento archivado."
#: documents/models.py:211
msgid "page count"
msgstr "número de páginas"
msgstr ""
#: documents/models.py:218
msgid "The number of pages of the document."
msgstr "El número de páginas del documento."
msgstr ""
#: documents/models.py:222 documents/models.py:402 documents/models.py:722
#: documents/models.py:760 documents/models.py:831 documents/models.py:889
@@ -353,7 +353,7 @@ msgstr "ASN"
#: documents/models.py:431
msgid "Pages"
msgstr "Páginas"
msgstr ""
#: documents/models.py:437
msgid "show on dashboard"
@@ -561,7 +561,7 @@ msgstr "no tiene campo personalizado"
#: documents/models.py:525
msgid "custom fields query"
msgstr "consulta de campos personalizados"
msgstr ""
#: documents/models.py:535
msgid "rule type"
@@ -1543,7 +1543,7 @@ msgstr "Usar STARTTLS"
#: paperless_mail/models.py:19
msgid "IMAP"
msgstr "IMAP"
msgstr ""
#: paperless_mail/models.py:20
msgid "Gmail OAuth"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-19 22:56-0700\n"
"PO-Revision-Date: 2024-11-03 00:33\n"
"PO-Revision-Date: 2024-10-29 18:05\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"Language: it_IT\n"
@@ -23,11 +23,11 @@ msgstr "Documenti"
#: documents/filters.py:334
msgid "Value must be valid JSON."
msgstr "Il valore deve essere un JSON valido."
msgstr ""
#: documents/filters.py:353
msgid "Invalid custom field query expression"
msgstr "Espressione della query del campo personalizzato non valida"
msgstr ""
#: documents/filters.py:363
msgid "Invalid expression list. Must be nonempty."
@@ -35,7 +35,7 @@ msgstr ""
#: documents/filters.py:384
msgid "Invalid logical operator {op!r}"
msgstr "Operatore logico non valido {op!r}"
msgstr ""
#: documents/filters.py:398
msgid "Maximum number of query conditions exceeded."
@@ -43,7 +43,7 @@ msgstr ""
#: documents/filters.py:455
msgid "{name!r} is not a valid custom field."
msgstr "{name!r} non è un campo personalizzato valido."
msgstr ""
#: documents/filters.py:492
msgid "{data_type} does not support query expr {expr!r}."

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-19 22:56-0700\n"
"PO-Revision-Date: 2024-11-02 00:29\n"
"PO-Revision-Date: 2024-10-25 12:12\n"
"Last-Translator: \n"
"Language-Team: Swedish\n"
"Language: sv_SE\n"
@@ -1015,7 +1015,7 @@ msgstr "Vänligen logga in."
#: documents/templates/account/login.html:12
#, python-format
msgid "Don't have an account yet? <a href=\"%(signup_url)s\">Sign up</a>"
msgstr "Har du inget konto än? <a href=\"%(signup_url)s\">Registrera dig</a>"
msgstr ""
#: documents/templates/account/login.html:19
#: documents/templates/account/signup.html:15
@@ -1117,7 +1117,7 @@ msgstr ""
#: documents/templates/account/signup.html:10
#, python-format
msgid "Already have an account? <a href=\"%(login_url)s\">Sign in</a>"
msgstr "Har du redan ett konto? <a href=\"%(login_url)s\">Logga in</a>"
msgstr ""
#: documents/templates/account/signup.html:16
#: documents/templates/socialaccount/signup.html:14
@@ -1131,7 +1131,7 @@ msgstr ""
#: documents/templates/account/signup.html:36
#: documents/templates/socialaccount/signup.html:27
msgid "Sign up"
msgstr "Registrera dig"
msgstr ""
#: documents/templates/index.html:61
msgid "Paperless-ngx is loading..."
@@ -1170,7 +1170,7 @@ msgstr ""
#: documents/templates/socialaccount/login.html:13
msgid "Continue"
msgstr "Fortsätt"
msgstr ""
#: documents/templates/socialaccount/signup.html:5
msgid "Paperless-ngx social account sign up"
@@ -1366,7 +1366,7 @@ msgstr "Belarusiska"
#: paperless/settings.py:688
msgid "Bulgarian"
msgstr "Bulgariska"
msgstr ""
#: paperless/settings.py:689
msgid "Catalan"
@@ -1386,7 +1386,7 @@ msgstr "Tyska"
#: paperless/settings.py:693
msgid "Greek"
msgstr "Grekiska"
msgstr ""
#: paperless/settings.py:694
msgid "English (GB)"
@@ -1414,7 +1414,7 @@ msgstr "Italienska"
#: paperless/settings.py:700
msgid "Japanese"
msgstr "Japanska"
msgstr ""
#: paperless/settings.py:701
msgid "Korean"
@@ -1426,7 +1426,7 @@ msgstr "Luxemburgiska"
#: paperless/settings.py:703
msgid "Norwegian"
msgstr "Norska"
msgstr ""
#: paperless/settings.py:704
msgid "Dutch"
@@ -1542,7 +1542,7 @@ msgstr "Använd STARTTLS"
#: paperless_mail/models.py:19
msgid "IMAP"
msgstr "IMAP"
msgstr ""
#: paperless_mail/models.py:20
msgid "Gmail OAuth"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-19 22:56-0700\n"
"PO-Revision-Date: 2024-10-31 12:12\n"
"PO-Revision-Date: 2024-10-20 05:58\n"
"Last-Translator: \n"
"Language-Team: Thai\n"
"Language: th_TH\n"
@@ -23,35 +23,35 @@ msgstr "เอกสาร"
#: documents/filters.py:334
msgid "Value must be valid JSON."
msgstr "ค่า ต้องอยู่ในรูปแบบ JSON ที่ถูกต้อง"
msgstr ""
#: documents/filters.py:353
msgid "Invalid custom field query expression"
msgstr "รูปแบบการค้นหาฟิลด์ที่กำหนดเองไม่ถูกต้อง"
msgstr ""
#: documents/filters.py:363
msgid "Invalid expression list. Must be nonempty."
msgstr "รายการคำสั่งไม่ถูกต้อง ต้องไม่เว้นว่าง"
msgstr ""
#: documents/filters.py:384
msgid "Invalid logical operator {op!r}"
msgstr "ตัวดำเนินการเชิงตรรกะ {op!r} ไม่ถูกต้อง"
msgstr ""
#: documents/filters.py:398
msgid "Maximum number of query conditions exceeded."
msgstr "จำนวนเงื่อนไขในการค้นหาเกินกำหนด"
msgstr ""
#: documents/filters.py:455
msgid "{name!r} is not a valid custom field."
msgstr "{name!r} ไม่ใช่ฟิลด์ที่กำหนดเองที่ถูกต้อง"
msgstr ""
#: documents/filters.py:492
msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} ไม่รองรับรูปแบบการค้นหา {expr!r}"
msgstr ""
#: documents/filters.py:600
msgid "Maximum nesting depth exceeded."
msgstr "จำนวนการซ้อนเงื่อนไขสูงสุดเกินขีดจำกัด"
msgstr ""
#: documents/models.py:41 documents/models.py:802
msgid "owner"
@@ -192,11 +192,11 @@ msgstr "ค่า checksum ของเอกสารประเภทเก
#: documents/models.py:211
msgid "page count"
msgstr "จำนวนหน้า"
msgstr ""
#: documents/models.py:218
msgid "The number of pages of the document."
msgstr "จำนวนหน้าทั้งหมดของเอกสาร"
msgstr ""
#: documents/models.py:222 documents/models.py:402 documents/models.py:722
#: documents/models.py:760 documents/models.py:831 documents/models.py:889
@@ -301,11 +301,11 @@ msgstr "ตาราง"
#: documents/models.py:416
msgid "Small Cards"
msgstr "การ์ดขนาดเล็ก"
msgstr ""
#: documents/models.py:417
msgid "Large Cards"
msgstr "การ์ดขนาดใหญ่"
msgstr ""
#: documents/models.py:420
msgid "Title"
@@ -313,11 +313,11 @@ msgstr ""
#: documents/models.py:421
msgid "Created"
msgstr "วันที่สร้าง"
msgstr ""
#: documents/models.py:422
msgid "Added"
msgstr "วันที่เพิ่ม"
msgstr ""
#: documents/models.py:423
msgid "Tags"
@@ -333,7 +333,7 @@ msgstr "ประเภทเอกสาร"
#: documents/models.py:426
msgid "Storage Path"
msgstr "ตำแหน่งจัดเก็บ"
msgstr ""
#: documents/models.py:427
msgid "Note"
@@ -345,7 +345,7 @@ msgstr "เจ้าของ"
#: documents/models.py:429
msgid "Shared"
msgstr "แชร์แล้ว"
msgstr ""
#: documents/models.py:430
msgid "ASN"
@@ -353,7 +353,7 @@ msgstr ""
#: documents/models.py:431
msgid "Pages"
msgstr "หน้า"
msgstr ""
#: documents/models.py:437
msgid "show on dashboard"
@@ -373,15 +373,15 @@ msgstr "เรียงย้อนกลับ"
#: documents/models.py:452
msgid "View page size"
msgstr "ขนาดการแสดงผลหน้า"
msgstr ""
#: documents/models.py:460
msgid "View display mode"
msgstr "โหมดการแสดงผล"
msgstr ""
#: documents/models.py:467
msgid "Document display fields"
msgstr "ฟิลด์การแสดงผลของเอกสาร"
msgstr ""
#: documents/models.py:474 documents/models.py:532
msgid "saved view"
@@ -537,31 +537,31 @@ msgstr "ไม่มีเจ้าของเป็น"
#: documents/models.py:519
msgid "has custom field value"
msgstr "มีค่าฟิลด์ที่กำหนดเอง"
msgstr ""
#: documents/models.py:520
msgid "is shared by me"
msgstr "แชร์โดยฉัน"
msgstr ""
#: documents/models.py:521
msgid "has custom fields"
msgstr "มีฟิลด์ที่กำหนดเอง"
msgstr ""
#: documents/models.py:522
msgid "has custom field in"
msgstr "มีฟิลด์ที่กำหนดเองใน"
msgstr ""
#: documents/models.py:523
msgid "does not have custom field in"
msgstr "ไม่มีฟิลด์ที่กำหนดเอง"
msgstr ""
#: documents/models.py:524
msgid "does not have custom field"
msgstr "ไม่มีฟิลด์ที่กำหนดเองใน"
msgstr ""
#: documents/models.py:525
msgid "custom fields query"
msgstr "การค้นหาฟิลด์ที่กำหนดเอง"
msgstr ""
#: documents/models.py:535
msgid "rule type"
@@ -717,11 +717,11 @@ msgstr "Float"
#: documents/models.py:826
msgid "Monetary"
msgstr "จำนวนเงิน"
msgstr ""
#: documents/models.py:827
msgid "Document Link"
msgstr "ลิงก์ของเอกสาร"
msgstr ""
#: documents/models.py:828
msgid "Select"
@@ -733,23 +733,23 @@ msgstr "ชนิดข้อมูล"
#: documents/models.py:847
msgid "extra data"
msgstr "ข้อมูลเพิ่มเติม"
msgstr ""
#: documents/models.py:851
msgid "Extra data for the custom field, such as select options"
msgstr "ข้อมูลเพิ่มเติมสำหรับฟิลด์ที่กำหนดเอง เช่น ตัวเลือก"
msgstr ""
#: documents/models.py:857
msgid "custom field"
msgstr "ฟิลด์ที่กำหนดเอง"
msgstr ""
#: documents/models.py:858
msgid "custom fields"
msgstr "ฟิลด์ที่กำหนดเอง"
msgstr ""
#: documents/models.py:955
msgid "custom field instance"
msgstr "ชุดข้อมูลฟิลด์ที่กำหนดเอง"
msgstr ""
#: documents/models.py:956
msgid "custom field instances"
@@ -757,7 +757,7 @@ msgstr ""
#: documents/models.py:1017
msgid "Consumption Started"
msgstr "เริ่มใช้งาน"
msgstr ""
#: documents/models.py:1018
msgid "Document Added"
@@ -769,7 +769,7 @@ msgstr "ปรับปรุงเอกสารแล้ว"
#: documents/models.py:1022
msgid "Consume Folder"
msgstr "โฟลเดอร์ใช้งาน"
msgstr ""
#: documents/models.py:1023
msgid "Api Upload"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-19 22:56-0700\n"
"PO-Revision-Date: 2024-11-03 04:57\n"
"PO-Revision-Date: 2024-10-29 12:12\n"
"Last-Translator: \n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -1603,7 +1603,7 @@ msgstr ""
#: paperless_mail/models.py:80
msgid "The expiration date of the refresh token. "
msgstr "刷新令牌的到期日期。 "
msgstr ""
#: paperless_mail/models.py:90
msgid "mail rule"

File diff suppressed because it is too large Load Diff

View File

@@ -65,6 +65,10 @@ def paths_check(app_configs, **kwargs):
+ path_check("PAPERLESS_EMPTY_TRASH_DIR", settings.EMPTY_TRASH_DIR)
+ path_check("PAPERLESS_MEDIA_ROOT", settings.MEDIA_ROOT)
+ path_check("PAPERLESS_CONSUMPTION_DIR", settings.CONSUMPTION_DIR)
+ path_check(
"PAPERLESS_CONSUMPTION_FAILED_DIR",
settings.CONSUMPTION_FAILED_DIR,
)
)

View File

@@ -281,6 +281,11 @@ CONSUMPTION_DIR = __get_path(
BASE_DIR.parent / "consume",
)
CONSUMPTION_FAILED_DIR = __get_path(
"PAPERLESS_CONSUMPTION_FAILED_DIR",
CONSUMPTION_DIR / "failed",
)
# This will be created if it doesn't exist
SCRATCH_DIR = __get_path(
"PAPERLESS_SCRATCH_DIR",
@@ -890,6 +895,8 @@ CONSUMER_IGNORE_PATTERNS = list(
),
),
)
if CONSUMPTION_DIR in CONSUMPTION_FAILED_DIR.parents:
CONSUMER_IGNORE_PATTERNS.append(CONSUMPTION_FAILED_DIR.name)
CONSUMER_SUBDIRS_AS_TAGS = __get_boolean("PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS")

View File

@@ -29,10 +29,11 @@ class TestChecks(DirectoriesMixin, TestCase):
MEDIA_ROOT="uuh",
DATA_DIR="whatever",
CONSUMPTION_DIR="idontcare",
CONSUMPTION_FAILED_DIR="nope",
)
def test_paths_check_dont_exist(self):
msgs = paths_check(None)
self.assertEqual(len(msgs), 3, str(msgs))
self.assertEqual(len(msgs), 4, str(msgs))
for msg in msgs:
self.assertTrue(msg.msg.endswith("is set but doesn't exist."))
@@ -41,13 +42,15 @@ class TestChecks(DirectoriesMixin, TestCase):
os.chmod(self.dirs.data_dir, 0o000)
os.chmod(self.dirs.media_dir, 0o000)
os.chmod(self.dirs.consumption_dir, 0o000)
os.chmod(self.dirs.consumption_failed_dir, 0o000)
self.addCleanup(os.chmod, self.dirs.data_dir, 0o777)
self.addCleanup(os.chmod, self.dirs.media_dir, 0o777)
self.addCleanup(os.chmod, self.dirs.consumption_dir, 0o777)
self.addCleanup(os.chmod, self.dirs.consumption_failed_dir, 0o777)
msgs = paths_check(None)
self.assertEqual(len(msgs), 3)
self.assertEqual(len(msgs), 4)
for msg in msgs:
self.assertTrue(msg.msg.endswith("is not writeable"))

View File

@@ -1,6 +1,6 @@
from typing import Final
__version__: Final[tuple[int, int, int]] = (2, 13, 4)
__version__: Final[tuple[int, int, int]] = (2, 13, 2)
# Version string like X.Y.Z
__full_version_str__: Final[str] = ".".join(map(str, __version__))
# Version string like X.Y