mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-09-09 03:07:59 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8de1d18762 | ||
|
|
b989b74140 | ||
|
|
5194f47291 | ||
|
|
714885d7a5 | ||
|
|
73e777a48c | ||
|
|
e9141366bb | ||
|
|
7813375123 | ||
|
|
0132c7bd6e | ||
|
|
4d5897ec80 | ||
|
|
f197d09b3e | ||
|
|
937feb1bef |
@@ -72,7 +72,7 @@ jobs:
|
|||||||
'You are welcome to open a new issue that describes the problem you observed in your own words.'
|
'You are welcome to open a new issue that describes the problem you observed in your own words.'
|
||||||
: 'This issue was automatically closed because it was not opened using our bug report form. ' +
|
: 'This issue was automatically closed because it was not opened using our bug report form. ' +
|
||||||
'Issues have to be created through the form so that the details we need to investigate are included.\n\n' +
|
'Issues have to be created through the form so that the details we need to investigate are included.\n\n' +
|
||||||
`If the problem is still there, please [open a new issue](${newIssue}) using the form. No other action is needed here.\n\n' +
|
`If the problem is still there, please [open a new issue](${newIssue}) using the form. No other action is needed here.\n\n` +
|
||||||
'If any part of your report was written by an AI tool or agent, you must say so: undisclosed AI-generated ' +
|
'If any part of your report was written by an AI tool or agent, you must say so: undisclosed AI-generated ' +
|
||||||
`contributions are a violation of our [Code of Conduct](${codeOfConduct}).`;
|
`contributions are a violation of our [Code of Conduct](${codeOfConduct}).`;
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ jobs:
|
|||||||
pr-bot:
|
pr-bot:
|
||||||
name: Automated PR Bot
|
name: Automated PR Bot
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Runs after Anti-slop so the welcome comment can see whether the PR was closed
|
||||||
|
# instead of racing it. Still runs if that job fails, so labeling is not lost.
|
||||||
|
needs: Anti-slop
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
@@ -99,8 +103,25 @@ jobs:
|
|||||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const pr = context.payload.pull_request;
|
const user = context.payload.pull_request.user.login;
|
||||||
const user = pr.user.login;
|
|
||||||
|
// Re-read the PR: Anti-slop may have closed and labeled it after the webhook
|
||||||
|
const { data: pr } = await github.rest.pulls.get({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
pull_number: context.payload.pull_request.number,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pr.state === 'closed') {
|
||||||
|
core.info('Skipping comment: PR is already closed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const labels = pr.labels.map((label) => (typeof label === 'string' ? label : label.name));
|
||||||
|
if (labels.includes('ai')) {
|
||||||
|
core.info('Skipping comment: PR is labeled ai');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { data: members } = await github.rest.orgs.listMembers({
|
const { data: members } = await github.rest.orgs.listMembers({
|
||||||
org: 'paperless-ngx',
|
org: 'paperless-ngx',
|
||||||
|
|||||||
@@ -138,7 +138,9 @@ for suggested generation and embedding models.
|
|||||||
With AI enabled, Paperless-ngx can suggest a title, tags, correspondent, document type,
|
With AI enabled, Paperless-ngx can suggest a title, tags, correspondent, document type,
|
||||||
storage path and dates by sending the document to the LLM. This is **opt-in per request**
|
storage path and dates by sending the document to the LLM. This is **opt-in per request**
|
||||||
and surfaces through the "Suggest" control on the document detail page, alongside the
|
and surfaces through the "Suggest" control on the document detail page, alongside the
|
||||||
classic classifier-based suggestions — it does not disable them. Suggestion output
|
classic classifier-based suggestions — it does not disable them. Suggestions are requested
|
||||||
|
automatically when you open a document that carries an inbox tag unless "Automatically request
|
||||||
|
suggestions for inbox documents" under Settings > Documents is disabled. Suggestion output
|
||||||
language can be steered with
|
language can be steered with
|
||||||
[`PAPERLESS_AI_LLM_OUTPUT_LANGUAGE`](configuration.md#PAPERLESS_AI_LLM_OUTPUT_LANGUAGE)
|
[`PAPERLESS_AI_LLM_OUTPUT_LANGUAGE`](configuration.md#PAPERLESS_AI_LLM_OUTPUT_LANGUAGE)
|
||||||
(otherwise it follows the user's UI language).
|
(otherwise it follows the user's UI language).
|
||||||
|
|||||||
+4
-1
@@ -317,6 +317,8 @@ a "document already exists" message.
|
|||||||
|
|
||||||
Paperless-ngx can suggest tags, correspondents, document types and storage paths for documents based on the content of the document. This is done using a (non-LLM) machine learning model that is trained on the documents in your database. The suggestions are shown in the document detail page and can be accepted or rejected by the user.
|
Paperless-ngx can suggest tags, correspondents, document types and storage paths for documents based on the content of the document. This is done using a (non-LLM) machine learning model that is trained on the documents in your database. The suggestions are shown in the document detail page and can be accepted or rejected by the user.
|
||||||
|
|
||||||
|
Suggestions are requested automatically when you open a document that still has an inbox tag. To only request them by pressing the "Suggest" button instead, turn off "Automatically request suggestions for inbox documents" under Settings > Documents.
|
||||||
|
|
||||||
## AI Features
|
## AI Features
|
||||||
|
|
||||||
Paperless-ngx includes several features that use AI to enhance the document management experience. These features are optional and can be enabled or disabled in the settings. If you are using the AI features, you may want to also enable the "LLM index" feature, which supports Retrieval-Augmented Generation (RAG) designed to improve the quality of AI responses. The LLM index feature is not enabled by default and requires additional configuration.
|
Paperless-ngx includes several features that use AI to enhance the document management experience. These features are optional and can be enabled or disabled in the settings. If you are using the AI features, you may want to also enable the "LLM index" feature, which supports Retrieval-Augmented Generation (RAG) designed to improve the quality of AI responses. The LLM index feature is not enabled by default and requires additional configuration.
|
||||||
@@ -684,7 +686,8 @@ It requires [AI features](configuration.md#ai) to be enabled. You can specify:
|
|||||||
never replace the document's existing tags.
|
never replace the document's existing tags.
|
||||||
|
|
||||||
The action works with every trigger **except Consumption Started**, because suggestions are made from
|
The action works with every trigger **except Consumption Started**, because suggestions are made from
|
||||||
the document's text, which does not exist until after the document has been processed.
|
the document's text, which does not exist until after the document has been processed. Documents whose
|
||||||
|
processed text is empty or contains only whitespace are skipped.
|
||||||
|
|
||||||
Because the query to the AI service is slow, the action is queued and runs in the background rather
|
Because the query to the AI service is slow, the action is queued and runs in the background rather
|
||||||
than as part of the workflow run itself. The document is updated once the suggestions come back.
|
than as part of the workflow run itself. The document is updated once the suggestions come back.
|
||||||
|
|||||||
+140
-98
@@ -501,15 +501,43 @@
|
|||||||
<context context-type="linenumber">30</context>
|
<context context-type="linenumber">30</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="7057826840809102816" datatype="html">
|
||||||
|
<source>This value overrides <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>, which is set outside Paperless.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||||
|
<context context-type="linenumber">35</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="7221396516204435584" datatype="html">
|
||||||
|
<source><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||||
|
<context context-type="linenumber">37</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="8318849619178340389" datatype="html">
|
||||||
|
<source>Use the externally configured value</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||||
|
<context context-type="linenumber">42</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6032629623003430385" datatype="html">
|
||||||
|
<source>Reset to external</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||||
|
<context context-type="linenumber">43</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="7808756054397155068" datatype="html">
|
<trans-unit id="7808756054397155068" datatype="html">
|
||||||
<source>Reset</source>
|
<source>Reset</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||||
<context context-type="linenumber">34</context>
|
<context context-type="linenumber">46</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||||
<context context-type="linenumber">35</context>
|
<context context-type="linenumber">47</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
@@ -520,7 +548,7 @@
|
|||||||
<source>Enable</source>
|
<source>Enable</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||||
<context context-type="linenumber">43</context>
|
<context context-type="linenumber">56</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||||
@@ -531,11 +559,11 @@
|
|||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||||
<context context-type="linenumber">67,68</context>
|
<context context-type="linenumber">80,81</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">401,402</context>
|
<context context-type="linenumber">407,408</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/confirm-dialog/confirm-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/confirm-dialog/confirm-dialog.component.ts</context>
|
||||||
@@ -610,11 +638,11 @@
|
|||||||
<source>Save</source>
|
<source>Save</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
|
||||||
<context context-type="linenumber">70,71</context>
|
<context context-type="linenumber">83,84</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">402,403</context>
|
<context context-type="linenumber">408,409</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||||
@@ -681,42 +709,42 @@
|
|||||||
<source>Error retrieving config</source>
|
<source>Error retrieving config</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||||
<context context-type="linenumber">117</context>
|
<context context-type="linenumber">118</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1172622527269118932" datatype="html">
|
<trans-unit id="1172622527269118932" datatype="html">
|
||||||
<source>Invalid JSON</source>
|
<source>Invalid JSON</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||||
<context context-type="linenumber">143</context>
|
<context context-type="linenumber">144</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5103146006962696736" datatype="html">
|
<trans-unit id="5103146006962696736" datatype="html">
|
||||||
<source>Configuration updated</source>
|
<source>Configuration updated</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||||
<context context-type="linenumber">187</context>
|
<context context-type="linenumber">193</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1664963291286452273" datatype="html">
|
<trans-unit id="1664963291286452273" datatype="html">
|
||||||
<source>An error occurred updating configuration</source>
|
<source>An error occurred updating configuration</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||||
<context context-type="linenumber">192</context>
|
<context context-type="linenumber">198</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2653081282186526824" datatype="html">
|
<trans-unit id="2653081282186526824" datatype="html">
|
||||||
<source>File successfully updated</source>
|
<source>File successfully updated</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||||
<context context-type="linenumber">214</context>
|
<context context-type="linenumber">220</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5902783625859504265" datatype="html">
|
<trans-unit id="5902783625859504265" datatype="html">
|
||||||
<source>An error occurred uploading file</source>
|
<source>An error occurred uploading file</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/config/config.component.ts</context>
|
||||||
<context context-type="linenumber">219</context>
|
<context context-type="linenumber">225</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4804785061014590286" datatype="html">
|
<trans-unit id="4804785061014590286" datatype="html">
|
||||||
@@ -1226,53 +1254,67 @@
|
|||||||
<context context-type="linenumber">236</context>
|
<context context-type="linenumber">236</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="1043449647727456069" datatype="html">
|
||||||
|
<source>Automatically request suggestions for inbox documents</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">242</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="460700039378563125" datatype="html">
|
||||||
|
<source>If un-checked, suggestions must be requested via the Suggest button.</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">242</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="8793267604636304297" datatype="html">
|
<trans-unit id="8793267604636304297" datatype="html">
|
||||||
<source>Show document thumbnail during loading</source>
|
<source>Show document thumbnail during loading</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">242</context>
|
<context context-type="linenumber">248</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1783600598811723080" datatype="html">
|
<trans-unit id="1783600598811723080" datatype="html">
|
||||||
<source>Built-in fields to show:</source>
|
<source>Built-in fields to show:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">248,249</context>
|
<context context-type="linenumber">254,255</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3467966318201103991" datatype="html">
|
<trans-unit id="3467966318201103991" datatype="html">
|
||||||
<source>Uncheck fields to hide them on the document details page.</source>
|
<source>Uncheck fields to hide them on the document details page.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">260,261</context>
|
<context context-type="linenumber">266,267</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8508424367627989968" datatype="html">
|
<trans-unit id="8508424367627989968" datatype="html">
|
||||||
<source>Bulk editing</source>
|
<source>Bulk editing</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">266,267</context>
|
<context context-type="linenumber">272,273</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8158899674926420054" datatype="html">
|
<trans-unit id="8158899674926420054" datatype="html">
|
||||||
<source>Show confirmation dialogs</source>
|
<source>Show confirmation dialogs</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">269</context>
|
<context context-type="linenumber">275</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="290238406234356122" datatype="html">
|
<trans-unit id="290238406234356122" datatype="html">
|
||||||
<source>Apply on close</source>
|
<source>Apply on close</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">270</context>
|
<context context-type="linenumber">276</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5084275925647254161" datatype="html">
|
<trans-unit id="5084275925647254161" datatype="html">
|
||||||
<source>PDF Editor</source>
|
<source>PDF Editor</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">274,275</context>
|
<context context-type="linenumber">280,281</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.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||||
@@ -1280,21 +1322,21 @@
|
|||||||
</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">1800</context>
|
<context context-type="linenumber">1808</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1577733187050997705" datatype="html">
|
<trans-unit id="1577733187050997705" datatype="html">
|
||||||
<source>Default editing mode</source>
|
<source>Default editing mode</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">277,278</context>
|
<context context-type="linenumber">283,284</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7273640930165035289" datatype="html">
|
<trans-unit id="7273640930165035289" datatype="html">
|
||||||
<source>Create new document(s)</source>
|
<source>Create new document(s)</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">281,282</context>
|
<context context-type="linenumber">287,288</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
|
||||||
@@ -1305,7 +1347,7 @@
|
|||||||
<source>Add document version</source>
|
<source>Add document version</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">282,283</context>
|
<context context-type="linenumber">288,289</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
|
||||||
@@ -1316,7 +1358,7 @@
|
|||||||
<source>Notes</source>
|
<source>Notes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">287,288</context>
|
<context context-type="linenumber">293,294</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>
|
||||||
@@ -1335,14 +1377,14 @@
|
|||||||
<source>Enable notes</source>
|
<source>Enable notes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">290</context>
|
<context context-type="linenumber">296</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7314814725704332646" datatype="html">
|
<trans-unit id="7314814725704332646" datatype="html">
|
||||||
<source>Permissions</source>
|
<source>Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">299,300</context>
|
<context context-type="linenumber">305,306</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html</context>
|
||||||
@@ -1397,28 +1439,28 @@
|
|||||||
<source>Default Permissions</source>
|
<source>Default Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">302,304</context>
|
<context context-type="linenumber">308,310</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1267490156259885391" datatype="html">
|
<trans-unit id="1267490156259885391" datatype="html">
|
||||||
<source> Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI. These settings do not apply to documents. </source>
|
<source> Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI. These settings do not apply to documents. </source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">307</context>
|
<context context-type="linenumber">313</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4292903881380648974" datatype="html">
|
<trans-unit id="4292903881380648974" datatype="html">
|
||||||
<source>Default Owner</source>
|
<source>Default Owner</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">313,314</context>
|
<context context-type="linenumber">319,320</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="734147282056744882" datatype="html">
|
<trans-unit id="734147282056744882" datatype="html">
|
||||||
<source>Objects without an owner can be viewed and edited by all users</source>
|
<source>Objects without an owner can be viewed and edited by all users</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">317,318</context>
|
<context context-type="linenumber">323,324</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
||||||
@@ -1429,18 +1471,18 @@
|
|||||||
<source>Default View Permissions</source>
|
<source>Default View Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">322,323</context>
|
<context context-type="linenumber">328,329</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2191775412581217688" datatype="html">
|
<trans-unit id="2191775412581217688" datatype="html">
|
||||||
<source>Users:</source>
|
<source>Users:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">327,328</context>
|
<context context-type="linenumber">333,334</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">354,355</context>
|
<context context-type="linenumber">360,361</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
@@ -1471,11 +1513,11 @@
|
|||||||
<source>Groups:</source>
|
<source>Groups:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">337,338</context>
|
<context context-type="linenumber">343,344</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">364,365</context>
|
<context context-type="linenumber">370,371</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
@@ -1506,14 +1548,14 @@
|
|||||||
<source>Default Edit Permissions</source>
|
<source>Default Edit Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">349,350</context>
|
<context context-type="linenumber">355,356</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3728984448750213892" datatype="html">
|
<trans-unit id="3728984448750213892" datatype="html">
|
||||||
<source>Edit permissions also grant viewing permissions</source>
|
<source>Edit permissions also grant viewing permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">373,374</context>
|
<context context-type="linenumber">379,380</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
@@ -1532,7 +1574,7 @@
|
|||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">381,382</context>
|
<context context-type="linenumber">387,388</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.html</context>
|
<context context-type="sourcefile">src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.html</context>
|
||||||
@@ -1547,42 +1589,42 @@
|
|||||||
<source>Document processing</source>
|
<source>Document processing</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">384,386</context>
|
<context context-type="linenumber">390,392</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3656786776644872398" datatype="html">
|
<trans-unit id="3656786776644872398" datatype="html">
|
||||||
<source>Show notifications when new documents are detected</source>
|
<source>Show notifications when new documents are detected</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">388</context>
|
<context context-type="linenumber">394</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6057053428592387613" datatype="html">
|
<trans-unit id="6057053428592387613" datatype="html">
|
||||||
<source>Show notifications when document processing completes successfully</source>
|
<source>Show notifications when document processing completes successfully</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">389</context>
|
<context context-type="linenumber">395</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="370315664367425513" datatype="html">
|
<trans-unit id="370315664367425513" datatype="html">
|
||||||
<source>Show notifications when document processing fails</source>
|
<source>Show notifications when document processing fails</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">390</context>
|
<context context-type="linenumber">396</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6838309441164918531" datatype="html">
|
<trans-unit id="6838309441164918531" datatype="html">
|
||||||
<source>Suppress notifications on dashboard</source>
|
<source>Suppress notifications on dashboard</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">391</context>
|
<context context-type="linenumber">397</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2741919327232918179" datatype="html">
|
<trans-unit id="2741919327232918179" datatype="html">
|
||||||
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">391</context>
|
<context context-type="linenumber">397</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6839066544204061364" datatype="html">
|
<trans-unit id="6839066544204061364" datatype="html">
|
||||||
@@ -1766,7 +1808,7 @@
|
|||||||
<source>Error retrieving users</source>
|
<source>Error retrieving users</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">255</context>
|
<context context-type="linenumber">256</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||||
@@ -1785,7 +1827,7 @@
|
|||||||
<source>Error retrieving groups</source>
|
<source>Error retrieving groups</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">274</context>
|
<context context-type="linenumber">275</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||||
@@ -1804,28 +1846,28 @@
|
|||||||
<source>Settings were saved successfully.</source>
|
<source>Settings were saved successfully.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">591</context>
|
<context context-type="linenumber">599</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="525012668859298131" datatype="html">
|
<trans-unit id="525012668859298131" datatype="html">
|
||||||
<source>Settings were saved successfully. Reload is required to apply some changes.</source>
|
<source>Settings were saved successfully. Reload is required to apply some changes.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">595</context>
|
<context context-type="linenumber">603</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8491974984518503778" datatype="html">
|
<trans-unit id="8491974984518503778" datatype="html">
|
||||||
<source>Reload now</source>
|
<source>Reload now</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">596</context>
|
<context context-type="linenumber">604</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3011185103048412841" datatype="html">
|
<trans-unit id="3011185103048412841" datatype="html">
|
||||||
<source>An error occurred while saving settings.</source>
|
<source>An error occurred while saving settings.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||||
<context context-type="linenumber">606</context>
|
<context context-type="linenumber">614</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
|
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
|
||||||
@@ -2293,7 +2335,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">666</context>
|
<context context-type="linenumber">673</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/document-version-dropdown.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/document-version-dropdown.component.html</context>
|
||||||
@@ -3200,11 +3242,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">1414</context>
|
<context context-type="linenumber">1422</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">1801</context>
|
<context context-type="linenumber">1809</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>
|
||||||
@@ -3846,7 +3888,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">1367</context>
|
<context context-type="linenumber">1375</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>
|
||||||
@@ -3990,7 +4032,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">1854</context>
|
<context context-type="linenumber">1862</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6661109599266152398" datatype="html">
|
<trans-unit id="6661109599266152398" datatype="html">
|
||||||
@@ -4001,7 +4043,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">1855</context>
|
<context context-type="linenumber">1863</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5162686434580248853" datatype="html">
|
<trans-unit id="5162686434580248853" datatype="html">
|
||||||
@@ -4012,7 +4054,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">1856</context>
|
<context context-type="linenumber">1864</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6665634854532231106" datatype="html">
|
<trans-unit id="6665634854532231106" datatype="html">
|
||||||
@@ -6149,7 +6191,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">1371</context>
|
<context context-type="linenumber">1379</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>
|
||||||
@@ -8573,81 +8615,81 @@
|
|||||||
<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">433</context>
|
<context context-type="linenumber">440</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">535,537</context>
|
<context context-type="linenumber">542,544</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">992,994</context>
|
<context context-type="linenumber">1000,1002</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6357361810318120957" datatype="html">
|
<trans-unit id="6357361810318120957" datatype="html">
|
||||||
<source>Document was updated</source>
|
<source>Document was updated</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">661</context>
|
<context context-type="linenumber">668</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5154064822428631306" datatype="html">
|
<trans-unit id="5154064822428631306" datatype="html">
|
||||||
<source>Document was updated at <x id="PH" equiv-text="formattedModified"/>.</source>
|
<source>Document was updated at <x id="PH" equiv-text="formattedModified"/>.</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">662</context>
|
<context context-type="linenumber">669</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8462497568316256794" datatype="html">
|
<trans-unit id="8462497568316256794" datatype="html">
|
||||||
<source>Reload to discard your local unsaved edits and load the latest remote version.</source>
|
<source>Reload to discard your local unsaved edits and load the latest remote 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">663</context>
|
<context context-type="linenumber">670</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7967484035994732534" datatype="html">
|
<trans-unit id="7967484035994732534" datatype="html">
|
||||||
<source>Reload</source>
|
<source>Reload</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">665</context>
|
<context context-type="linenumber">672</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2907037627372942104" datatype="html">
|
<trans-unit id="2907037627372942104" datatype="html">
|
||||||
<source>Document reloaded with latest changes.</source>
|
<source>Document reloaded with latest changes.</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">721</context>
|
<context context-type="linenumber">728</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6435639868943916539" datatype="html">
|
<trans-unit id="6435639868943916539" datatype="html">
|
||||||
<source>Document reloaded.</source>
|
<source>Document reloaded.</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">732</context>
|
<context context-type="linenumber">739</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">834</context>
|
<context context-type="linenumber">841</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">844</context>
|
<context context-type="linenumber">851</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">852</context>
|
<context context-type="linenumber">859</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>
|
||||||
@@ -8658,67 +8700,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">859</context>
|
<context context-type="linenumber">866</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">868</context>
|
<context context-type="linenumber">875</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7427704425579737895" datatype="html">
|
<trans-unit id="7427704425579737895" datatype="html">
|
||||||
<source>Error retrieving version content</source>
|
<source>Error retrieving version 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">975</context>
|
<context context-type="linenumber">983</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">1035</context>
|
<context context-type="linenumber">1043</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">1247</context>
|
<context context-type="linenumber">1255</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">1274</context>
|
<context context-type="linenumber">1282</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">1280</context>
|
<context context-type="linenumber">1288</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">1335</context>
|
<context context-type="linenumber">1343</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">1368</context>
|
<context context-type="linenumber">1376</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">1369</context>
|
<context context-type="linenumber">1377</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>
|
||||||
@@ -8729,14 +8771,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">1390</context>
|
<context context-type="linenumber">1398</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">1410</context>
|
<context context-type="linenumber">1418</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>
|
||||||
@@ -8747,102 +8789,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">1411</context>
|
<context context-type="linenumber">1419</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">1412</context>
|
<context context-type="linenumber">1420</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4700389117298802932" datatype="html">
|
<trans-unit id="4700389117298802932" datatype="html">
|
||||||
<source>Reprocess operation for "<x id="PH" equiv-text="this.document().title"/>" will begin in the background.</source>
|
<source>Reprocess 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">1425</context>
|
<context context-type="linenumber">1433</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">1436</context>
|
<context context-type="linenumber">1444</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">1500</context>
|
<context context-type="linenumber">1508</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">1578</context>
|
<context context-type="linenumber">1586</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">1821</context>
|
<context context-type="linenumber">1829</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">1833</context>
|
<context context-type="linenumber">1841</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">1844</context>
|
<context context-type="linenumber">1852</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">1878</context>
|
<context context-type="linenumber">1886</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">1892</context>
|
<context context-type="linenumber">1900</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">1942</context>
|
<context context-type="linenumber">1950</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">1951</context>
|
<context context-type="linenumber">1959</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">2034</context>
|
<context context-type="linenumber">2042</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">2040</context>
|
<context context-type="linenumber">2048</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4958946940233632319" datatype="html">
|
<trans-unit id="4958946940233632319" datatype="html">
|
||||||
|
|||||||
@@ -23,17 +23,30 @@
|
|||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card bg-light">
|
<div class="card bg-light">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="card-title d-flex align-items-center">
|
<div class="card-title d-flex align-items-center flex-wrap">
|
||||||
<h6 class="mb-0">
|
<h6 class="mb-0">
|
||||||
{{option.title}}
|
{{option.title}}
|
||||||
</h6>
|
</h6>
|
||||||
<a class="btn btn-sm btn-link" title="Read the documentation about this setting" i18n-title [href]="getDocsUrl(option.config_key)" target="_blank" referrerpolicy="no-referrer">
|
<a class="btn btn-sm btn-link" title="Read the documentation about this setting" i18n-title [href]="getDocsUrl(option.config_key)" target="_blank" referrerpolicy="no-referrer">
|
||||||
<i-bs name="info-circle"></i-bs>
|
<i-bs name="info-circle"></i-bs>
|
||||||
</a>
|
</a>
|
||||||
|
@if (isExternallyConfigured(option.config_key)) {
|
||||||
|
@if (isSet(option.key)) {
|
||||||
|
<span class="badge rounded-pill bg-body-secondary text-dark fw-normal" title="This value overrides {{option.config_key}}, which is set outside Paperless." i18n-title>Overrides external</span>
|
||||||
|
} @else {
|
||||||
|
<span class="badge rounded-pill bg-body-secondary text-dark fw-normal" title="{{option.config_key}} is set outside Paperless. Enter a value here to override it." i18n-title>Set externally</span>
|
||||||
|
}
|
||||||
|
}
|
||||||
@if (isSet(option.key)) {
|
@if (isSet(option.key)) {
|
||||||
<button type="button" class="btn btn-sm btn-link text-danger ms-auto pe-0" title="Reset" i18n-title (click)="resetOption(option.key)">
|
@if (isExternallyConfigured(option.config_key)) {
|
||||||
<i-bs class="me-1" name="x"></i-bs><ng-container i18n>Reset</ng-container>
|
<button type="button" class="btn btn-sm btn-link text-danger ms-auto pe-0" title="Use the externally configured value" i18n-title (click)="resetOption(option.key)">
|
||||||
</button>
|
<i-bs class="me-1" name="x"></i-bs><ng-container i18n>Reset to external</ng-container>
|
||||||
|
</button>
|
||||||
|
} @else {
|
||||||
|
<button type="button" class="btn btn-sm btn-link text-danger ms-auto pe-0" title="Reset" i18n-title (click)="resetOption(option.key)">
|
||||||
|
<i-bs class="me-1" name="x"></i-bs><ng-container i18n>Reset</ng-container>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-n3">
|
<div class="mb-n3">
|
||||||
|
|||||||
@@ -163,6 +163,19 @@ describe('ConfigComponent', () => {
|
|||||||
expect(component.configForm.get('barcodes_enabled').value).toBeNull()
|
expect(component.configForm.get('barcodes_enabled').value).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should identify externally configured options', () => {
|
||||||
|
component.externallyConfiguredVariables = new Set([
|
||||||
|
'PAPERLESS_OCR_LANGUAGE',
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(
|
||||||
|
component.isExternallyConfigured('PAPERLESS_OCR_LANGUAGE')
|
||||||
|
).toBeTruthy()
|
||||||
|
expect(
|
||||||
|
component.isExternallyConfigured('PAPERLESS_OCR_OUTPUT_TYPE')
|
||||||
|
).toBeFalsy()
|
||||||
|
})
|
||||||
|
|
||||||
it('should group options into sections within a category, or not', () => {
|
it('should group options into sections within a category, or not', () => {
|
||||||
const sections = component.getCategorySections(ConfigCategory.OCR)
|
const sections = component.getCategorySections(ConfigCategory.OCR)
|
||||||
expect(sections).toEqual([null, ConfigSection.RemoteOCR])
|
expect(sections).toEqual([null, ConfigSection.RemoteOCR])
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export class ConfigComponent
|
|||||||
public configForm = new FormGroup({})
|
public configForm = new FormGroup({})
|
||||||
|
|
||||||
public errors = {}
|
public errors = {}
|
||||||
|
public externallyConfiguredVariables = new Set<string>()
|
||||||
|
|
||||||
get optionCategories(): string[] {
|
get optionCategories(): string[] {
|
||||||
return Object.values(ConfigCategory)
|
return Object.values(ConfigCategory)
|
||||||
@@ -152,6 +153,9 @@ export class ConfigComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initialize(config: PaperlessConfig) {
|
private initialize(config: PaperlessConfig) {
|
||||||
|
this.externallyConfiguredVariables = new Set(
|
||||||
|
config.externally_configured_variables ?? []
|
||||||
|
)
|
||||||
if (!this.store) {
|
if (!this.store) {
|
||||||
this.store = new BehaviorSubject(config)
|
this.store = new BehaviorSubject(config)
|
||||||
|
|
||||||
@@ -162,7 +166,9 @@ export class ConfigComponent
|
|||||||
this.configForm.patchValue(state, { emitEvent: false })
|
this.configForm.patchValue(state, { emitEvent: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
this.isDirty$ = dirtyCheck(this.configForm, this.store.asObservable())
|
this.isDirty$ = dirtyCheck(this.configForm, this.store.asObservable(), {
|
||||||
|
excludeKeys: ['externally_configured_variables'],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
this.configForm.patchValue(config)
|
this.configForm.patchValue(config)
|
||||||
|
|
||||||
@@ -227,6 +233,10 @@ export class ConfigComponent
|
|||||||
return this.configForm.get(key).value != null
|
return this.configForm.get(key).value != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isExternallyConfigured(configKey: string): boolean {
|
||||||
|
return this.externallyConfiguredVariables.has(configKey)
|
||||||
|
}
|
||||||
|
|
||||||
public resetOption(key: string) {
|
public resetOption(key: string) {
|
||||||
this.configForm.get(key).setValue(null)
|
this.configForm.get(key).setValue(null)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,6 +237,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<pngx-input-check i18n-title title="Automatically request suggestions for inbox documents" i18n-hint hint="If un-checked, suggestions must be requested via the Suggest button." formControlName="documentEditingAutoSuggest"></pngx-input-check>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Show document thumbnail during loading" formControlName="documentEditingOverlayThumbnail"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show document thumbnail during loading" formControlName="documentEditingOverlayThumbnail"></pngx-input-check>
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ describe('SettingsComponent', () => {
|
|||||||
expect(toastErrorSpy).toHaveBeenCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
expect(storeSpy).toHaveBeenCalled()
|
expect(storeSpy).toHaveBeenCalled()
|
||||||
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
|
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
|
||||||
expect(setSpy).toHaveBeenCalledTimes(32)
|
expect(setSpy).toHaveBeenCalledTimes(33)
|
||||||
|
|
||||||
// succeed
|
// succeed
|
||||||
storeSpy.mockReturnValueOnce(of(true))
|
storeSpy.mockReturnValueOnce(of(true))
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ export class SettingsComponent
|
|||||||
pdfEditorDefaultEditMode: new FormControl(null),
|
pdfEditorDefaultEditMode: new FormControl(null),
|
||||||
documentEditingRemoveInboxTags: new FormControl(null),
|
documentEditingRemoveInboxTags: new FormControl(null),
|
||||||
documentEditingOverlayThumbnail: new FormControl(null),
|
documentEditingOverlayThumbnail: new FormControl(null),
|
||||||
|
documentEditingAutoSuggest: new FormControl(null),
|
||||||
documentDetailsHiddenFields: new FormControl([]),
|
documentDetailsHiddenFields: new FormControl([]),
|
||||||
searchDbOnly: new FormControl(null),
|
searchDbOnly: new FormControl(null),
|
||||||
searchLink: new FormControl(null),
|
searchLink: new FormControl(null),
|
||||||
@@ -368,6 +369,9 @@ export class SettingsComponent
|
|||||||
documentEditingOverlayThumbnail: this.settings.get(
|
documentEditingOverlayThumbnail: this.settings.get(
|
||||||
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL
|
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL
|
||||||
),
|
),
|
||||||
|
documentEditingAutoSuggest: this.settings.get(
|
||||||
|
SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST
|
||||||
|
),
|
||||||
documentDetailsHiddenFields: this.settings.get(
|
documentDetailsHiddenFields: this.settings.get(
|
||||||
SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS
|
SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS
|
||||||
),
|
),
|
||||||
@@ -565,6 +569,10 @@ export class SettingsComponent
|
|||||||
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL,
|
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL,
|
||||||
this.settingsForm.value.documentEditingOverlayThumbnail
|
this.settingsForm.value.documentEditingOverlayThumbnail
|
||||||
)
|
)
|
||||||
|
this.settings.set(
|
||||||
|
SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST,
|
||||||
|
this.settingsForm.value.documentEditingAutoSuggest
|
||||||
|
)
|
||||||
this.settings.set(
|
this.settings.set(
|
||||||
SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS,
|
SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS,
|
||||||
this.settingsForm.value.documentDetailsHiddenFields
|
this.settingsForm.value.documentDetailsHiddenFields
|
||||||
|
|||||||
@@ -1473,6 +1473,35 @@ describe('DocumentDetailComponent', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should not automatically get suggestions if auto-suggest is disabled', () => {
|
||||||
|
settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST, false)
|
||||||
|
const suggestionsSpy = jest.spyOn(documentService, 'getSuggestions')
|
||||||
|
suggestionsSpy.mockReturnValue(of({ tags: [42] }))
|
||||||
|
initNormally()
|
||||||
|
expect(suggestionsSpy).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
// still available on demand
|
||||||
|
component.getSuggestions()
|
||||||
|
expect(suggestionsSpy).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not automatically get AI suggestions if auto-suggest is disabled', () => {
|
||||||
|
settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST, false)
|
||||||
|
const getSetting = settingsService.get.bind(settingsService)
|
||||||
|
jest
|
||||||
|
.spyOn(settingsService, 'get')
|
||||||
|
.mockImplementation((key) =>
|
||||||
|
key === SETTINGS_KEYS.AI_ENABLED ? true : getSetting(key)
|
||||||
|
)
|
||||||
|
const aiSuggestionsSpy = jest.spyOn(documentService, 'getAiSuggestions')
|
||||||
|
aiSuggestionsSpy.mockReturnValue(of({ tags: [42] }))
|
||||||
|
initNormally()
|
||||||
|
expect(aiSuggestionsSpy).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
component.getSuggestions()
|
||||||
|
expect(aiSuggestionsSpy).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
it('should reset the suggestions loading state if the document changes mid-request', () => {
|
it('should reset the suggestions loading state if the document changes mid-request', () => {
|
||||||
const getSetting = settingsService.get.bind(settingsService)
|
const getSetting = settingsService.get.bind(settingsService)
|
||||||
jest
|
jest
|
||||||
|
|||||||
@@ -237,6 +237,9 @@ export class DocumentDetailComponent
|
|||||||
this.settings.getSignal<boolean>(
|
this.settings.getSignal<boolean>(
|
||||||
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL
|
SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL
|
||||||
)
|
)
|
||||||
|
private readonly autoSuggestSetting = this.settings.getSignal<boolean>(
|
||||||
|
SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST
|
||||||
|
)
|
||||||
private readonly hiddenFieldsSetting = this.settings.getSignal<
|
private readonly hiddenFieldsSetting = this.settings.getSignal<
|
||||||
DocumentDetailFieldID[]
|
DocumentDetailFieldID[]
|
||||||
>(SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS)
|
>(SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS)
|
||||||
@@ -357,6 +360,10 @@ export class DocumentDetailComponent
|
|||||||
return this.aiEnabledSetting()
|
return this.aiEnabledSetting()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get autoSuggest(): boolean {
|
||||||
|
return this.autoSuggestSetting()
|
||||||
|
}
|
||||||
|
|
||||||
get archiveContentRenderType(): ContentRenderType {
|
get archiveContentRenderType(): ContentRenderType {
|
||||||
const hasArchiveVersion =
|
const hasArchiveVersion =
|
||||||
this.metadata()?.has_archive_version ??
|
this.metadata()?.has_archive_version ??
|
||||||
@@ -904,6 +911,7 @@ export class DocumentDetailComponent
|
|||||||
this.updateFormForCustomFields()
|
this.updateFormForCustomFields()
|
||||||
this.loadMetadataForSelectedVersion()
|
this.loadMetadataForSelectedVersion()
|
||||||
if (
|
if (
|
||||||
|
this.autoSuggest &&
|
||||||
this.permissionsService.currentUserHasObjectPermissions(
|
this.permissionsService.currentUserHasObjectPermissions(
|
||||||
PermissionAction.Change,
|
PermissionAction.Change,
|
||||||
doc
|
doc
|
||||||
|
|||||||
@@ -422,6 +422,7 @@ export const PaperlessConfigOptions: ConfigOption[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export interface PaperlessConfig extends ObjectWithId {
|
export interface PaperlessConfig extends ObjectWithId {
|
||||||
|
externally_configured_variables: string[]
|
||||||
output_type: OutputTypeConfig
|
output_type: OutputTypeConfig
|
||||||
pages: number
|
pages: number
|
||||||
language: string
|
language: string
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ export const SETTINGS_KEYS = {
|
|||||||
'general-settings:document-editing:remove-inbox-tags',
|
'general-settings:document-editing:remove-inbox-tags',
|
||||||
DOCUMENT_EDITING_OVERLAY_THUMBNAIL:
|
DOCUMENT_EDITING_OVERLAY_THUMBNAIL:
|
||||||
'general-settings:document-editing:overlay-thumbnail',
|
'general-settings:document-editing:overlay-thumbnail',
|
||||||
|
DOCUMENT_EDITING_AUTO_SUGGEST:
|
||||||
|
'general-settings:document-editing:auto-suggest',
|
||||||
DOCUMENT_DETAILS_HIDDEN_FIELDS:
|
DOCUMENT_DETAILS_HIDDEN_FIELDS:
|
||||||
'general-settings:document-details:hidden-fields',
|
'general-settings:document-details:hidden-fields',
|
||||||
SEARCH_DB_ONLY: 'general-settings:search:db-only',
|
SEARCH_DB_ONLY: 'general-settings:search:db-only',
|
||||||
@@ -300,6 +302,11 @@ export const SETTINGS: UiSetting[] = [
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST,
|
||||||
|
type: 'boolean',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS,
|
key: SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS,
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ from typing import TYPE_CHECKING
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import FieldError
|
|
||||||
from django.db.models import Case
|
from django.db.models import Case
|
||||||
from django.db.models import CharField
|
from django.db.models import CharField
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
@@ -53,6 +52,7 @@ from documents.models import StoragePath
|
|||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
from documents.permissions import permitted_document_ids
|
from documents.permissions import permitted_document_ids
|
||||||
from documents.permissions import permitted_object_ids
|
from documents.permissions import permitted_object_ids
|
||||||
|
from documents.versioning import annotate_effective_content
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
@@ -182,14 +182,9 @@ class TitleContentFilter(Filter):
|
|||||||
logger.warning(
|
logger.warning(
|
||||||
"Deprecated document filter parameter 'title_content' used; use `text` instead.",
|
"Deprecated document filter parameter 'title_content' used; use `text` instead.",
|
||||||
)
|
)
|
||||||
try:
|
return annotate_effective_content(qs).filter(
|
||||||
return qs.filter(
|
Q(title__icontains=value) | Q(effective_content__icontains=value),
|
||||||
Q(title__icontains=value) | Q(effective_content__icontains=value),
|
)
|
||||||
)
|
|
||||||
except FieldError:
|
|
||||||
return qs.filter(
|
|
||||||
Q(title__icontains=value) | Q(content__icontains=value),
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
@@ -200,14 +195,9 @@ class EffectiveContentFilter(Filter):
|
|||||||
value = value.strip() if isinstance(value, str) else value
|
value = value.strip() if isinstance(value, str) else value
|
||||||
if not value:
|
if not value:
|
||||||
return qs
|
return qs
|
||||||
try:
|
return annotate_effective_content(qs).filter(
|
||||||
return qs.filter(
|
**{f"effective_content__{self.lookup_expr}": value},
|
||||||
**{f"effective_content__{self.lookup_expr}": value},
|
)
|
||||||
)
|
|
||||||
except FieldError:
|
|
||||||
return qs.filter(
|
|
||||||
**{f"content__{self.lookup_expr}": value},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@extend_schema_field(serializers.BooleanField)
|
@extend_schema_field(serializers.BooleanField)
|
||||||
|
|||||||
@@ -674,6 +674,9 @@ class TagSerializer(MatchingModelSerializer, OwnedObjectSerializer):
|
|||||||
ordering = ordering or (Lower("name"),)
|
ordering = ordering or (Lower("name"),)
|
||||||
children = children.order_by(*ordering)
|
children = children.order_by(*ordering)
|
||||||
|
|
||||||
|
if not children:
|
||||||
|
return []
|
||||||
|
|
||||||
serializer = TagSerializer(
|
serializer = TagSerializer(
|
||||||
children,
|
children,
|
||||||
many=True,
|
many=True,
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
|
|||||||
THEN:
|
THEN:
|
||||||
- Existing config
|
- Existing config
|
||||||
"""
|
"""
|
||||||
response = self.client.get(self.ENDPOINT, format="json")
|
with patch.dict("os.environ", {}, clear=True):
|
||||||
|
response = self.client.get(self.ENDPOINT, format="json")
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
|
|||||||
response.data[0],
|
response.data[0],
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
"externally_configured_variables": [],
|
||||||
"output_type": None,
|
"output_type": None,
|
||||||
"pages": None,
|
"pages": None,
|
||||||
"language": None,
|
"language": None,
|
||||||
@@ -91,6 +93,31 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_api_get_config_reports_external_configuration_without_values(self) -> None:
|
||||||
|
with patch.dict(
|
||||||
|
"os.environ",
|
||||||
|
{
|
||||||
|
"PAPERLESS_OCR_LANGUAGE": "eng",
|
||||||
|
"PAPERLESS_REMOTE_OCR_API_KEY": "secret-value",
|
||||||
|
"PAPERLESS_FUTURE_SETTING": "future-value",
|
||||||
|
"UNRELATED_SETTING": "unrelated-value",
|
||||||
|
},
|
||||||
|
clear=True,
|
||||||
|
):
|
||||||
|
response = self.client.get(self.ENDPOINT, format="json")
|
||||||
|
|
||||||
|
self.assertCountEqual(
|
||||||
|
response.data[0]["externally_configured_variables"],
|
||||||
|
[
|
||||||
|
"PAPERLESS_FUTURE_SETTING",
|
||||||
|
"PAPERLESS_OCR_LANGUAGE",
|
||||||
|
"PAPERLESS_REMOTE_OCR_API_KEY",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
self.assertNotContains(response, "secret-value")
|
||||||
|
self.assertNotContains(response, "future-value")
|
||||||
|
self.assertNotContains(response, "UNRELATED_SETTING")
|
||||||
|
|
||||||
def test_api_get_ui_settings_with_config(self) -> None:
|
def test_api_get_ui_settings_with_config(self) -> None:
|
||||||
"""
|
"""
|
||||||
GIVEN:
|
GIVEN:
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from unittest import TestCase
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from auditlog.models import LogEntry # type: ignore[import-untyped]
|
from auditlog.models import LogEntry # type: ignore[import-untyped]
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import FieldError
|
|
||||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
from django.test import TestCase as DjangoTestCase
|
from django.test import TestCase as DjangoTestCase
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@@ -22,6 +20,7 @@ from documents.filters import TitleContentFilter
|
|||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.tests.utils import DirectoriesMixin
|
from documents.tests.utils import DirectoriesMixin
|
||||||
from documents.tests.utils import read_streaming_response
|
from documents.tests.utils import read_streaming_response
|
||||||
|
from documents.versioning import annotate_effective_content
|
||||||
from documents.views import DocumentSelectionMixin
|
from documents.views import DocumentSelectionMixin
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -892,32 +891,104 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestVersionAwareFilters(TestCase):
|
class TestVersionAwareFilters(DjangoTestCase):
|
||||||
def test_title_content_filter_falls_back_to_content(self) -> None:
|
"""
|
||||||
queryset = mock.Mock()
|
The filters annotate effective_content themselves rather than relying on
|
||||||
fallback_queryset = mock.Mock()
|
the caller's queryset carrying it, so they stay version-aware on a plain
|
||||||
queryset.filter.side_effect = [FieldError("missing field"), fallback_queryset]
|
Document queryset (e.g. the bulk-edit "select all matching" path).
|
||||||
|
"""
|
||||||
|
|
||||||
result = TitleContentFilter().filter(queryset, " latest ")
|
def setUp(self) -> None:
|
||||||
|
super().setUp()
|
||||||
|
self.root = Document.objects.create(
|
||||||
|
title="root",
|
||||||
|
checksum="root",
|
||||||
|
mime_type="application/pdf",
|
||||||
|
content="superseded-content",
|
||||||
|
)
|
||||||
|
Document.objects.create(
|
||||||
|
title="version",
|
||||||
|
checksum="version",
|
||||||
|
mime_type="application/pdf",
|
||||||
|
root_document=self.root,
|
||||||
|
version_index=1,
|
||||||
|
content="latest-content",
|
||||||
|
)
|
||||||
|
self.unversioned = Document.objects.create(
|
||||||
|
title="unversioned",
|
||||||
|
checksum="unversioned",
|
||||||
|
mime_type="application/pdf",
|
||||||
|
content="latest-content",
|
||||||
|
)
|
||||||
|
|
||||||
self.assertIs(result, fallback_queryset)
|
def test_title_content_filter_matches_latest_version_content(self) -> None:
|
||||||
self.assertEqual(queryset.filter.call_count, 2)
|
result = TitleContentFilter().filter(
|
||||||
|
Document.objects.filter(root_document__isnull=True),
|
||||||
def test_effective_content_filter_falls_back_to_content_lookup(self) -> None:
|
|
||||||
queryset = mock.Mock()
|
|
||||||
fallback_queryset = mock.Mock()
|
|
||||||
queryset.filter.side_effect = [FieldError("missing field"), fallback_queryset]
|
|
||||||
|
|
||||||
result = EffectiveContentFilter(lookup_expr="icontains").filter(
|
|
||||||
queryset,
|
|
||||||
" latest ",
|
" latest ",
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertIs(result, fallback_queryset)
|
self.assertCountEqual(
|
||||||
first_kwargs = queryset.filter.call_args_list[0].kwargs
|
[doc.id for doc in result],
|
||||||
second_kwargs = queryset.filter.call_args_list[1].kwargs
|
[self.root.id, self.unversioned.id],
|
||||||
self.assertEqual(first_kwargs, {"effective_content__icontains": "latest"})
|
)
|
||||||
self.assertEqual(second_kwargs, {"content__icontains": "latest"})
|
|
||||||
|
def test_effective_content_filter_matches_latest_version_content(self) -> None:
|
||||||
|
result = EffectiveContentFilter(lookup_expr="icontains").filter(
|
||||||
|
Document.objects.filter(root_document__isnull=True),
|
||||||
|
" latest ",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertCountEqual(
|
||||||
|
[doc.id for doc in result],
|
||||||
|
[self.root.id, self.unversioned.id],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_effective_content_filter_ignores_superseded_content(self) -> None:
|
||||||
|
result = EffectiveContentFilter(lookup_expr="icontains").filter(
|
||||||
|
Document.objects.filter(root_document__isnull=True),
|
||||||
|
"superseded",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(list(result), [])
|
||||||
|
|
||||||
|
def test_filters_reuse_an_existing_annotation(self) -> None:
|
||||||
|
"""
|
||||||
|
Annotating twice under the same alias is an error, so an already
|
||||||
|
annotated queryset (the search path) has to be left alone.
|
||||||
|
"""
|
||||||
|
annotated = annotate_effective_content(
|
||||||
|
Document.objects.filter(root_document__isnull=True),
|
||||||
|
)
|
||||||
|
self.assertIs(annotate_effective_content(annotated), annotated)
|
||||||
|
|
||||||
|
result = EffectiveContentFilter(lookup_expr="icontains").filter(
|
||||||
|
annotated,
|
||||||
|
"latest",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertCountEqual(
|
||||||
|
[doc.id for doc in result],
|
||||||
|
[self.root.id, self.unversioned.id],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_bulk_selection_does_not_match_superseded_content(self) -> None:
|
||||||
|
"""
|
||||||
|
Bulk edit's "select all matching" builds its own queryset, so before
|
||||||
|
the filters annotated for themselves it matched the root document's
|
||||||
|
superseded content -- selecting documents the list view, filtered by
|
||||||
|
the same term, does not show.
|
||||||
|
"""
|
||||||
|
user = User.objects.create_superuser(username="bulk_selection")
|
||||||
|
|
||||||
|
selected = DocumentSelectionMixin()._resolve_document_ids(
|
||||||
|
user=user,
|
||||||
|
validated_data={
|
||||||
|
"all": True,
|
||||||
|
"filters": {"content__icontains": "superseded"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(selected, [])
|
||||||
|
|
||||||
def test_effective_content_filter_returns_input_for_empty_values(self) -> None:
|
def test_effective_content_filter_returns_input_for_empty_values(self) -> None:
|
||||||
queryset = mock.Mock()
|
queryset = mock.Mock()
|
||||||
|
|||||||
@@ -1947,6 +1947,29 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
|
|||||||
self.assertEqual(len(response.data["documents"]), 1)
|
self.assertEqual(len(response.data["documents"]), 1)
|
||||||
self.assertEqual(response.data["documents"][0]["id"], title_match.id)
|
self.assertEqual(response.data["documents"][0]["id"], title_match.id)
|
||||||
|
|
||||||
|
def test_global_search_returns_latest_version_content(self) -> None:
|
||||||
|
root = Document.objects.create(
|
||||||
|
title="bank statement",
|
||||||
|
content="superseded content",
|
||||||
|
checksum="GSV1",
|
||||||
|
pk=23,
|
||||||
|
)
|
||||||
|
Document.objects.create(
|
||||||
|
title="bank statement v2",
|
||||||
|
content="latest content",
|
||||||
|
checksum="GSV2",
|
||||||
|
pk=24,
|
||||||
|
root_document=root,
|
||||||
|
version_index=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.client.force_authenticate(self.user)
|
||||||
|
|
||||||
|
response = self.client.get("/api/search/?query=bank&db_only=true")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
returned = {doc["id"]: doc["content"] for doc in response.data["documents"]}
|
||||||
|
self.assertEqual(returned.get(root.id), "latest content")
|
||||||
|
|
||||||
def test_global_search_filters_owned_mail_objects(self) -> None:
|
def test_global_search_filters_owned_mail_objects(self) -> None:
|
||||||
user1 = User.objects.create_user("mail-search-user")
|
user1 = User.objects.create_user("mail-search-user")
|
||||||
user2 = User.objects.create_user("other-mail-search-user")
|
user2 = User.objects.create_user("other-mail-search-user")
|
||||||
|
|||||||
@@ -5711,6 +5711,39 @@ class TestApplyAISuggestionsWorkflowAction(
|
|||||||
self.assertEqual(changed, [])
|
self.assertEqual(changed, [])
|
||||||
self.assertIn("AI is not enabled", "".join(cm.output))
|
self.assertIn("AI is not enabled", "".join(cm.output))
|
||||||
|
|
||||||
|
def test_document_without_content_does_nothing(self) -> None:
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- A document whose OCR content is empty or whitespace-only
|
||||||
|
WHEN:
|
||||||
|
- AI suggestions are applied by a workflow
|
||||||
|
THEN:
|
||||||
|
- The classifier is not called and the document is left unchanged
|
||||||
|
"""
|
||||||
|
action = self.make_action(ai_overwrite_existing=True)
|
||||||
|
|
||||||
|
for content in ("", " \n\t"):
|
||||||
|
with self.subTest(content=content):
|
||||||
|
self.doc.content = content
|
||||||
|
self.doc.save(update_fields=["content"])
|
||||||
|
|
||||||
|
with (
|
||||||
|
mock.patch(
|
||||||
|
"documents.workflows.ai.get_ai_document_classification",
|
||||||
|
) as get_classification,
|
||||||
|
self.assertLogs(
|
||||||
|
"paperless.workflows.ai",
|
||||||
|
level="WARNING",
|
||||||
|
) as cm,
|
||||||
|
):
|
||||||
|
changed = apply_ai_suggestions_to_document(action, self.doc)
|
||||||
|
|
||||||
|
self.assertEqual(changed, [])
|
||||||
|
get_classification.assert_not_called()
|
||||||
|
self.assertIn("has no content", "".join(cm.output))
|
||||||
|
self.doc.refresh_from_db()
|
||||||
|
self.assertEqual(self.doc.title, "original.pdf")
|
||||||
|
|
||||||
def test_invalid_configuration_leaves_document_untouched(self) -> None:
|
def test_invalid_configuration_leaves_document_untouched(self) -> None:
|
||||||
"""
|
"""
|
||||||
GIVEN:
|
GIVEN:
|
||||||
|
|||||||
@@ -27,10 +27,13 @@ def versions_newest_first(documents: QuerySet[Document]) -> QuerySet[Document]:
|
|||||||
|
|
||||||
def annotate_effective_content(documents: QuerySet[Document]) -> QuerySet[Document]:
|
def annotate_effective_content(documents: QuerySet[Document]) -> QuerySet[Document]:
|
||||||
"""
|
"""
|
||||||
Annotates documents with the content of their newest version, falling back
|
Annotates documents with the content of their newest version unless the
|
||||||
to their own, so get_effective_content() can answer from the row rather
|
queryset already carries the annotation, falling back to their own, so
|
||||||
than querying for the versions of each document
|
get_effective_content() can answer from the row rather than querying for
|
||||||
|
the versions of each document.
|
||||||
"""
|
"""
|
||||||
|
if "effective_content" in documents.query.annotations:
|
||||||
|
return documents
|
||||||
return documents.annotate(
|
return documents.annotate(
|
||||||
effective_content=Coalesce(
|
effective_content=Coalesce(
|
||||||
Subquery(
|
Subquery(
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ from documents.tasks import train_classifier
|
|||||||
from documents.tasks import update_document_parent_tags
|
from documents.tasks import update_document_parent_tags
|
||||||
from documents.utils import get_boolean
|
from documents.utils import get_boolean
|
||||||
from documents.versioning import VersionResolutionError
|
from documents.versioning import VersionResolutionError
|
||||||
|
from documents.versioning import annotate_effective_content
|
||||||
from documents.versioning import get_latest_version_for_root
|
from documents.versioning import get_latest_version_for_root
|
||||||
from documents.versioning import get_request_version_param
|
from documents.versioning import get_request_version_param
|
||||||
from documents.versioning import get_root_document
|
from documents.versioning import get_root_document
|
||||||
@@ -3632,8 +3633,13 @@ class GlobalSearchView(PassUserMixin):
|
|||||||
OBJECT_LIMIT = 3
|
OBJECT_LIMIT = 3
|
||||||
docs = []
|
docs = []
|
||||||
if request.user.has_perm("documents.view_document"):
|
if request.user.has_perm("documents.view_document"):
|
||||||
all_docs = Document.objects.filter(
|
# Never more than OBJECT_LIMIT rows come back here, so annotating
|
||||||
id__in=permitted_document_ids(request.user),
|
# is cheap -- and without it these results show the root
|
||||||
|
# document's superseded content.
|
||||||
|
all_docs = annotate_effective_content(
|
||||||
|
Document.objects.filter(
|
||||||
|
id__in=permitted_document_ids(request.user),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
if db_only:
|
if db_only:
|
||||||
docs = all_docs.filter(title__icontains=query)[:OBJECT_LIMIT]
|
docs = all_docs.filter(title__icontains=query)[:OBJECT_LIMIT]
|
||||||
|
|||||||
@@ -138,6 +138,16 @@ def apply_ai_suggestions_to_document(
|
|||||||
)
|
)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
if not document.content.strip():
|
||||||
|
logger.warning(
|
||||||
|
"Document %s has no content, skipping AI suggestions for workflow "
|
||||||
|
"action %s",
|
||||||
|
document.pk,
|
||||||
|
action.pk,
|
||||||
|
extra={"group": logging_group},
|
||||||
|
)
|
||||||
|
return []
|
||||||
|
|
||||||
# Workflows run without a user, so we use the document owner
|
# Workflows run without a user, so we use the document owner
|
||||||
owner = document.owner
|
owner = document.owner
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: paperless-ngx\n"
|
"Project-Id-Version: paperless-ngx\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-09-07 20:47+0000\n"
|
"POT-Creation-Date: 2026-09-08 15:56+0000\n"
|
||||||
"PO-Revision-Date: 2022-02-17 04:17\n"
|
"PO-Revision-Date: 2022-02-17 04:17\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: English\n"
|
"Language-Team: English\n"
|
||||||
@@ -21,39 +21,39 @@ msgstr ""
|
|||||||
msgid "Documents"
|
msgid "Documents"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:473
|
#: documents/filters.py:463
|
||||||
msgid "Value must be valid JSON."
|
msgid "Value must be valid JSON."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:492
|
#: documents/filters.py:482
|
||||||
msgid "Invalid custom field query expression"
|
msgid "Invalid custom field query expression"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:502
|
#: documents/filters.py:492
|
||||||
msgid "Invalid expression list. Must be nonempty."
|
msgid "Invalid expression list. Must be nonempty."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:523
|
#: documents/filters.py:513
|
||||||
msgid "Invalid logical operator {op!r}"
|
msgid "Invalid logical operator {op!r}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:537
|
#: documents/filters.py:527
|
||||||
msgid "Maximum number of query conditions exceeded."
|
msgid "Maximum number of query conditions exceeded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:601
|
#: documents/filters.py:591
|
||||||
msgid "{name!r} is not a valid custom field."
|
msgid "{name!r} is not a valid custom field."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:638
|
#: documents/filters.py:628
|
||||||
msgid "{data_type} does not support query expr {expr!r}."
|
msgid "{data_type} does not support query expr {expr!r}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:757 documents/models.py:136
|
#: documents/filters.py:747 documents/models.py:136
|
||||||
msgid "Maximum nesting depth exceeded."
|
msgid "Maximum nesting depth exceeded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:1119
|
#: documents/filters.py:1109
|
||||||
msgid "Custom field not found"
|
msgid "Custom field not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@@ -1631,49 +1631,49 @@ msgstr ""
|
|||||||
msgid "workflow runs"
|
msgid "workflow runs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:524 documents/serialisers.py:878
|
#: documents/serialisers.py:524 documents/serialisers.py:881
|
||||||
#: documents/serialisers.py:2838 documents/views.py:314 documents/views.py:2624
|
#: documents/serialisers.py:2841 documents/views.py:315 documents/views.py:2625
|
||||||
#: paperless_mail/serialisers.py:156
|
#: paperless_mail/serialisers.py:156
|
||||||
msgid "Insufficient permissions."
|
msgid "Insufficient permissions."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:714
|
#: documents/serialisers.py:717
|
||||||
msgid "Invalid color."
|
msgid "Invalid color."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:2315
|
#: documents/serialisers.py:2318
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "File type %(type)s not supported"
|
msgid "File type %(type)s not supported"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:2359
|
#: documents/serialisers.py:2362
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Custom field id must be an integer: %(id)s"
|
msgid "Custom field id must be an integer: %(id)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:2366
|
#: documents/serialisers.py:2369
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Custom field with id %(id)s does not exist"
|
msgid "Custom field with id %(id)s does not exist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:2383 documents/serialisers.py:2393
|
#: documents/serialisers.py:2386 documents/serialisers.py:2396
|
||||||
msgid ""
|
msgid ""
|
||||||
"Custom fields must be a list of integers or an object mapping ids to values."
|
"Custom fields must be a list of integers or an object mapping ids to values."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:2388
|
#: documents/serialisers.py:2391
|
||||||
msgid "Some custom fields don't exist or were specified twice."
|
msgid "Some custom fields don't exist or were specified twice."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:2535
|
#: documents/serialisers.py:2538
|
||||||
msgid "Invalid variable detected."
|
msgid "Invalid variable detected."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:2894
|
#: documents/serialisers.py:2897
|
||||||
msgid "Duplicate document identifiers are not allowed."
|
msgid "Duplicate document identifiers are not allowed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:2924 documents/views.py:4626
|
#: documents/serialisers.py:2927 documents/views.py:4632
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Documents not found: %(ids)s"
|
msgid "Documents not found: %(ids)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1941,36 +1941,36 @@ msgstr ""
|
|||||||
msgid "Unable to parse URI {value}"
|
msgid "Unable to parse URI {value}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/views.py:307 documents/views.py:2621
|
#: documents/views.py:308 documents/views.py:2622
|
||||||
msgid "Invalid more_like_id"
|
msgid "Invalid more_like_id"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/views.py:1591
|
#: documents/views.py:1592
|
||||||
msgid "Invalid AI configuration."
|
msgid "Invalid AI configuration."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/views.py:1602
|
#: documents/views.py:1603
|
||||||
msgid "AI backend request timed out."
|
msgid "AI backend request timed out."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/views.py:2446 documents/views.py:2767
|
#: documents/views.py:2447 documents/views.py:2768
|
||||||
msgid "Specify only one of text, title_search, query, or more_like_id."
|
msgid "Specify only one of text, title_search, query, or more_like_id."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/views.py:4639
|
#: documents/views.py:4645
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Insufficient permissions to share document %(id)s."
|
msgid "Insufficient permissions to share document %(id)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/views.py:4685
|
#: documents/views.py:4691
|
||||||
msgid "Bundle is already being processed."
|
msgid "Bundle is already being processed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/views.py:4749
|
#: documents/views.py:4755
|
||||||
msgid "The share link bundle is still being prepared. Please try again later."
|
msgid "The share link bundle is still being prepared. Please try again later."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/views.py:4763
|
#: documents/views.py:4769
|
||||||
msgid "The share link bundle is unavailable."
|
msgid "The share link bundle is unavailable."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import magic
|
import magic
|
||||||
@@ -212,6 +213,7 @@ class ProfileSerializer(PasswordValidationMixin, serializers.ModelSerializer[Use
|
|||||||
class ApplicationConfigurationSerializer(
|
class ApplicationConfigurationSerializer(
|
||||||
serializers.ModelSerializer[ApplicationConfiguration],
|
serializers.ModelSerializer[ApplicationConfiguration],
|
||||||
):
|
):
|
||||||
|
externally_configured_variables = serializers.SerializerMethodField()
|
||||||
user_args = serializers.JSONField(binary=True, allow_null=True)
|
user_args = serializers.JSONField(binary=True, allow_null=True)
|
||||||
barcode_tag_mapping = serializers.JSONField(binary=True, allow_null=True)
|
barcode_tag_mapping = serializers.JSONField(binary=True, allow_null=True)
|
||||||
llm_api_key = ObfuscatedPasswordField(
|
llm_api_key = ObfuscatedPasswordField(
|
||||||
@@ -227,6 +229,12 @@ class ApplicationConfigurationSerializer(
|
|||||||
|
|
||||||
OBFUSCATED_FIELDS = ("llm_api_key", "remote_ocr_api_key")
|
OBFUSCATED_FIELDS = ("llm_api_key", "remote_ocr_api_key")
|
||||||
|
|
||||||
|
def get_externally_configured_variables(
|
||||||
|
self,
|
||||||
|
instance: ApplicationConfiguration,
|
||||||
|
) -> list[str]:
|
||||||
|
return sorted(name for name in os.environ if name.startswith("PAPERLESS_"))
|
||||||
|
|
||||||
def run_validation(self, data):
|
def run_validation(self, data):
|
||||||
# Empty strings treated as None to avoid unexpected behavior
|
# Empty strings treated as None to avoid unexpected behavior
|
||||||
if "user_args" in data and data["user_args"] == "":
|
if "user_args" in data and data["user_args"] == "":
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from celery import Task
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
|
|
||||||
|
from documents.models import PaperlessTask
|
||||||
from paperless_mail.mail import MailAccountHandler
|
from paperless_mail.mail import MailAccountHandler
|
||||||
from paperless_mail.mail import MailError
|
from paperless_mail.mail import MailError
|
||||||
from paperless_mail.models import MailAccount
|
from paperless_mail.models import MailAccount
|
||||||
@@ -10,8 +12,27 @@ from paperless_mail.models import MailRule
|
|||||||
logger = logging.getLogger("paperless.mail.tasks")
|
logger = logging.getLogger("paperless.mail.tasks")
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task(bind=True)
|
||||||
def process_mail_accounts(account_ids: list[int] | None = None) -> str:
|
def process_mail_accounts(self: Task, account_ids: list[int] | None = None) -> str:
|
||||||
|
# A scheduled check can still be running (or queued) when the next one
|
||||||
|
# fires, e.g. a large attachment batch that takes longer to process than
|
||||||
|
# the check interval. ProcessedMail dedup only records a message once its
|
||||||
|
# handling has finished, so an overlapping run can still pick up the same
|
||||||
|
# not-yet-recorded message. Skip outright rather than race it.
|
||||||
|
other_mail_fetch_running = (
|
||||||
|
PaperlessTask.objects.filter(
|
||||||
|
task_type=PaperlessTask.TaskType.MAIL_FETCH,
|
||||||
|
status__in=[PaperlessTask.Status.PENDING, PaperlessTask.Status.STARTED],
|
||||||
|
)
|
||||||
|
.exclude(task_id=self.request.id)
|
||||||
|
.exists()
|
||||||
|
)
|
||||||
|
if other_mail_fetch_running:
|
||||||
|
logger.info(
|
||||||
|
"Mail account processing is already running; skipping this run.",
|
||||||
|
)
|
||||||
|
return "Skipped: mail account processing already in progress."
|
||||||
|
|
||||||
total_new_documents = 0
|
total_new_documents = 0
|
||||||
accounts = (
|
accounts = (
|
||||||
MailAccount.objects.filter(pk__in=account_ids)
|
MailAccount.objects.filter(pk__in=account_ids)
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from documents.models import PaperlessTask
|
||||||
|
from paperless_mail import tasks
|
||||||
|
from paperless_mail.tests.factories import MailAccountFactory
|
||||||
|
from paperless_mail.tests.factories import MailRuleFactory
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class TestProcessMailAccountsOverlap:
|
||||||
|
def test_skips_when_another_mail_fetch_task_is_running(self) -> None:
|
||||||
|
account = MailAccountFactory.create()
|
||||||
|
MailRuleFactory.create(account=account, enabled=True)
|
||||||
|
|
||||||
|
PaperlessTask.objects.create(
|
||||||
|
task_id="other-running-task",
|
||||||
|
task_type=PaperlessTask.TaskType.MAIL_FETCH,
|
||||||
|
trigger_source=PaperlessTask.TriggerSource.SCHEDULED,
|
||||||
|
status=PaperlessTask.Status.STARTED,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
tasks.MailAccountHandler,
|
||||||
|
"handle_mail_account",
|
||||||
|
) as mocked_handle:
|
||||||
|
result = tasks.process_mail_accounts()
|
||||||
|
|
||||||
|
mocked_handle.assert_not_called()
|
||||||
|
assert result == "Skipped: mail account processing already in progress."
|
||||||
|
|
||||||
|
def test_runs_when_no_other_mail_fetch_task_is_running(self) -> None:
|
||||||
|
account = MailAccountFactory.create()
|
||||||
|
MailRuleFactory.create(account=account, enabled=True)
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
tasks.MailAccountHandler,
|
||||||
|
"handle_mail_account",
|
||||||
|
return_value=0,
|
||||||
|
) as mocked_handle:
|
||||||
|
result = tasks.process_mail_accounts()
|
||||||
|
|
||||||
|
mocked_handle.assert_called_once()
|
||||||
|
assert result == "No new documents were added."
|
||||||
|
|
||||||
|
def test_ignores_completed_mail_fetch_tasks(self) -> None:
|
||||||
|
account = MailAccountFactory.create()
|
||||||
|
MailRuleFactory.create(account=account, enabled=True)
|
||||||
|
|
||||||
|
PaperlessTask.objects.create(
|
||||||
|
task_id="finished-task",
|
||||||
|
task_type=PaperlessTask.TaskType.MAIL_FETCH,
|
||||||
|
trigger_source=PaperlessTask.TriggerSource.SCHEDULED,
|
||||||
|
status=PaperlessTask.Status.SUCCESS,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
tasks.MailAccountHandler,
|
||||||
|
"handle_mail_account",
|
||||||
|
return_value=0,
|
||||||
|
) as mocked_handle:
|
||||||
|
result = tasks.process_mail_accounts()
|
||||||
|
|
||||||
|
mocked_handle.assert_called_once()
|
||||||
|
assert result == "No new documents were added."
|
||||||
|
|
||||||
|
def test_does_not_skip_due_to_its_own_task_row(self) -> None:
|
||||||
|
account = MailAccountFactory.create()
|
||||||
|
MailRuleFactory.create(account=account, enabled=True)
|
||||||
|
|
||||||
|
PaperlessTask.objects.create(
|
||||||
|
task_id="self-task-id",
|
||||||
|
task_type=PaperlessTask.TaskType.MAIL_FETCH,
|
||||||
|
trigger_source=PaperlessTask.TriggerSource.SCHEDULED,
|
||||||
|
status=PaperlessTask.Status.STARTED,
|
||||||
|
)
|
||||||
|
|
||||||
|
with mock.patch.object(
|
||||||
|
tasks.MailAccountHandler,
|
||||||
|
"handle_mail_account",
|
||||||
|
return_value=0,
|
||||||
|
) as mocked_handle:
|
||||||
|
result = tasks.process_mail_accounts.apply(
|
||||||
|
task_id="self-task-id",
|
||||||
|
).result
|
||||||
|
|
||||||
|
mocked_handle.assert_called_once()
|
||||||
|
assert result == "No new documents were added."
|
||||||
Reference in New Issue
Block a user