mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-01-26 22:49:01 -06:00
Compare commits
1 Commits
dev
...
chore/ubun
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f910441f7 |
4
.github/workflows/ci-docs.yml
vendored
4
.github/workflows/ci-docs.yml
vendored
@@ -23,7 +23,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build Documentation
|
name: Build Documentation
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
name: Deploy Documentation
|
name: Deploy Documentation
|
||||||
needs: build
|
needs: build
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|||||||
2
.github/workflows/ci-lint.yml
vendored
2
.github/workflows/ci-lint.yml
vendored
@@ -12,7 +12,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
pre-commit:
|
pre-commit:
|
||||||
name: Pre-commit Checks
|
name: Pre-commit Checks
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|||||||
2
.github/workflows/crowdin.yml
vendored
2
.github/workflows/crowdin.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
synchronize-with-crowdin:
|
synchronize-with-crowdin:
|
||||||
name: Crowdin Sync
|
name: Crowdin Sync
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
|
|||||||
2
.github/workflows/pr-bot.yml
vendored
2
.github/workflows/pr-bot.yml
vendored
@@ -8,7 +8,7 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
pr-bot:
|
pr-bot:
|
||||||
name: Automated PR Bot
|
name: Automated PR Bot
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- name: Label PR by file path or branch name
|
- name: Label PR by file path or branch name
|
||||||
# see .github/labeler.yml for the labeler config
|
# see .github/labeler.yml for the labeler config
|
||||||
|
|||||||
2
.github/workflows/project-actions.yml
vendored
2
.github/workflows/project-actions.yml
vendored
@@ -12,7 +12,7 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
pr_opened_or_reopened:
|
pr_opened_or_reopened:
|
||||||
name: pr_opened_or_reopened
|
name: pr_opened_or_reopened
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
permissions:
|
permissions:
|
||||||
# write permission is required for autolabeler
|
# write permission is required for autolabeler
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|||||||
10
.github/workflows/repo-maintenance.yml
vendored
10
.github/workflows/repo-maintenance.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
name: 'Stale'
|
name: 'Stale'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v10
|
- uses: actions/stale@v10
|
||||||
with:
|
with:
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
lock-threads:
|
lock-threads:
|
||||||
name: 'Lock Old Threads'
|
name: 'Lock Old Threads'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v6
|
- uses: dessant/lock-threads@v6
|
||||||
with:
|
with:
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
close-answered-discussions:
|
close-answered-discussions:
|
||||||
name: 'Close Answered Discussions'
|
name: 'Close Answered Discussions'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v8
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
@@ -112,7 +112,7 @@ jobs:
|
|||||||
close-outdated-discussions:
|
close-outdated-discussions:
|
||||||
name: 'Close Outdated Discussions'
|
name: 'Close Outdated Discussions'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v8
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
@@ -204,7 +204,7 @@ jobs:
|
|||||||
close-unsupported-feature-requests:
|
close-unsupported-feature-requests:
|
||||||
name: 'Close Unsupported Feature Requests'
|
name: 'Close Unsupported Feature Requests'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v8
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/translate-strings.yml
vendored
2
.github/workflows/translate-strings.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
generate-translate-strings:
|
generate-translate-strings:
|
||||||
name: Generate Translation Strings
|
name: Generate Translation Strings
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -1152,9 +1152,8 @@ via the consumption directory, you can disable the consumer to save resources.
|
|||||||
|
|
||||||
#### [`PAPERLESS_CONSUMER_DELETE_DUPLICATES=<bool>`](#PAPERLESS_CONSUMER_DELETE_DUPLICATES) {#PAPERLESS_CONSUMER_DELETE_DUPLICATES}
|
#### [`PAPERLESS_CONSUMER_DELETE_DUPLICATES=<bool>`](#PAPERLESS_CONSUMER_DELETE_DUPLICATES) {#PAPERLESS_CONSUMER_DELETE_DUPLICATES}
|
||||||
|
|
||||||
: As of version 3.0 Paperless-ngx allows duplicate documents to be consumed by default, _except_ when
|
: When the consumer detects a duplicate document, it will not touch
|
||||||
this setting is enabled. When enabled, Paperless will check if a document with the same hash already
|
the original document. This default behavior can be changed here.
|
||||||
exists in the system and delete the duplicate file from the consumption directory without consuming it.
|
|
||||||
|
|
||||||
Defaults to false.
|
Defaults to false.
|
||||||
|
|
||||||
|
|||||||
@@ -534,7 +534,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||||
<context context-type="linenumber">427</context>
|
<context context-type="linenumber">396</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3768927257183755959" datatype="html">
|
<trans-unit id="3768927257183755959" datatype="html">
|
||||||
@@ -593,7 +593,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||||
<context context-type="linenumber">420</context>
|
<context context-type="linenumber">389</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component.html</context>
|
||||||
@@ -761,7 +761,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||||
<context context-type="linenumber">440</context>
|
<context context-type="linenumber">409</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||||
@@ -1867,18 +1867,11 @@
|
|||||||
<context context-type="linenumber">97</context>
|
<context context-type="linenumber">97</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1418249006616536391" datatype="html">
|
|
||||||
<source>Duplicate(s) detected</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
|
||||||
<context context-type="linenumber">103</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="1536087519743707362" datatype="html">
|
<trans-unit id="1536087519743707362" datatype="html">
|
||||||
<source>Dismiss</source>
|
<source>Dismiss</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
<context context-type="linenumber">116</context>
|
<context context-type="linenumber">110</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
|
||||||
@@ -1889,49 +1882,49 @@
|
|||||||
<source>Open Document</source>
|
<source>Open Document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
<context context-type="linenumber">121</context>
|
<context context-type="linenumber">115</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="428536141871853903" datatype="html">
|
<trans-unit id="428536141871853903" datatype="html">
|
||||||
<source>{VAR_PLURAL, plural, =1 {One <x id="INTERPOLATION"/> task} other {<x id="INTERPOLATION_1"/> total <x id="INTERPOLATION"/> tasks}}</source>
|
<source>{VAR_PLURAL, plural, =1 {One <x id="INTERPOLATION"/> task} other {<x id="INTERPOLATION_1"/> total <x id="INTERPOLATION"/> tasks}}</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
<context context-type="linenumber">140</context>
|
<context context-type="linenumber">134</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1943508481059904274" datatype="html">
|
<trans-unit id="1943508481059904274" datatype="html">
|
||||||
<source> (<x id="INTERPOLATION" equiv-text="{{selectedTasks.size}}"/> selected)</source>
|
<source> (<x id="INTERPOLATION" equiv-text="{{selectedTasks.size}}"/> selected)</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
<context context-type="linenumber">142</context>
|
<context context-type="linenumber">136</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5639839509673911668" datatype="html">
|
<trans-unit id="5639839509673911668" datatype="html">
|
||||||
<source>Failed<x id="START_BLOCK_IF" equiv-text="@if (tasksService.failedFileTasks.length > 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-danger ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.failedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
|
<source>Failed<x id="START_BLOCK_IF" equiv-text="@if (tasksService.failedFileTasks.length > 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-danger ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.failedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
<context context-type="linenumber">154,156</context>
|
<context context-type="linenumber">148,150</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8210778930307085868" datatype="html">
|
<trans-unit id="8210778930307085868" datatype="html">
|
||||||
<source>Complete<x id="START_BLOCK_IF" equiv-text="@if (tasksService.completedFileTasks.length > 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-secondary ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.completedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
|
<source>Complete<x id="START_BLOCK_IF" equiv-text="@if (tasksService.completedFileTasks.length > 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-secondary ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.completedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
<context context-type="linenumber">162,164</context>
|
<context context-type="linenumber">156,158</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3522801015717851360" datatype="html">
|
<trans-unit id="3522801015717851360" datatype="html">
|
||||||
<source>Started<x id="START_BLOCK_IF" equiv-text="@if (tasksService.startedFileTasks.length > 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-secondary ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.startedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
|
<source>Started<x id="START_BLOCK_IF" equiv-text="@if (tasksService.startedFileTasks.length > 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-secondary ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.startedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
<context context-type="linenumber">170,172</context>
|
<context context-type="linenumber">164,166</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2341807459308874922" datatype="html">
|
<trans-unit id="2341807459308874922" datatype="html">
|
||||||
<source>Queued<x id="START_BLOCK_IF" equiv-text="@if (tasksService.queuedFileTasks.length > 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-secondary ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.queuedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
|
<source>Queued<x id="START_BLOCK_IF" equiv-text="@if (tasksService.queuedFileTasks.length > 0) {"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge bg-secondary ms-2">"/><x id="INTERPOLATION" equiv-text="{{tasksService.queuedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_BLOCK_IF" equiv-text="}"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
|
||||||
<context context-type="linenumber">178,180</context>
|
<context context-type="linenumber">172,174</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2525230676386818985" datatype="html">
|
<trans-unit id="2525230676386818985" datatype="html">
|
||||||
@@ -2598,11 +2591,11 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1112</context>
|
<context context-type="linenumber">1098</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1477</context>
|
<context context-type="linenumber">1463</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@@ -3228,7 +3221,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1065</context>
|
<context context-type="linenumber">1051</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@@ -3333,7 +3326,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1528</context>
|
<context context-type="linenumber">1514</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6661109599266152398" datatype="html">
|
<trans-unit id="6661109599266152398" datatype="html">
|
||||||
@@ -3344,7 +3337,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1529</context>
|
<context context-type="linenumber">1515</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5162686434580248853" datatype="html">
|
<trans-unit id="5162686434580248853" datatype="html">
|
||||||
@@ -3355,7 +3348,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1530</context>
|
<context context-type="linenumber">1516</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8157388568390631653" datatype="html">
|
<trans-unit id="8157388568390631653" datatype="html">
|
||||||
@@ -3444,7 +3437,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">113</context>
|
<context context-type="linenumber">111</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
|
||||||
@@ -3704,14 +3697,14 @@
|
|||||||
<source>This month</source>
|
<source>This month</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">107</context>
|
<context context-type="linenumber">106</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4498682414491138092" datatype="html">
|
<trans-unit id="4498682414491138092" datatype="html">
|
||||||
<source>Yesterday</source>
|
<source>Yesterday</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">118</context>
|
<context context-type="linenumber">116</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
|
<context context-type="sourcefile">src/app/pipes/custom-date.pipe.ts</context>
|
||||||
@@ -3722,28 +3715,28 @@
|
|||||||
<source>Previous week</source>
|
<source>Previous week</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">123</context>
|
<context context-type="linenumber">121</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8586908745456864217" datatype="html">
|
<trans-unit id="8586908745456864217" datatype="html">
|
||||||
<source>Previous month</source>
|
<source>Previous month</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">137</context>
|
<context context-type="linenumber">135</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="357608474534295480" datatype="html">
|
<trans-unit id="357608474534295480" datatype="html">
|
||||||
<source>Previous quarter</source>
|
<source>Previous quarter</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">143</context>
|
<context context-type="linenumber">141</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="100513227838842152" datatype="html">
|
<trans-unit id="100513227838842152" datatype="html">
|
||||||
<source>Previous year</source>
|
<source>Previous year</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/dates-dropdown/dates-dropdown.component.ts</context>
|
||||||
<context context-type="linenumber">157</context>
|
<context context-type="linenumber">155</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8743659855412792665" datatype="html">
|
<trans-unit id="8743659855412792665" datatype="html">
|
||||||
@@ -6991,7 +6984,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1476</context>
|
<context context-type="linenumber">1462</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2336375155355449543" datatype="html">
|
<trans-unit id="2336375155355449543" datatype="html">
|
||||||
@@ -7256,109 +7249,88 @@
|
|||||||
<context context-type="linenumber">354</context>
|
<context context-type="linenumber">354</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8236092845697214347" datatype="html">
|
|
||||||
<source> Duplicates <x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="badge text-bg-secondary ms-1">"/><x id="INTERPOLATION" equiv-text="te_documents.length }}"/><x id="CLOSE_TAG_SPAN" ctype="x-span"/></source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
|
||||||
<context context-type="linenumber">376,380</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="6449374629822973702" datatype="html">
|
|
||||||
<source>Duplicate documents detected:</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
|
||||||
<context context-type="linenumber">382</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="14058600336670816" datatype="html">
|
|
||||||
<source>In trash</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
|
||||||
<context context-type="linenumber">393</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="5129524307369213584" datatype="html">
|
<trans-unit id="5129524307369213584" datatype="html">
|
||||||
<source>Save & next</source>
|
<source>Save & next</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||||
<context context-type="linenumber">422</context>
|
<context context-type="linenumber">391</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4910102545766233758" datatype="html">
|
<trans-unit id="4910102545766233758" datatype="html">
|
||||||
<source>Save & close</source>
|
<source>Save & close</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||||
<context context-type="linenumber">425</context>
|
<context context-type="linenumber">394</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1309556917227148591" datatype="html">
|
<trans-unit id="1309556917227148591" datatype="html">
|
||||||
<source>Document loading...</source>
|
<source>Document loading...</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||||
<context context-type="linenumber">435</context>
|
<context context-type="linenumber">404</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8191371354890763172" datatype="html">
|
<trans-unit id="8191371354890763172" datatype="html">
|
||||||
<source>Enter Password</source>
|
<source>Enter Password</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||||
<context context-type="linenumber">489</context>
|
<context context-type="linenumber">458</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2218903673684131427" datatype="html">
|
<trans-unit id="2218903673684131427" datatype="html">
|
||||||
<source>An error occurred loading content: <x id="PH" equiv-text="err.message ?? err.toString()"/></source>
|
<source>An error occurred loading content: <x id="PH" equiv-text="err.message ?? err.toString()"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">432,434</context>
|
<context context-type="linenumber">430,432</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3200733026060976258" datatype="html">
|
<trans-unit id="3200733026060976258" datatype="html">
|
||||||
<source>Document changes detected</source>
|
<source>Document changes detected</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">471</context>
|
<context context-type="linenumber">464</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2887155916749964" datatype="html">
|
<trans-unit id="2887155916749964" datatype="html">
|
||||||
<source>The version of this document in your browser session appears older than the existing version.</source>
|
<source>The version of this document in your browser session appears older than the existing version.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">472</context>
|
<context context-type="linenumber">465</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="237142428785956348" datatype="html">
|
<trans-unit id="237142428785956348" datatype="html">
|
||||||
<source>Saving the document here may overwrite other changes that were made. To restore the existing version, discard your changes or close the document.</source>
|
<source>Saving the document here may overwrite other changes that were made. To restore the existing version, discard your changes or close the document.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">473</context>
|
<context context-type="linenumber">466</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8720977247725652816" datatype="html">
|
<trans-unit id="8720977247725652816" datatype="html">
|
||||||
<source>Ok</source>
|
<source>Ok</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">475</context>
|
<context context-type="linenumber">468</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6142395741265832184" datatype="html">
|
<trans-unit id="6142395741265832184" datatype="html">
|
||||||
<source>Next document</source>
|
<source>Next document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">601</context>
|
<context context-type="linenumber">594</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="651985345816518480" datatype="html">
|
<trans-unit id="651985345816518480" datatype="html">
|
||||||
<source>Previous document</source>
|
<source>Previous document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">611</context>
|
<context context-type="linenumber">604</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2885986061416655600" datatype="html">
|
<trans-unit id="2885986061416655600" datatype="html">
|
||||||
<source>Close document</source>
|
<source>Close document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">619</context>
|
<context context-type="linenumber">612</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
|
<context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
|
||||||
@@ -7369,67 +7341,67 @@
|
|||||||
<source>Save document</source>
|
<source>Save document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">626</context>
|
<context context-type="linenumber">619</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1784543155727940353" datatype="html">
|
<trans-unit id="1784543155727940353" datatype="html">
|
||||||
<source>Save and close / next</source>
|
<source>Save and close / next</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">635</context>
|
<context context-type="linenumber">628</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5758784066858623886" datatype="html">
|
<trans-unit id="5758784066858623886" datatype="html">
|
||||||
<source>Error retrieving metadata</source>
|
<source>Error retrieving metadata</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">690</context>
|
<context context-type="linenumber">683</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3456881259945295697" datatype="html">
|
<trans-unit id="3456881259945295697" datatype="html">
|
||||||
<source>Error retrieving suggestions.</source>
|
<source>Error retrieving suggestions.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">745</context>
|
<context context-type="linenumber">731</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2194092841814123758" datatype="html">
|
<trans-unit id="2194092841814123758" datatype="html">
|
||||||
<source>Document "<x id="PH" equiv-text="newValues.title"/>" saved successfully.</source>
|
<source>Document "<x id="PH" equiv-text="newValues.title"/>" saved successfully.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">954</context>
|
<context context-type="linenumber">940</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">978</context>
|
<context context-type="linenumber">964</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6626387786259219838" datatype="html">
|
<trans-unit id="6626387786259219838" datatype="html">
|
||||||
<source>Error saving document "<x id="PH" equiv-text="this.document.title"/>"</source>
|
<source>Error saving document "<x id="PH" equiv-text="this.document.title"/>"</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">984</context>
|
<context context-type="linenumber">970</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="448882439049417053" datatype="html">
|
<trans-unit id="448882439049417053" datatype="html">
|
||||||
<source>Error saving document</source>
|
<source>Error saving document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1034</context>
|
<context context-type="linenumber">1020</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8410796510716511826" datatype="html">
|
<trans-unit id="8410796510716511826" datatype="html">
|
||||||
<source>Do you really want to move the document "<x id="PH" equiv-text="this.document.title"/>" to the trash?</source>
|
<source>Do you really want to move the document "<x id="PH" equiv-text="this.document.title"/>" to the trash?</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1066</context>
|
<context context-type="linenumber">1052</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="282586936710748252" datatype="html">
|
<trans-unit id="282586936710748252" datatype="html">
|
||||||
<source>Documents can be restored prior to permanent deletion.</source>
|
<source>Documents can be restored prior to permanent deletion.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1067</context>
|
<context context-type="linenumber">1053</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@@ -7440,7 +7412,7 @@
|
|||||||
<source>Move to trash</source>
|
<source>Move to trash</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1069</context>
|
<context context-type="linenumber">1055</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@@ -7451,14 +7423,14 @@
|
|||||||
<source>Error deleting document</source>
|
<source>Error deleting document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1088</context>
|
<context context-type="linenumber">1074</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="619486176823357521" datatype="html">
|
<trans-unit id="619486176823357521" datatype="html">
|
||||||
<source>Reprocess confirm</source>
|
<source>Reprocess confirm</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1108</context>
|
<context context-type="linenumber">1094</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
@@ -7469,102 +7441,102 @@
|
|||||||
<source>This operation will permanently recreate the archive file for this document.</source>
|
<source>This operation will permanently recreate the archive file for this document.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1109</context>
|
<context context-type="linenumber">1095</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="302054111564709516" datatype="html">
|
<trans-unit id="302054111564709516" datatype="html">
|
||||||
<source>The archive file will be re-generated with the current settings.</source>
|
<source>The archive file will be re-generated with the current settings.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1110</context>
|
<context context-type="linenumber">1096</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8251197608401006898" datatype="html">
|
<trans-unit id="8251197608401006898" datatype="html">
|
||||||
<source>Reprocess operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background. Close and re-open or reload this document after the operation has completed to see new content.</source>
|
<source>Reprocess operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background. Close and re-open or reload this document after the operation has completed to see new content.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1120</context>
|
<context context-type="linenumber">1106</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4409560272830824468" datatype="html">
|
<trans-unit id="4409560272830824468" datatype="html">
|
||||||
<source>Error executing operation</source>
|
<source>Error executing operation</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1131</context>
|
<context context-type="linenumber">1117</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6030453331794586802" datatype="html">
|
<trans-unit id="6030453331794586802" datatype="html">
|
||||||
<source>Error downloading document</source>
|
<source>Error downloading document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1180</context>
|
<context context-type="linenumber">1166</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4458954481601077369" datatype="html">
|
<trans-unit id="4458954481601077369" datatype="html">
|
||||||
<source>Page Fit</source>
|
<source>Page Fit</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1257</context>
|
<context context-type="linenumber">1243</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4663705961777238777" datatype="html">
|
<trans-unit id="4663705961777238777" datatype="html">
|
||||||
<source>PDF edit operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background.</source>
|
<source>PDF edit operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1495</context>
|
<context context-type="linenumber">1481</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="9043972994040261999" datatype="html">
|
<trans-unit id="9043972994040261999" datatype="html">
|
||||||
<source>Error executing PDF edit operation</source>
|
<source>Error executing PDF edit operation</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1507</context>
|
<context context-type="linenumber">1493</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6172690334763056188" datatype="html">
|
<trans-unit id="6172690334763056188" datatype="html">
|
||||||
<source>Please enter the current password before attempting to remove it.</source>
|
<source>Please enter the current password before attempting to remove it.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1518</context>
|
<context context-type="linenumber">1504</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="968660764814228922" datatype="html">
|
<trans-unit id="968660764814228922" datatype="html">
|
||||||
<source>Password removal operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background.</source>
|
<source>Password removal operation for "<x id="PH" equiv-text="this.document.title"/>" will begin in the background.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1550</context>
|
<context context-type="linenumber">1536</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2282118435712883014" datatype="html">
|
<trans-unit id="2282118435712883014" datatype="html">
|
||||||
<source>Error executing password removal operation</source>
|
<source>Error executing password removal operation</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1564</context>
|
<context context-type="linenumber">1550</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3740891324955700797" datatype="html">
|
<trans-unit id="3740891324955700797" datatype="html">
|
||||||
<source>Print failed.</source>
|
<source>Print failed.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1601</context>
|
<context context-type="linenumber">1587</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6457245677384603573" datatype="html">
|
<trans-unit id="6457245677384603573" datatype="html">
|
||||||
<source>Error loading document for printing.</source>
|
<source>Error loading document for printing.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1613</context>
|
<context context-type="linenumber">1599</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6085793215710522488" datatype="html">
|
<trans-unit id="6085793215710522488" datatype="html">
|
||||||
<source>An error occurred loading tiff: <x id="PH" equiv-text="err.toString()"/></source>
|
<source>An error occurred loading tiff: <x id="PH" equiv-text="err.toString()"/></source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1678</context>
|
<context context-type="linenumber">1664</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||||
<context context-type="linenumber">1682</context>
|
<context context-type="linenumber">1668</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4958946940233632319" datatype="html">
|
<trans-unit id="4958946940233632319" datatype="html">
|
||||||
|
|||||||
@@ -97,12 +97,6 @@
|
|||||||
<br/><em>(<ng-container i18n>click for full output</ng-container>)</em>
|
<br/><em>(<ng-container i18n>click for full output</ng-container>)</em>
|
||||||
}
|
}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@if (task.duplicate_documents?.length > 0) {
|
|
||||||
<div class="small text-warning-emphasis d-flex align-items-center gap-1">
|
|
||||||
<i-bs class="lh-1" width="1em" height="1em" name="exclamation-triangle"></i-bs>
|
|
||||||
<span i18n>Duplicate(s) detected</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</td>
|
</td>
|
||||||
}
|
}
|
||||||
<td class="d-lg-none">
|
<td class="d-lg-none">
|
||||||
|
|||||||
@@ -164,11 +164,9 @@
|
|||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
<span class="ms-auto text-muted small">
|
<span class="ms-auto text-muted small">
|
||||||
@if (item.dateEnd) {
|
@if (item.dateEnd) {
|
||||||
{{ item.date | customDate:'mediumDate' }} – {{ item.dateEnd | customDate:'mediumDate' }}
|
{{ item.date | customDate:'MMM d' }} – {{ item.dateEnd | customDate:'mediumDate' }}
|
||||||
} @else if (item.dateTilNow) {
|
|
||||||
{{ item.dateTilNow | customDate:'mediumDate' }} – <ng-container i18n>now</ng-container>
|
|
||||||
} @else {
|
} @else {
|
||||||
{{ item.date | customDate:'mediumDate' }}
|
{{ item.date | customDate:'mediumDate' }} – <ng-container i18n>now</ng-container>
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -79,34 +79,32 @@ export class DatesDropdownComponent implements OnInit, OnDestroy {
|
|||||||
{
|
{
|
||||||
id: RelativeDate.WITHIN_1_WEEK,
|
id: RelativeDate.WITHIN_1_WEEK,
|
||||||
name: $localize`Within 1 week`,
|
name: $localize`Within 1 week`,
|
||||||
dateTilNow: new Date().setDate(new Date().getDate() - 7),
|
date: new Date().setDate(new Date().getDate() - 7),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: RelativeDate.WITHIN_1_MONTH,
|
id: RelativeDate.WITHIN_1_MONTH,
|
||||||
name: $localize`Within 1 month`,
|
name: $localize`Within 1 month`,
|
||||||
dateTilNow: new Date().setMonth(new Date().getMonth() - 1),
|
date: new Date().setMonth(new Date().getMonth() - 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: RelativeDate.WITHIN_3_MONTHS,
|
id: RelativeDate.WITHIN_3_MONTHS,
|
||||||
name: $localize`Within 3 months`,
|
name: $localize`Within 3 months`,
|
||||||
dateTilNow: new Date().setMonth(new Date().getMonth() - 3),
|
date: new Date().setMonth(new Date().getMonth() - 3),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: RelativeDate.WITHIN_1_YEAR,
|
id: RelativeDate.WITHIN_1_YEAR,
|
||||||
name: $localize`Within 1 year`,
|
name: $localize`Within 1 year`,
|
||||||
dateTilNow: new Date().setFullYear(new Date().getFullYear() - 1),
|
date: new Date().setFullYear(new Date().getFullYear() - 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: RelativeDate.THIS_YEAR,
|
id: RelativeDate.THIS_YEAR,
|
||||||
name: $localize`This year`,
|
name: $localize`This year`,
|
||||||
date: new Date('1/1/' + new Date().getFullYear()),
|
date: new Date('1/1/' + new Date().getFullYear()),
|
||||||
dateEnd: new Date('12/31/' + new Date().getFullYear()),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: RelativeDate.THIS_MONTH,
|
id: RelativeDate.THIS_MONTH,
|
||||||
name: $localize`This month`,
|
name: $localize`This month`,
|
||||||
date: new Date().setDate(1),
|
date: new Date().setDate(1),
|
||||||
dateEnd: new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: RelativeDate.TODAY,
|
id: RelativeDate.TODAY,
|
||||||
|
|||||||
@@ -370,37 +370,6 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (document?.duplicate_documents?.length) {
|
|
||||||
<li [ngbNavItem]="DocumentDetailNavIDs.Duplicates">
|
|
||||||
<a class="text-nowrap" ngbNavLink i18n>
|
|
||||||
Duplicates
|
|
||||||
<span class="badge text-bg-secondary ms-1">{{ document.duplicate_documents.length }}</span>
|
|
||||||
</a>
|
|
||||||
<ng-template ngbNavContent>
|
|
||||||
<div class="d-flex flex-column gap-2">
|
|
||||||
<div class="fst-italic" i18n>Duplicate documents detected:</div>
|
|
||||||
<div class="list-group">
|
|
||||||
@for (duplicate of document.duplicate_documents; track duplicate.id) {
|
|
||||||
<a
|
|
||||||
class="list-group-item list-group-item-action d-flex justify-content-between align-items-center"
|
|
||||||
[routerLink]="['/documents', duplicate.id, 'details']"
|
|
||||||
[class.disabled]="duplicate.deleted_at"
|
|
||||||
>
|
|
||||||
<span class="d-flex align-items-center gap-2">
|
|
||||||
<span>{{ duplicate.title || ('#' + duplicate.id) }}</span>
|
|
||||||
@if (duplicate.deleted_at) {
|
|
||||||
<span class="badge text-bg-secondary" i18n>In trash</span>
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
<span class="text-secondary">#{{ duplicate.id }}</span>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div [ngbNavOutlet]="nav" class="mt-3"></div>
|
<div [ngbNavOutlet]="nav" class="mt-3"></div>
|
||||||
|
|||||||
@@ -301,16 +301,16 @@ describe('DocumentDetailComponent', () => {
|
|||||||
.spyOn(openDocumentsService, 'openDocument')
|
.spyOn(openDocumentsService, 'openDocument')
|
||||||
.mockReturnValueOnce(of(true))
|
.mockReturnValueOnce(of(true))
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
expect(component.activeNavID).toEqual(component.DocumentDetailNavIDs.Notes)
|
expect(component.activeNavID).toEqual(5) // DocumentDetailNavIDs.Notes
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should change url on tab switch', () => {
|
it('should change url on tab switch', () => {
|
||||||
initNormally()
|
initNormally()
|
||||||
const navigateSpy = jest.spyOn(router, 'navigate')
|
const navigateSpy = jest.spyOn(router, 'navigate')
|
||||||
component.nav.select(component.DocumentDetailNavIDs.Notes)
|
component.nav.select(5)
|
||||||
component.nav.navChange.next({
|
component.nav.navChange.next({
|
||||||
activeId: 1,
|
activeId: 1,
|
||||||
nextId: component.DocumentDetailNavIDs.Notes,
|
nextId: 5,
|
||||||
preventDefault: () => {},
|
preventDefault: () => {},
|
||||||
})
|
})
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
@@ -352,18 +352,6 @@ describe('DocumentDetailComponent', () => {
|
|||||||
expect(component.document).toEqual(doc)
|
expect(component.document).toEqual(doc)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fall back to details tab when duplicates tab is active but no duplicates', () => {
|
|
||||||
initNormally()
|
|
||||||
component.activeNavID = component.DocumentDetailNavIDs.Duplicates
|
|
||||||
const noDupDoc = { ...doc, duplicate_documents: [] }
|
|
||||||
|
|
||||||
component.updateComponent(noDupDoc)
|
|
||||||
|
|
||||||
expect(component.activeNavID).toEqual(
|
|
||||||
component.DocumentDetailNavIDs.Details
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should load already-opened document via param', () => {
|
it('should load already-opened document via param', () => {
|
||||||
initNormally()
|
initNormally()
|
||||||
jest.spyOn(documentService, 'get').mockReturnValueOnce(of(doc))
|
jest.spyOn(documentService, 'get').mockReturnValueOnce(of(doc))
|
||||||
@@ -379,38 +367,6 @@ describe('DocumentDetailComponent', () => {
|
|||||||
expect(component.document).toEqual(doc)
|
expect(component.document).toEqual(doc)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update cached open document duplicates when reloading an open doc', () => {
|
|
||||||
const openDoc = { ...doc, duplicate_documents: [{ id: 1, title: 'Old' }] }
|
|
||||||
const updatedDuplicates = [
|
|
||||||
{ id: 2, title: 'Newer duplicate', deleted_at: null },
|
|
||||||
]
|
|
||||||
jest
|
|
||||||
.spyOn(activatedRoute, 'paramMap', 'get')
|
|
||||||
.mockReturnValue(of(convertToParamMap({ id: 3, section: 'details' })))
|
|
||||||
jest.spyOn(documentService, 'get').mockReturnValue(
|
|
||||||
of({
|
|
||||||
...doc,
|
|
||||||
modified: new Date('2024-01-02T00:00:00Z'),
|
|
||||||
duplicate_documents: updatedDuplicates,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
jest.spyOn(openDocumentsService, 'getOpenDocument').mockReturnValue(openDoc)
|
|
||||||
const saveSpy = jest.spyOn(openDocumentsService, 'save')
|
|
||||||
jest.spyOn(openDocumentsService, 'openDocument').mockReturnValue(of(true))
|
|
||||||
jest.spyOn(customFieldsService, 'listAll').mockReturnValue(
|
|
||||||
of({
|
|
||||||
count: customFields.length,
|
|
||||||
all: customFields.map((f) => f.id),
|
|
||||||
results: customFields,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
fixture.detectChanges()
|
|
||||||
|
|
||||||
expect(openDoc.duplicate_documents).toEqual(updatedDuplicates)
|
|
||||||
expect(saveSpy).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should disable form if user cannot edit', () => {
|
it('should disable form if user cannot edit', () => {
|
||||||
currentUserHasObjectPermissions = false
|
currentUserHasObjectPermissions = false
|
||||||
initNormally()
|
initNormally()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router'
|
import { ActivatedRoute, Router } from '@angular/router'
|
||||||
import {
|
import {
|
||||||
NgbDateStruct,
|
NgbDateStruct,
|
||||||
NgbDropdownModule,
|
NgbDropdownModule,
|
||||||
@@ -124,7 +124,6 @@ enum DocumentDetailNavIDs {
|
|||||||
Notes = 5,
|
Notes = 5,
|
||||||
Permissions = 6,
|
Permissions = 6,
|
||||||
History = 7,
|
History = 7,
|
||||||
Duplicates = 8,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ContentRenderType {
|
enum ContentRenderType {
|
||||||
@@ -182,7 +181,6 @@ export enum ZoomSetting {
|
|||||||
NgxBootstrapIconsModule,
|
NgxBootstrapIconsModule,
|
||||||
PdfViewerModule,
|
PdfViewerModule,
|
||||||
TextAreaComponent,
|
TextAreaComponent,
|
||||||
RouterModule,
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class DocumentDetailComponent
|
export class DocumentDetailComponent
|
||||||
@@ -456,11 +454,6 @@ export class DocumentDetailComponent
|
|||||||
const openDocument = this.openDocumentService.getOpenDocument(
|
const openDocument = this.openDocumentService.getOpenDocument(
|
||||||
this.documentId
|
this.documentId
|
||||||
)
|
)
|
||||||
// update duplicate documents if present
|
|
||||||
if (openDocument && doc?.duplicate_documents) {
|
|
||||||
openDocument.duplicate_documents = doc.duplicate_documents
|
|
||||||
this.openDocumentService.save()
|
|
||||||
}
|
|
||||||
const useDoc = openDocument || doc
|
const useDoc = openDocument || doc
|
||||||
if (openDocument) {
|
if (openDocument) {
|
||||||
if (
|
if (
|
||||||
@@ -711,13 +704,6 @@ export class DocumentDetailComponent
|
|||||||
}
|
}
|
||||||
this.title = this.documentTitlePipe.transform(doc.title)
|
this.title = this.documentTitlePipe.transform(doc.title)
|
||||||
this.prepareForm(doc)
|
this.prepareForm(doc)
|
||||||
|
|
||||||
if (
|
|
||||||
this.activeNavID === DocumentDetailNavIDs.Duplicates &&
|
|
||||||
!doc?.duplicate_documents?.length
|
|
||||||
) {
|
|
||||||
this.activeNavID = DocumentDetailNavIDs.Details
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get customFieldFormFields(): FormArray {
|
get customFieldFormFields(): FormArray {
|
||||||
|
|||||||
@@ -159,8 +159,6 @@ export interface Document extends ObjectWithPermissions {
|
|||||||
|
|
||||||
page_count?: number
|
page_count?: number
|
||||||
|
|
||||||
duplicate_documents?: Document[]
|
|
||||||
|
|
||||||
// Frontend only
|
// Frontend only
|
||||||
__changedFields?: string[]
|
__changedFields?: string[]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Document } from './document'
|
|
||||||
import { ObjectWithId } from './object-with-id'
|
import { ObjectWithId } from './object-with-id'
|
||||||
|
|
||||||
export enum PaperlessTaskType {
|
export enum PaperlessTaskType {
|
||||||
@@ -43,7 +42,5 @@ export interface PaperlessTask extends ObjectWithId {
|
|||||||
|
|
||||||
related_document?: number
|
related_document?: number
|
||||||
|
|
||||||
duplicate_documents?: Document[]
|
|
||||||
|
|
||||||
owner?: number
|
owner?: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -779,44 +779,18 @@ class ConsumerPreflightPlugin(
|
|||||||
Q(checksum=checksum) | Q(archive_checksum=checksum),
|
Q(checksum=checksum) | Q(archive_checksum=checksum),
|
||||||
)
|
)
|
||||||
if existing_doc.exists():
|
if existing_doc.exists():
|
||||||
existing_doc = existing_doc.order_by("-created")
|
msg = ConsumerStatusShortMessage.DOCUMENT_ALREADY_EXISTS
|
||||||
duplicates_in_trash = existing_doc.filter(deleted_at__isnull=False)
|
log_msg = f"Not consuming {self.filename}: It is a duplicate of {existing_doc.get().title} (#{existing_doc.get().pk})."
|
||||||
log_msg = (
|
|
||||||
f"Consuming duplicate {self.filename}: "
|
|
||||||
f"{existing_doc.count()} existing document(s) share the same content."
|
|
||||||
)
|
|
||||||
|
|
||||||
if duplicates_in_trash.exists():
|
if existing_doc.first().deleted_at is not None:
|
||||||
log_msg += " Note: at least one existing document is in the trash."
|
msg = ConsumerStatusShortMessage.DOCUMENT_ALREADY_EXISTS_IN_TRASH
|
||||||
|
log_msg += " Note: existing document is in the trash."
|
||||||
self.log.warning(log_msg)
|
|
||||||
|
|
||||||
if settings.CONSUMER_DELETE_DUPLICATES:
|
if settings.CONSUMER_DELETE_DUPLICATES:
|
||||||
duplicate = existing_doc.first()
|
|
||||||
duplicate_label = (
|
|
||||||
duplicate.title
|
|
||||||
or duplicate.original_filename
|
|
||||||
or (Path(duplicate.filename).name if duplicate.filename else None)
|
|
||||||
or str(duplicate.pk)
|
|
||||||
)
|
|
||||||
|
|
||||||
Path(self.input_doc.original_file).unlink()
|
Path(self.input_doc.original_file).unlink()
|
||||||
|
|
||||||
failure_msg = (
|
|
||||||
f"Not consuming {self.filename}: "
|
|
||||||
f"It is a duplicate of {duplicate_label} (#{duplicate.pk})"
|
|
||||||
)
|
|
||||||
status_msg = ConsumerStatusShortMessage.DOCUMENT_ALREADY_EXISTS
|
|
||||||
|
|
||||||
if duplicates_in_trash.exists():
|
|
||||||
status_msg = (
|
|
||||||
ConsumerStatusShortMessage.DOCUMENT_ALREADY_EXISTS_IN_TRASH
|
|
||||||
)
|
|
||||||
failure_msg += " Note: existing document is in the trash."
|
|
||||||
|
|
||||||
self._fail(
|
self._fail(
|
||||||
status_msg,
|
msg,
|
||||||
failure_msg,
|
log_msg,
|
||||||
)
|
)
|
||||||
|
|
||||||
def pre_check_directories(self):
|
def pre_check_directories(self):
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 5.2.7 on 2026-01-14 17:45
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
dependencies = [
|
|
||||||
("documents", "0005_workflowtrigger_filter_has_any_correspondents_and_more"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="document",
|
|
||||||
name="checksum",
|
|
||||||
field=models.CharField(
|
|
||||||
editable=False,
|
|
||||||
max_length=32,
|
|
||||||
verbose_name="checksum",
|
|
||||||
help_text="The checksum of the original document.",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2026-01-24 07:33
|
|
||||||
|
|
||||||
import django.db.models.functions.text
|
|
||||||
from django.db import migrations
|
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
dependencies = [
|
|
||||||
("documents", "0006_alter_document_checksum_unique"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name="document",
|
|
||||||
name="content_length",
|
|
||||||
field=models.GeneratedField(
|
|
||||||
db_persist=True,
|
|
||||||
expression=django.db.models.functions.text.Length("content"),
|
|
||||||
null=False,
|
|
||||||
help_text="Length of the content field in characters. Automatically maintained by the database for faster statistics computation.",
|
|
||||||
output_field=models.PositiveIntegerField(default=0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -20,9 +20,7 @@ if settings.AUDIT_LOG_ENABLED:
|
|||||||
from auditlog.registry import auditlog
|
from auditlog.registry import auditlog
|
||||||
|
|
||||||
from django.db.models import Case
|
from django.db.models import Case
|
||||||
from django.db.models import PositiveIntegerField
|
|
||||||
from django.db.models.functions import Cast
|
from django.db.models.functions import Cast
|
||||||
from django.db.models.functions import Length
|
|
||||||
from django.db.models.functions import Substr
|
from django.db.models.functions import Substr
|
||||||
from django_softdelete.models import SoftDeleteModel
|
from django_softdelete.models import SoftDeleteModel
|
||||||
|
|
||||||
@@ -194,15 +192,6 @@ class Document(SoftDeleteModel, ModelWithOwner):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
content_length = models.GeneratedField(
|
|
||||||
expression=Length("content"),
|
|
||||||
output_field=PositiveIntegerField(default=0),
|
|
||||||
db_persist=True,
|
|
||||||
null=False,
|
|
||||||
serialize=False,
|
|
||||||
help_text="Length of the content field in characters. Automatically maintained by the database for faster statistics computation.",
|
|
||||||
)
|
|
||||||
|
|
||||||
mime_type = models.CharField(_("mime type"), max_length=256, editable=False)
|
mime_type = models.CharField(_("mime type"), max_length=256, editable=False)
|
||||||
|
|
||||||
tags = models.ManyToManyField(
|
tags = models.ManyToManyField(
|
||||||
@@ -216,6 +205,7 @@ class Document(SoftDeleteModel, ModelWithOwner):
|
|||||||
_("checksum"),
|
_("checksum"),
|
||||||
max_length=32,
|
max_length=32,
|
||||||
editable=False,
|
editable=False,
|
||||||
|
unique=True,
|
||||||
help_text=_("The checksum of the original document."),
|
help_text=_("The checksum of the original document."),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -956,7 +946,7 @@ if settings.AUDIT_LOG_ENABLED:
|
|||||||
auditlog.register(
|
auditlog.register(
|
||||||
Document,
|
Document,
|
||||||
m2m_fields={"tags"},
|
m2m_fields={"tags"},
|
||||||
exclude_fields=["content_length", "modified"],
|
exclude_fields=["modified"],
|
||||||
)
|
)
|
||||||
auditlog.register(Correspondent)
|
auditlog.register(Correspondent)
|
||||||
auditlog.register(Tag)
|
auditlog.register(Tag)
|
||||||
|
|||||||
@@ -148,29 +148,13 @@ def get_document_count_filter_for_user(user):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_objects_for_user_owner_aware(
|
def get_objects_for_user_owner_aware(user, perms, Model) -> QuerySet:
|
||||||
user,
|
objects_owned = Model.objects.filter(owner=user)
|
||||||
perms,
|
objects_unowned = Model.objects.filter(owner__isnull=True)
|
||||||
Model,
|
|
||||||
*,
|
|
||||||
include_deleted=False,
|
|
||||||
) -> QuerySet:
|
|
||||||
"""
|
|
||||||
Returns objects the user owns, are unowned, or has explicit perms.
|
|
||||||
When include_deleted is True, soft-deleted items are also included.
|
|
||||||
"""
|
|
||||||
manager = (
|
|
||||||
Model.global_objects
|
|
||||||
if include_deleted and hasattr(Model, "global_objects")
|
|
||||||
else Model.objects
|
|
||||||
)
|
|
||||||
|
|
||||||
objects_owned = manager.filter(owner=user)
|
|
||||||
objects_unowned = manager.filter(owner__isnull=True)
|
|
||||||
objects_with_perms = get_objects_for_user(
|
objects_with_perms = get_objects_for_user(
|
||||||
user=user,
|
user=user,
|
||||||
perms=perms,
|
perms=perms,
|
||||||
klass=manager.all(),
|
klass=Model,
|
||||||
accept_global_perms=False,
|
accept_global_perms=False,
|
||||||
)
|
)
|
||||||
return objects_owned | objects_unowned | objects_with_perms
|
return objects_owned | objects_unowned | objects_with_perms
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ from django.core.validators import MinValueValidator
|
|||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
from django.core.validators import integer_validator
|
from django.core.validators import integer_validator
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.db.models import Q
|
|
||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
from django.utils.crypto import get_random_string
|
from django.utils.crypto import get_random_string
|
||||||
from django.utils.dateparse import parse_datetime
|
from django.utils.dateparse import parse_datetime
|
||||||
@@ -73,7 +72,6 @@ from documents.models import WorkflowTrigger
|
|||||||
from documents.parsers import is_mime_type_supported
|
from documents.parsers import is_mime_type_supported
|
||||||
from documents.permissions import get_document_count_filter_for_user
|
from documents.permissions import get_document_count_filter_for_user
|
||||||
from documents.permissions import get_groups_with_only_permission
|
from documents.permissions import get_groups_with_only_permission
|
||||||
from documents.permissions import get_objects_for_user_owner_aware
|
|
||||||
from documents.permissions import set_permissions_for_object
|
from documents.permissions import set_permissions_for_object
|
||||||
from documents.regex import validate_regex_pattern
|
from documents.regex import validate_regex_pattern
|
||||||
from documents.templating.filepath import validate_filepath_template_and_render
|
from documents.templating.filepath import validate_filepath_template_and_render
|
||||||
@@ -84,9 +82,6 @@ from documents.validators import url_validator
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
|
|
||||||
from django.db.models.query import QuerySet
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("paperless.serializers")
|
logger = logging.getLogger("paperless.serializers")
|
||||||
|
|
||||||
|
|
||||||
@@ -1019,32 +1014,6 @@ class NotesSerializer(serializers.ModelSerializer):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def _get_viewable_duplicates(
|
|
||||||
document: Document,
|
|
||||||
user: User | None,
|
|
||||||
) -> QuerySet[Document]:
|
|
||||||
checksums = {document.checksum}
|
|
||||||
if document.archive_checksum:
|
|
||||||
checksums.add(document.archive_checksum)
|
|
||||||
duplicates = Document.global_objects.filter(
|
|
||||||
Q(checksum__in=checksums) | Q(archive_checksum__in=checksums),
|
|
||||||
).exclude(pk=document.pk)
|
|
||||||
duplicates = duplicates.order_by("-created")
|
|
||||||
allowed = get_objects_for_user_owner_aware(
|
|
||||||
user,
|
|
||||||
"documents.view_document",
|
|
||||||
Document,
|
|
||||||
include_deleted=True,
|
|
||||||
)
|
|
||||||
return duplicates.filter(id__in=allowed)
|
|
||||||
|
|
||||||
|
|
||||||
class DuplicateDocumentSummarySerializer(serializers.Serializer):
|
|
||||||
id = serializers.IntegerField()
|
|
||||||
title = serializers.CharField()
|
|
||||||
deleted_at = serializers.DateTimeField(allow_null=True)
|
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_serializer(
|
@extend_schema_serializer(
|
||||||
deprecate_fields=["created_date"],
|
deprecate_fields=["created_date"],
|
||||||
)
|
)
|
||||||
@@ -1062,7 +1031,6 @@ class DocumentSerializer(
|
|||||||
archived_file_name = SerializerMethodField()
|
archived_file_name = SerializerMethodField()
|
||||||
created_date = serializers.DateField(required=False)
|
created_date = serializers.DateField(required=False)
|
||||||
page_count = SerializerMethodField()
|
page_count = SerializerMethodField()
|
||||||
duplicate_documents = SerializerMethodField()
|
|
||||||
|
|
||||||
notes = NotesSerializer(many=True, required=False, read_only=True)
|
notes = NotesSerializer(many=True, required=False, read_only=True)
|
||||||
|
|
||||||
@@ -1088,16 +1056,6 @@ class DocumentSerializer(
|
|||||||
def get_page_count(self, obj) -> int | None:
|
def get_page_count(self, obj) -> int | None:
|
||||||
return obj.page_count
|
return obj.page_count
|
||||||
|
|
||||||
@extend_schema_field(DuplicateDocumentSummarySerializer(many=True))
|
|
||||||
def get_duplicate_documents(self, obj):
|
|
||||||
view = self.context.get("view")
|
|
||||||
if view and getattr(view, "action", None) != "retrieve":
|
|
||||||
return []
|
|
||||||
request = self.context.get("request")
|
|
||||||
user = request.user if request else None
|
|
||||||
duplicates = _get_viewable_duplicates(obj, user)
|
|
||||||
return list(duplicates.values("id", "title", "deleted_at"))
|
|
||||||
|
|
||||||
def get_original_file_name(self, obj) -> str | None:
|
def get_original_file_name(self, obj) -> str | None:
|
||||||
return obj.original_filename
|
return obj.original_filename
|
||||||
|
|
||||||
@@ -1275,7 +1233,6 @@ class DocumentSerializer(
|
|||||||
"archive_serial_number",
|
"archive_serial_number",
|
||||||
"original_file_name",
|
"original_file_name",
|
||||||
"archived_file_name",
|
"archived_file_name",
|
||||||
"duplicate_documents",
|
|
||||||
"owner",
|
"owner",
|
||||||
"permissions",
|
"permissions",
|
||||||
"user_can_change",
|
"user_can_change",
|
||||||
@@ -2137,12 +2094,10 @@ class TasksViewSerializer(OwnedObjectSerializer):
|
|||||||
"result",
|
"result",
|
||||||
"acknowledged",
|
"acknowledged",
|
||||||
"related_document",
|
"related_document",
|
||||||
"duplicate_documents",
|
|
||||||
"owner",
|
"owner",
|
||||||
)
|
)
|
||||||
|
|
||||||
related_document = serializers.SerializerMethodField()
|
related_document = serializers.SerializerMethodField()
|
||||||
duplicate_documents = serializers.SerializerMethodField()
|
|
||||||
created_doc_re = re.compile(r"New document id (\d+) created")
|
created_doc_re = re.compile(r"New document id (\d+) created")
|
||||||
duplicate_doc_re = re.compile(r"It is a duplicate of .* \(#(\d+)\)")
|
duplicate_doc_re = re.compile(r"It is a duplicate of .* \(#(\d+)\)")
|
||||||
|
|
||||||
@@ -2167,17 +2122,6 @@ class TasksViewSerializer(OwnedObjectSerializer):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@extend_schema_field(DuplicateDocumentSummarySerializer(many=True))
|
|
||||||
def get_duplicate_documents(self, obj):
|
|
||||||
related_document = self.get_related_document(obj)
|
|
||||||
request = self.context.get("request")
|
|
||||||
user = request.user if request else None
|
|
||||||
document = Document.global_objects.filter(pk=related_document).first()
|
|
||||||
if not related_document or not user or not document:
|
|
||||||
return []
|
|
||||||
duplicates = _get_viewable_duplicates(document, user)
|
|
||||||
return list(duplicates.values("id", "title", "deleted_at"))
|
|
||||||
|
|
||||||
|
|
||||||
class RunTaskViewSerializer(serializers.Serializer):
|
class RunTaskViewSerializer(serializers.Serializer):
|
||||||
task_name = serializers.ChoiceField(
|
task_name = serializers.ChoiceField(
|
||||||
|
|||||||
@@ -131,10 +131,6 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
|||||||
self.assertIn("content", results_full[0])
|
self.assertIn("content", results_full[0])
|
||||||
self.assertIn("id", results_full[0])
|
self.assertIn("id", results_full[0])
|
||||||
|
|
||||||
# Content length is used internally for performance reasons.
|
|
||||||
# No need to expose this field.
|
|
||||||
self.assertNotIn("content_length", results_full[0])
|
|
||||||
|
|
||||||
response = self.client.get("/api/documents/?fields=id", format="json")
|
response = self.client.get("/api/documents/?fields=id", format="json")
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
results = response.data["results"]
|
results = response.data["results"]
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from django.contrib.auth.models import User
|
|||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
from documents.models import Document
|
|
||||||
from documents.models import PaperlessTask
|
from documents.models import PaperlessTask
|
||||||
from documents.tests.utils import DirectoriesMixin
|
from documents.tests.utils import DirectoriesMixin
|
||||||
from documents.views import TasksViewSet
|
from documents.views import TasksViewSet
|
||||||
@@ -259,7 +258,7 @@ class TestTasks(DirectoriesMixin, APITestCase):
|
|||||||
task_id=str(uuid.uuid4()),
|
task_id=str(uuid.uuid4()),
|
||||||
task_file_name="task_one.pdf",
|
task_file_name="task_one.pdf",
|
||||||
status=celery.states.FAILURE,
|
status=celery.states.FAILURE,
|
||||||
result="test.pdf: Unexpected error during ingestion.",
|
result="test.pdf: Not consuming test.pdf: It is a duplicate.",
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.get(self.ENDPOINT)
|
response = self.client.get(self.ENDPOINT)
|
||||||
@@ -271,7 +270,7 @@ class TestTasks(DirectoriesMixin, APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
returned_data["result"],
|
returned_data["result"],
|
||||||
"test.pdf: Unexpected error during ingestion.",
|
"test.pdf: Not consuming test.pdf: It is a duplicate.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_task_name_webui(self):
|
def test_task_name_webui(self):
|
||||||
@@ -326,34 +325,20 @@ class TestTasks(DirectoriesMixin, APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(returned_data["task_file_name"], "anothertest.pdf")
|
self.assertEqual(returned_data["task_file_name"], "anothertest.pdf")
|
||||||
|
|
||||||
def test_task_result_duplicate_warning_includes_count(self):
|
def test_task_result_failed_duplicate_includes_related_doc(self):
|
||||||
"""
|
"""
|
||||||
GIVEN:
|
GIVEN:
|
||||||
- A celery task succeeds, but a duplicate exists
|
- A celery task failed with a duplicate error
|
||||||
WHEN:
|
WHEN:
|
||||||
- API call is made to get tasks
|
- API call is made to get tasks
|
||||||
THEN:
|
THEN:
|
||||||
- The returned data includes duplicate warning metadata
|
- The returned data includes a related document link
|
||||||
"""
|
"""
|
||||||
checksum = "duplicate-checksum"
|
|
||||||
Document.objects.create(
|
|
||||||
title="Existing",
|
|
||||||
content="",
|
|
||||||
mime_type="application/pdf",
|
|
||||||
checksum=checksum,
|
|
||||||
)
|
|
||||||
created_doc = Document.objects.create(
|
|
||||||
title="Created",
|
|
||||||
content="",
|
|
||||||
mime_type="application/pdf",
|
|
||||||
checksum=checksum,
|
|
||||||
archive_checksum="another-checksum",
|
|
||||||
)
|
|
||||||
PaperlessTask.objects.create(
|
PaperlessTask.objects.create(
|
||||||
task_id=str(uuid.uuid4()),
|
task_id=str(uuid.uuid4()),
|
||||||
task_file_name="task_one.pdf",
|
task_file_name="task_one.pdf",
|
||||||
status=celery.states.SUCCESS,
|
status=celery.states.FAILURE,
|
||||||
result=f"Success. New document id {created_doc.pk} created",
|
result="Not consuming task_one.pdf: It is a duplicate of task_one_existing.pdf (#1234).",
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.get(self.ENDPOINT)
|
response = self.client.get(self.ENDPOINT)
|
||||||
@@ -363,7 +348,7 @@ class TestTasks(DirectoriesMixin, APITestCase):
|
|||||||
|
|
||||||
returned_data = response.data[0]
|
returned_data = response.data[0]
|
||||||
|
|
||||||
self.assertEqual(returned_data["related_document"], str(created_doc.pk))
|
self.assertEqual(returned_data["related_document"], "1234")
|
||||||
|
|
||||||
def test_run_train_classifier_task(self):
|
def test_run_train_classifier_task(self):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -485,21 +485,21 @@ class TestConsumer(
|
|||||||
with self.get_consumer(self.get_test_file()) as consumer:
|
with self.get_consumer(self.get_test_file()) as consumer:
|
||||||
consumer.run()
|
consumer.run()
|
||||||
|
|
||||||
|
with self.assertRaisesMessage(ConsumerError, "It is a duplicate"):
|
||||||
with self.get_consumer(self.get_test_file()) as consumer:
|
with self.get_consumer(self.get_test_file()) as consumer:
|
||||||
consumer.run()
|
consumer.run()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 2)
|
self._assert_first_last_send_progress(last_status="FAILED")
|
||||||
self._assert_first_last_send_progress()
|
|
||||||
|
|
||||||
def testDuplicates2(self):
|
def testDuplicates2(self):
|
||||||
with self.get_consumer(self.get_test_file()) as consumer:
|
with self.get_consumer(self.get_test_file()) as consumer:
|
||||||
consumer.run()
|
consumer.run()
|
||||||
|
|
||||||
|
with self.assertRaisesMessage(ConsumerError, "It is a duplicate"):
|
||||||
with self.get_consumer(self.get_test_archive_file()) as consumer:
|
with self.get_consumer(self.get_test_archive_file()) as consumer:
|
||||||
consumer.run()
|
consumer.run()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 2)
|
self._assert_first_last_send_progress(last_status="FAILED")
|
||||||
self._assert_first_last_send_progress()
|
|
||||||
|
|
||||||
def testDuplicates3(self):
|
def testDuplicates3(self):
|
||||||
with self.get_consumer(self.get_test_archive_file()) as consumer:
|
with self.get_consumer(self.get_test_archive_file()) as consumer:
|
||||||
@@ -513,11 +513,10 @@ class TestConsumer(
|
|||||||
|
|
||||||
Document.objects.all().delete()
|
Document.objects.all().delete()
|
||||||
|
|
||||||
|
with self.assertRaisesMessage(ConsumerError, "document is in the trash"):
|
||||||
with self.get_consumer(self.get_test_file()) as consumer:
|
with self.get_consumer(self.get_test_file()) as consumer:
|
||||||
consumer.run()
|
consumer.run()
|
||||||
|
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
|
||||||
|
|
||||||
def testAsnExists(self):
|
def testAsnExists(self):
|
||||||
with self.get_consumer(
|
with self.get_consumer(
|
||||||
self.get_test_file(),
|
self.get_test_file(),
|
||||||
@@ -719,45 +718,12 @@ class TestConsumer(
|
|||||||
dst = self.get_test_file()
|
dst = self.get_test_file()
|
||||||
self.assertIsFile(dst)
|
self.assertIsFile(dst)
|
||||||
|
|
||||||
expected_message = (
|
with self.assertRaises(ConsumerError):
|
||||||
f"{dst.name}: Not consuming {dst.name}: "
|
|
||||||
f"It is a duplicate of {document.title} (#{document.pk})"
|
|
||||||
)
|
|
||||||
|
|
||||||
with self.assertRaisesMessage(ConsumerError, expected_message):
|
|
||||||
with self.get_consumer(dst) as consumer:
|
with self.get_consumer(dst) as consumer:
|
||||||
consumer.run()
|
consumer.run()
|
||||||
|
|
||||||
self.assertIsNotFile(dst)
|
self.assertIsNotFile(dst)
|
||||||
self.assertEqual(Document.objects.count(), 1)
|
self._assert_first_last_send_progress(last_status="FAILED")
|
||||||
self._assert_first_last_send_progress(last_status=ProgressStatusOptions.FAILED)
|
|
||||||
|
|
||||||
@override_settings(CONSUMER_DELETE_DUPLICATES=True)
|
|
||||||
def test_delete_duplicate_in_trash(self):
|
|
||||||
dst = self.get_test_file()
|
|
||||||
with self.get_consumer(dst) as consumer:
|
|
||||||
consumer.run()
|
|
||||||
|
|
||||||
# Move the existing document to trash
|
|
||||||
document = Document.objects.first()
|
|
||||||
document.delete()
|
|
||||||
|
|
||||||
dst = self.get_test_file()
|
|
||||||
self.assertIsFile(dst)
|
|
||||||
|
|
||||||
expected_message = (
|
|
||||||
f"{dst.name}: Not consuming {dst.name}: "
|
|
||||||
f"It is a duplicate of {document.title} (#{document.pk})"
|
|
||||||
f" Note: existing document is in the trash."
|
|
||||||
)
|
|
||||||
|
|
||||||
with self.assertRaisesMessage(ConsumerError, expected_message):
|
|
||||||
with self.get_consumer(dst) as consumer:
|
|
||||||
consumer.run()
|
|
||||||
|
|
||||||
self.assertIsNotFile(dst)
|
|
||||||
self.assertEqual(Document.global_objects.count(), 1)
|
|
||||||
self.assertEqual(Document.objects.count(), 0)
|
|
||||||
|
|
||||||
@override_settings(CONSUMER_DELETE_DUPLICATES=False)
|
@override_settings(CONSUMER_DELETE_DUPLICATES=False)
|
||||||
def test_no_delete_duplicate(self):
|
def test_no_delete_duplicate(self):
|
||||||
@@ -777,12 +743,15 @@ class TestConsumer(
|
|||||||
dst = self.get_test_file()
|
dst = self.get_test_file()
|
||||||
self.assertIsFile(dst)
|
self.assertIsFile(dst)
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
ConsumerError,
|
||||||
|
r"sample\.pdf: Not consuming sample\.pdf: It is a duplicate of sample \(#\d+\)",
|
||||||
|
):
|
||||||
with self.get_consumer(dst) as consumer:
|
with self.get_consumer(dst) as consumer:
|
||||||
consumer.run()
|
consumer.run()
|
||||||
|
|
||||||
self.assertIsNotFile(dst)
|
self.assertIsFile(dst)
|
||||||
self.assertEqual(Document.objects.count(), 2)
|
self._assert_first_last_send_progress(last_status="FAILED")
|
||||||
self._assert_first_last_send_progress()
|
|
||||||
|
|
||||||
@override_settings(FILENAME_FORMAT="{title}")
|
@override_settings(FILENAME_FORMAT="{title}")
|
||||||
@mock.patch("documents.parsers.document_consumer_declaration.send")
|
@mock.patch("documents.parsers.document_consumer_declaration.send")
|
||||||
|
|||||||
@@ -241,10 +241,6 @@ class TestExportImport(
|
|||||||
checksum = hashlib.md5(f.read()).hexdigest()
|
checksum = hashlib.md5(f.read()).hexdigest()
|
||||||
self.assertEqual(checksum, element["fields"]["checksum"])
|
self.assertEqual(checksum, element["fields"]["checksum"])
|
||||||
|
|
||||||
# Generated field "content_length" should not be exported,
|
|
||||||
# it is automatically computed during import.
|
|
||||||
self.assertNotIn("content_length", element["fields"])
|
|
||||||
|
|
||||||
if document_exporter.EXPORTER_ARCHIVE_NAME in element:
|
if document_exporter.EXPORTER_ARCHIVE_NAME in element:
|
||||||
fname = (
|
fname = (
|
||||||
self.target / element[document_exporter.EXPORTER_ARCHIVE_NAME]
|
self.target / element[document_exporter.EXPORTER_ARCHIVE_NAME]
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ from django.db.models import Model
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.db.models import Sum
|
from django.db.models import Sum
|
||||||
from django.db.models import When
|
from django.db.models import When
|
||||||
|
from django.db.models.functions import Length
|
||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
from django.db.models.manager import Manager
|
from django.db.models.manager import Manager
|
||||||
from django.http import FileResponse
|
from django.http import FileResponse
|
||||||
@@ -2325,6 +2326,7 @@ class StatisticsView(GenericAPIView):
|
|||||||
user = request.user if request.user is not None else None
|
user = request.user if request.user is not None else None
|
||||||
|
|
||||||
documents = (
|
documents = (
|
||||||
|
(
|
||||||
Document.objects.all()
|
Document.objects.all()
|
||||||
if user is None
|
if user is None
|
||||||
else get_objects_for_user_owner_aware(
|
else get_objects_for_user_owner_aware(
|
||||||
@@ -2333,11 +2335,14 @@ class StatisticsView(GenericAPIView):
|
|||||||
Document,
|
Document,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.only("mime_type", "content")
|
||||||
|
.prefetch_related("tags")
|
||||||
|
)
|
||||||
tags = (
|
tags = (
|
||||||
Tag.objects.all()
|
Tag.objects.all()
|
||||||
if user is None
|
if user is None
|
||||||
else get_objects_for_user_owner_aware(user, "documents.view_tag", Tag)
|
else get_objects_for_user_owner_aware(user, "documents.view_tag", Tag)
|
||||||
).only("id", "is_inbox_tag")
|
)
|
||||||
correspondent_count = (
|
correspondent_count = (
|
||||||
Correspondent.objects.count()
|
Correspondent.objects.count()
|
||||||
if user is None
|
if user is None
|
||||||
@@ -2366,33 +2371,31 @@ class StatisticsView(GenericAPIView):
|
|||||||
).count()
|
).count()
|
||||||
)
|
)
|
||||||
|
|
||||||
inbox_tag_pks = list(
|
documents_total = documents.count()
|
||||||
tags.filter(is_inbox_tag=True).values_list("pk", flat=True),
|
|
||||||
)
|
inbox_tags = tags.filter(is_inbox_tag=True)
|
||||||
|
|
||||||
documents_inbox = (
|
documents_inbox = (
|
||||||
documents.filter(tags__id__in=inbox_tag_pks).values("id").distinct().count()
|
documents.filter(tags__id__in=inbox_tags).distinct().count()
|
||||||
if inbox_tag_pks
|
if inbox_tags.exists()
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
# Single SQL request for document stats and mime type counts
|
document_file_type_counts = (
|
||||||
mime_type_stats = list(
|
|
||||||
documents.values("mime_type")
|
documents.values("mime_type")
|
||||||
.annotate(
|
.annotate(mime_type_count=Count("mime_type"))
|
||||||
mime_type_count=Count("id"),
|
.order_by("-mime_type_count")
|
||||||
mime_type_chars=Sum("content_length"),
|
if documents_total > 0
|
||||||
)
|
else []
|
||||||
.order_by("-mime_type_count"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Calculate totals from grouped results
|
character_count = (
|
||||||
documents_total = sum(row["mime_type_count"] for row in mime_type_stats)
|
documents.annotate(
|
||||||
character_count = sum(row["mime_type_chars"] or 0 for row in mime_type_stats)
|
characters=Length("content"),
|
||||||
document_file_type_counts = [
|
)
|
||||||
{"mime_type": row["mime_type"], "mime_type_count": row["mime_type_count"]}
|
.aggregate(Sum("characters"))
|
||||||
for row in mime_type_stats
|
.get("characters__sum")
|
||||||
]
|
)
|
||||||
|
|
||||||
current_asn = Document.objects.aggregate(
|
current_asn = Document.objects.aggregate(
|
||||||
Max("archive_serial_number", default=0),
|
Max("archive_serial_number", default=0),
|
||||||
@@ -2405,9 +2408,11 @@ class StatisticsView(GenericAPIView):
|
|||||||
"documents_total": documents_total,
|
"documents_total": documents_total,
|
||||||
"documents_inbox": documents_inbox,
|
"documents_inbox": documents_inbox,
|
||||||
"inbox_tag": (
|
"inbox_tag": (
|
||||||
inbox_tag_pks[0] if inbox_tag_pks else None
|
inbox_tags.first().pk if inbox_tags.exists() else None
|
||||||
), # backwards compatibility
|
), # backwards compatibility
|
||||||
"inbox_tags": (inbox_tag_pks if inbox_tag_pks else None),
|
"inbox_tags": (
|
||||||
|
[tag.pk for tag in inbox_tags] if inbox_tags.exists() else None
|
||||||
|
),
|
||||||
"document_file_type_counts": document_file_type_counts,
|
"document_file_type_counts": document_file_type_counts,
|
||||||
"character_count": character_count,
|
"character_count": character_count,
|
||||||
"tag_count": len(tags),
|
"tag_count": len(tags),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user