mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-08-07 19:08:32 -05:00
Compare commits
12 Commits
v2.14.5
...
feature-fl
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e1b9434645 | ||
![]() |
38296d9426 | ||
![]() |
8311313e6e | ||
![]() |
ac292999ef | ||
![]() |
f3cda54cd1 | ||
![]() |
8f9a294529 | ||
![]() |
702de0cac3 | ||
![]() |
ca42762841 | ||
![]() |
13cfd6f904 | ||
![]() |
18c4e6029f | ||
![]() |
6c34e37838 | ||
![]() |
2c28348b56 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -16,7 +16,7 @@ on:
|
||||
env:
|
||||
# This is the version of pipenv all the steps will use
|
||||
# If changing this, change Dockerfile
|
||||
DEFAULT_PIP_ENV_VERSION: "2024.4.0"
|
||||
DEFAULT_PIP_ENV_VERSION: "2024.4.1"
|
||||
# This is the default version of Python to use in most steps which aren't specific
|
||||
DEFAULT_PYTHON_VERSION: "3.11"
|
||||
|
||||
|
@@ -29,7 +29,7 @@ repos:
|
||||
- id: check-case-conflict
|
||||
- id: detect-private-key
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.3.0
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: codespell
|
||||
exclude: "(^src-ui/src/locale/)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)"
|
||||
|
10
Dockerfile
10
Dockerfile
@@ -39,7 +39,7 @@ COPY Pipfile* ./
|
||||
|
||||
RUN set -eux \
|
||||
&& echo "Installing pipenv" \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2024.4.0 \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2024.4.1 \
|
||||
&& echo "Generating requirement.txt" \
|
||||
&& pipenv requirements > requirements.txt
|
||||
|
||||
@@ -233,11 +233,11 @@ RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
|
||||
&& python3 -m pip install --no-cache-dir --upgrade wheel \
|
||||
&& echo "Installing Python requirements" \
|
||||
&& curl --fail --silent --show-error --location \
|
||||
--output psycopg_c-3.2.3-cp312-cp312-linux_x86_64.whl \
|
||||
https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.3/psycopg_c-3.2.3-cp312-cp312-linux_x86_64.whl \
|
||||
--output psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl \
|
||||
https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl \
|
||||
&& curl --fail --silent --show-error --location \
|
||||
--output psycopg_c-3.2.3-cp312-cp312-linux_aarch64.whl \
|
||||
https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.3/psycopg_c-3.2.3-cp312-cp312-linux_aarch64.whl \
|
||||
--output psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl \
|
||||
https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl \
|
||||
&& python3 -m pip install --default-timeout=1000 --find-links . --requirement requirements.txt \
|
||||
&& echo "Installing NLTK data" \
|
||||
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
|
||||
|
2362
Pipfile.lock
generated
2362
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
## paperless-ngx 2.14.5
|
||||
|
||||
### Features
|
||||
|
||||
- Change: restrict altering and creation of superusers to superusers only [@shamoon](https://github.com/shamoon) ([#8837](https://github.com/paperless-ngx/paperless-ngx/pull/8837))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: fix long tag visual wrapping [@shamoon](https://github.com/shamoon) ([#8833](https://github.com/paperless-ngx/paperless-ngx/pull/8833))
|
||||
- Fix: Enforce classifier training ordering to prevent extra training [@stumpylog](https://github.com/stumpylog) ([#8822](https://github.com/paperless-ngx/paperless-ngx/pull/8822))
|
||||
- Fix: import router module to not found component [@shamoon](https://github.com/shamoon) ([#8821](https://github.com/paperless-ngx/paperless-ngx/pull/8821))
|
||||
- Fix: better reflect some mail account / rule permissions in UI [@shamoon](https://github.com/shamoon) ([#8812](https://github.com/paperless-ngx/paperless-ngx/pull/8812))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Chore(deps-dev): Bump undici from 5.28.4 to 5.28.5 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#8851](https://github.com/paperless-ngx/paperless-ngx/pull/8851))
|
||||
- Chore(deps-dev): Bump the development group with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#8841](https://github.com/paperless-ngx/paperless-ngx/pull/8841))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>9 changes</summary>
|
||||
|
||||
- Chore(deps-dev): Bump undici from 5.28.4 to 5.28.5 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#8851](https://github.com/paperless-ngx/paperless-ngx/pull/8851))
|
||||
- Chore(deps-dev): Bump the development group with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#8841](https://github.com/paperless-ngx/paperless-ngx/pull/8841))
|
||||
- Chore: use simpler method for attaching files to emails [@shamoon](https://github.com/shamoon) ([#8845](https://github.com/paperless-ngx/paperless-ngx/pull/8845))
|
||||
- Change: restrict altering and creation of superusers to superusers only [@shamoon](https://github.com/shamoon) ([#8837](https://github.com/paperless-ngx/paperless-ngx/pull/8837))
|
||||
- Fix: fix long tag visual wrapping [@shamoon](https://github.com/shamoon) ([#8833](https://github.com/paperless-ngx/paperless-ngx/pull/8833))
|
||||
- Change: allow generate auth token without a usable password [@shamoon](https://github.com/shamoon) ([#8824](https://github.com/paperless-ngx/paperless-ngx/pull/8824))
|
||||
- Fix: Enforce classifier training ordering to prevent extra training [@stumpylog](https://github.com/stumpylog) ([#8822](https://github.com/paperless-ngx/paperless-ngx/pull/8822))
|
||||
- Fix: import router module to not found component [@shamoon](https://github.com/shamoon) ([#8821](https://github.com/paperless-ngx/paperless-ngx/pull/8821))
|
||||
- Fix: better reflect some mail account / rule permissions in UI [@shamoon](https://github.com/shamoon) ([#8812](https://github.com/paperless-ngx/paperless-ngx/pull/8812))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.14.4
|
||||
|
||||
### Features
|
||||
|
@@ -81,7 +81,8 @@
|
||||
"scripts": [],
|
||||
"allowedCommonJsDependencies": [
|
||||
"ng2-pdf-viewer",
|
||||
"file-saver"
|
||||
"file-saver",
|
||||
"utif"
|
||||
],
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
|
@@ -324,7 +324,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">187</context>
|
||||
<context context-type="linenumber">193</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.html</context>
|
||||
@@ -523,7 +523,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">124</context>
|
||||
<context context-type="linenumber">123</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3823219296477075982" datatype="html">
|
||||
@@ -589,7 +589,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">184</context>
|
||||
<context context-type="linenumber">183</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||
@@ -723,11 +723,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">111</context>
|
||||
<context context-type="linenumber">110</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">127</context>
|
||||
<context context-type="linenumber">126</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/system-status-dialog/system-status-dialog.component.html</context>
|
||||
@@ -1128,11 +1128,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">63</context>
|
||||
<context context-type="linenumber">58</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">100</context>
|
||||
<context context-type="linenumber">95</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="293524471897878391" datatype="html">
|
||||
@@ -1445,7 +1445,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">183</context>
|
||||
<context context-type="linenumber">182</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
|
||||
@@ -1712,11 +1712,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
<context context-type="linenumber">34</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
<context context-type="linenumber">92</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5968132631442328843" datatype="html">
|
||||
@@ -2232,7 +2232,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">922</context>
|
||||
<context context-type="linenumber">924</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7266264608936522311" datatype="html">
|
||||
@@ -2509,19 +2509,19 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">946</context>
|
||||
<context context-type="linenumber">948</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1253</context>
|
||||
<context context-type="linenumber">1255</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1292</context>
|
||||
<context context-type="linenumber">1294</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1333</context>
|
||||
<context context-type="linenumber">1335</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
@@ -2738,7 +2738,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">42</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4369111787961525769" datatype="html">
|
||||
@@ -2950,7 +2950,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
|
||||
<context context-type="linenumber">80</context>
|
||||
<context context-type="linenumber">103</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
|
||||
@@ -3115,7 +3115,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">899</context>
|
||||
<context context-type="linenumber">901</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
@@ -3439,11 +3439,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
<context context-type="linenumber">38</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">98</context>
|
||||
<context context-type="linenumber">93</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4873149362496451858" datatype="html">
|
||||
@@ -4310,11 +4310,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">104</context>
|
||||
<context context-type="linenumber">103</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">138</context>
|
||||
<context context-type="linenumber">137</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8418597938335066730" datatype="html">
|
||||
@@ -4329,43 +4329,43 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">169</context>
|
||||
<context context-type="linenumber">168</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">171</context>
|
||||
<context context-type="linenumber">170</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1436831433675346331" datatype="html">
|
||||
<source>Create new user account</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">66</context>
|
||||
<context context-type="linenumber">71</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2887331217965896363" datatype="html">
|
||||
<source>Edit user account</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">70</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5872286584705575476" datatype="html">
|
||||
<source>Totp deactivated</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">126</context>
|
||||
<context context-type="linenumber">131</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6439190193788239059" datatype="html">
|
||||
<source>Totp deactivation failed</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">129</context>
|
||||
<context context-type="linenumber">134</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">134</context>
|
||||
<context context-type="linenumber">139</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8419515490539218007" datatype="html">
|
||||
@@ -4775,35 +4775,42 @@
|
||||
<source>Use parameters for webhook body</source>
|
||||
<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="linenumber">341</context>
|
||||
<context context-type="linenumber">342</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4078214298308732810" datatype="html">
|
||||
<source>Send webhook payload as JSON</source>
|
||||
<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="linenumber">343</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6806149889743731985" datatype="html">
|
||||
<source>Webhook params</source>
|
||||
<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="linenumber">343</context>
|
||||
<context context-type="linenumber">346</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7089924379374330" datatype="html">
|
||||
<source>Webhook body</source>
|
||||
<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="linenumber">345</context>
|
||||
<context context-type="linenumber">348</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3829826512656746316" datatype="html">
|
||||
<source>Webhook headers</source>
|
||||
<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="linenumber">347</context>
|
||||
<context context-type="linenumber">350</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2114525789021600887" datatype="html">
|
||||
<source>Include document</source>
|
||||
<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="linenumber">348</context>
|
||||
<context context-type="linenumber">351</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4626030417479279989" datatype="html">
|
||||
@@ -4835,7 +4842,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">99</context>
|
||||
<context context-type="linenumber">94</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8686921715946540725" datatype="html">
|
||||
@@ -4967,7 +4974,7 @@
|
||||
<source>Not assigned</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
|
||||
<context context-type="linenumber">390</context>
|
||||
<context context-type="linenumber">380</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">Filter drop down element to filter for documents with no correspondent/type/tag assigned</note>
|
||||
</trans-unit>
|
||||
@@ -4975,7 +4982,7 @@
|
||||
<source>Open <x id="PH" equiv-text="this.title"/> filter</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
|
||||
<context context-type="linenumber">511</context>
|
||||
<context context-type="linenumber">501</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7005745151564974365" datatype="html">
|
||||
@@ -5181,8 +5188,8 @@
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-card-small/document-card-small.component.ts</context>
|
||||
<context context-type="linenumber">118</context>
|
||||
<context context-type="sourcefile">src/app/pipes/object-name.pipe.ts</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2504502765849142619" datatype="html">
|
||||
@@ -5387,7 +5394,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">156</context>
|
||||
<context context-type="linenumber">155</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/share-links-dropdown/share-links-dropdown.component.html</context>
|
||||
@@ -5425,11 +5432,11 @@
|
||||
<source>Copied!</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">163</context>
|
||||
<context context-type="linenumber">162</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/share-links-dropdown/share-links-dropdown.component.html</context>
|
||||
@@ -5440,91 +5447,91 @@
|
||||
<source>Warning: changing the token cannot be undone</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">57</context>
|
||||
<context context-type="linenumber">56</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8935717557476105185" datatype="html">
|
||||
<source>Connected social accounts</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">63</context>
|
||||
<context context-type="linenumber">62</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8383227756109993898" datatype="html">
|
||||
<source>Set a password before disconnecting social account.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
<context context-type="linenumber">66</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2907016025519254862" datatype="html">
|
||||
<source>Disconnect</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">73</context>
|
||||
<context context-type="linenumber">72</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5322995394400578831" datatype="html">
|
||||
<source>Disconnect <x id="INTERPOLATION" equiv-text="{{ account.name }}"/> social account</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="649824314893051979" datatype="html">
|
||||
<source>Warning: disconnecting social accounts cannot be undone</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">85</context>
|
||||
<context context-type="linenumber">84</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1375396510511350122" datatype="html">
|
||||
<source>Connect new social account</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">90</context>
|
||||
<context context-type="linenumber">89</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4187671210825254690" datatype="html">
|
||||
<source>Scan the QR code with your authenticator app and then enter the code below</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">115</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5867169599865838267" datatype="html">
|
||||
<source>Authenticator secret</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">118</context>
|
||||
<context context-type="linenumber">117</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5331198279926709145" datatype="html">
|
||||
<source>You can store this secret and use it to reinstall your authenticator app at a later time.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">119</context>
|
||||
<context context-type="linenumber">118</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8186013988289067040" datatype="html">
|
||||
<source>Code</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">122</context>
|
||||
<context context-type="linenumber">121</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3176701652604668614" datatype="html">
|
||||
<source>Recovery codes will not be shown again, make sure to save them.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">141</context>
|
||||
<context context-type="linenumber">140</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2722512118372958038" datatype="html">
|
||||
<source>Copy codes</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">159</context>
|
||||
<context context-type="linenumber">158</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6141884091799403188" datatype="html">
|
||||
@@ -5954,11 +5961,40 @@
|
||||
<context context-type="linenumber">345</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="883965278435032344" datatype="html">
|
||||
<source>Filter by owner</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
|
||||
<context context-type="linenumber">73</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2807800733729323332" datatype="html">
|
||||
<source>Yes</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">366</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3542042671420335679" datatype="html">
|
||||
<source>No</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">366</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="872092479747931526" datatype="html">
|
||||
<source>No documents</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html</context>
|
||||
<context context-type="linenumber">125</context>
|
||||
<context context-type="linenumber">148</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1069523139277190436" datatype="html">
|
||||
@@ -6205,7 +6241,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1310</context>
|
||||
<context context-type="linenumber">1312</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/guards/dirty-saved-view.guard.ts</context>
|
||||
@@ -6242,11 +6278,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">35</context>
|
||||
<context context-type="linenumber">30</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">95</context>
|
||||
<context context-type="linenumber">90</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1379170675585571971" datatype="html">
|
||||
@@ -6283,11 +6319,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
<context context-type="linenumber">46</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">94</context>
|
||||
<context context-type="linenumber">89</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5066119607229701477" datatype="html">
|
||||
@@ -6310,11 +6346,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">96</context>
|
||||
<context context-type="linenumber">91</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2091353339965748767" datatype="html">
|
||||
@@ -6337,7 +6373,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">59</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5607669932062416162" datatype="html">
|
||||
@@ -6595,21 +6631,21 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">872</context>
|
||||
<context context-type="linenumber">874</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">900</context>
|
||||
<context context-type="linenumber">902</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="282586936710748252" datatype="html">
|
||||
<source>Documents can be restored prior to permanent deletion.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">901</context>
|
||||
<context context-type="linenumber">903</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
@@ -6620,7 +6656,7 @@
|
||||
<source>Move to trash</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">903</context>
|
||||
<context context-type="linenumber">905</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
@@ -6631,7 +6667,7 @@
|
||||
<source>Reprocess confirm</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">942</context>
|
||||
<context context-type="linenumber">944</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
@@ -6642,70 +6678,70 @@
|
||||
<source>This operation will permanently recreate the archive file for this document.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">943</context>
|
||||
<context context-type="linenumber">945</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="302054111564709516" datatype="html">
|
||||
<source>The archive file will be re-generated with the current settings.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">944</context>
|
||||
<context context-type="linenumber">946</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1192507664585066165" datatype="html">
|
||||
<source>Reprocess operation will begin in the background. Close and re-open or reload this document after the operation has completed to see new content.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">954</context>
|
||||
<context context-type="linenumber">956</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4409560272830824468" datatype="html">
|
||||
<source>Error executing operation</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">965</context>
|
||||
<context context-type="linenumber">967</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4458954481601077369" datatype="html">
|
||||
<source>Page Fit</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1038</context>
|
||||
<context context-type="linenumber">1040</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1217563727923422413" datatype="html">
|
||||
<source>Split confirm</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1251</context>
|
||||
<context context-type="linenumber">1253</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2805304563009985503" datatype="html">
|
||||
<source>This operation will split the selected document(s) into new documents.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1252</context>
|
||||
<context context-type="linenumber">1254</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4158171846914923744" datatype="html">
|
||||
<source>Split operation will begin in the background.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1268</context>
|
||||
<context context-type="linenumber">1270</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3235014591864339926" datatype="html">
|
||||
<source>Error executing split operation</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1277</context>
|
||||
<context context-type="linenumber">1279</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6555329262222566158" datatype="html">
|
||||
<source>Rotate confirm</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1290</context>
|
||||
<context context-type="linenumber">1292</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
@@ -6716,60 +6752,60 @@
|
||||
<source>This operation will permanently rotate the original version of the current document.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1291</context>
|
||||
<context context-type="linenumber">1293</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4069543875319587651" datatype="html">
|
||||
<source>Rotation will begin in the background. Close and re-open the document after the operation has completed to see the changes.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1307</context>
|
||||
<context context-type="linenumber">1309</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2962674215361798818" datatype="html">
|
||||
<source>Error executing rotate operation</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1319</context>
|
||||
<context context-type="linenumber">1321</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3539261415918606512" datatype="html">
|
||||
<source>Delete pages confirm</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1331</context>
|
||||
<context context-type="linenumber">1333</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5854352498125813866" datatype="html">
|
||||
<source>This operation will permanently delete the selected pages from the original document.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1332</context>
|
||||
<context context-type="linenumber">1334</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8617528702531167646" datatype="html">
|
||||
<source>Delete pages operation will begin in the background. Close and re-open or reload this document after the operation has completed to see the changes.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1347</context>
|
||||
<context context-type="linenumber">1349</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1249139200486584973" datatype="html">
|
||||
<source>Error executing delete pages operation</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1356</context>
|
||||
<context context-type="linenumber">1358</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6085793215710522488" datatype="html">
|
||||
<source>An error occurred loading tiff: <x id="PH" equiv-text="err.toString()"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1396</context>
|
||||
<context context-type="linenumber">1398</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">1400</context>
|
||||
<context context-type="linenumber">1402</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4958946940233632319" datatype="html">
|
||||
@@ -7269,11 +7305,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">71</context>
|
||||
<context context-type="linenumber">66</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/pipes/username.pipe.ts</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2332107018974972998" datatype="html">
|
||||
@@ -7337,7 +7373,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">304</context>
|
||||
<context context-type="linenumber">310</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1494518490116523821" datatype="html">
|
||||
@@ -7348,7 +7384,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">297</context>
|
||||
<context context-type="linenumber">303</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8461842260159597706" datatype="html">
|
||||
@@ -7455,11 +7491,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
<context context-type="linenumber">70</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">93</context>
|
||||
<context context-type="linenumber">88</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6954625430271090777" datatype="html">
|
||||
@@ -7491,11 +7527,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
<context context-type="linenumber">62</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">101</context>
|
||||
<context context-type="linenumber">96</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3557446856808034218" datatype="html">
|
||||
@@ -7548,11 +7584,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">79</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">102</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/paperless-config.ts</context>
|
||||
@@ -7587,60 +7623,46 @@
|
||||
<context context-type="linenumber">307</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2807800733729323332" datatype="html">
|
||||
<source>Yes</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">366</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3542042671420335679" datatype="html">
|
||||
<source>No</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">366</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4512084577073831437" datatype="html">
|
||||
<source>Reset filters / selection</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">285</context>
|
||||
<context context-type="linenumber">291</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4135055128446167640" datatype="html">
|
||||
<source>Open first [selected] document</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">313</context>
|
||||
<context context-type="linenumber">319</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3629960544875360046" datatype="html">
|
||||
<source>Previous page</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">329</context>
|
||||
<context context-type="linenumber">335</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3337301694210287595" datatype="html">
|
||||
<source>Next page</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">341</context>
|
||||
<context context-type="linenumber">347</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2155249406916744630" datatype="html">
|
||||
<source>View "<x id="PH" equiv-text="this.list.activeSavedViewTitle"/>" saved successfully.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">373</context>
|
||||
<context context-type="linenumber">379</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6837554170707123455" datatype="html">
|
||||
<source>View "<x id="PH" equiv-text="savedView.name"/>" created successfully.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">416</context>
|
||||
<context context-type="linenumber">422</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="739880801667335279" datatype="html">
|
||||
@@ -8807,7 +8829,7 @@
|
||||
<source>Search score</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||
<context context-type="linenumber">108</context>
|
||||
<context context-type="linenumber">103</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">Score is a value returned by the full text search engine and specifies how well a result matches the given query</note>
|
||||
</trans-unit>
|
||||
|
@@ -49,7 +49,7 @@ const CONSUMPTION_SCOPE_OPTIONS = [
|
||||
name: $localize`Only process attachments`,
|
||||
},
|
||||
{
|
||||
id: MailRuleConsumptionScope.Email_Only,
|
||||
id: MailRuleConsumptionScope.EmailOnly,
|
||||
name: $localize`Process message as .eml`,
|
||||
},
|
||||
{
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import {
|
||||
ComponentFixture,
|
||||
TestBed,
|
||||
@@ -51,7 +53,11 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [FilterPipe],
|
||||
providers: [
|
||||
FilterPipe,
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
imports: [NgxBootstrapIconsModule.pick(allIcons)],
|
||||
}).compileComponents()
|
||||
|
||||
@@ -490,37 +496,6 @@ describe('FilterableDropdownComponent & FilterableDropdownSelectionModel', () =>
|
||||
])
|
||||
})
|
||||
|
||||
it('selection model should sort items by state and document counts, if set', () => {
|
||||
component.items = items.concat([{ id: 4, name: 'Item D' }])
|
||||
component.selectionModel = selectionModel
|
||||
component.documentCounts = [
|
||||
{ id: 1, document_count: 0 }, // Tag1
|
||||
{ id: 2, document_count: 1 }, // Tag2
|
||||
{ id: 4, document_count: 2 },
|
||||
]
|
||||
component.selectionModel.apply()
|
||||
expect(selectionModel.items).toEqual([
|
||||
nullItem,
|
||||
{ id: 4, name: 'Item D' },
|
||||
items[1], // Tag2
|
||||
items[0], // Tag1
|
||||
])
|
||||
|
||||
selectionModel.toggle(items[1].id)
|
||||
component.documentCounts = [
|
||||
{ id: 1, document_count: 0 },
|
||||
{ id: 2, document_count: 1 },
|
||||
{ id: 4, document_count: 0 },
|
||||
]
|
||||
selectionModel.apply()
|
||||
expect(selectionModel.items).toEqual([
|
||||
nullItem,
|
||||
items[1], // Tag2
|
||||
{ id: 4, name: 'Item D' },
|
||||
items[0], // Tag1
|
||||
])
|
||||
})
|
||||
|
||||
it('should set support create, keep open model and call createRef method', fakeAsync(() => {
|
||||
component.items = items
|
||||
component.icon = 'tag-fill'
|
||||
|
@@ -81,16 +81,6 @@ export class FilterableDropdownSelectionModel {
|
||||
this.getNonTemporary(b.id) == ToggleableItemState.NotSelected
|
||||
) {
|
||||
return -1
|
||||
} else if (
|
||||
this._documentCounts.length &&
|
||||
this.getDocumentCount(a.id) > this.getDocumentCount(b.id)
|
||||
) {
|
||||
return -1
|
||||
} else if (
|
||||
this._documentCounts.length &&
|
||||
this.getDocumentCount(a.id) < this.getDocumentCount(b.id)
|
||||
) {
|
||||
return 1
|
||||
} else {
|
||||
return a.name.localeCompare(b.name)
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { Tag } from 'src/app/data/tag'
|
||||
import { TagComponent } from '../../tag/tag.component'
|
||||
@@ -12,7 +14,10 @@ describe('ToggleableDropdownButtonComponent', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [],
|
||||
providers: [
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
imports: [ToggleableDropdownButtonComponent, TagComponent],
|
||||
}).compileComponents()
|
||||
|
||||
|
@@ -28,7 +28,8 @@
|
||||
[original-size]="false"
|
||||
[show-borders]="false"
|
||||
[show-all]="true"
|
||||
(error)="onError($event)">
|
||||
(text-layer-rendered)="onPageRendered()"
|
||||
(error)="onError($event)" #pdfViewer>
|
||||
</pdf-viewer>
|
||||
}
|
||||
}
|
||||
|
@@ -158,4 +158,24 @@ describe('PreviewPopupComponent', () => {
|
||||
jest.advanceTimersByTime(1)
|
||||
expect(component.popover.isOpen()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should dispatch find event on viewer loaded if searchQuery set', () => {
|
||||
documentService.searchQuery = 'test'
|
||||
settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, false)
|
||||
component.popover.open()
|
||||
jest.advanceTimersByTime(1000)
|
||||
fixture.detectChanges()
|
||||
// normally setup by pdf-viewer
|
||||
jest.replaceProperty(component.pdfViewer, 'eventBus', {
|
||||
dispatch: jest.fn(),
|
||||
} as any)
|
||||
const dispatchSpy = jest.spyOn(component.pdfViewer.eventBus, 'dispatch')
|
||||
component.onPageRendered()
|
||||
expect(dispatchSpy).toHaveBeenCalledWith('find', {
|
||||
query: 'test',
|
||||
caseSensitive: false,
|
||||
highlightAll: true,
|
||||
phraseSearch: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { HttpClient } from '@angular/common/http'
|
||||
import { Component, Input, OnDestroy, ViewChild } from '@angular/core'
|
||||
import { NgbPopover, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { PdfViewerModule } from 'ng2-pdf-viewer'
|
||||
import { PdfViewerComponent, PdfViewerModule } from 'ng2-pdf-viewer'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { first, Subject, takeUntil } from 'rxjs'
|
||||
import { Document } from 'src/app/data/document'
|
||||
@@ -57,6 +57,8 @@ export class PreviewPopupComponent implements OnDestroy {
|
||||
|
||||
@ViewChild('popover') popover: NgbPopover
|
||||
|
||||
@ViewChild('pdfViewer') pdfViewer: PdfViewerComponent
|
||||
|
||||
mouseOnPreview: boolean = false
|
||||
|
||||
popoverClass: string = 'shadow popover-preview'
|
||||
@@ -114,6 +116,18 @@ export class PreviewPopupComponent implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
onPageRendered() {
|
||||
// Only triggered by the pngx pdf viewer
|
||||
if (this.documentService.searchQuery) {
|
||||
this.pdfViewer.eventBus.dispatch('find', {
|
||||
query: this.documentService.searchQuery,
|
||||
caseSensitive: false,
|
||||
highlightAll: true,
|
||||
phraseSearch: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
get previewUrl() {
|
||||
return this.documentService.getPreviewUrl(this.document.id)
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
@if (tag !== undefined) {
|
||||
@if (tag) {
|
||||
@if (!clickable) {
|
||||
<span class="badge" [style.background]="tag.color" [style.color]="tag.text_color">{{tag.name}}</span>
|
||||
}
|
||||
|
@@ -1,6 +1,11 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { of } from 'rxjs'
|
||||
import { Tag } from 'src/app/data/tag'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { TagService } from 'src/app/services/rest/tag.service'
|
||||
import { TagComponent } from './tag.component'
|
||||
|
||||
const tag: Tag = {
|
||||
@@ -12,13 +17,20 @@ const tag: Tag = {
|
||||
describe('TagComponent', () => {
|
||||
let component: TagComponent
|
||||
let fixture: ComponentFixture<TagComponent>
|
||||
let permissionsService: PermissionsService
|
||||
let tagService: TagService
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [],
|
||||
providers: [
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
imports: [TagComponent],
|
||||
}).compileComponents()
|
||||
|
||||
permissionsService = TestBed.inject(PermissionsService)
|
||||
tagService = TestBed.inject(TagService)
|
||||
fixture = TestBed.createComponent(TagComponent)
|
||||
component = fixture.componentInstance
|
||||
fixture.detectChanges()
|
||||
@@ -47,4 +59,13 @@ describe('TagComponent', () => {
|
||||
fixture.detectChanges()
|
||||
expect(fixture.debugElement.query(By.css('a.badge'))).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should support retrieving tag by ID', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
const getCachedSpy = jest.spyOn(tagService, 'getCached')
|
||||
getCachedSpy.mockReturnValue(of(tag))
|
||||
component.tagID = 1
|
||||
expect(getCachedSpy).toHaveBeenCalledWith(1)
|
||||
expect(component.tag).toEqual(tag)
|
||||
})
|
||||
})
|
||||
|
@@ -1,5 +1,11 @@
|
||||
import { Component, Input } from '@angular/core'
|
||||
import { Tag } from 'src/app/data/tag'
|
||||
import {
|
||||
PermissionAction,
|
||||
PermissionsService,
|
||||
PermissionType,
|
||||
} from 'src/app/services/permissions.service'
|
||||
import { TagService } from 'src/app/services/rest/tag.service'
|
||||
|
||||
@Component({
|
||||
selector: 'pngx-tag',
|
||||
@@ -7,10 +13,39 @@ import { Tag } from 'src/app/data/tag'
|
||||
styleUrls: ['./tag.component.scss'],
|
||||
})
|
||||
export class TagComponent {
|
||||
constructor() {}
|
||||
private _tag: Tag
|
||||
private _tagID: number
|
||||
|
||||
constructor(
|
||||
private permissionsService: PermissionsService,
|
||||
private tagService: TagService
|
||||
) {}
|
||||
|
||||
@Input()
|
||||
tag: Tag
|
||||
public set tag(tag: Tag) {
|
||||
this._tag = tag
|
||||
}
|
||||
|
||||
public get tag(): Tag {
|
||||
return this._tag
|
||||
}
|
||||
|
||||
@Input()
|
||||
set tagID(tagID: number) {
|
||||
if (tagID !== this._tagID) {
|
||||
this._tagID = tagID
|
||||
if (
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.Tag
|
||||
)
|
||||
) {
|
||||
this.tagService.getCached(this._tagID).subscribe((tag) => {
|
||||
this.tag = tag
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
linkTitle: string = ''
|
||||
|
@@ -50,22 +50,45 @@
|
||||
}
|
||||
@case (DisplayField.CORRESPONDENT) {
|
||||
@if (doc.correspondent) {
|
||||
<a class="btn-link text-dark text-decoration-none" type="button" (click)="clickCorrespondent(doc.correspondent, $event)" title="Filter by correspondent" i18n-title>{{(doc.correspondent$ | async)?.name}}</a>
|
||||
<a class="btn-link text-dark text-decoration-none" type="button" (click)="clickCorrespondent(doc.correspondent, $event)" title="Filter by correspondent" i18n-title>{{doc.correspondent | correspondentName | async}}</a>
|
||||
}
|
||||
}
|
||||
@case (DisplayField.TAGS) {
|
||||
@for (t of doc.tags$ | async; track t) {
|
||||
<pngx-tag [tag]="t" class="ms-1" (click)="clickTag(t.id, $event)" [clickable]="true" linkTitle="Filter by tag" i18n-title></pngx-tag>
|
||||
@for (tagID of doc.tags; track tagID) {
|
||||
<pngx-tag [tagID]="tagID" class="ms-1" (click)="clickTag(tagID, $event)" [clickable]="true" linkTitle="Filter by tag" i18n-title></pngx-tag>
|
||||
}
|
||||
}
|
||||
@case (DisplayField.DOCUMENT_TYPE) {
|
||||
@if (doc.document_type) {
|
||||
<a class="btn-link text-dark text-decoration-none" type="button" (click)="clickDocType(doc.document_type, $event)" title="Filter by document type" i18n-title>{{(doc.document_type$ | async)?.name}}</a>
|
||||
<a class="btn-link text-dark text-decoration-none" type="button" (click)="clickDocType(doc.document_type, $event)" title="Filter by document type" i18n-title>{{doc.document_type | documentTypeName | async}}</a>
|
||||
}
|
||||
}
|
||||
@case (DisplayField.STORAGE_PATH) {
|
||||
@if (doc.storage_path) {
|
||||
<a class="btn-link text-dark text-decoration-none" type="button" (click)="clickStoragePath(doc.storage_path, $event)" title="Filter by storage path" i18n-title>{{(doc.storage_path$ | async)?.name}}</a>
|
||||
<a class="btn-link text-dark text-decoration-none" type="button" (click)="clickStoragePath(doc.storage_path, $event)" title="Filter by storage path" i18n-title>{{doc.storage_path | storagePathName | async}}</a>
|
||||
}
|
||||
}
|
||||
@case (DisplayField.OWNER) {
|
||||
@if (doc.owner) {
|
||||
<a class="btn-link text-dark text-decoration-none" type="button" (click)="clickOwner(doc.owner, $event)" title="Filter by owner" i18n-title>{{doc.owner | username | async}}</a>
|
||||
}
|
||||
}
|
||||
@case (DisplayField.ASN) {
|
||||
{{doc.archive_serial_number}}
|
||||
}
|
||||
@case (DisplayField.PAGE_COUNT) {
|
||||
{{ doc.page_count }}
|
||||
}
|
||||
@case (DisplayField.SHARED) {
|
||||
@if (doc.is_shared_by_requester) { <ng-container i18n>Yes</ng-container> } @else { <ng-container i18n>No</ng-container> }
|
||||
}
|
||||
@case (DisplayField.NOTES) {
|
||||
@if (doc.notes.length) {
|
||||
<a routerLink="/documents/{{doc.id}}/notes" class="btn btn-sm p-0">
|
||||
<span class="badge rounded-pill bg-light border text-primary">
|
||||
<i-bs width="1.2em" height="1.2em" class="ms-1 me-1" name="chat-left-text"></i-bs>
|
||||
{{doc.notes.length}}</span>
|
||||
</a>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ import {
|
||||
FILTER_DOCUMENT_TYPE,
|
||||
FILTER_FULLTEXT_MORELIKE,
|
||||
FILTER_HAS_TAGS_ALL,
|
||||
FILTER_OWNER_ANY,
|
||||
FILTER_STORAGE_PATH,
|
||||
} from 'src/app/data/filter-rule-type'
|
||||
import { SavedView } from 'src/app/data/saved-view'
|
||||
@@ -295,6 +296,15 @@ describe('SavedViewWidgetComponent', () => {
|
||||
component.clickStoragePath(11) // coverage
|
||||
})
|
||||
|
||||
it('should navigate via quickfilter on click owner', () => {
|
||||
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
|
||||
component.clickOwner(11, new MouseEvent('click'))
|
||||
expect(qfSpy).toHaveBeenCalledWith([
|
||||
{ rule_type: FILTER_OWNER_ANY, value: '11' },
|
||||
])
|
||||
component.clickOwner(11) // coverage
|
||||
})
|
||||
|
||||
it('should navigate via quickfilter on click more like', () => {
|
||||
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
|
||||
component.clickMoreLike(11)
|
||||
|
@@ -31,12 +31,17 @@ import {
|
||||
FILTER_DOCUMENT_TYPE,
|
||||
FILTER_FULLTEXT_MORELIKE,
|
||||
FILTER_HAS_TAGS_ALL,
|
||||
FILTER_OWNER_ANY,
|
||||
FILTER_STORAGE_PATH,
|
||||
} from 'src/app/data/filter-rule-type'
|
||||
import { SavedView } from 'src/app/data/saved-view'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
||||
import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe'
|
||||
import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe'
|
||||
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
||||
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
|
||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
||||
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
|
||||
@@ -62,6 +67,10 @@ import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
|
||||
TagComponent,
|
||||
WidgetFrameComponent,
|
||||
IfPermissionsDirective,
|
||||
UsernamePipe,
|
||||
CorrespondentNamePipe,
|
||||
DocumentTypeNamePipe,
|
||||
StoragePathNamePipe,
|
||||
AsyncPipe,
|
||||
DocumentTitlePipe,
|
||||
CustomDatePipe,
|
||||
@@ -229,6 +238,15 @@ export class SavedViewWidgetComponent
|
||||
])
|
||||
}
|
||||
|
||||
clickOwner(ownerID: number, event: MouseEvent = null) {
|
||||
event?.preventDefault()
|
||||
event?.stopImmediatePropagation()
|
||||
|
||||
this.list.quickFilter([
|
||||
{ rule_type: FILTER_OWNER_ANY, value: ownerID.toString() },
|
||||
])
|
||||
}
|
||||
|
||||
openDocumentDetail(document: Document) {
|
||||
this.router.navigate(['documents', document.id])
|
||||
}
|
||||
|
@@ -848,12 +848,14 @@ export class DocumentDetailComponent
|
||||
)
|
||||
.pipe(
|
||||
switchMap(({ nextDocId, updateResult }) => {
|
||||
if (nextDocId && updateResult)
|
||||
if (nextDocId && updateResult) {
|
||||
this.openDocumentService.setDirty(this.document, false)
|
||||
return this.openDocumentService
|
||||
.closeDocument(this.document)
|
||||
.pipe(
|
||||
map((closeResult) => ({ updateResult, nextDocId, closeResult }))
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
.pipe(first())
|
||||
|
@@ -22,9 +22,9 @@
|
||||
@if (document) {
|
||||
@if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
|
||||
@if (clickCorrespondent.observers.length ) {
|
||||
<a title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name}}</a>
|
||||
<a title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{document.correspondent | correspondentName | async}}</a>
|
||||
} @else {
|
||||
{{(document.correspondent$ | async)?.name}}
|
||||
{{document.correspondent | correspondentName | async}}
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.TITLE)) {:}
|
||||
}
|
||||
@@ -32,8 +32,8 @@
|
||||
{{document.title | documentTitle}}
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.TAGS)) {
|
||||
@for (t of document.tags$ | async; track t) {
|
||||
<pngx-tag [tag]="t" linkTitle="Filter by tag" i18n-linkTitle class="ms-1" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="clickTag.observers.length"></pngx-tag>
|
||||
@for (tagID of document.tags; track t) {
|
||||
<pngx-tag [tagID]="tagID" linkTitle="Filter by tag" i18n-linkTitle class="ms-1" (click)="clickTag.emit(tagID);$event.stopPropagation()" [clickable]="clickTag.observers.length"></pngx-tag>
|
||||
}
|
||||
}
|
||||
} @else {
|
||||
@@ -95,13 +95,13 @@
|
||||
@if (displayFields.includes(DisplayField.DOCUMENT_TYPE) && document.document_type) {
|
||||
<button type="button" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="Filter by document type" i18n-title
|
||||
(click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
|
||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="file-earmark"></i-bs><small>{{(document.document_type$ | async)?.name}}</small>
|
||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="file-earmark"></i-bs><small>{{document.document_type | documentTypeName | async}}</small>
|
||||
</button>
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.STORAGE_PATH) && document.storage_path) {
|
||||
<button type="button" class="list-group-item btn btn-sm bg-light text-dark p-1 border-0 me-2 d-flex align-items-center" title="Filter by storage path" i18n-title
|
||||
(click)="clickStoragePath.emit(document.storage_path);$event.stopPropagation()">
|
||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="archive"></i-bs><small>{{(document.storage_path$ | async)?.name}}</small>
|
||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="archive"></i-bs><small>{{document.storage_path | storagePathName | async}}</small>
|
||||
</button>
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.ASN) && document.archive_serial_number | isNumber) {
|
||||
@@ -136,7 +136,7 @@
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) {
|
||||
<div class="list-group-item bg-light text-dark p-1 border-0 d-flex align-items-center">
|
||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="person-fill-lock"></i-bs><small>{{document.owner | username}}</small>
|
||||
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="person-fill-lock"></i-bs><small>{{document.owner | username | async}}</small>
|
||||
</div>
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) {
|
||||
|
@@ -13,6 +13,7 @@ import {
|
||||
NgbTooltipModule,
|
||||
} from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { delay, of } from 'rxjs'
|
||||
import {
|
||||
DEFAULT_DISPLAY_FIELDS,
|
||||
DisplayField,
|
||||
@@ -20,9 +21,12 @@ import {
|
||||
} from 'src/app/data/document'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
||||
import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe'
|
||||
import { IsNumberPipe } from 'src/app/pipes/is-number.pipe'
|
||||
import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe'
|
||||
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
@@ -43,6 +47,9 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
|
||||
CustomFieldDisplayComponent,
|
||||
AsyncPipe,
|
||||
UsernamePipe,
|
||||
CorrespondentNamePipe,
|
||||
DocumentTypeNamePipe,
|
||||
StoragePathNamePipe,
|
||||
IfPermissionsDirective,
|
||||
CustomDatePipe,
|
||||
RouterModule,
|
||||
@@ -104,9 +111,11 @@ export class DocumentCardLargeComponent
|
||||
popoverHidden = true
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
setInterval(() => {
|
||||
this.show = true
|
||||
}, 100)
|
||||
of(true)
|
||||
.pipe(delay(50))
|
||||
.subscribe(() => {
|
||||
this.show = true
|
||||
})
|
||||
}
|
||||
|
||||
get searchScoreClass() {
|
||||
|
@@ -16,8 +16,8 @@
|
||||
|
||||
@if (document && displayFields?.includes(DisplayField.TAGS)) {
|
||||
<div class="tags d-flex flex-column text-end position-absolute me-1 fs-6">
|
||||
@for (t of getTagsLimited$() | async; track t) {
|
||||
<pngx-tag [tag]="t" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="true" linkTitle="Toggle tag filter" i18n-linkTitle></pngx-tag>
|
||||
@for (tagID of tagIDs; track tagID) {
|
||||
<pngx-tag [tagID]="tagID" (click)="clickTag.emit(tagID);$event.stopPropagation()" [clickable]="true" linkTitle="Toggle tag filter" i18n-linkTitle></pngx-tag>
|
||||
}
|
||||
@if (moreTags) {
|
||||
<div>
|
||||
@@ -40,7 +40,7 @@
|
||||
<p class="card-text">
|
||||
@if (document) {
|
||||
@if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) {
|
||||
<a title="Toggle correspondent filter" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{(document.correspondent$ | async)?.name ?? privateName}}</a>
|
||||
<a title="Toggle correspondent filter" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="fw-bold btn-link">{{document.correspondent | correspondentName | async}}</a>
|
||||
@if (displayFields.includes(DisplayField.TITLE)) {:}
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.TITLE)) {
|
||||
@@ -59,14 +59,14 @@
|
||||
<button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle document type filter" i18n-title
|
||||
(click)="clickDocumentType.emit(document.document_type);$event.stopPropagation()">
|
||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="file-earmark"></i-bs>
|
||||
<small>{{(document.document_type$ | async)?.name ?? privateName}}</small>
|
||||
<small>{{document.document_type | documentTypeName | async}}</small>
|
||||
</button>
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.STORAGE_PATH) && document.storage_path) {
|
||||
<button type="button" class="list-group-item list-group-item-action bg-transparent ps-0 p-1 border-0" title="Toggle storage path filter" i18n-title
|
||||
(click)="clickStoragePath.emit(document.storage_path);$event.stopPropagation()">
|
||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="folder"></i-bs>
|
||||
<small>{{(document.storage_path$ | async)?.name ?? privateName}}</small>
|
||||
<small>{{document.storage_path | storagePathName | async}}</small>
|
||||
</button>
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.CREATED)) {
|
||||
@@ -116,7 +116,7 @@
|
||||
@if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) {
|
||||
<div class="ps-0 p-1">
|
||||
<i-bs width="1em" height="1em" class="me-2 text-muted" name="person-fill-lock"></i-bs>
|
||||
<small>{{document.owner | username}}</small>
|
||||
<small>{{document.owner | username | async}}</small>
|
||||
</div>
|
||||
}
|
||||
@if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) {
|
||||
|
@@ -5,8 +5,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||
import { By } from '@angular/platform-browser'
|
||||
import { RouterTestingModule } from '@angular/router/testing'
|
||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { of } from 'rxjs'
|
||||
import { Tag } from 'src/app/data/tag'
|
||||
import { TagComponent } from '../../common/tag/tag.component'
|
||||
import { DocumentCardSmallComponent } from './document-card-small.component'
|
||||
|
||||
@@ -24,16 +22,6 @@ const doc = {
|
||||
note: 'This is some note content bananas',
|
||||
},
|
||||
],
|
||||
tags$: of([
|
||||
{ id: 1, name: 'Tag1' },
|
||||
{ id: 2, name: 'Tag2' },
|
||||
{ id: 3, name: 'Tag3' },
|
||||
{ id: 4, name: 'Tag4' },
|
||||
{ id: 5, name: 'Tag5' },
|
||||
{ id: 6, name: 'Tag6' },
|
||||
{ id: 7, name: 'Tag7' },
|
||||
{ id: 8, name: 'Tag8' },
|
||||
]),
|
||||
content:
|
||||
'Cupcake ipsum dolor sit amet ice cream. Donut shortbread cheesecake caramels tiramisu pastry caramels chocolate bar. Tart tootsie roll muffin icing cotton candy topping sweet roll. Pie lollipop dragée sesame snaps donut tart pudding. Oat cake apple pie danish danish candy canes. Shortbread candy canes sesame snaps muffin tiramisu marshmallow chocolate bar halvah. Cake lemon drops candy apple pie carrot cake bonbon halvah pastry gummi bears. Sweet roll candy ice cream sesame snaps marzipan cookie ice cream. Cake cheesecake apple pie muffin candy toffee lollipop. Carrot cake oat cake cookie biscuit cupcake cake marshmallow. Sweet roll jujubes carrot cake cheesecake cake candy canes sweet roll gingerbread jelly beans. Apple pie sugar plum oat cake halvah cake. Pie oat cake chocolate cake cookie gingerbread marzipan. Lemon drops cheesecake lollipop danish marzipan candy.',
|
||||
}
|
||||
@@ -80,7 +68,6 @@ describe('DocumentCardSmallComponent', () => {
|
||||
fixture.debugElement.queryAll(By.directive(TagComponent))
|
||||
).toHaveLength(5)
|
||||
component.document.tags = [1, 2]
|
||||
component.document.tags$ = of([{ id: 1 } as Tag, { id: 2 } as Tag])
|
||||
fixture.detectChanges()
|
||||
expect(
|
||||
fixture.debugElement.queryAll(By.directive(TagComponent))
|
||||
|
@@ -13,7 +13,8 @@ import {
|
||||
NgbTooltipModule,
|
||||
} from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||
import { map } from 'rxjs/operators'
|
||||
import { of } from 'rxjs'
|
||||
import { delay } from 'rxjs/operators'
|
||||
import {
|
||||
DEFAULT_DISPLAY_FIELDS,
|
||||
DisplayField,
|
||||
@@ -21,9 +22,12 @@ import {
|
||||
} from 'src/app/data/document'
|
||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
|
||||
import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
||||
import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe'
|
||||
import { IsNumberPipe } from 'src/app/pipes/is-number.pipe'
|
||||
import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe'
|
||||
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
@@ -44,6 +48,9 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
|
||||
CustomFieldDisplayComponent,
|
||||
AsyncPipe,
|
||||
UsernamePipe,
|
||||
CorrespondentNamePipe,
|
||||
DocumentTypeNamePipe,
|
||||
StoragePathNamePipe,
|
||||
IfPermissionsDirective,
|
||||
CustomDatePipe,
|
||||
RouterModule,
|
||||
@@ -97,9 +104,11 @@ export class DocumentCardSmallComponent
|
||||
@ViewChild('popupPreview') popupPreview: PreviewPopupComponent
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
setInterval(() => {
|
||||
this.show = true
|
||||
}, 50)
|
||||
of(true)
|
||||
.pipe(delay(50))
|
||||
.subscribe(() => {
|
||||
this.show = true
|
||||
})
|
||||
}
|
||||
|
||||
getIsThumbInverted() {
|
||||
@@ -114,22 +123,14 @@ export class DocumentCardSmallComponent
|
||||
return this.documentService.getDownloadUrl(this.document.id)
|
||||
}
|
||||
|
||||
get privateName() {
|
||||
return $localize`Private`
|
||||
}
|
||||
|
||||
getTagsLimited$() {
|
||||
get tagIDs() {
|
||||
const limit = this.document.notes.length > 0 ? 6 : 7
|
||||
return this.document.tags$?.pipe(
|
||||
map((tags) => {
|
||||
if (tags.length > limit) {
|
||||
this.moreTags = tags.length - (limit - 1)
|
||||
return tags.slice(0, limit - 1)
|
||||
} else {
|
||||
return tags
|
||||
}
|
||||
})
|
||||
)
|
||||
if (this.document.tags.length > limit) {
|
||||
this.moreTags = this.document.tags.length - (limit - 1)
|
||||
return this.document.tags.slice(0, limit - 1)
|
||||
} else {
|
||||
return this.document.tags
|
||||
}
|
||||
}
|
||||
|
||||
mouseLeaveCard() {
|
||||
|
@@ -295,7 +295,7 @@
|
||||
@if (activeDisplayFields.includes(DisplayField.CORRESPONDENT) && permissionService.currentUserCan(PermissionAction.View, PermissionType.Correspondent)) {
|
||||
<td class="">
|
||||
@if (d.correspondent) {
|
||||
<a (click)="clickCorrespondent(d.correspondent);$event.stopPropagation()" title="Filter by correspondent" i18n-title>{{(d.correspondent$ | async)?.name}}</a>
|
||||
<a (click)="clickCorrespondent(d.correspondent);$event.stopPropagation()" title="Filter by correspondent" i18n-title>{{d.correspondent | correspondentName | async}}</a>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
@@ -310,15 +310,15 @@
|
||||
</div>
|
||||
}
|
||||
@if (activeDisplayFields.includes(DisplayField.TAGS)) {
|
||||
@for (t of d.tags$ | async; track t) {
|
||||
<pngx-tag [tag]="t" class="ms-1" clickable="true" linkTitle="Filter by tag" i18n-linkTitle (click)="clickTag(t.id);$event.stopPropagation()"></pngx-tag>
|
||||
@for (tagID of d.tags; track t) {
|
||||
<pngx-tag [tagID]="tagID" class="ms-1" clickable="true" linkTitle="Filter by tag" i18n-linkTitle (click)="clickTag(tagID);$event.stopPropagation()"></pngx-tag>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
}
|
||||
@if (activeDisplayFields.includes(DisplayField.OWNER) && permissionService.currentUserCan(PermissionAction.View, PermissionType.User)) {
|
||||
<td>
|
||||
{{d.owner | username}}
|
||||
{{d.owner | username | async}}
|
||||
</td>
|
||||
}
|
||||
@if (activeDisplayFields.includes(DisplayField.NOTES) && notesEnabled) {
|
||||
@@ -335,14 +335,14 @@
|
||||
@if (activeDisplayFields.includes(DisplayField.DOCUMENT_TYPE) && permissionService.currentUserCan(PermissionAction.View, PermissionType.DocumentType)) {
|
||||
<td class="">
|
||||
@if (d.document_type) {
|
||||
<a (click)="clickDocumentType(d.document_type);$event.stopPropagation()" title="Filter by document type" i18n-title>{{(d.document_type$ | async)?.name}}</a>
|
||||
<a (click)="clickDocumentType(d.document_type);$event.stopPropagation()" title="Filter by document type" i18n-title>{{d.document_type | documentTypeName | async}}</a>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
@if (activeDisplayFields.includes(DisplayField.STORAGE_PATH) && permissionService.currentUserCan(PermissionAction.View, PermissionType.StoragePath)) {
|
||||
<td class="">
|
||||
@if (d.storage_path) {
|
||||
<a (click)="clickStoragePath(d.storage_path);$event.stopPropagation()" title="Filter by storage path" i18n-title>{{(d.storage_path$ | async)?.name}}</a>
|
||||
<a (click)="clickStoragePath(d.storage_path);$event.stopPropagation()" title="Filter by storage path" i18n-title>{{d.storage_path | storagePathName | async}}</a>
|
||||
}
|
||||
</td>
|
||||
}
|
||||
@@ -357,10 +357,10 @@
|
||||
</td>
|
||||
}
|
||||
@if (activeDisplayFields.includes(DisplayField.PAGE_COUNT)) {
|
||||
<td>
|
||||
{{ d.page_count }}
|
||||
</td>
|
||||
}
|
||||
<td>
|
||||
{{ d.page_count }}
|
||||
</td>
|
||||
}
|
||||
@if (activeDisplayFields.includes(DisplayField.SHARED)) {
|
||||
<td>
|
||||
@if (d.is_shared_by_requester) { <ng-container i18n>Yes</ng-container> } @else { <ng-container i18n>No</ng-container> }
|
||||
|
@@ -57,21 +57,21 @@ const docs: Document[] = [
|
||||
id: 1,
|
||||
title: 'Doc1',
|
||||
notes: [],
|
||||
tags$: new Subject(),
|
||||
tags: [],
|
||||
content: 'document content 1',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Doc2',
|
||||
notes: [],
|
||||
tags$: new Subject(),
|
||||
tags: [],
|
||||
content: 'document content 2',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Doc3',
|
||||
notes: [],
|
||||
tags$: new Subject(),
|
||||
tags: [],
|
||||
content: 'document content 3',
|
||||
},
|
||||
]
|
||||
@@ -650,7 +650,6 @@ describe('DocumentListComponent', () => {
|
||||
id: i + 1,
|
||||
title: `Doc${i + 1}`,
|
||||
notes: [],
|
||||
tags$: new Subject(),
|
||||
content: `document content ${i + 1}`,
|
||||
}))
|
||||
jest
|
||||
|
@@ -37,8 +37,11 @@ import {
|
||||
SortableDirective,
|
||||
SortEvent,
|
||||
} from 'src/app/directives/sortable.directive'
|
||||
import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe'
|
||||
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
|
||||
import { DocumentTypeNamePipe } from 'src/app/pipes/document-type-name.pipe'
|
||||
import { StoragePathNamePipe } from 'src/app/pipes/storage-path-name.pipe'
|
||||
import { UsernamePipe } from 'src/app/pipes/username.pipe'
|
||||
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
|
||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
|
||||
@@ -81,6 +84,9 @@ import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-vi
|
||||
IfPermissionsDirective,
|
||||
SortableDirective,
|
||||
UsernamePipe,
|
||||
CorrespondentNamePipe,
|
||||
DocumentTypeNamePipe,
|
||||
StoragePathNamePipe,
|
||||
NgxBootstrapIconsModule,
|
||||
AsyncPipe,
|
||||
FormsModule,
|
||||
|
@@ -1,11 +1,6 @@
|
||||
import { Observable } from 'rxjs'
|
||||
import { Correspondent } from './correspondent'
|
||||
import { CustomFieldInstance } from './custom-field-instance'
|
||||
import { DocumentNote } from './document-note'
|
||||
import { DocumentType } from './document-type'
|
||||
import { ObjectWithPermissions } from './object-with-permissions'
|
||||
import { StoragePath } from './storage-path'
|
||||
import { Tag } from './tag'
|
||||
|
||||
export enum DisplayMode {
|
||||
TABLE = 'table',
|
||||
@@ -118,24 +113,16 @@ export interface SearchHit {
|
||||
}
|
||||
|
||||
export interface Document extends ObjectWithPermissions {
|
||||
correspondent$?: Observable<Correspondent>
|
||||
|
||||
correspondent?: number
|
||||
|
||||
document_type$?: Observable<DocumentType>
|
||||
|
||||
document_type?: number
|
||||
|
||||
storage_path$?: Observable<StoragePath>
|
||||
|
||||
storage_path?: number
|
||||
|
||||
title?: string
|
||||
|
||||
content?: string
|
||||
|
||||
tags$?: Observable<Tag[]>
|
||||
|
||||
tags?: number[]
|
||||
|
||||
checksum?: string
|
||||
|
@@ -7,7 +7,7 @@ export enum MailFilterAttachmentType {
|
||||
|
||||
export enum MailRuleConsumptionScope {
|
||||
Attachments = 1,
|
||||
Email_Only = 2,
|
||||
EmailOnly = 2,
|
||||
Everything = 3,
|
||||
}
|
||||
|
||||
|
28
src-ui/src/app/pipes/correspondent-name.pipe.spec.ts
Normal file
28
src-ui/src/app/pipes/correspondent-name.pipe.spec.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import { TestBed } from '@angular/core/testing'
|
||||
import { PermissionsService } from '../services/permissions.service'
|
||||
import { CorrespondentService } from '../services/rest/correspondent.service'
|
||||
import { CorrespondentNamePipe } from './correspondent-name.pipe'
|
||||
|
||||
describe('CorrespondentNamePipe', () => {
|
||||
let pipe: CorrespondentNamePipe
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
// The pipe is a simple wrapper around ObjectNamePipe, see ObjectNamePipe for the actual tests.
|
||||
it('should be created', () => {
|
||||
pipe = new CorrespondentNamePipe(
|
||||
TestBed.inject(PermissionsService),
|
||||
TestBed.inject(CorrespondentService)
|
||||
)
|
||||
expect(pipe).toBeTruthy()
|
||||
})
|
||||
})
|
22
src-ui/src/app/pipes/correspondent-name.pipe.ts
Normal file
22
src-ui/src/app/pipes/correspondent-name.pipe.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import {
|
||||
PermissionsService,
|
||||
PermissionType,
|
||||
} from '../services/permissions.service'
|
||||
import { CorrespondentService } from '../services/rest/correspondent.service'
|
||||
import { ObjectNamePipe } from './object-name.pipe'
|
||||
|
||||
@Pipe({
|
||||
name: 'correspondentName',
|
||||
})
|
||||
export class CorrespondentNamePipe
|
||||
extends ObjectNamePipe
|
||||
implements PipeTransform
|
||||
{
|
||||
constructor(
|
||||
permissionsService: PermissionsService,
|
||||
objectService: CorrespondentService
|
||||
) {
|
||||
super(permissionsService, PermissionType.Correspondent, objectService)
|
||||
}
|
||||
}
|
28
src-ui/src/app/pipes/document-type-name.pipe.spec.ts
Normal file
28
src-ui/src/app/pipes/document-type-name.pipe.spec.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import { TestBed } from '@angular/core/testing'
|
||||
import { PermissionsService } from '../services/permissions.service'
|
||||
import { DocumentTypeService } from '../services/rest/document-type.service'
|
||||
import { DocumentTypeNamePipe } from './document-type-name.pipe'
|
||||
|
||||
describe('DocumentTypeNamePipe', () => {
|
||||
let pipe: DocumentTypeNamePipe
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
// The pipe is a simple wrapper around ObjectNamePipe, see ObjectNamePipe for the actual tests.
|
||||
it('should be created', () => {
|
||||
pipe = new DocumentTypeNamePipe(
|
||||
TestBed.inject(PermissionsService),
|
||||
TestBed.inject(DocumentTypeService)
|
||||
)
|
||||
expect(pipe).toBeTruthy()
|
||||
})
|
||||
})
|
22
src-ui/src/app/pipes/document-type-name.pipe.ts
Normal file
22
src-ui/src/app/pipes/document-type-name.pipe.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import {
|
||||
PermissionsService,
|
||||
PermissionType,
|
||||
} from '../services/permissions.service'
|
||||
import { DocumentTypeService } from '../services/rest/document-type.service'
|
||||
import { ObjectNamePipe } from './object-name.pipe'
|
||||
|
||||
@Pipe({
|
||||
name: 'documentTypeName',
|
||||
})
|
||||
export class DocumentTypeNamePipe
|
||||
extends ObjectNamePipe
|
||||
implements PipeTransform
|
||||
{
|
||||
constructor(
|
||||
permissionsService: PermissionsService,
|
||||
objectService: DocumentTypeService
|
||||
) {
|
||||
super(permissionsService, PermissionType.DocumentType, objectService)
|
||||
}
|
||||
}
|
88
src-ui/src/app/pipes/object-name.pipe.spec.ts
Normal file
88
src-ui/src/app/pipes/object-name.pipe.spec.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import { TestBed } from '@angular/core/testing'
|
||||
import { of, throwError } from 'rxjs'
|
||||
import { MatchingModel } from '../data/matching-model'
|
||||
import { PermissionsService } from '../services/permissions.service'
|
||||
import { AbstractNameFilterService } from '../services/rest/abstract-name-filter-service'
|
||||
import { CorrespondentService } from '../services/rest/correspondent.service'
|
||||
import { CorrespondentNamePipe } from './correspondent-name.pipe'
|
||||
import { ObjectNamePipe } from './object-name.pipe'
|
||||
|
||||
describe('ObjectNamePipe', () => {
|
||||
/*
|
||||
ObjectNamePipe is an abstract class to prevent instantiation,
|
||||
so we test the concrete implementation CorrespondentNamePipe instead.
|
||||
*/
|
||||
let pipe: CorrespondentNamePipe
|
||||
let permissionsService: PermissionsService
|
||||
let objectService: AbstractNameFilterService<MatchingModel>
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ObjectNamePipe,
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
})
|
||||
|
||||
permissionsService = TestBed.inject(PermissionsService)
|
||||
objectService = TestBed.inject(CorrespondentService)
|
||||
pipe = new CorrespondentNamePipe(permissionsService, objectService)
|
||||
})
|
||||
|
||||
it('should return object name if user has permission', (done) => {
|
||||
const mockObjects = {
|
||||
results: [
|
||||
{ id: 1, name: 'Object 1' },
|
||||
{ id: 2, name: 'Object 2' },
|
||||
],
|
||||
count: 2,
|
||||
all: [1, 2],
|
||||
}
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
jest.spyOn(objectService, 'listAll').mockReturnValue(of(mockObjects))
|
||||
|
||||
pipe.transform(1).subscribe((result) => {
|
||||
expect(result).toBe('Object 1')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should return empty string if object not found', (done) => {
|
||||
const mockObjects = {
|
||||
results: [{ id: 2, name: 'Object 2' }],
|
||||
count: 1,
|
||||
all: [2],
|
||||
}
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
jest.spyOn(objectService, 'listAll').mockReturnValue(of(mockObjects))
|
||||
|
||||
pipe.transform(1).subscribe((result) => {
|
||||
expect(result).toBe('')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should return "Private" if user does not have permission', (done) => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(false)
|
||||
|
||||
pipe.transform(1).subscribe((result) => {
|
||||
expect(result).toBe('Private')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle error and return empty string', (done) => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
jest
|
||||
.spyOn(objectService, 'listAll')
|
||||
.mockReturnValueOnce(throwError(() => new Error('Error getting objects')))
|
||||
|
||||
pipe.transform(1).subscribe((result) => {
|
||||
expect(result).toBe('')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
46
src-ui/src/app/pipes/object-name.pipe.ts
Normal file
46
src-ui/src/app/pipes/object-name.pipe.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { catchError, map, Observable, of } from 'rxjs'
|
||||
import { MatchingModel } from '../data/matching-model'
|
||||
import {
|
||||
PermissionAction,
|
||||
PermissionsService,
|
||||
PermissionType,
|
||||
} from '../services/permissions.service'
|
||||
import { AbstractNameFilterService } from '../services/rest/abstract-name-filter-service'
|
||||
|
||||
@Pipe({
|
||||
name: 'objectName',
|
||||
})
|
||||
export abstract class ObjectNamePipe implements PipeTransform {
|
||||
/*
|
||||
ObjectNamePipe is an abstract class to prevent instantiation,
|
||||
object-specific pipes extend this class and provide the
|
||||
correct permission type, and object service.
|
||||
*/
|
||||
protected objects: MatchingModel[]
|
||||
|
||||
constructor(
|
||||
protected permissionsService: PermissionsService,
|
||||
protected permissionType: PermissionType,
|
||||
protected objectService: AbstractNameFilterService<MatchingModel>
|
||||
) {}
|
||||
|
||||
transform(obejctId: number): Observable<string> {
|
||||
if (
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
this.permissionType
|
||||
)
|
||||
) {
|
||||
return this.objectService.listAll().pipe(
|
||||
map((objects) => {
|
||||
this.objects = objects.results
|
||||
return this.objects.find((o) => o.id === obejctId)?.name || ''
|
||||
}),
|
||||
catchError(() => of(''))
|
||||
)
|
||||
} else {
|
||||
return of($localize`Private`)
|
||||
}
|
||||
}
|
||||
}
|
28
src-ui/src/app/pipes/storage-path-name.pipe.spec.ts
Normal file
28
src-ui/src/app/pipes/storage-path-name.pipe.spec.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||
import { TestBed } from '@angular/core/testing'
|
||||
import { PermissionsService } from '../services/permissions.service'
|
||||
import { StoragePathService } from '../services/rest/storage-path.service'
|
||||
import { StoragePathNamePipe } from './storage-path-name.pipe'
|
||||
|
||||
describe('StoragePathNamePipe', () => {
|
||||
let pipe: StoragePathNamePipe
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
// The pipe is a simple wrapper around ObjectNamePipe, see ObjectNamePipe for the actual tests.
|
||||
it('should be created', () => {
|
||||
pipe = new StoragePathNamePipe(
|
||||
TestBed.inject(PermissionsService),
|
||||
TestBed.inject(StoragePathService)
|
||||
)
|
||||
expect(pipe).toBeTruthy()
|
||||
})
|
||||
})
|
22
src-ui/src/app/pipes/storage-path-name.pipe.ts
Normal file
22
src-ui/src/app/pipes/storage-path-name.pipe.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import {
|
||||
PermissionsService,
|
||||
PermissionType,
|
||||
} from '../services/permissions.service'
|
||||
import { StoragePathService } from '../services/rest/storage-path.service'
|
||||
import { ObjectNamePipe } from './object-name.pipe'
|
||||
|
||||
@Pipe({
|
||||
name: 'storagePathName',
|
||||
})
|
||||
export class StoragePathNamePipe
|
||||
extends ObjectNamePipe
|
||||
implements PipeTransform
|
||||
{
|
||||
constructor(
|
||||
permissionsService: PermissionsService,
|
||||
objectService: StoragePathService
|
||||
) {
|
||||
super(permissionsService, PermissionType.StoragePath, objectService)
|
||||
}
|
||||
}
|
@@ -37,7 +37,11 @@ describe('UsernamePipe', () => {
|
||||
httpTestingController.verify()
|
||||
})
|
||||
|
||||
it('should transform user id to username', () => {
|
||||
it('should transform user id to username', (done) => {
|
||||
pipe.transform(2).subscribe((username) => {
|
||||
expect(username).toEqual('username2')
|
||||
})
|
||||
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}users/?page=1&page_size=100000`
|
||||
)
|
||||
@@ -55,24 +59,39 @@ describe('UsernamePipe', () => {
|
||||
},
|
||||
],
|
||||
})
|
||||
pipe.transform(3).subscribe((username) => {
|
||||
expect(username).toEqual('User Name3')
|
||||
})
|
||||
|
||||
let username = pipe.transform(2)
|
||||
expect(username).toEqual('username2')
|
||||
|
||||
username = pipe.transform(3)
|
||||
expect(username).toEqual('User Name3')
|
||||
|
||||
username = pipe.transform(4)
|
||||
expect(username).toEqual('')
|
||||
pipe.transform(4).subscribe((username) => {
|
||||
expect(username).toEqual('')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should show generic label when no users retrieved', () => {
|
||||
it('should show generic label when insufficient permissions', (done) => {
|
||||
jest
|
||||
.spyOn(permissionsService, 'currentUserCan')
|
||||
.mockImplementation((action, type) => {
|
||||
return false
|
||||
})
|
||||
pipe.transform(4).subscribe((username) => {
|
||||
expect(username).toEqual('Shared')
|
||||
done()
|
||||
})
|
||||
httpTestingController.expectNone(
|
||||
`${environment.apiBaseUrl}users/?page=1&page_size=100000`
|
||||
)
|
||||
})
|
||||
|
||||
it('should show empty string when no users retrieved due to error', (done) => {
|
||||
pipe.transform(4).subscribe((username) => {
|
||||
expect(username).toEqual('')
|
||||
done()
|
||||
})
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}users/?page=1&page_size=100000`
|
||||
)
|
||||
req.flush(null)
|
||||
|
||||
let username = pipe.transform(4)
|
||||
expect(username).toEqual('Shared')
|
||||
req.error(new ProgressEvent('error'))
|
||||
})
|
||||
})
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core'
|
||||
import { catchError, map, Observable, of } from 'rxjs'
|
||||
import { User } from '../data/user'
|
||||
import {
|
||||
PermissionAction,
|
||||
PermissionType,
|
||||
PermissionsService,
|
||||
PermissionType,
|
||||
} from '../services/permissions.service'
|
||||
import { UserService } from '../services/rest/user.service'
|
||||
|
||||
@@ -14,25 +15,29 @@ export class UsernamePipe implements PipeTransform {
|
||||
users: User[]
|
||||
|
||||
constructor(
|
||||
permissionsService: PermissionsService,
|
||||
userService: UserService
|
||||
) {
|
||||
private permissionsService: PermissionsService,
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
transform(userID: number): Observable<string> {
|
||||
if (
|
||||
permissionsService.currentUserCan(
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.User
|
||||
)
|
||||
) {
|
||||
userService.listAll().subscribe((r) => (this.users = r.results))
|
||||
return this.userService.listAll().pipe(
|
||||
map((users) => {
|
||||
this.users = users.results
|
||||
return this.getName(this.users.find((u) => u.id === userID))
|
||||
}),
|
||||
catchError(() => of(''))
|
||||
)
|
||||
} else {
|
||||
return of($localize`Shared`)
|
||||
}
|
||||
}
|
||||
|
||||
transform(userID: number): string {
|
||||
return this.users
|
||||
? (this.getName(this.users.find((u) => u.id === userID)) ?? '')
|
||||
: $localize`Shared`
|
||||
}
|
||||
|
||||
getName(user: User): string {
|
||||
if (!user) return ''
|
||||
const name = [user.first_name, user.last_name].join(' ')
|
||||
|
@@ -101,13 +101,13 @@ export const commonAbstractPaperlessServiceTests = (endpoint, ServiceClass) => {
|
||||
test('should call appropriate api endpoint for get a few objects', () => {
|
||||
subscription = service.getFew([1, 2, 3]).subscribe()
|
||||
const req = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}${endpoint}/?id__in=1,2,3&ordering=-id`
|
||||
`${environment.apiBaseUrl}${endpoint}/?id__in=1,2,3&ordering=-id&page_size=1000`
|
||||
)
|
||||
expect(req.request.method).toEqual('GET')
|
||||
req.flush([])
|
||||
subscription = service.getFew([4, 5, 6], { foo: 'bar' }).subscribe()
|
||||
const req2 = httpTestingController.expectOne(
|
||||
`${environment.apiBaseUrl}${endpoint}/?id__in=4,5,6&ordering=-id&foo=bar`
|
||||
`${environment.apiBaseUrl}${endpoint}/?id__in=4,5,6&ordering=-id&page_size=1000&foo=bar`
|
||||
)
|
||||
expect(req2.request.method).toEqual('GET')
|
||||
req2.flush([])
|
||||
|
@@ -95,6 +95,7 @@ export abstract class AbstractPaperlessService<T extends ObjectWithId> {
|
||||
let httpParams = new HttpParams()
|
||||
httpParams = httpParams.set('id__in', ids.join(','))
|
||||
httpParams = httpParams.set('ordering', '-id')
|
||||
httpParams = httpParams.set('page_size', 1000)
|
||||
for (let extraParamKey in extraParams) {
|
||||
if (extraParams[extraParamKey] != null) {
|
||||
httpParams = httpParams.set(extraParamKey, extraParams[extraParamKey])
|
||||
|
@@ -251,32 +251,12 @@ describe(`DocumentService`, () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('should add observables to document', () => {
|
||||
subscription = service
|
||||
.listFiltered(1, 25, 'title', false, [])
|
||||
.subscribe((result) => {
|
||||
expect(result.results).toHaveLength(3)
|
||||
const doc = result.results[0]
|
||||
expect(doc.correspondent$).not.toBeNull()
|
||||
expect(doc.document_type$).not.toBeNull()
|
||||
expect(doc.tags$).not.toBeNull()
|
||||
expect(doc.storage_path$).not.toBeNull()
|
||||
})
|
||||
httpTestingController
|
||||
.expectOne(
|
||||
`${environment.apiBaseUrl}${endpoint}/?page=1&page_size=25&ordering=title`
|
||||
)
|
||||
.flush({
|
||||
results: documents,
|
||||
})
|
||||
})
|
||||
|
||||
it('should set search query', () => {
|
||||
const searchQuery = 'hello'
|
||||
service.searchQuery = searchQuery
|
||||
let url = service.getPreviewUrl(documents[0].id)
|
||||
expect(url).toEqual(
|
||||
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/preview/#search="${searchQuery}"`
|
||||
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/preview/#search=%22${searchQuery}%22`
|
||||
)
|
||||
})
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { HttpClient } from '@angular/common/http'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Observable } from 'rxjs'
|
||||
import { map, tap } from 'rxjs/operators'
|
||||
import { map } from 'rxjs/operators'
|
||||
import { AuditLogEntry } from 'src/app/data/auditlog-entry'
|
||||
import { CustomField } from 'src/app/data/custom-field'
|
||||
import {
|
||||
@@ -22,11 +22,7 @@ import {
|
||||
} from '../permissions.service'
|
||||
import { SettingsService } from '../settings.service'
|
||||
import { AbstractPaperlessService } from './abstract-paperless-service'
|
||||
import { CorrespondentService } from './correspondent.service'
|
||||
import { CustomFieldsService } from './custom-fields.service'
|
||||
import { DocumentTypeService } from './document-type.service'
|
||||
import { StoragePathService } from './storage-path.service'
|
||||
import { TagService } from './tag.service'
|
||||
|
||||
export interface SelectionDataItem {
|
||||
id: number
|
||||
@@ -61,10 +57,6 @@ export class DocumentService extends AbstractPaperlessService<Document> {
|
||||
|
||||
constructor(
|
||||
http: HttpClient,
|
||||
private correspondentService: CorrespondentService,
|
||||
private documentTypeService: DocumentTypeService,
|
||||
private tagService: TagService,
|
||||
private storagePathService: StoragePathService,
|
||||
private permissionsService: PermissionsService,
|
||||
private settingsService: SettingsService,
|
||||
private customFieldService: CustomFieldsService
|
||||
@@ -137,54 +129,6 @@ export class DocumentService extends AbstractPaperlessService<Document> {
|
||||
]
|
||||
}
|
||||
|
||||
addObservablesToDocument(doc: Document) {
|
||||
if (
|
||||
doc.correspondent &&
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.Correspondent
|
||||
)
|
||||
) {
|
||||
doc.correspondent$ = this.correspondentService.getCached(
|
||||
doc.correspondent
|
||||
)
|
||||
}
|
||||
if (
|
||||
doc.document_type &&
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.DocumentType
|
||||
)
|
||||
) {
|
||||
doc.document_type$ = this.documentTypeService.getCached(doc.document_type)
|
||||
}
|
||||
if (
|
||||
doc.tags &&
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.Tag
|
||||
)
|
||||
) {
|
||||
doc.tags$ = this.tagService
|
||||
.getCachedMany(doc.tags)
|
||||
.pipe(
|
||||
tap((tags) =>
|
||||
tags.sort((tagA, tagB) => tagA.name.localeCompare(tagB.name))
|
||||
)
|
||||
)
|
||||
}
|
||||
if (
|
||||
doc.storage_path &&
|
||||
this.permissionsService.currentUserCan(
|
||||
PermissionAction.View,
|
||||
PermissionType.StoragePath
|
||||
)
|
||||
) {
|
||||
doc.storage_path$ = this.storagePathService.getCached(doc.storage_path)
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
listFiltered(
|
||||
page?: number,
|
||||
pageSize?: number,
|
||||
@@ -199,11 +143,6 @@ export class DocumentService extends AbstractPaperlessService<Document> {
|
||||
sortField,
|
||||
sortReverse,
|
||||
Object.assign(extraParams, queryParamsFromFilterRules(filterRules))
|
||||
).pipe(
|
||||
map((results) => {
|
||||
results.results.forEach((doc) => this.addObservablesToDocument(doc))
|
||||
return results
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
@@ -222,12 +161,12 @@ export class DocumentService extends AbstractPaperlessService<Document> {
|
||||
}
|
||||
|
||||
getPreviewUrl(id: number, original: boolean = false): string {
|
||||
let url = this.getResourceUrl(id, 'preview')
|
||||
if (this._searchQuery) url += `#search="${this._searchQuery}"`
|
||||
let url = new URL(this.getResourceUrl(id, 'preview'))
|
||||
if (this._searchQuery) url.hash = `#search="${this.searchQuery}"`
|
||||
if (original) {
|
||||
url += '?original=true'
|
||||
url.searchParams.append('original', 'true')
|
||||
}
|
||||
return url
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
getThumbUrl(id: number): string {
|
||||
@@ -309,6 +248,10 @@ export class DocumentService extends AbstractPaperlessService<Document> {
|
||||
}
|
||||
|
||||
public set searchQuery(query: string) {
|
||||
this._searchQuery = query
|
||||
this._searchQuery = query.trim()
|
||||
}
|
||||
|
||||
public get searchQuery(): string {
|
||||
return this._searchQuery
|
||||
}
|
||||
}
|
||||
|
@@ -425,6 +425,12 @@ export class SettingsService {
|
||||
)
|
||||
}
|
||||
|
||||
this._renderer.setAttribute(
|
||||
this.document.documentElement,
|
||||
'data-bs-theme',
|
||||
'dark-flat'
|
||||
)
|
||||
|
||||
if (themeColor?.length) {
|
||||
const hsl = hexToHsl(themeColor)
|
||||
const bgBrightnessEstimate = estimateBrightnessForColor(themeColor)
|
||||
|
@@ -180,6 +180,9 @@ import localeSv from '@angular/common/locales/sv'
|
||||
import localeTr from '@angular/common/locales/tr'
|
||||
import localeUk from '@angular/common/locales/uk'
|
||||
import localeZh from '@angular/common/locales/zh'
|
||||
import { CorrespondentNamePipe } from './app/pipes/correspondent-name.pipe'
|
||||
import { DocumentTypeNamePipe } from './app/pipes/document-type-name.pipe'
|
||||
import { StoragePathNamePipe } from './app/pipes/storage-path-name.pipe'
|
||||
|
||||
registerLocaleData(localeAf)
|
||||
registerLocaleData(localeAr)
|
||||
@@ -375,6 +378,9 @@ bootstrapApplication(AppComponent, {
|
||||
DirtyDocGuard,
|
||||
DirtySavedViewGuard,
|
||||
UsernamePipe,
|
||||
CorrespondentNamePipe,
|
||||
DocumentTypeNamePipe,
|
||||
StoragePathNamePipe,
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
],
|
||||
}).catch((err) => console.error(err))
|
||||
|
@@ -366,3 +366,52 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[data-bs-theme="dark-flat"] {
|
||||
body:not(.primary-light):not(.primary-dark) {
|
||||
@include paperless-green-dark-mode;
|
||||
|
||||
.navbar.bg-primary {
|
||||
// navbar is og green in dark mode
|
||||
@include paperless-green;
|
||||
}
|
||||
}
|
||||
|
||||
@include dark-mode;
|
||||
|
||||
.btn-outline-primary, .btn-outline-secondary {
|
||||
border-color: var(--pngx-bg-alt) !important;
|
||||
background-color: var(--pngx-bg-alt) !important;
|
||||
color: var(--bs-body-color) !important;
|
||||
}
|
||||
|
||||
.btn-outline-secondary:hover, .btn-outline-secondary:focus, .btn-outline-secondary:active, .btn-outline-secondary.active {
|
||||
background-color: var(--pngx-bg-darker) !important;
|
||||
color: var(--pngx-body-color-accent) !important;
|
||||
}
|
||||
|
||||
.btn-outline-danger {
|
||||
border-color: var(--pngx-bg-alt) !important;
|
||||
background-color: var(--pngx-bg-alt) !important;
|
||||
color: var(--bs-danger) !important;
|
||||
|
||||
&:hover, &:focus, &.active, &:active {
|
||||
background-color: var(--pngx-bg-darker) !important;
|
||||
color: var(--bs-danger) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.form-control:not(.btn), input, select, textarea,
|
||||
.form-select:not(.is-invalid):not(:disabled), .form-check-input,
|
||||
.ng-select .ng-select-container {
|
||||
background-color: var(--pngx-bg-darker) !important;
|
||||
color: var(--bs-body-color) !important;
|
||||
border-color: var(--pngx-bg-alt) !important;
|
||||
}
|
||||
|
||||
.input-group .input-group-text {
|
||||
background-color: var(--pngx-bg-alt);
|
||||
color: var(--bs-body-color);
|
||||
border-color: var(--pngx-bg-alt);
|
||||
}
|
||||
}
|
||||
|
@@ -79,7 +79,8 @@ class BogusClient:
|
||||
flag = args[2]
|
||||
if flag == "processed":
|
||||
message._raw_flag_data.append(b"+FLAGS (processed)")
|
||||
MailMessage.flags.fget.cache_clear()
|
||||
if hasattr(message, "flags"):
|
||||
del message.flags
|
||||
|
||||
|
||||
class BogusMailBox(AbstractContextManager):
|
||||
@@ -176,7 +177,8 @@ class BogusMailBox(AbstractContextManager):
|
||||
message.seen = value
|
||||
if flag == "processed":
|
||||
message._raw_flag_data.append(b"+FLAGS (processed)")
|
||||
MailMessage.flags.fget.cache_clear()
|
||||
if hasattr(message, "flags"):
|
||||
del message.flags
|
||||
|
||||
def move(self, uid_list, folder):
|
||||
if folder == "spam":
|
||||
@@ -263,7 +265,8 @@ class MessageBuilder:
|
||||
imap_msg.flagged = flagged
|
||||
if processed:
|
||||
imap_msg._raw_flag_data.append(b"+FLAGS (processed)")
|
||||
MailMessage.flags.fget.cache_clear()
|
||||
if hasattr(imap_msg, "flags"):
|
||||
del imap_msg.flags
|
||||
|
||||
return imap_msg
|
||||
|
||||
|
Reference in New Issue
Block a user