mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-11-01 20:45:35 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7268d6680 | ||
|
|
5b63c9a0ff | ||
|
|
217c5e681d | ||
|
|
45e99cde5a | ||
|
|
7ea58eaf46 | ||
|
|
a19804a0b7 | ||
|
|
3289be7383 | ||
|
|
d98918dd97 | ||
|
|
34c88613f6 | ||
|
|
13acdabb47 | ||
|
|
b67cfdf3fd | ||
|
|
98d2ddd1c6 | ||
|
|
91b35eada6 | ||
|
|
11248f776d | ||
|
|
331fa13088 | ||
|
|
ebddd5687c | ||
|
|
2925b8de81 | ||
|
|
5e397e1222 | ||
|
|
6f99ad9469 | ||
|
|
a5d3247ca7 | ||
|
|
8a67481a9d | ||
|
|
652f964f5d | ||
|
|
80a1827fa0 |
No files matched your search
@@ -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
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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
@@ -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">
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "paperless-ngx-ui",
|
||||
"version": "3.4.0",
|
||||
"version": "3.4.1",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"ng": "ng",
|
||||
@@ -42,7 +42,7 @@
|
||||
"rxjs": "^7.8.2",
|
||||
"tslib": "^2.8.1",
|
||||
"utif": "^3.1.0",
|
||||
"uuid": "^14.0.2"
|
||||
"uuid": "^14.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-builders/jest": "^22.0.1",
|
||||
|
||||
Generated
+6
-6
@@ -94,8 +94,8 @@ importers:
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0
|
||||
uuid:
|
||||
specifier: ^14.0.2
|
||||
version: 14.0.2
|
||||
specifier: ^14.0.3
|
||||
version: 14.0.3
|
||||
devDependencies:
|
||||
'@angular-builders/jest':
|
||||
specifier: ^22.0.1
|
||||
@@ -4100,8 +4100,8 @@ packages:
|
||||
utif@3.1.0:
|
||||
resolution: {integrity: sha512-WEo4D/xOvFW53K5f5QTaTbbiORcm2/pCL9P6qmJnup+17eYfKaEhDeX9PeQkuyEoIxlbGklDuGl8xwuXYMrrXQ==}
|
||||
|
||||
uuid@14.0.2:
|
||||
resolution: {integrity: sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==}
|
||||
uuid@14.0.3:
|
||||
resolution: {integrity: sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw==}
|
||||
hasBin: true
|
||||
|
||||
v8-compile-cache-lib@3.0.1:
|
||||
@@ -7100,7 +7100,7 @@ snapshots:
|
||||
dependencies:
|
||||
mkdirp: 1.0.4
|
||||
strip-ansi: 6.0.1
|
||||
uuid: 14.0.2
|
||||
uuid: 14.0.3
|
||||
xml: 1.0.1
|
||||
|
||||
jest-leak-detector@30.5.1:
|
||||
@@ -8295,7 +8295,7 @@ snapshots:
|
||||
dependencies:
|
||||
pako: 1.0.11
|
||||
|
||||
uuid@14.0.2: {}
|
||||
uuid@14.0.3: {}
|
||||
|
||||
v8-compile-cache-lib@3.0.1: {}
|
||||
|
||||
|
||||
+2
-4
@@ -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>
|
||||
|
||||
-7
@@ -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 />
|
||||
@@ -485,7 +485,7 @@
|
||||
</div>
|
||||
}
|
||||
<div class="input-group input-group-sm w-auto flex-nowrap">
|
||||
<button class="btn btn-outline-secondary" (click)="decreaseZoom()" aria-label="Zoom out" i18n-aria-label>
|
||||
<button class="btn btn-outline-secondary" type="button" (click)="decreaseZoom()" aria-label="Zoom out" i18n-aria-label>
|
||||
<i-bs width="1em" height="1em" name="dash"></i-bs>
|
||||
</button>
|
||||
<select class="form-select" (change)="setZoom($event.target.value)" [ngModelOptions]="{standalone: true}" [ngModel]="currentZoom" aria-label="Zoom" i18n-aria-label>
|
||||
@@ -495,7 +495,7 @@
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
<button class="btn btn-outline-secondary" (click)="increaseZoom()" aria-label="Zoom in" i18n-aria-label>
|
||||
<button class="btn btn-outline-secondary" type="button" (click)="increaseZoom()" aria-label="Zoom in" i18n-aria-label>
|
||||
<i-bs width="1em" height="1em" name="plus"></i-bs>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+2
@@ -47,6 +47,8 @@
|
||||
<span>{{ change.key | titlecase }}</span>:
|
||||
@if (change.key === 'content') {
|
||||
<code class="text-primary">{{ change.value[1]?.substring(0,100) }}...</code>
|
||||
} @else if (change.key === 'tags') {
|
||||
<code class="text-primary">{{ getTagNames($any(change.value[1])) | async }}</code>
|
||||
} @else {
|
||||
<code class="text-primary">{{ getPrettyName(change.key, change.value[1]) | async }}</code>
|
||||
}
|
||||
|
||||
+34
@@ -12,6 +12,7 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic
|
||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
||||
import { TagService } from 'src/app/services/rest/tag.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { DocumentHistoryComponent } from './document-history.component'
|
||||
|
||||
@@ -174,6 +175,39 @@ describe('DocumentHistoryComponent', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should render bulk tag history with names and preserve missing tag IDs', () => {
|
||||
const getCachedSpy = jest
|
||||
.spyOn(TestBed.inject(TagService), 'getCached')
|
||||
.mockImplementation((id) =>
|
||||
of(id === 1 ? { id: 1, name: 'Invoice' } : null)
|
||||
)
|
||||
component.entries.set([
|
||||
{
|
||||
id: 1,
|
||||
action: AuditLogAction.Update,
|
||||
timestamp: '2021-01-01T00:00:00Z',
|
||||
remote_addr: null,
|
||||
changes: JSON.parse('{"tags":[[],[1,2345]]}'),
|
||||
},
|
||||
])
|
||||
component.loading.set(false)
|
||||
fixture.detectChanges()
|
||||
|
||||
expect(fixture.nativeElement.querySelector('code').textContent).toBe(
|
||||
'Invoice, 2345'
|
||||
)
|
||||
fixture.detectChanges()
|
||||
expect(getCachedSpy).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('should handle removal of all tags without a lookup', () => {
|
||||
const getCachedSpy = jest.spyOn(TestBed.inject(TagService), 'getCached')
|
||||
component.getTagNames([]).subscribe((result) => {
|
||||
expect(result).toBe('')
|
||||
})
|
||||
expect(getCachedSpy).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('getPrettyName should return the value as is for other types', () => {
|
||||
const id = '123'
|
||||
component.getPrettyName('other', id).subscribe((result) => {
|
||||
|
||||
+27
-1
@@ -2,7 +2,7 @@ import { AsyncPipe, KeyValuePipe, TitleCasePipe } from '@angular/common'
|
||||
import { Component, Input, OnInit, inject, signal } from '@angular/core'
|
||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { Observable, first, map, of, shareReplay } from 'rxjs'
|
||||
import { Observable, first, forkJoin, map, of, shareReplay } from 'rxjs'
|
||||
import { AuditLogAction, AuditLogEntry } from 'src/app/data/auditlog-entry'
|
||||
import { DataType } from 'src/app/data/datatype'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
@@ -10,6 +10,7 @@ import { CorrespondentService } from 'src/app/services/rest/correspondent.servic
|
||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||
import { StoragePathService } from 'src/app/services/rest/storage-path.service'
|
||||
import { TagService } from 'src/app/services/rest/tag.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
|
||||
@Component({
|
||||
@@ -30,6 +31,7 @@ export class DocumentHistoryComponent implements OnInit {
|
||||
private readonly correspondentService = inject(CorrespondentService)
|
||||
private readonly storagePathService = inject(StoragePathService)
|
||||
private readonly documentTypeService = inject(DocumentTypeService)
|
||||
private readonly tagService = inject(TagService)
|
||||
private readonly userService = inject(UserService)
|
||||
|
||||
public AuditLogAction = AuditLogAction
|
||||
@@ -63,6 +65,30 @@ export class DocumentHistoryComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
getTagNames(tagIds: number[]): Observable<string> {
|
||||
const cacheKey = `tags:${JSON.stringify(tagIds)}`
|
||||
const cachedNames = this.prettyNameCache.get(cacheKey)
|
||||
if (cachedNames) {
|
||||
return cachedNames
|
||||
}
|
||||
|
||||
const tagNames$ = tagIds.length
|
||||
? forkJoin(
|
||||
tagIds.map((tagId) =>
|
||||
this.tagService.getCached(tagId).pipe(
|
||||
first(),
|
||||
map((tag) => tag?.name ?? String(tagId))
|
||||
)
|
||||
)
|
||||
).pipe(map((names) => names.join(', ')))
|
||||
: of('')
|
||||
const names$ = tagNames$.pipe(
|
||||
shareReplay({ bufferSize: 1, refCount: true })
|
||||
)
|
||||
this.prettyNameCache.set(cacheKey, names$)
|
||||
return names$
|
||||
}
|
||||
|
||||
getPrettyName(type: DataType | string, id: string): Observable<string> {
|
||||
const cacheKey = `${type}:${id}`
|
||||
const cached = this.prettyNameCache.get(cacheKey)
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
-2
@@ -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,
|
||||
|
||||
-2
@@ -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,
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
-2
@@ -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) {
|
||||
|
||||
@@ -24,7 +24,7 @@ export interface SystemStatus {
|
||||
status: SystemStatusItemStatus
|
||||
error?: string
|
||||
migration_status: {
|
||||
latest_migration: string
|
||||
latest_migration: string | null
|
||||
unapplied_migrations: string[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/',
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 "<x id="PH" equiv-text="newGroup.name"/>".</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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
+1503
-1504
File diff suppressed because it is too large.
Load diff
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -11398,22 +11398,22 @@ tüm <x id="CLOSE_EMPHASISED_TEXT" ctype="x-em" equiv-text="</em>"/> 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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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,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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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": []},
|
||||
)
|
||||
@@ -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()
|
||||
@@ -4,6 +4,7 @@ from typing import Literal
|
||||
|
||||
import pytest
|
||||
|
||||
from documents.templating.filters import format_datetime
|
||||
from documents.templating.filters import localize_date
|
||||
|
||||
|
||||
@@ -294,3 +295,23 @@ class TestDateLocalization:
|
||||
Tests `localize_date` with `date` string across different locales and formats.
|
||||
"""
|
||||
assert localize_date(value, format_style, locale_str) == expected_output
|
||||
|
||||
|
||||
class TestFormatDatetime:
|
||||
@pytest.mark.parametrize(
|
||||
("value", "expected"),
|
||||
[
|
||||
pytest.param("2026-09-21", "2026-09-21 00:00", id="date"),
|
||||
pytest.param(
|
||||
"2026-09-21T13:45:00+00:00",
|
||||
"2026-09-21 13:45",
|
||||
id="datetime",
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_parses_iso_strings(self, value: str, expected: str) -> None:
|
||||
assert format_datetime(value, "%Y-%m-%d %H:%M") == expected
|
||||
|
||||
def test_rejects_unparseable_string(self) -> None:
|
||||
with pytest.raises(ValueError, match="not a date"):
|
||||
format_datetime("not a date", "%Y")
|
||||
@@ -572,7 +572,7 @@ class TestAiChatAllDocumentsPermissionBoundary:
|
||||
ENDPOINT = "/api/documents/chat/"
|
||||
|
||||
@override_settings(AI_ENABLED=True)
|
||||
@patch("documents.views.stream_chat_with_documents")
|
||||
@patch("paperless_ai.chat.stream_chat_with_documents")
|
||||
def test_chat_all_documents_excludes_unshared_document(self, mock_stream_chat):
|
||||
mock_stream_chat.return_value = iter([b"data"])
|
||||
|
||||
|
||||
Loaded 100 of 170 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user