Compare commits

..
Author SHA1 Message Date
shamoon 787c8bae45 Chore: keep main and dev CI runs from canceling 2026-10-31 13:55:22 -07:00
GitHub Actions 45e99cde5a Auto translate strings 2026-10-31 20:44:16 +00:00
shamoon 7ea58eaf46 Fix: move document list preview button to own column (#14610) 2026-10-31 13:42:56 -07:00
GitHub Actions a19804a0b7 Auto translate strings 2026-10-31 16:34:17 +00:00
Trenton Handshamoon 3289be7383 Fix: Various small bugs found during typing updates (#14593)
* Fix: match Apple Mail tag colors case-insensitively

* Fix: reject OAuth callbacks with an unrecognized scope

* Fix: refuse to refresh OAuth tokens for non-OAuth accounts or missing refresh tokens

* Fix: parse datetime strings in the format_datetime filter and reject invalid ones

* Fix: reject a PAPERLESS_URL without a hostname

* Fix: report a database error in the system status instead of failing with a 500

* Fix: surface the database error when refreshing a document before moving its files

* Fix: report no unapplied migrations when the applied migrations cannot be read

* Fix: return the parsed value directly in format_datetime for typing

* Chore: annotate the paperless_dirs fixture in the refresh failure test

* Allow null for the latest_migration system status type

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2026-10-31 09:32:58 -07:00
GitHub Actions d98918dd97 Auto translate strings 2026-10-31 06:13:00 +00:00
shamoon 34c88613f6 Fix: fix custom fields dropdown text size (#14606) 2026-10-30 23:11:45 -07:00
github-actions[bot] 13acdabb47 Changelog v3.4.1 - GHA (#14605)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-30 22:44:05 -07:00
shamoon b67cfdf3fd Bump version to 3.4.1 2026-10-30 22:20:31 -07:00
github-actions[bot]andCrowdin Bot 98d2ddd1c6 New Crowdin translations by GitHub Action (#14584)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-10-31 05:12:07 +00:00
GitHub Actions 91b35eada6 Auto translate strings 2026-10-31 04:48:33 +00:00
shamoon 11248f776d Fix: fix the id__none schema in the right place (#14604) 2026-10-30 21:47:12 -07:00
shamoon 331fa13088 Fix: drop redundant change perms checks on cards open button (#14601) 2026-10-30 16:16:48 -07:00
shamoon ebddd5687c Chore: fix a couple e2e warnings / flakiness (#14596) 2026-10-30 15:45:46 -07:00
Trenton H 2925b8de81 Chore: Skip repeating Django system checks in the Docker consumer, scheduler and worker (#14598)
The init-system-checks step already runs them once and all three services
depend on init-complete. Measured idle anon PSS saved on
Python 3.14: consumer 14 MiB, beat 26 MiB, worker 27 MiB.
2026-10-30 22:37:37 +00:00
GitHub Actions 5e397e1222 Auto translate strings 2026-10-30 22:26:54 +00:00
Trenton H 6f99ad9469 Chore: Improve idle memory via more lazy imports (#14599)
* Chore: Import dateparser lazily so processes that never parse dates skip it

* Chore: Import turbohtml lazily in the mail parser

* Chore: Import lxml lazily in the SVG validator

* Chore: Import paperless_ai.indexing lazily in document tasks

* Chore: Import paperless_ai helpers lazily in views

* Chore: Read the OCRmyPDF sRGB profile on first use instead of at import

* Chore: Import pikepdf and img2pdf lazily so startup does not load them

* Chore: Reuse the initialized Django ASGI application for HTTP routing
2026-10-30 22:25:42 +00:00
shamoon a5d3247ca7 Tweakhancement: make new pdf controls mostly hidden (#14595) 2026-10-30 10:22:50 -07:00
GitHub Actions 8a67481a9d Auto translate strings 2026-10-30 16:59:59 +00:00
shamoon 652f964f5d Tweak: move preview button in doc list (#14594) 2026-10-30 09:58:29 -07:00
shamoon 80a1827fa0 Fix: checksum light mode display and cf dropdown mobile position (#14590) 2026-10-30 07:03:08 -07:00
172 changed files with 4410 additions and 4291 deletions

No files matched your search

+2 -1
View File
@@ -8,7 +8,8 @@ on:
- 'translations**'
workflow_dispatch:
concurrency:
group: backend-${{ github.event.pull_request.number || github.ref }}
# Let dev/main commits finish, PR and feature-branch runs supersede older ones
group: backend-${{ github.event.pull_request.number || ((github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main') && github.run_id) || github.ref }}
cancel-in-progress: true
env:
DEFAULT_UV_VERSION: "0.13.x"
+2 -1
View File
@@ -13,7 +13,8 @@ on:
- main
workflow_dispatch:
concurrency:
group: docker-${{ github.event.pull_request.number || github.ref }}
# Let dev/main commits finish, PR and feature-branch runs supersede older ones
group: docker-${{ github.event.pull_request.number || ((github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main') && github.run_id) || github.ref }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
+2 -1
View File
@@ -6,7 +6,8 @@ on:
pull_request:
workflow_dispatch:
concurrency:
group: docs-${{ github.event.pull_request.number || github.ref }}
# Let dev/main commits finish, PR and feature-branch runs supersede older ones
group: docs-${{ github.event.pull_request.number || ((github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main') && github.run_id) || github.ref }}
cancel-in-progress: true
permissions:
contents: read
+2 -1
View File
@@ -8,7 +8,8 @@ on:
- 'translations**'
workflow_dispatch:
concurrency:
group: frontend-${{ github.event.pull_request.number || github.ref }}
# Let dev/main commits finish, PR and feature-branch runs supersede older ones
group: frontend-${{ github.event.pull_request.number || ((github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main') && github.run_id) || github.ref }}
cancel-in-progress: true
permissions: {}
jobs:
+2 -1
View File
@@ -7,7 +7,8 @@ on:
branches-ignore:
- 'translations**'
concurrency:
group: lint-${{ github.event.pull_request.number || github.ref }}
# Let dev/main commits finish, PR and feature-branch runs supersede older ones
group: lint-${{ github.event.pull_request.number || ((github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main') && github.run_id) || github.ref }}
cancel-in-progress: true
permissions:
contents: read
+2 -1
View File
@@ -8,7 +8,8 @@ on:
- 'translations**'
workflow_dispatch:
concurrency:
group: static-analysis-${{ github.event.pull_request.number || github.ref }}
# Let dev/main commits finish, PR and feature-branch runs supersede older ones
group: static-analysis-${{ github.event.pull_request.number || ((github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main') && github.run_id) || github.ref }}
cancel-in-progress: true
permissions:
contents: read
@@ -39,6 +39,7 @@ declare -a scope=(
"${site_packages}/allauth"
"${site_packages}/drf_spectacular"
"${site_packages}/drf_spectacular_sidecar"
"${site_packages}/treenode"
"${site_packages}/compression_middleware"
)
@@ -10,9 +10,11 @@ if [[ -n "${PAPERLESS_CONSUMER_DISABLE}" ]]; then
else
cd ${PAPERLESS_SRC_DIR}
# Django system checks already ran once in init-system-checks, skip repeating them
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
exec python3 manage.py document_consumer
exec python3 manage.py document_consumer --skip-checks
else
exec s6-setuidgid paperless python3 manage.py document_consumer
exec s6-setuidgid paperless python3 manage.py document_consumer --skip-checks
fi
fi
@@ -3,8 +3,9 @@
cd ${PAPERLESS_SRC_DIR}
# Django system checks already ran once in init-system-checks, skip repeating them
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
exec /usr/local/bin/celery --app paperless beat --loglevel INFO
exec /usr/local/bin/celery --skip-checks --app paperless beat --loglevel INFO
else
exec s6-setuidgid paperless /usr/local/bin/celery --app paperless beat --loglevel INFO
exec s6-setuidgid paperless /usr/local/bin/celery --skip-checks --app paperless beat --loglevel INFO
fi
@@ -3,8 +3,9 @@
cd ${PAPERLESS_SRC_DIR}
# Django system checks already ran once in init-system-checks, skip repeating them
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
exec /usr/local/bin/celery --app paperless worker --loglevel INFO --without-mingle --without-gossip
exec /usr/local/bin/celery --skip-checks --app paperless worker --loglevel INFO --without-mingle --without-gossip
else
exec s6-setuidgid paperless /usr/local/bin/celery --app paperless worker --loglevel INFO --without-mingle --without-gossip
exec s6-setuidgid paperless /usr/local/bin/celery --skip-checks --app paperless worker --loglevel INFO --without-mingle --without-gossip
fi
+24
View File
@@ -1,5 +1,29 @@
# Changelog
## paperless-ngx 3.4.1
### Bug Fixes
- Fix the id\_\_ schemae in the right place [@shamoon](https://github.com/shamoon) ([#14604](https://github.com/paperless-ngx/paperless-ngx/pull/14604))
- Fix: drop redundant change perms checks on cards open button [@shamoon](https://github.com/shamoon) ([#14601](https://github.com/paperless-ngx/paperless-ngx/pull/14601))
- Tweak/fix: make new pdf controls mostly hidden [@shamoon](https://github.com/shamoon) ([#14595](https://github.com/paperless-ngx/paperless-ngx/pull/14595))
- Tweak/fix: move preview button in doc list [@shamoon](https://github.com/shamoon) ([#14594](https://github.com/paperless-ngx/paperless-ngx/pull/14594))
- Fix: checksum light mode display and cf dropdown mobile position [@shamoon](https://github.com/shamoon) ([#14590](https://github.com/paperless-ngx/paperless-ngx/pull/14590))
### All App Changes
<details>
<summary>6 changes</summary>
- Fix the id\_\_ schemae in the right place [@shamoon](https://github.com/shamoon) ([#14604](https://github.com/paperless-ngx/paperless-ngx/pull/14604))
- Fix: drop redundant change perms checks on cards open button [@shamoon](https://github.com/shamoon) ([#14601](https://github.com/paperless-ngx/paperless-ngx/pull/14601))
- Chore: Improve idle memory via more lazy imports [@stumpylog](https://github.com/stumpylog) ([#14599](https://github.com/paperless-ngx/paperless-ngx/pull/14599))
- Tweak/fix: make new pdf controls mostly hidden [@shamoon](https://github.com/shamoon) ([#14595](https://github.com/paperless-ngx/paperless-ngx/pull/14595))
- Tweak/fix: move preview button in doc list [@shamoon](https://github.com/shamoon) ([#14594](https://github.com/paperless-ngx/paperless-ngx/pull/14594))
- Fix: checksum light mode display and cf dropdown mobile position [@shamoon](https://github.com/shamoon) ([#14590](https://github.com/paperless-ngx/paperless-ngx/pull/14590))
</details>
## paperless-ngx 3.4.0
### Features / Enhancements
+2 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "paperless-ngx"
version = "3.4.0"
version = "3.4.1"
description = """\
A community-supported supercharged document management system: scan, index and archive all your physical documents\
"""
@@ -38,6 +38,7 @@ dependencies = [
"django-multiselectfield~=1.0.1",
"django-rich~=2.2.0",
"django-soft-delete~=1.0.18",
"django-treenode>=0.24",
"djangorestframework~=3.16",
"drf-spectacular~=0.30",
"drf-spectacular-sidecar>=2026.7.1,<2026.10",
+9 -1
View File
@@ -43,6 +43,7 @@ def configure_environment(instance_root: Path) -> None:
def seed_database() -> None:
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User
from django.core.management import call_command
from django.utils import timezone
@@ -63,11 +64,18 @@ def seed_database() -> None:
username="playwright",
password="playwright", # NOSONAR
)
User.objects.create_user(
viewer = User.objects.create_user(
username="viewer",
password="viewer", # NOSONAR
)
viewer.user_permissions.add(
Permission.objects.get(
content_type__app_label="documents",
codename="view_uisettings",
),
)
inbox = Tag.objects.create(name="Inbox", is_inbox_tag=True, owner=admin)
quick_filter = Tag.objects.create(name="Another Sample Tag", owner=admin)
Tag.objects.create(name="TagWithPartial", owner=admin)
@@ -80,12 +80,21 @@ test('should show a list of notes', async ({ page }) => {
test('should support quick filters', async ({ page }) => {
await page.goto('/documents/1/details')
// filter button is available before tags have loaded
const selectedTags = page.locator('pngx-input-tags .ng-value pngx-tag')
await expect(selectedTags).toHaveCount(2)
await expect(
selectedTags.getByText('Another Sample Tag', { exact: true })
).toBeVisible()
await expect(selectedTags.getByText('Inbox', { exact: true })).toBeVisible()
await page
.getByRole('button', { name: 'Filter documents with these Tags' })
.click()
await expect(page).toHaveURL(
/tags__id__all=2,1&sort=created&reverse=1&page=1/
)
await expect(page).toHaveURL((url) => {
const tags = url.searchParams.get('tags__id__all')?.split(',').sort()
return url.pathname === '/documents' && tags?.join(',') === '1,2'
})
await expect(page.locator('pngx-document-list')).toHaveText(/one document/i)
})
test('should finish reloading the preview after a remote document update', async ({
@@ -6,8 +6,15 @@ test.use({
},
})
test('should not allow user to edit settings', async ({ page }) => {
test.beforeEach(async ({ page }) => {
const settingsLoaded = page.waitForResponse((response) =>
response.url().endsWith('/api/ui_settings/')
)
await page.goto('/dashboard')
expect((await settingsLoaded).status()).toBe(200)
})
test('should not allow user to edit settings', async ({ page }) => {
await expect(page.getByRole('link', { name: 'Settings' })).not.toBeAttached()
await page.goto('/settings')
await expect(page.locator('body')).toHaveText(
@@ -16,7 +23,6 @@ test('should not allow user to edit settings', async ({ page }) => {
})
test('should not allow user to view documents', async ({ page }) => {
await page.goto('/dashboard')
await expect(
page.locator('nav').getByRole('link', { name: 'Documents' })
).not.toBeAttached()
@@ -31,7 +37,6 @@ test('should not allow user to view documents', async ({ page }) => {
})
test('should not allow user to view correspondents', async ({ page }) => {
await page.goto('/dashboard')
await expect(
page.getByRole('link', { name: 'Attributes' })
).not.toBeAttached()
@@ -42,7 +47,6 @@ test('should not allow user to view correspondents', async ({ page }) => {
})
test('should not allow user to view tags', async ({ page }) => {
await page.goto('/dashboard')
await expect(
page.getByRole('link', { name: 'Attributes' })
).not.toBeAttached()
@@ -53,7 +57,6 @@ test('should not allow user to view tags', async ({ page }) => {
})
test('should not allow user to view document types', async ({ page }) => {
await page.goto('/dashboard')
await expect(
page.getByRole('link', { name: 'Attributes' })
).not.toBeAttached()
@@ -64,7 +67,6 @@ test('should not allow user to view document types', async ({ page }) => {
})
test('should not allow user to view storage paths', async ({ page }) => {
await page.goto('/dashboard')
await expect(
page.getByRole('link', { name: 'Attributes' })
).not.toBeAttached()
@@ -75,7 +77,6 @@ test('should not allow user to view storage paths', async ({ page }) => {
})
test('should not allow user to view logs', async ({ page }) => {
await page.goto('/dashboard')
await expect(page.getByRole('link', { name: 'Logs' })).not.toBeAttached()
await page.goto('/logs')
await expect(page.locator('body')).toHaveText(
@@ -84,7 +85,6 @@ test('should not allow user to view logs', async ({ page }) => {
})
test('should not allow user to view tasks', async ({ page }) => {
await page.goto('/dashboard')
await expect(page.getByRole('link', { name: 'Tasks' })).not.toBeAttached()
await page.goto('/tasks')
await expect(page.locator('body')).toHaveText(
+34 -34
View File
@@ -1402,7 +1402,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">239,240</context>
<context context-type="linenumber">240,241</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/document.ts</context>
@@ -1733,7 +1733,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">248,249</context>
<context context-type="linenumber">249,250</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
@@ -1768,7 +1768,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">257,258</context>
<context context-type="linenumber">258,259</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
@@ -1811,7 +1811,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">221,222</context>
<context context-type="linenumber">222,223</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
@@ -2289,7 +2289,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">266,267</context>
<context context-type="linenumber">267,268</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.html</context>
@@ -4115,7 +4115,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">388</context>
<context context-type="linenumber">391</context>
</context-group>
</trans-unit>
<trans-unit id="7515883357904500238" datatype="html">
@@ -4389,7 +4389,7 @@
<source>Create new field</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.html</context>
<context context-type="linenumber">20</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="6973528734330066202" datatype="html">
@@ -4786,7 +4786,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">275,276</context>
<context context-type="linenumber">276,277</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/document.ts</context>
@@ -6604,7 +6604,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">218,219</context>
<context context-type="linenumber">219,220</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
@@ -8410,7 +8410,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">320</context>
<context context-type="linenumber">321</context>
</context-group>
</trans-unit>
<trans-unit id="78870852467682010" datatype="html">
@@ -8425,7 +8425,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">360</context>
<context context-type="linenumber">363</context>
</context-group>
</trans-unit>
<trans-unit id="157572966557284263" datatype="html">
@@ -8440,7 +8440,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">367</context>
<context context-type="linenumber">370</context>
</context-group>
</trans-unit>
<trans-unit id="883965278435032344" datatype="html">
@@ -8458,7 +8458,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">388</context>
<context context-type="linenumber">391</context>
</context-group>
</trans-unit>
<trans-unit id="872092479747931526" datatype="html">
@@ -9919,7 +9919,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">336</context>
<context context-type="linenumber">339</context>
</context-group>
</trans-unit>
<trans-unit id="106713086593101376" datatype="html">
@@ -10234,21 +10234,21 @@
<source>Sort by title</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">213</context>
<context context-type="linenumber">214</context>
</context-group>
</trans-unit>
<trans-unit id="6232673011753681091" datatype="html">
<source>Sort by owner</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">226</context>
<context context-type="linenumber">227</context>
</context-group>
</trans-unit>
<trans-unit id="3715596725146409911" datatype="html">
<source>Owner</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">230,231</context>
<context context-type="linenumber">231,232</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/document.ts</context>
@@ -10263,49 +10263,49 @@
<source>Sort by notes</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">235</context>
<context context-type="linenumber">236</context>
</context-group>
</trans-unit>
<trans-unit id="5499001829734502606" datatype="html">
<source>Sort by document type</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">244</context>
<context context-type="linenumber">245</context>
</context-group>
</trans-unit>
<trans-unit id="6213829731736042759" datatype="html">
<source>Sort by storage path</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">253</context>
<context context-type="linenumber">254</context>
</context-group>
</trans-unit>
<trans-unit id="3406167410329973166" datatype="html">
<source>Sort by created date</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">262</context>
<context context-type="linenumber">263</context>
</context-group>
</trans-unit>
<trans-unit id="3769035778779263084" datatype="html">
<source>Sort by added date</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">271</context>
<context context-type="linenumber">272</context>
</context-group>
</trans-unit>
<trans-unit id="4874754501044009042" datatype="html">
<source>Sort by number of pages</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">280</context>
<context context-type="linenumber">281</context>
</context-group>
</trans-unit>
<trans-unit id="3817498941817715969" datatype="html">
<source>Pages</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">284,285</context>
<context context-type="linenumber">285,286</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/data/document.ts</context>
@@ -10324,28 +10324,28 @@
<source> Shared </source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">288</context>
<context context-type="linenumber">289</context>
</context-group>
</trans-unit>
<trans-unit id="5083658411133224968" datatype="html">
<source>Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">294,295</context>
</context-group>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
<context context-type="linenumber">295,296</context>
</context-group>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">327</context>
</context-group>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">335</context>
</context-group>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "paperless-ngx-ui",
"version": "3.4.0",
"version": "3.4.1",
"scripts": {
"preinstall": "npx only-allow pnpm",
"ng": "ng",
@@ -11,14 +11,12 @@
</div>
@for (field of filteredFields; track field.id) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addField(field)" #button>
<small class="d-flex">{{field.name}} <small class="ms-auto text-muted">{{getDataTypeLabel(field.data_type)}}</small></small>
<div class="d-flex">{{field.name}} <small class="ms-auto text-muted">{{getDataTypeLabel(field.data_type)}}</small></div>
</button>
}
@if (!filterText?.length || filteredFields.length === 0) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="createField(filterText)" [disabled]="!canCreateFields" #button>
<small>
<i-bs width=".9em" height=".9em" name="asterisk" class="me-1"></i-bs><ng-container i18n>Create new field</ng-container>
</small>
<i-bs width=".9em" height=".9em" name="asterisk" class="me-1"></i-bs><ng-container i18n>Create new field</ng-container>
</button>
}
</div>
@@ -1,10 +1,3 @@
.custom-fields-dropdown {
min-width: 300px;
// correct position on mobile
@media (max-width: 575.98px) {
&.show {
margin-left: -245px !important;
}
}
}
@@ -308,7 +308,7 @@
</tr>
<tr class="align-middle">
<td i18n>Original SHA256 checksum</td>
<td><span class="badge bg-light font-monospace text-wrap text-break text-start">{{metadata()?.original_checksum}}</span></td>
<td><span class="badge bg-light text-dark font-monospace text-wrap text-break text-start">{{metadata()?.original_checksum}}</span></td>
</tr>
<tr>
<td i18n>Original file size</td>
@@ -465,7 +465,7 @@
@switch (archiveContentRenderType) {
@case (ContentRenderType.PDF) {
@if (!useNativePdfViewer) {
<div class="preview-sticky pdf-viewer-container">
<div class="preview-sticky pdf-viewer-container" tabindex="-1">
<pngx-pdf-viewer
[src]="pdfSource()"
[sourceRevision]="previewRevision()"
@@ -477,7 +477,7 @@
(loadError)="onError($event)"
(afterLoadComplete)="pdfPreviewLoaded($event)">
</pngx-pdf-viewer>
<div class="position-absolute bottom-0 start-50 translate-middle-x z-1 mb-3 d-flex gap-2 p-1 bg-body border rounded shadow">
<div class="preview-controls position-absolute bottom-0 start-50 translate-middle-x z-1 mb-3 d-flex gap-2 p-1 bg-body border rounded shadow">
@if (previewNumPages()) {
<div class="input-group input-group-sm w-auto flex-nowrap">
<input class="form-control flex-grow-0 w-auto" type="number" min="1" [max]="previewNumPages()" [ngModelOptions]="{standalone: true}" [ngModel]="previewCurrentPage()" (ngModelChange)="previewCurrentPage.set($event)" aria-label="Page" i18n-aria-label />
@@ -12,6 +12,22 @@
width: 100%;
height: 100%;
}
&:focus {
outline: none;
}
.preview-controls {
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease-in-out;
}
&:hover .preview-controls,
&:focus-within .preview-controls {
opacity: 1;
pointer-events: auto;
}
}
.btn-group .dropdown-toggle-split {
@@ -72,7 +72,7 @@
<a class="btn btn-sm btn-outline-secondary" (click)="clickMoreLike.emit()">
<i-bs name="diagram-3" class="me-1"></i-bs><span class="d-none d-md-inline" i18n>More like this</span>
</a>
<a routerLink="/documents/{{document().id}}" class="btn btn-sm btn-outline-secondary" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }">
<a routerLink="/documents/{{document().id}}" class="btn btn-sm btn-outline-secondary">
<i-bs name="file-earmark-richtext" class="me-1"></i-bs><span class="d-none d-md-inline" i18n>Open</span>
</a>
<pngx-preview-popup [document]="document()" #popupPreview>
@@ -10,7 +10,6 @@ import {
NgbTooltipModule,
} from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
import { IsNumberPipe } from 'src/app/pipes/is-number.pipe'
@@ -52,7 +51,6 @@ describe('DocumentCardLargeComponent', () => {
DocumentCardLargeComponent,
DocumentTitlePipe,
CustomDatePipe,
IfPermissionsDirective,
SafeUrlPipe,
IsNumberPipe,
PreviewPopupComponent,
@@ -20,7 +20,6 @@ import {
Document,
} from 'src/app/data/document'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
@@ -51,7 +50,6 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
CorrespondentNamePipe,
DocumentTypeNamePipe,
StoragePathNamePipe,
IfPermissionsDirective,
CustomDatePipe,
RouterModule,
NgbTooltipModule,
@@ -144,7 +144,7 @@
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group w-100">
@if (document()) {
<a routerLink="/documents/{{document().id}}" class="btn btn-sm btn-outline-secondary" title="Open" i18n-title *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }" i18n-title>
<a routerLink="/documents/{{document().id}}" class="btn btn-sm btn-outline-secondary" title="Open" i18n-title>
<i-bs name="file-earmark-richtext"></i-bs>
</a>
<pngx-preview-popup [document]="document()" #popupPreview>
@@ -20,7 +20,6 @@ import {
Document,
} from 'src/app/data/document'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
@@ -51,7 +50,6 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
CorrespondentNamePipe,
DocumentTypeNamePipe,
StoragePathNamePipe,
IfPermissionsDirective,
CustomDatePipe,
RouterModule,
NgbTooltipModule,
@@ -208,6 +208,7 @@
i18n>Correspondent</th>
}
@if (activeDisplayFields.includes(DisplayField.TITLE)) {
<th style="width: 2rem"><!-- Preview --></th>
<th class="cursor-pointer"
pngxSortable="title"
title="Sort by title" i18n-title
@@ -321,15 +322,17 @@
}
</td>
}
@if (activeDisplayFields.includes(DisplayField.TITLE)) {
<td class="pe-0 text-end align-baseline" (mouseleave)="popupPreview.close()">
<pngx-preview-popup [document]="d" linkClasses="btn btn-sm btn-link text-secondary px-1" linkTitle="Preview document" (click)="$event.stopPropagation()" i18n-linkTitle #popupPreview>
<i-bs name="eye"></i-bs>
</pngx-preview-popup>
</td>
}
@if (activeDisplayFields.includes(DisplayField.TITLE) || activeDisplayFields.includes(DisplayField.TAGS)) {
<td width="30%">
<td width="30%" class="align-baseline">
@if (activeDisplayFields.includes(DisplayField.TITLE)) {
<div class="d-inline-block" (mouseleave)="popupPreview.close()">
<a routerLink="/documents/{{d.id}}" title="Edit document" i18n-title style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a>
<pngx-preview-popup [document]="d" linkClasses="btn btn-sm btn-link text-secondary" linkTitle="Preview document" (click)="$event.stopPropagation()" i18n-linkTitle #popupPreview>
<i-bs name="eye"></i-bs>
</pngx-preview-popup>
</div>
<a routerLink="/documents/{{d.id}}" title="Edit document" i18n-title style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a>
}
@if (activeDisplayFields.includes(DisplayField.TAGS)) {
@for (tagID of d.tags; track tagID) {
+1 -1
View File
@@ -24,7 +24,7 @@ export interface SystemStatus {
status: SystemStatusItemStatus
error?: string
migration_status: {
latest_migration: string
latest_migration: string | null
unapplied_migrations: string[]
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ export const environment = {
apiVersion: '10', // match src/paperless/settings.py
appTitle: DEFAULT_APP_TITLE,
tag: 'prod',
version: '3.4.0',
version: '3.4.1',
webSocketHost: window.location.host,
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
webSocketBaseUrl: base_url.pathname + 'ws/',
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Wysig dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Wysig dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">تعديل المستند</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">تعديل المستند</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Рэдагаваць дакумент</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Рэдагаваць дакумент</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">Сортирай по: <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Редактиране на документа</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Визуализация на документа</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Редактиране на документа</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">Ordena per <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Edita document</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Previsualitza document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Edita document</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">Seřadit podle <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Upravit dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Náhled dokumentu</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Upravit dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">Nach <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/> sortieren</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Dokument bearbeiten</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Dokumentenvorschau</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Dokument bearbeiten</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+25 -25
View File
@@ -4795,7 +4795,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">38</context>
</context-group>
<target state="needs-translation">Date mode</target>
<target state="translated">Datumsmodus</target>
</trans-unit>
<trans-unit id="7572800523712857519" datatype="html">
<source>Fixed date</source>
@@ -4807,7 +4807,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">40</context>
</context-group>
<target state="needs-translation">Fixed date</target>
<target state="translated">Festes Datum</target>
</trans-unit>
<trans-unit id="4108478337308251505" datatype="html">
<source>Fixed</source>
@@ -4815,7 +4815,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">41,42</context>
</context-group>
<target state="needs-translation">Fixed</target>
<target state="translated">Fixiert</target>
</trans-unit>
<trans-unit id="8283919074858117329" datatype="html">
<source>Relative date</source>
@@ -4827,7 +4827,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">44</context>
</context-group>
<target state="needs-translation">Relative date</target>
<target state="translated">Relatives Datum</target>
</trans-unit>
<trans-unit id="2731996625798485199" datatype="html">
<source>Relative</source>
@@ -4835,7 +4835,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">45,46</context>
</context-group>
<target state="needs-translation">Relative</target>
<target state="translated">Relativ</target>
</trans-unit>
<trans-unit id="6533918067030990396" datatype="html">
<source>Amount</source>
@@ -4843,7 +4843,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">51</context>
</context-group>
<target state="needs-translation">Amount</target>
<target state="translated">Anzahl</target>
</trans-unit>
<trans-unit id="8932917860978648376" datatype="html">
<source>Unit</source>
@@ -4851,7 +4851,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">56,57</context>
</context-group>
<target state="needs-translation">Unit</target>
<target state="translated">Einheit</target>
</trans-unit>
<trans-unit id="9138811584797805840" datatype="html">
<source>Open date picker</source>
@@ -4991,7 +4991,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">119,120</context>
</context-group>
<target state="needs-translation">Operator</target>
<target state="translated">Operator</target>
</trans-unit>
<trans-unit id="2328549728463836983" datatype="html">
<source>Remove query</source>
@@ -5051,7 +5051,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts</context>
<context context-type="linenumber">330</context>
</context-group>
<target state="needs-translation">days ago</target>
<target state="translated">vor Tagen</target>
</trans-unit>
<trans-unit id="2871318661796659216" datatype="html">
<source>weeks ago</source>
@@ -5059,7 +5059,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="needs-translation">weeks ago</target>
<target state="translated">vor Wochen</target>
</trans-unit>
<trans-unit id="6041340836190906216" datatype="html">
<source>months ago</source>
@@ -5067,7 +5067,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts</context>
<context context-type="linenumber">332</context>
</context-group>
<target state="needs-translation">months ago</target>
<target state="translated">vor Monaten</target>
</trans-unit>
<trans-unit id="963494111451627204" datatype="html">
<source>years ago</source>
@@ -5075,7 +5075,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts</context>
<context context-type="linenumber">333</context>
</context-group>
<target state="needs-translation">years ago</target>
<target state="translated">vor Jahren</target>
</trans-unit>
<trans-unit id="5555905617885007774" datatype="html">
<source>days from now</source>
@@ -5083,7 +5083,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts</context>
<context context-type="linenumber">334</context>
</context-group>
<target state="needs-translation">days from now</target>
<target state="translated">Tage von heute an</target>
</trans-unit>
<trans-unit id="4298377021616940216" datatype="html">
<source>weeks from now</source>
@@ -5091,7 +5091,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts</context>
<context context-type="linenumber">335</context>
</context-group>
<target state="needs-translation">weeks from now</target>
<target state="translated">Wochen von heute an</target>
</trans-unit>
<trans-unit id="2772874767134148305" datatype="html">
<source>months from now</source>
@@ -5099,7 +5099,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts</context>
<context context-type="linenumber">336</context>
</context-group>
<target state="needs-translation">months from now</target>
<target state="translated">Monate von heute an</target>
</trans-unit>
<trans-unit id="6363533917780225967" datatype="html">
<source>years from now</source>
@@ -5107,7 +5107,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.ts</context>
<context context-type="linenumber">337</context>
</context-group>
<target state="needs-translation">years from now</target>
<target state="translated">Jahr von heute an</target>
</trans-unit>
<trans-unit id="6630533861307510614" datatype="html">
<source>Clear created relative date</source>
@@ -11396,22 +11396,22 @@
</context-group>
<target state="final">Nach <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/> sortieren</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html" approved="yes">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="final">Dokument bearbeiten</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html" approved="yes">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="final">Dokumentenvorschau</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html" approved="yes">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="final">Dokument bearbeiten</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html" approved="yes">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Επεξεργασία εγγράφου</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Επεξεργασία εγγράφου</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">Ordenar por <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Editar documento</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Previsualizar documento</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Editar documento</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">سند ویرایش</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">سند پیش نمایش</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">سند ویرایش</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Muokkaa asiakirjaa</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Esikatsele asiakirja</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Muokkaa asiakirjaa</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+13 -13
View File
@@ -3719,7 +3719,7 @@
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
<context context-type="linenumber">163</context>
</context-group>
<target state="translated">Erreur lors de la supression de l’utilisateur « <x id="PH" equiv-text="user.username"/> ».</target>
<target state="translated">Erreur lors de la suppression de l’utilisateur « <x id="PH" equiv-text="user.username"/> ».</target>
</trans-unit>
<trans-unit id="5766640174051730159" datatype="html" approved="yes">
<source>Saved group &quot;<x id="PH" equiv-text="newGroup.name"/>&quot;.</source>
@@ -3767,7 +3767,7 @@
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
<context context-type="linenumber">220</context>
</context-group>
<target state="translated">Erreur lors de la supression du groupe « <x id="PH" equiv-text="group.name"/> ».</target>
<target state="translated">Erreur lors de la suppression du groupe « <x id="PH" equiv-text="group.name"/> ».</target>
</trans-unit>
<trans-unit id="7931334600001636863" datatype="html" approved="yes">
<source>by Paperless-ngx</source>
@@ -6935,7 +6935,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
<context context-type="linenumber">410</context>
</context-group>
<target state="translated">Destinatires du courriel</target>
<target state="translated">Destinataires du courriel</target>
</trans-unit>
<trans-unit id="7916910101279824329" datatype="html">
<source>Attach document</source>
@@ -10930,7 +10930,7 @@
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.html</context>
<context context-type="linenumber">8,9</context>
</context-group>
<target state="translated"><x id="INTERPOLATION" equiv-text="{{ document().title | documentTitle }}"/> mignature</target>
<target state="translated"><x id="INTERPOLATION" equiv-text="{{ document().title | documentTitle }}"/> miniature</target>
</trans-unit>
<trans-unit id="2784168796433474565" datatype="html" approved="yes">
<source>Filter by tag</source>
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">Trier par <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html" approved="yes">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="final">Modifier le document</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Prévisualiser le document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html" approved="yes">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="final">Modifier le document</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">מיון לפי <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">עריכת מסמך</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">תצוגה מקדימה של המסמך</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">עריכת מסמך</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">Sortiraj po <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Uredi dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Pregledaj dokument</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Uredi dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">Rendezés <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/> szerint</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Dokumentum szerkesztése</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Dokumentum előnézete</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Dokumentum szerkesztése</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">Urutkan berdasarkan <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Edit dokumen</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Pratinjau dokumen</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Edit dokumen</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">Ordina per <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Modifica documento</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Anteprima documento</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Modifica documento</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated"><x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/> でソート</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">ドキュメントを編集</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">ドキュメントのプレビュー</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">ドキュメントを編集</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">문서 수정</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">문서 미리보기</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">문서 수정</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Dokument editéieren</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Dokument editéieren</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Rediģēt dokumentu</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Rediģēt dokumentu</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
File diff suppressed because it is too large. Load diff
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="needs-translation">Edit document</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">Sorteer op <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Document bewerken</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Voorbeeld van document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Document bewerken</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">Sorter etter <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Rediger dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Forhåndsvis dokument</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Rediger dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">Sortuj według <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Edytuj dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Podgląd dokumentu</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Edytuj dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11398,22 +11398,22 @@ Erro ao enviar documentos por e-mail</target>
</context-group>
<target state="translated">Ordenar por <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Editar documento</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Prévisualizar documento</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Editar documento</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Editar documento</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Editar documento</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">Sortează după <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Editează document</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Previzualizare document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Editează document</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">Сортировать по <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Изменить документ</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Предпросмотр документа</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Изменить документ</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11398,22 +11398,22 @@ zhodujúce sa</target>
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Upraviť dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Upraviť dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">Razvrsti po <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Uredi dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Predogled dokumenta</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Uredi dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">Rendit by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Redakto dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Preview dokument</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Redakto dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11398,22 +11398,22 @@
</context-group>
<target state="translated">Složi prema <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Uredi dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Pregledaj dokument</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Uredi dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">Sortera efter <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Redigera dokument</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Förhandsvisa dokument</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Redigera dokument</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">แก้ไขเอกสาร</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="needs-translation">Preview document</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">แก้ไขเอกสาร</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11398,22 +11398,22 @@ tüm <x id="CLOSE_EMPHASISED_TEXT" ctype="x-em" equiv-text="&lt;/em&gt;"/> krite
</context-group>
<target state="translated"><x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/> göre sırala</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Belgeyi düzenle</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Belgeyi ön izle</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Belgeyi düzenle</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="needs-translation">Sort by <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/></target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Редагувати документ</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">Попередній перегляд документа</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">Редагувати документ</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11459,22 +11459,22 @@
Sắp xếp theo
</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html" approved="yes">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="final">Chỉnh sửa tài liệu</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html" approved="yes">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="final">Xem trước tài liệu</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html" approved="yes">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="final">Chỉnh sửa tài liệu</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html" approved="yes">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11396,22 +11396,22 @@
</context-group>
<target state="translated">按 <x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/> 排序</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">编辑文档</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">预览文档</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">编辑文档</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+9 -9
View File
@@ -11397,22 +11397,22 @@
</context-group>
<target state="translated">以<x id="INTERPOLATION" equiv-text="{{getDisplayCustomFieldTitle(field_id)}}"/>排序</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">編輯文件</target>
</trans-unit>
<trans-unit id="3420321797707163677" datatype="html">
<source>Preview document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">329</context>
<context context-type="linenumber">328</context>
</context-group>
<target state="translated">預覽文件</target>
</trans-unit>
<trans-unit id="2179847500064178686" datatype="html">
<source>Edit document</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="translated">編輯文件</target>
</trans-unit>
<trans-unit id="4512084577073831437" datatype="html">
<source>Reset filters / selection</source>
<context-group purpose="location">
+2 -1
View File
@@ -1,6 +1,7 @@
from django.conf import settings
from django.contrib import admin
from guardian.admin import GuardedModelAdmin
from treenode.admin import TreeNodeModelAdmin
from documents.models import Correspondent
from documents.models import CustomField
@@ -28,7 +29,7 @@ class CorrespondentAdmin(GuardedModelAdmin):
list_editable = ("match", "matching_algorithm")
class TagAdmin(GuardedModelAdmin):
class TagAdmin(GuardedModelAdmin, TreeNodeModelAdmin):
list_display = ("name", "color", "match", "matching_algorithm")
list_filter = ("matching_algorithm",)
list_editable = ("color", "match", "matching_algorithm")
+7 -3
View File
@@ -10,9 +10,6 @@ from typing import TYPE_CHECKING
import regex as regex_mod
from django.conf import settings
from pdf2image import convert_from_path
from pikepdf import Page
from pikepdf import PasswordError
from pikepdf import Pdf
from documents.converters import convert_from_tiff_to_pdf
from documents.data_models import ConsumableDocument
@@ -34,6 +31,7 @@ from documents.utils import maybe_override_pixel_limit
from paperless.config import BarcodeConfig
if TYPE_CHECKING:
from pikepdf import Page
from PIL import Image
logger = logging.getLogger("paperless.barcodes")
@@ -282,6 +280,8 @@ class BarcodePlugin(ConsumeTaskPlugin):
# No op if not a TIFF
self.convert_from_tiff_to_pdf()
from pikepdf import PasswordError
try:
self.barcodes = scan_pdf(
self.pdf_file,
@@ -433,6 +433,8 @@ class BarcodePlugin(ConsumeTaskPlugin):
document_paths = []
fname: str = self.input_doc.original_file.stem
from pikepdf import Pdf
with Pdf.open(self.pdf_file) as input_pdf:
# Start with an empty document
current_document: list[Page] = []
@@ -516,6 +518,8 @@ def scan_pdf(pdf_path: Path, settings: BarcodeConfig, work_dir: Path) -> list[Ba
"""
barcodes: list[Barcode] = []
from pikepdf import Pdf
with Pdf.open(pdf_path) as pdf:
num_of_pages = len(pdf.pages)
logger.debug(f"PDF has {num_of_pages} pages")
+13 -7
View File
@@ -255,13 +255,19 @@ def modify_tags(
affected_docs = list(qs.values_list("pk", flat=True))
DocumentTagRelationship = Document.tags.through
hierarchy = Tag.get_hierarchy()
expanded_add_tags = hierarchy.with_ancestors(
Tag.objects.filter(pk__in=add_tags).values_list("pk", flat=True),
)
expanded_remove_tags = hierarchy.with_descendants(
Tag.objects.filter(pk__in=remove_tags).values_list("pk", flat=True),
)
# add with all ancestors
expanded_add_tags: set[int] = set()
add_tag_objects = Tag.objects.filter(pk__in=add_tags)
for t in add_tag_objects:
expanded_add_tags.add(int(t.id))
expanded_add_tags.update(int(pk) for pk in t.get_ancestors_pks())
# remove with all descendants
expanded_remove_tags: set[int] = set()
remove_tag_objects = Tag.objects.filter(pk__in=remove_tags)
for t in remove_tag_objects:
expanded_remove_tags.add(int(t.id))
expanded_remove_tags.update(int(pk) for pk in t.get_descendants_pks())
with transaction.atomic():
if expanded_remove_tags:
+2 -1
View File
@@ -1,6 +1,5 @@
from pathlib import Path
import img2pdf
from PIL import Image
from documents.utils import copy_basic_file_stats
@@ -17,6 +16,8 @@ def convert_from_tiff_to_pdf(tiff_path: Path, target_directory: Path) -> Path:
Returns the path of the PDF created.
"""
import img2pdf
# override pixel setting if needed
maybe_override_pixel_limit()
+2 -1
View File
@@ -6,7 +6,6 @@ from pathlib import Path
from typing import Final
from django.conf import settings
from pikepdf import Pdf
from documents.consumer import ConsumerError
from documents.converters import convert_from_tiff_to_pdf
@@ -85,6 +84,8 @@ class CollatePlugin(NoCleanupPluginMixin, NoSetupPluginMixin, ConsumeTaskPlugin)
valid_staging_exists = True
if valid_staging_exists:
from pikepdf import Pdf
try:
# Collate pages from second PDF in reverse order
with Pdf.open(staging) as pdf1, Pdf.open(pdf_file) as pdf2:
+2 -1
View File
@@ -11,7 +11,6 @@ from decimal import InvalidOperation
from typing import TYPE_CHECKING
from typing import Any
import dateparser
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db.models import Case
@@ -350,6 +349,8 @@ class DateQueryField(serializers.DateField):
if not isinstance(value, str):
raise
import dateparser
try:
parsed = dateparser.parse(
value,
@@ -108,24 +108,6 @@ def _deserialize_record(
) from e
for field_name, field_value in record.get("fields", {}).items():
# Older exports include django-treenode's derived bookkeeping. The
# parent FK is authoritative; accept only these known removed fields.
if Model is Tag and field_name in {
"tn_ancestors_count",
"tn_ancestors_pks",
"tn_children_count",
"tn_children_pks",
"tn_depth",
"tn_descendants_count",
"tn_descendants_pks",
"tn_index",
"tn_level",
"tn_order",
"tn_priority",
"tn_siblings_count",
"tn_siblings_pks",
}:
continue
field = Model._meta.get_field(field_name)
remote = field.remote_field
@@ -1,64 +0,0 @@
# Generated by Django 5.2.17 on 2026-10-28 22:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("documents", "0027_documentbarcode"),
]
operations = [
migrations.RemoveField(
model_name="tag",
name="tn_ancestors_count",
),
migrations.RemoveField(
model_name="tag",
name="tn_ancestors_pks",
),
migrations.RemoveField(
model_name="tag",
name="tn_children_count",
),
migrations.RemoveField(
model_name="tag",
name="tn_children_pks",
),
migrations.RemoveField(
model_name="tag",
name="tn_depth",
),
migrations.RemoveField(
model_name="tag",
name="tn_descendants_count",
),
migrations.RemoveField(
model_name="tag",
name="tn_descendants_pks",
),
migrations.RemoveField(
model_name="tag",
name="tn_index",
),
migrations.RemoveField(
model_name="tag",
name="tn_level",
),
migrations.RemoveField(
model_name="tag",
name="tn_order",
),
migrations.RemoveField(
model_name="tag",
name="tn_priority",
),
migrations.RemoveField(
model_name="tag",
name="tn_siblings_count",
),
migrations.RemoveField(
model_name="tag",
name="tn_siblings_pks",
),
]
+23 -53
View File
@@ -14,6 +14,7 @@ from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from multiselectfield import MultiSelectField
from treenode.models import TreeNodeModel
if settings.AUDIT_LOG_ENABLED:
from auditlog.registry import auditlog
@@ -99,17 +100,7 @@ class Correspondent(MatchingModel):
verbose_name_plural = _("correspondents")
class Tag(MatchingModel):
# Keep the existing column and reverse relation during the transition.
tn_parent = models.ForeignKey(
"self",
blank=True,
null=True,
on_delete=models.CASCADE,
related_name="tn_children",
verbose_name=_("Parent"),
)
class Tag(MatchingModel, TreeNodeModel):
color = models.CharField(_("color"), max_length=7, default="#a6cee3")
# Maximum allowed nesting depth for tags (root = 1, max depth = 5)
MAX_NESTING_DEPTH: Final[int] = 5
@@ -123,51 +114,27 @@ class Tag(MatchingModel):
),
)
class Meta(MatchingModel.Meta):
class Meta(MatchingModel.Meta, TreeNodeModel.Meta):
verbose_name = _("tag")
verbose_name_plural = _("tags")
@classmethod
def get_hierarchy(cls):
from documents.tag_hierarchy import TagHierarchy
return TagHierarchy(cls.objects.values_list("pk", "tn_parent_id"))
@property
def parent_pk(self):
return self.tn_parent_id
def get_parent(self):
return self.tn_parent
def get_ancestors_pks(self):
return self.get_hierarchy().ancestors(self.tn_parent_id)
def get_ancestors(self):
ids = self.get_ancestors_pks()
tags = self.__class__.objects.in_bulk(ids)
return [tags[pk] for pk in ids]
def get_descendants_pks(self):
return self.get_hierarchy().descendants([self.pk])
def get_descendants(self):
return self.__class__.objects.filter(pk__in=self.get_descendants_pks())
def get_children_queryset(self):
return self.__class__.objects.filter(tn_parent_id=self.pk)
def clean(self) -> None:
hierarchy = self.get_hierarchy()
if self.pk is not None and self.tn_parent_id == self.pk:
raise ValidationError({"tn_parent": _("Cannot set itself as parent.")})
ancestors = hierarchy.ancestors(self.tn_parent_id)
if self.pk is not None and self.pk in ancestors:
raise ValidationError(
{"tn_parent": _("Cannot set parent to a descendant.")},
)
if len(ancestors) + 1 + hierarchy.height(self.pk) > self.MAX_NESTING_DEPTH:
raise ValidationError({"tn_parent": _("Maximum nesting depth exceeded.")})
# Prevent self-parenting and assigning a descendant as parent
parent = self.get_parent()
if parent == self:
raise ValidationError({"parent": _("Cannot set itself as parent.")})
if parent and self.pk is not None and self.is_ancestor_of(parent):
raise ValidationError({"parent": _("Cannot set parent to a descendant.")})
# Enforce maximum nesting depth
new_parent_depth = 0
if parent:
new_parent_depth = parent.get_ancestors_count() + 1
height = 0 if self.pk is None else self.get_depth()
deepest_new_depth = (new_parent_depth + 1) + height
if deepest_new_depth > self.MAX_NESTING_DEPTH:
raise ValidationError({"parent": _("Maximum nesting depth exceeded.")})
return super().clean()
@@ -572,7 +539,10 @@ class Document(SoftDeleteModel, ModelWithOwner): # type: ignore[django-manager-
return self.created
def add_nested_tags(self, tags) -> None:
tag_ids = Tag.get_hierarchy().with_ancestors(tag.pk for tag in tags)
tag_ids = set()
for tag in tags:
tag_ids.add(tag.id)
tag_ids.update(tag.get_ancestors_pks())
tags_to_add = self.tags.model.objects.filter(id__in=tag_ids)
self.tags.add(*tags_to_add)
+14 -2
View File
@@ -14,8 +14,6 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from typing import NamedTuple
import pikepdf
if TYPE_CHECKING:
from collections.abc import Callable
from collections.abc import Iterable
@@ -43,6 +41,8 @@ def rotate_pdf(src: Path, dst: Path, degrees: int) -> None:
Rotate every page relatively on the opened document, not a rebuild, so Info,
XMP and outlines are kept. ``src`` is not modified.
"""
import pikepdf
with pikepdf.open(src) as pdf:
for page in pdf.pages:
page.rotate(degrees, relative=True)
@@ -58,6 +58,8 @@ def remove_pages(src: Path, dst: Path, pages: Iterable[int]) -> None:
Duplicates are ignored. Pages are removed highest first so earlier removals
never shift the index of later ones.
"""
import pikepdf
unique = sorted(set(pages))
_require_positive(unique)
with pikepdf.open(src) as pdf:
@@ -81,6 +83,8 @@ def build_pdfs(
(Info, XMP, outlines) is not carried over. Returns the written paths in output
order.
"""
import pikepdf
for specs, _ in outputs:
_require_positive(spec.page for spec in specs)
@@ -140,6 +144,8 @@ def needs_decrypt(src: Path) -> bool:
True if ``src`` is encrypted. A PDF that needs a password to open at all
counts as encrypted.
"""
import pikepdf
try:
with pikepdf.open(src) as pdf:
return bool(pdf.is_encrypted)
@@ -154,6 +160,8 @@ def decrypt_pdf(src: Path, make_dst: Callable[[], Path], password: str) -> Path:
``make_dst`` is only called once the password has been accepted, so a wrong
password never leaves a destination behind.
"""
import pikepdf
with pikepdf.open(src, password=password) as pdf:
pdf.remove_unreferenced_resources()
dst = make_dst()
@@ -170,6 +178,8 @@ class PdfMerger:
"""
def __init__(self) -> None:
import pikepdf
self._pdf = pikepdf.new()
self._version: str = self._pdf.pdf_version
@@ -185,6 +195,8 @@ class PdfMerger:
self._pdf.close()
def add(self, path: Path) -> None:
import pikepdf
with pikepdf.open(str(path)) as pdf:
self._version = max(self._version, pdf.pdf_version)
self._pdf.pages.extend(pdf.pages)
+2 -2
View File
@@ -7,8 +7,6 @@ from dataclasses import dataclass
from types import TracebackType
from typing import Self
import dateparser
logger = logging.getLogger(__name__)
@@ -80,6 +78,8 @@ class DateParserPluginBase(ABC):
Uses configuration from `self.config`.
"""
import dateparser
try:
return dateparser.parse(
date_string,
+50 -28
View File
@@ -589,7 +589,7 @@ class TagSerializer(MatchingModelSerializer, OwnedObjectSerializer):
text_color = serializers.SerializerMethodField()
# Preserve the public API parent field.
# map to treenode's tn_parent
parent = serializers.PrimaryKeyRelatedField(
queryset=Tag.objects.all(),
allow_null=True,
@@ -692,9 +692,7 @@ class TagSerializer(MatchingModelSerializer, OwnedObjectSerializer):
self.instance.clean()
except ValidationError as e:
logger.debug("Tag parent validation failed: %s", e)
raise serializers.ValidationError(
{"parent": e.message_dict["tn_parent"]},
) from e
raise e
finally:
self.instance.tn_parent = original_parent
else:
@@ -704,9 +702,7 @@ class TagSerializer(MatchingModelSerializer, OwnedObjectSerializer):
temp.clean()
except ValidationError as e:
logger.debug("Tag parent validation failed: %s", e)
raise serializers.ValidationError(
{"parent": e.message_dict["tn_parent"]},
) from e
raise e
return super().validate(attrs)
@@ -1202,35 +1198,61 @@ class DocumentSerializer(
doc_id,
)
if "tags" in validated_data:
hierarchy = Tag.get_hierarchy()
previous_ids = set(instance.tags.values_list("pk", flat=True))
requested_ids = {tag.pk for tag in validated_data["tags"]}
required_ids = hierarchy.with_ancestors(requested_ids - previous_ids)
blocked_ids = hierarchy.with_descendants(previous_ids - requested_ids)
blocked_ids.difference_update(required_ids)
final_ids = hierarchy.with_ancestors(requested_ids) - blocked_ids
validated_data["tags"] = list(Tag.objects.filter(pk__in=final_ids))
# Respect tag hierarchy on updates:
# - Adding a child adds its ancestors
# - Removing a parent removes all its descendants
prev_tags = set(instance.tags.all())
requested_tags = set(validated_data["tags"])
# Tags newly added in this update and the ancestors they require
added_tags = requested_tags - prev_tags
required_by_add_tags = set(added_tags)
for t in added_tags:
required_by_add_tags.update(t.get_ancestors())
# Tags being removed in this update and all descendants, except
# those required by a tag that is being added in this same update
removed_tags = prev_tags - requested_tags
blocked_tags = set(removed_tags)
for t in removed_tags:
blocked_tags.update(t.get_descendants())
blocked_tags.difference_update(required_by_add_tags)
# Add all parent tags
final_tags = set(requested_tags)
for t in requested_tags:
final_tags.update(t.get_ancestors())
# Drop removed parents and their descendants
final_tags.difference_update(blocked_tags)
validated_data["tags"] = list(final_tags)
if validated_data.get("remove_inbox_tags"):
hierarchy = Tag.get_hierarchy()
current_tag_ids = {t.pk for t in instance.tags.all()}
tags = (
validated_data["tags"]
if "tags" in validated_data
else list(instance.tags.all())
)
current_ids = set(instance.tags.values_list("pk", flat=True))
keep_ids = hierarchy.with_ancestors(
tag.pk for tag in tags if tag.pk not in current_ids
)
inbox_ids = (
# Tags newly added in this update, plus their ancestors, are kept
keep_ids: set[int] = set()
for tag in tags:
if tag.pk not in current_tag_ids:
keep_ids.add(tag.pk)
keep_ids.update(int(pk) for pk in tag.get_ancestors_pks())
# Remove inbox tags and their descendants, except those being kept
remove_ids: set[int] = set()
for inbox_tag in (
Tag.objects.filter(is_inbox_tag=True)
.exclude(
pk__in=keep_ids,
)
.values_list("pk", flat=True)
)
remove_ids = hierarchy.with_descendants(inbox_ids) - keep_ids
validated_data["tags"] = [tag for tag in tags if tag.pk not in remove_ids]
.exclude(pk__in=keep_ids)
.only("pk", "tn_descendants_pks")
):
remove_ids.add(inbox_tag.pk)
remove_ids.update(int(pk) for pk in inbox_tag.get_descendants_pks())
validated_data["tags"] = [t for t in tags if t.pk not in remove_ids]
if settings.AUDIT_LOG_ENABLED:
with set_actor(self.user):
+6 -6
View File
@@ -476,13 +476,13 @@ def update_filename_and_move_files(
return
with FileLock(settings.MEDIA_LOCK):
try:
# If this was waiting for the lock, the filename or archive_filename
# of this document may have been updated. This happens if multiple updates
# get queued from the UI for the same document
# So freshen up the data before doing anything
instance.refresh_from_db()
# If this was waiting for the lock, the filename or archive_filename
# of this document may have been updated. This happens if multiple updates
# get queued from the UI for the same document
# So freshen up the data before doing anything
instance.refresh_from_db()
try:
old_filename = instance.filename
old_source_path = instance.source_path
move_original = False
-61
View File
@@ -1,61 +0,0 @@
"""An operation-local snapshot of tag edges, with no persistent derived fields.
Never cache this globally: tasks, bulk updates and other processes can change
parents. Callers traversing multiple tags should share one snapshot.
"""
from collections import defaultdict
from collections.abc import Iterable
class TagHierarchy:
def __init__(self, edges: Iterable[tuple[int, int | None]]) -> None:
self.parents = dict(edges)
self.children: dict[int | None, list[int]] = defaultdict(list)
for pk, parent in self.parents.items():
self.children[parent].append(pk)
def ancestors(self, parent_id: int | None) -> list[int]:
result = []
seen = set()
while parent_id is not None and parent_id not in seen:
seen.add(parent_id)
result.append(parent_id)
parent_id = self.parents.get(parent_id)
return result[::-1]
def with_ancestors(self, roots: Iterable[int]) -> set[int]:
result = set(roots)
for pk in list(result):
result.update(self.ancestors(self.parents.get(pk)))
return result
def with_descendants(self, roots: Iterable[int]) -> set[int]:
roots = set(roots)
return roots | self.descendants(roots)
def descendants(self, roots: Iterable[int]) -> set[int]:
roots = {pk for pk in roots if pk is not None}
seen = set(roots)
pending = list(roots)
while pending:
for child in self.children.get(pending.pop(), ()):
if child not in seen:
seen.add(child)
pending.append(child)
return seen - roots
def height(self, pk: int | None) -> int:
if pk is None:
return 0
seen = {pk}
pending = [(pk, 0)]
height = 0
while pending:
parent, depth = pending.pop()
height = max(height, depth)
for child in self.children.get(parent, ()):
if child not in seen:
seen.add(child)
pending.append((child, depth + 1))
return height
+8 -3
View File
@@ -76,9 +76,6 @@ from paperless.logging import consume_task_id
from paperless.parsers import ParserContext
from paperless.parsers.registry import get_parser_registry
from paperless_ai.exceptions import LLMTimeoutError
from paperless_ai.indexing import llm_index_add_or_update_document
from paperless_ai.indexing import llm_index_remove_document
from paperless_ai.indexing import update_llm_index
if settings.AUDIT_LOG_ENABLED:
from auditlog.models import LogEntry
@@ -339,6 +336,8 @@ def bulk_update_documents(document_ids) -> None:
ai_config = AIConfig()
if ai_config.llm_index_enabled:
from paperless_ai.indexing import update_llm_index
update_llm_index(
rebuild=False,
document_ids=document_ids,
@@ -499,6 +498,8 @@ def update_document_content_maybe_archive_file(
ai_config = AIConfig()
if ai_config.llm_index_enabled:
from paperless_ai.indexing import llm_index_add_or_update_document
llm_index_add_or_update_document(document)
clear_document_caches(document.pk)
@@ -800,11 +801,15 @@ def apply_ai_suggestions(self, action_id: int, document_id: int) -> None:
@shared_task
def update_document_in_llm_index(document) -> None:
from paperless_ai.indexing import llm_index_add_or_update_document
llm_index_add_or_update_document(document)
@shared_task
def remove_document_from_llm_index(document) -> None:
from paperless_ai.indexing import llm_index_remove_document
llm_index_remove_document(document)
+4 -1
View File
@@ -44,7 +44,10 @@ def localize_date(value: date | datetime | str, format: str, locale: str) -> str
def format_datetime(value: str | datetime, format: str) -> str:
if isinstance(value, str):
value = parse_date(value)
parsed = parse_datetime(value) or parse_date(value)
if parsed is None:
raise ValueError(f"Invalid date or datetime string: {value!r}")
return parsed.strftime(format=format)
return value.strftime(format=format)
@@ -71,7 +71,7 @@ class TestParseString:
):
# We still need to mock dateparser.parse to force the exception
mocker.patch(
"documents.plugins.date_parsing.base.dateparser.parse",
"dateparser.parse",
side_effect=ValueError(
"Parsing error: 01/01/2024",
),
@@ -260,7 +260,7 @@ class TestRegexDateParser:
parser = RegexDateParserPlugin(base_config)
# Patch the dateparser.parse
target = "documents.plugins.date_parsing.base.dateparser.parse"
target = "dateparser.parse"
def fake_parse(
date_string: str,
@@ -335,7 +335,7 @@ class TestRegexDateParser:
)
parser = RegexDateParserPlugin(cfg)
target = "documents.plugins.date_parsing.base.dateparser.parse"
target = "dateparser.parse"
def fake_parse(
date_string: str,
@@ -400,7 +400,7 @@ class TestRegexDateParser:
parser = RegexDateParserPlugin(cfg)
# Patch the module's dateparser.parse so we can inspect calls
target = "documents.plugins.date_parsing.base.dateparser.parse"
target = "dateparser.parse"
def fake_parse(
date_string: str,
+5 -5
View File
@@ -1070,7 +1070,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists") as mock_exists,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists,
):
mock_exists.return_value = False
self.client.patch(
@@ -1095,7 +1095,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists", return_value=False),
patch("paperless_ai.indexing.llm_index_exists", return_value=False),
):
self.client.patch(
f"{self.ENDPOINT}1/",
@@ -1115,7 +1115,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists") as mock_exists,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists,
):
mock_exists.return_value = True
self.client.patch(
@@ -1136,7 +1136,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists") as mock_exists,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists,
):
mock_exists.return_value = True
self.client.patch(
@@ -1174,7 +1174,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless.views.llm_index_exists") as mock_exists,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists,
):
mock_exists.return_value = True
self.client.patch(
+2 -2
View File
@@ -61,7 +61,7 @@ class TestChatStreamingViewInputValidation(APITestCase):
return_value=self._mock_ai_enabled(),
),
mock.patch(
"documents.views.stream_chat_with_documents",
"paperless_ai.chat.stream_chat_with_documents",
return_value=iter(chunks),
),
):
@@ -104,7 +104,7 @@ class TestChatStreamingViewUnrestrictedFlag:
"""
mocker.patch("documents.views.AIConfig").return_value.ai_enabled = True
return mocker.patch(
"documents.views.stream_chat_with_documents",
"paperless_ai.chat.stream_chat_with_documents",
return_value=iter(()),
)
@@ -1110,7 +1110,7 @@ class TestVersionActionPermissions(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, 403)
with (
mock.patch("documents.views.AIConfig") as ai_config,
mock.patch("documents.views.stream_chat_with_documents") as chat,
mock.patch("paperless_ai.chat.stream_chat_with_documents") as chat,
):
ai_config.return_value.ai_enabled = True
response = self.client.post(
+1 -1
View File
@@ -2686,7 +2686,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
response = self.client.get("/api/documents/34676/suggestions/")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@mock.patch("documents.views.get_ai_document_classification")
@mock.patch("paperless_ai.ai_classifier.get_ai_document_classification")
@override_settings(AI_ENABLED=True)
def test_suggestions_still_uses_classifier_when_ai_enabled(
self,
+29
View File
@@ -308,3 +308,32 @@ class TestBulkDownloadSchema:
def test_bulk_download_response_403(self, api_schema: SchemaGenerator) -> None:
op = api_schema["paths"]["/api/documents/bulk_download/"]["post"]
assert "403" in op["responses"]
class TestDocumentListObjectFilterSchema:
"""documents_list: comma-separated ObjectFilter params must be typed as integer arrays."""
@pytest.mark.parametrize(
"param_name",
[
"tags__id__all",
"tags__id__none",
"tags__id__in",
"correspondent__id__none",
"document_type__id__none",
"storage_path__id__none",
"owner__id__none",
"custom_fields__id__all",
"custom_fields__id__none",
"custom_fields__id__in",
],
)
def test_object_filter_param_is_integer_array(
self,
api_schema: SchemaGenerator,
param_name: str,
) -> None:
params = api_schema["paths"]["/api/documents/"]["get"]["parameters"]
param = next(p for p in params if p["name"] == param_name)
assert param["schema"] == {"type": "array", "items": {"type": "integer"}}
assert param["explode"] is False
+47
View File
@@ -539,3 +539,50 @@ class TestSystemStatus(APITestCase):
self.assertEqual(response.data["tasks"]["summary"]["success_count"], 1)
self.assertEqual(response.data["tasks"]["summary"]["failure_count"], 1)
self.assertEqual(response.data["tasks"]["summary"]["pending_count"], 1)
def test_system_status_reports_database_error(self) -> None:
"""
GIVEN:
- Reading the migration state fails
WHEN:
- The system status is requested
THEN:
- The response reports the database error rather than a server error
"""
self.client.force_login(self.user)
with mock.patch(
"documents.views.MigrationLoader",
side_effect=Exception("database is down"),
):
response = self.client.get(self.ENDPOINT)
self.assertEqual(response.status_code, status.HTTP_200_OK)
database = response.data["database"]
self.assertEqual(database["status"], "ERROR")
self.assertIsNotNone(database["error"])
self.assertEqual(
database["migration_status"],
{"latest_migration": None, "unapplied_migrations": []},
)
def test_system_status_migration_query_error(self) -> None:
"""
GIVEN:
- The migration graph loads but reading the applied migrations fails
WHEN:
- The system status is requested
THEN:
- No migrations are reported as unapplied, since the state is unknown
"""
self.client.force_login(self.user)
with mock.patch("documents.views.MigrationRecorder") as recorder:
recorder.Migration.objects.all.side_effect = Exception("query failed")
response = self.client.get(self.ENDPOINT)
self.assertEqual(response.status_code, status.HTTP_200_OK)
database = response.data["database"]
self.assertEqual(database["status"], "ERROR")
self.assertEqual(
database["migration_status"],
{"latest_migration": None, "unapplied_migrations": []},
)
+26
View File
@@ -31,6 +31,7 @@ from documents.serialisers import DocumentSerializer
from documents.tasks import empty_trash
from paperless_testing.assertions import FileSystemAssertsMixin
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.dirs import PaperlessDirs
from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import UserFactory
@@ -2134,3 +2135,28 @@ class TestPathDateLocalization:
with override_settings(FILENAME_FORMAT=filename_format):
filename = generate_filename(document)
assert filename == Path(expected_filename)
@pytest.mark.django_db
def test_refresh_failure_surfaces_database_error(
paperless_dirs: PaperlessDirs,
) -> None:
"""
GIVEN:
- A document with a stored filename
WHEN:
- Saving it triggers a filename update and refreshing it from the database fails
THEN:
- The database error is raised, not an unbound variable error from the rollback
"""
document = DocumentFactory(filename="some.pdf")
with (
mock.patch.object(
Document,
"refresh_from_db",
side_effect=DatabaseError("database is locked"),
),
pytest.raises(DatabaseError, match="database is locked"),
):
document.save()
Loaded 100 of 172 files, more files were not shown because too many files have changed in this diff. Show more