mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-08-05 18:58:34 -05:00
Compare commits
14 Commits
chore/manu
...
more-info-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1b6a9d3816 | ||
![]() |
07c298523a | ||
![]() |
0ea159683d | ||
![]() |
f0b6e79d14 | ||
![]() |
302cb22ec6 | ||
![]() |
4210addb46 | ||
![]() |
06746b4b31 | ||
![]() |
2f5533a179 | ||
![]() |
2f267341f8 | ||
![]() |
88befee527 | ||
![]() |
c4a7186cd2 | ||
![]() |
d974f092aa | ||
![]() |
23501b9060 | ||
![]() |
f09965464a |
@@ -159,6 +159,23 @@ Available options are `postgresql` and `mariadb`.
|
||||
|
||||
Defaults to unset, which uses Django’s built-in defaults.
|
||||
|
||||
#### [`PAPERLESS_DB_POOLSIZE=<int>`](#PAPERLESS_DB_POOLSIZE) {#PAPERLESS_DB_POOLSIZE}
|
||||
|
||||
: Defines the maximum number of database connections to keep in the pool.
|
||||
|
||||
Only applies to PostgreSQL. This setting is ignored for other database engines.
|
||||
|
||||
The value must be greater than or equal to 1 to be used.
|
||||
Defaults to unset, which disables connection pooling.
|
||||
|
||||
!!! note
|
||||
|
||||
A small pool is typically sufficient — for example, a size of 4.
|
||||
Make sure your PostgreSQL server's max_connections setting is large enough to handle:
|
||||
```(Paperless workers + Celery workers) × pool size + safety margin```
|
||||
For example, with 4 Paperless workers and 2 Celery workers, and a pool size of 4:
|
||||
(4 + 2) × 4 + 10 = 34 connections required.
|
||||
|
||||
#### [`PAPERLESS_DB_READ_CACHE_ENABLED=<bool>`](#PAPERLESS_DB_READ_CACHE_ENABLED) {#PAPERLESS_DB_READ_CACHE_ENABLED}
|
||||
|
||||
: Caches the database read query results into Redis. This can significantly improve application response times by caching database queries, at the cost of slightly increased memory usage.
|
||||
|
@@ -30,6 +30,9 @@ Each document has data fields that you can assign to them:
|
||||
- A _document type_ is used to demarcate the type of a document such
|
||||
as letter, bank statement, invoice, contract, etc. It is used to
|
||||
identify what a document is about.
|
||||
- The document _storage path_ is the location where the document files
|
||||
are stored. See [Storage Paths](advanced_usage.md#storage-paths) for
|
||||
more information.
|
||||
- The _date added_ of a document is the date the document was scanned
|
||||
into paperless. You cannot and should not change this date.
|
||||
- The _date created_ of a document is the date the document was
|
||||
|
@@ -52,6 +52,7 @@ dependencies = [
|
||||
"ocrmypdf~=16.10.0",
|
||||
"pathvalidate~=3.3.1",
|
||||
"pdf2image~=1.17.0",
|
||||
"psycopg-pool",
|
||||
"python-dateutil~=2.9.0",
|
||||
"python-dotenv~=1.1.0",
|
||||
"python-gnupg~=0.5.4",
|
||||
@@ -74,9 +75,10 @@ optional-dependencies.mariadb = [
|
||||
"mysqlclient~=2.2.7",
|
||||
]
|
||||
optional-dependencies.postgres = [
|
||||
"psycopg[c]==3.2.9",
|
||||
"psycopg[c,pool]==3.2.9",
|
||||
# Direct dependency for proper resolution of the pre-built wheels
|
||||
"psycopg-c==3.2.9",
|
||||
"psycopg-pool==3.2.6",
|
||||
]
|
||||
optional-dependencies.webserver = [
|
||||
"granian[uvloop]~=2.4.1",
|
||||
@@ -239,6 +241,7 @@ testpaths = [
|
||||
"src/paperless_mail/tests/",
|
||||
"src/paperless_tesseract/tests/",
|
||||
"src/paperless_tika/tests",
|
||||
"src/paperless_text/tests/",
|
||||
]
|
||||
addopts = [
|
||||
"--pythonwarnings=all",
|
||||
|
@@ -5,14 +5,14 @@
|
||||
<trans-unit id="ngb.alert.close" datatype="html">
|
||||
<source>Close</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/alert/alert.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/alert/alert.ts</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.carousel.slide-number" datatype="html">
|
||||
<source> Slide <x id="INTERPOLATION" equiv-text="ueryList<NgbSli"/> of <x id="INTERPOLATION_1" equiv-text="EventSource = N"/> </source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="linenumber">131,135</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">Currently selected slide number read by screen reader</note>
|
||||
@@ -20,212 +20,212 @@
|
||||
<trans-unit id="ngb.carousel.previous" datatype="html">
|
||||
<source>Previous</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="linenumber">157,159</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.carousel.next" datatype="html">
|
||||
<source>Next</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="linenumber">198</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.datepicker.previous-month" datatype="html">
|
||||
<source>Previous month</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="linenumber">83,85</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="linenumber">112</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.datepicker.next-month" datatype="html">
|
||||
<source>Next month</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="linenumber">112</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="linenumber">112</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.HH" datatype="html">
|
||||
<source>HH</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.toast.close-aria" datatype="html">
|
||||
<source>Close</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.datepicker.select-month" datatype="html">
|
||||
<source>Select month</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.pagination.first" datatype="html">
|
||||
<source>««</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.hours" datatype="html">
|
||||
<source>Hours</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.pagination.previous" datatype="html">
|
||||
<source>«</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.MM" datatype="html">
|
||||
<source>MM</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.pagination.next" datatype="html">
|
||||
<source>»</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.datepicker.select-year" datatype="html">
|
||||
<source>Select year</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.minutes" datatype="html">
|
||||
<source>Minutes</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.pagination.last" datatype="html">
|
||||
<source>»»</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.pagination.first-aria" datatype="html">
|
||||
<source>First</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.increment-hours" datatype="html">
|
||||
<source>Increment hours</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.pagination.previous-aria" datatype="html">
|
||||
<source>Previous</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.decrement-hours" datatype="html">
|
||||
<source>Decrement hours</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.pagination.next-aria" datatype="html">
|
||||
<source>Next</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.increment-minutes" datatype="html">
|
||||
<source>Increment minutes</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.pagination.last-aria" datatype="html">
|
||||
<source>Last</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.decrement-minutes" datatype="html">
|
||||
<source>Decrement minutes</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.SS" datatype="html">
|
||||
<source>SS</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.seconds" datatype="html">
|
||||
<source>Seconds</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.increment-seconds" datatype="html">
|
||||
<source>Increment seconds</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.decrement-seconds" datatype="html">
|
||||
<source>Decrement seconds</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ngb.timepicker.PM" datatype="html">
|
||||
<source><x id="INTERPOLATION"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
@@ -233,7 +233,7 @@
|
||||
<source><x id="INTERPOLATION" equiv-text="barConfig);
|
||||
pu"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.0.6_@angular+core@20.0.6_@angular+_05316f125479eb303e49e3702b630d0f/node_modules/src/progressbar/progressbar.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.1.4_@angular+core@20.1.4_@angular+_4264661dcfc97b5bf5cf26958990f623/node_modules/src/progressbar/progressbar.ts</context>
|
||||
<context context-type="linenumber">41,42</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
@@ -1507,64 +1507,64 @@
|
||||
<source>Use system language</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">77</context>
|
||||
<context context-type="linenumber">78</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7729897675462249787" datatype="html">
|
||||
<source>Use date format of display language</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">80</context>
|
||||
<context context-type="linenumber">81</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1235706724900303689" datatype="html">
|
||||
<source>Error retrieving users</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">224</context>
|
||||
<context context-type="linenumber">225</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
<context context-type="linenumber">56</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3066660568529853846" datatype="html">
|
||||
<source>Error retrieving groups</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">243</context>
|
||||
<context context-type="linenumber">244</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
<context context-type="linenumber">68</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7217000812750597833" datatype="html">
|
||||
<source>Settings were saved successfully.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">546</context>
|
||||
<context context-type="linenumber">547</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="525012668859298131" datatype="html">
|
||||
<source>Settings were saved successfully. Reload is required to apply some changes.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">550</context>
|
||||
<context context-type="linenumber">551</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8491974984518503778" datatype="html">
|
||||
<source>Reload now</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">551</context>
|
||||
<context context-type="linenumber">552</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3011185103048412841" datatype="html">
|
||||
<source>An error occurred while saving settings.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">561</context>
|
||||
<context context-type="linenumber">562</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
|
||||
@@ -2229,11 +2229,11 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">120</context>
|
||||
<context context-type="linenumber">123</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">173</context>
|
||||
<context context-type="linenumber">176</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/custom-fields/custom-fields.component.ts</context>
|
||||
@@ -2497,50 +2497,50 @@
|
||||
<source>Password has been changed, you will be logged out momentarily.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">93</context>
|
||||
<context context-type="linenumber">94</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">194</context>
|
||||
<context context-type="linenumber">195</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2753185112875184719" datatype="html">
|
||||
<source>Saved user "<x id="PH" equiv-text="newUser.username"/>".</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">100</context>
|
||||
<context context-type="linenumber">103</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3471101514724661554" datatype="html">
|
||||
<source>Error saving user.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">110</context>
|
||||
<context context-type="linenumber">113</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5565868288871970148" datatype="html">
|
||||
<source>Confirm delete user account</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">118</context>
|
||||
<context context-type="linenumber">121</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8133663925694885325" datatype="html">
|
||||
<source>This operation will permanently delete this user account.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">119</context>
|
||||
<context context-type="linenumber">122</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1181910457994920507" datatype="html">
|
||||
<source>Proceed</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">122</context>
|
||||
<context context-type="linenumber">125</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">175</context>
|
||||
<context context-type="linenumber">178</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
@@ -2595,56 +2595,56 @@
|
||||
<source>Deleted user "<x id="PH" equiv-text="user.username"/>"</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">128</context>
|
||||
<context context-type="linenumber">131</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="286457042048584728" datatype="html">
|
||||
<source>Error deleting user "<x id="PH" equiv-text="user.username"/>".</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">135</context>
|
||||
<context context-type="linenumber">138</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5766640174051730159" datatype="html">
|
||||
<source>Saved group "<x id="PH" equiv-text="newGroup.name"/>".</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">155</context>
|
||||
<context context-type="linenumber">158</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8382042988405122578" datatype="html">
|
||||
<source>Error saving group.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">163</context>
|
||||
<context context-type="linenumber">166</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6538873300613683004" datatype="html">
|
||||
<source>Confirm delete user group</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">171</context>
|
||||
<context context-type="linenumber">174</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7710984639498518244" datatype="html">
|
||||
<source>This operation will permanently delete this user group.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">172</context>
|
||||
<context context-type="linenumber">175</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3756187211130340490" datatype="html">
|
||||
<source>Deleted group "<x id="PH" equiv-text="group.name"/>"</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">181</context>
|
||||
<context context-type="linenumber">184</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1697803415975901060" datatype="html">
|
||||
<source>Error deleting group "<x id="PH" equiv-text="group.name"/>".</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/admin/users-groups/users-groups.component.ts</context>
|
||||
<context context-type="linenumber">188</context>
|
||||
<context context-type="linenumber">191</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7931334600001636863" datatype="html">
|
||||
@@ -5828,85 +5828,85 @@
|
||||
<source>Emails must match</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">142</context>
|
||||
<context context-type="linenumber">143</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5281933990298241826" datatype="html">
|
||||
<source>Passwords must match</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">170</context>
|
||||
<context context-type="linenumber">171</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4219429959475101385" datatype="html">
|
||||
<source>Profile updated successfully</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">191</context>
|
||||
<context context-type="linenumber">192</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3417726855410304962" datatype="html">
|
||||
<source>Error saving profile</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">203</context>
|
||||
<context context-type="linenumber">206</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="154249228726292516" datatype="html">
|
||||
<source>Error generating auth token</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">220</context>
|
||||
<context context-type="linenumber">223</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4153637646944982460" datatype="html">
|
||||
<source>Error disconnecting social account</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">245</context>
|
||||
<context context-type="linenumber">248</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5939111172212776886" datatype="html">
|
||||
<source>Error fetching TOTP settings</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">264</context>
|
||||
<context context-type="linenumber">267</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1030314492414713260" datatype="html">
|
||||
<source>TOTP activated successfully</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">285</context>
|
||||
<context context-type="linenumber">288</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3755006064892435830" datatype="html">
|
||||
<source>Error activating TOTP</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">287</context>
|
||||
<context context-type="linenumber">290</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">293</context>
|
||||
<context context-type="linenumber">296</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5919827473541889422" datatype="html">
|
||||
<source>TOTP deactivated successfully</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">309</context>
|
||||
<context context-type="linenumber">312</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6214722303383624015" datatype="html">
|
||||
<source>Error deactivating TOTP</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">311</context>
|
||||
<context context-type="linenumber">314</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">316</context>
|
||||
<context context-type="linenumber">319</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6617773613987957957" datatype="html">
|
||||
|
@@ -11,17 +11,17 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/cdk": "^20.0.4",
|
||||
"@angular/common": "~20.0.6",
|
||||
"@angular/compiler": "~20.0.6",
|
||||
"@angular/core": "~20.0.6",
|
||||
"@angular/forms": "~20.0.6",
|
||||
"@angular/localize": "~20.0.6",
|
||||
"@angular/platform-browser": "~20.0.6",
|
||||
"@angular/platform-browser-dynamic": "~20.0.6",
|
||||
"@angular/router": "~20.0.6",
|
||||
"@angular/cdk": "^20.1.4",
|
||||
"@angular/common": "~20.1.4",
|
||||
"@angular/compiler": "~20.1.4",
|
||||
"@angular/core": "~20.1.4",
|
||||
"@angular/forms": "~20.1.4",
|
||||
"@angular/localize": "~20.1.4",
|
||||
"@angular/platform-browser": "~20.1.4",
|
||||
"@angular/platform-browser-dynamic": "~20.1.4",
|
||||
"@angular/router": "~20.1.4",
|
||||
"@ng-bootstrap/ng-bootstrap": "^19.0.1",
|
||||
"@ng-select/ng-select": "^15.1.3",
|
||||
"@ng-select/ng-select": "^20.0.1",
|
||||
"@ngneat/dirty-check-forms": "^3.0.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"bootstrap": "^5.3.7",
|
||||
@@ -32,7 +32,7 @@
|
||||
"ngx-color": "^10.0.0",
|
||||
"ngx-cookie-service": "^20.0.1",
|
||||
"ngx-device-detector": "^10.0.2",
|
||||
"ngx-ui-tour-ng-bootstrap": "^17.0.0",
|
||||
"ngx-ui-tour-ng-bootstrap": "^17.0.1",
|
||||
"rxjs": "^7.8.2",
|
||||
"tslib": "^2.8.1",
|
||||
"utif": "^3.1.0",
|
||||
@@ -42,33 +42,33 @@
|
||||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "^20.0.0",
|
||||
"@angular-builders/jest": "^20.0.0",
|
||||
"@angular-devkit/core": "^20.0.4",
|
||||
"@angular-devkit/schematics": "^20.0.4",
|
||||
"@angular-devkit/core": "^20.1.4",
|
||||
"@angular-devkit/schematics": "^20.1.4",
|
||||
"@angular-eslint/builder": "20.1.1",
|
||||
"@angular-eslint/eslint-plugin": "20.1.1",
|
||||
"@angular-eslint/eslint-plugin-template": "20.1.1",
|
||||
"@angular-eslint/schematics": "20.1.1",
|
||||
"@angular-eslint/template-parser": "20.1.1",
|
||||
"@angular/build": "^20.0.4",
|
||||
"@angular/cli": "~20.0.4",
|
||||
"@angular/compiler-cli": "~20.0.6",
|
||||
"@angular/build": "^20.1.4",
|
||||
"@angular/cli": "~20.1.4",
|
||||
"@angular/compiler-cli": "~20.1.4",
|
||||
"@codecov/webpack-plugin": "^1.9.1",
|
||||
"@playwright/test": "^1.53.2",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^24.0.10",
|
||||
"@typescript-eslint/eslint-plugin": "^8.35.1",
|
||||
"@typescript-eslint/parser": "^8.35.1",
|
||||
"@typescript-eslint/utils": "^8.35.1",
|
||||
"eslint": "^9.30.1",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"@playwright/test": "^1.54.2",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^24.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
||||
"@typescript-eslint/parser": "^8.38.0",
|
||||
"@typescript-eslint/utils": "^8.38.0",
|
||||
"eslint": "^9.32.0",
|
||||
"jest": "30.0.5",
|
||||
"jest-environment-jsdom": "^30.0.5",
|
||||
"jest-junit": "^16.0.0",
|
||||
"jest-preset-angular": "^14.5.5",
|
||||
"jest-preset-angular": "^15.0.0",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-organize-imports": "^4.2.0",
|
||||
"ts-node": "~10.9.1",
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.99.9"
|
||||
"webpack": "^5.101.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
|
5202
src-ui/pnpm-lock.yaml
generated
5202
src-ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,16 @@
|
||||
import '@angular/localize/init'
|
||||
import { jest } from '@jest/globals'
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'
|
||||
import { TextDecoder, TextEncoder } from 'util'
|
||||
import { TextDecoder, TextEncoder } from 'node:util'
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
setupZoneTestEnv()
|
||||
}
|
||||
global.TextEncoder = TextEncoder
|
||||
global.TextDecoder = TextDecoder
|
||||
;(globalThis as any).TextEncoder = TextEncoder as unknown as {
|
||||
new (): TextEncoder
|
||||
}
|
||||
;(globalThis as any).TextDecoder = TextDecoder as unknown as {
|
||||
new (): TextDecoder
|
||||
}
|
||||
|
||||
import { registerLocaleData } from '@angular/common'
|
||||
import localeAf from '@angular/common/locales/af'
|
||||
@@ -116,10 +120,6 @@ if (!URL.revokeObjectURL) {
|
||||
Object.defineProperty(window.URL, 'revokeObjectURL', { value: jest.fn() })
|
||||
}
|
||||
Object.defineProperty(window, 'ResizeObserver', { value: mock() })
|
||||
Object.defineProperty(window, 'location', {
|
||||
configurable: true,
|
||||
value: { reload: jest.fn() },
|
||||
})
|
||||
|
||||
HTMLCanvasElement.prototype.getContext = <
|
||||
typeof HTMLCanvasElement.prototype.getContext
|
||||
|
@@ -36,6 +36,7 @@ import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||
import { Toast, ToastService } from 'src/app/services/toast.service'
|
||||
import * as navUtils from 'src/app/utils/navigation'
|
||||
import { ConfirmButtonComponent } from '../../common/confirm-button/confirm-button.component'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
import { CheckComponent } from '../../common/input/check/check.component'
|
||||
@@ -225,6 +226,9 @@ describe('SettingsComponent', () => {
|
||||
})
|
||||
|
||||
it('should offer reload if settings changes require', () => {
|
||||
const reloadSpy = jest
|
||||
.spyOn(navUtils, 'locationReload')
|
||||
.mockImplementation(() => {})
|
||||
completeSetup()
|
||||
let toast: Toast
|
||||
toastService.getToasts().subscribe((t) => (toast = t[0]))
|
||||
@@ -241,6 +245,7 @@ describe('SettingsComponent', () => {
|
||||
|
||||
expect(toast.actionName).toEqual('Reload now')
|
||||
toast.action()
|
||||
expect(reloadSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should allow setting theme color, visually apply change immediately but not save', () => {
|
||||
@@ -269,7 +274,7 @@ describe('SettingsComponent', () => {
|
||||
)
|
||||
completeSetup(userService)
|
||||
fixture.detectChanges()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should show errors on load if load groups failure', () => {
|
||||
@@ -281,7 +286,7 @@ describe('SettingsComponent', () => {
|
||||
)
|
||||
completeSetup(groupService)
|
||||
fixture.detectChanges()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should load system status on initialize, show errors if needed', () => {
|
||||
|
@@ -57,6 +57,7 @@ import {
|
||||
} from 'src/app/services/settings.service'
|
||||
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||
import { Toast, ToastService } from 'src/app/services/toast.service'
|
||||
import { locationReload } from 'src/app/utils/navigation'
|
||||
import { CheckComponent } from '../../common/input/check/check.component'
|
||||
import { ColorComponent } from '../../common/input/color/color.component'
|
||||
import { PermissionsGroupComponent } from '../../common/input/permissions/permissions-group/permissions-group.component'
|
||||
@@ -550,7 +551,7 @@ export class SettingsComponent
|
||||
savedToast.content = $localize`Settings were saved successfully. Reload is required to apply some changes.`
|
||||
savedToast.actionName = $localize`Reload now`
|
||||
savedToast.action = () => {
|
||||
location.reload()
|
||||
locationReload()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ import { GroupService } from 'src/app/services/rest/group.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import * as navUtils from 'src/app/utils/navigation'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
|
||||
import { UserEditDialogComponent } from '../../common/edit-dialog/user-edit-dialog/user-edit-dialog.component'
|
||||
@@ -107,7 +108,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||
editDialog.failed.emit()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
settingsService.currentUser = users[1] // simulate logged in as different user
|
||||
editDialog.succeeded.emit(users[0])
|
||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||
@@ -130,7 +131,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
throwError(() => new Error('error deleting user'))
|
||||
)
|
||||
deleteDialog.confirm()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
deleteSpy.mockReturnValueOnce(of(true))
|
||||
deleteDialog.confirm()
|
||||
expect(listAllSpy).toHaveBeenCalled()
|
||||
@@ -142,19 +143,18 @@ describe('UsersAndGroupsComponent', () => {
|
||||
let modal: NgbModalRef
|
||||
modalService.activeInstances.subscribe((refs) => (modal = refs[0]))
|
||||
component.editUser(users[0])
|
||||
const navSpy = jest
|
||||
.spyOn(navUtils, 'setLocationHref')
|
||||
.mockImplementation(() => {})
|
||||
const editDialog = modal.componentInstance as UserEditDialogComponent
|
||||
editDialog.passwordIsSet = true
|
||||
settingsService.currentUser = users[0] // simulate logged in as same user
|
||||
editDialog.succeeded.emit(users[0])
|
||||
fixture.detectChanges()
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
href: 'http://localhost/',
|
||||
},
|
||||
writable: true, // possibility to override
|
||||
})
|
||||
tick(2600)
|
||||
expect(window.location.href).toContain('logout')
|
||||
expect(navSpy).toHaveBeenCalledWith(
|
||||
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
)
|
||||
}))
|
||||
|
||||
it('should support edit / create group, show error if needed', () => {
|
||||
@@ -166,7 +166,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||
editDialog.failed.emit()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
editDialog.succeeded.emit(groups[0])
|
||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||
`Saved group "${groups[0].name}".`
|
||||
@@ -188,7 +188,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
throwError(() => new Error('error deleting group'))
|
||||
)
|
||||
deleteDialog.confirm()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
deleteSpy.mockReturnValueOnce(of(true))
|
||||
deleteDialog.confirm()
|
||||
expect(listAllSpy).toHaveBeenCalled()
|
||||
@@ -210,7 +210,7 @@ describe('UsersAndGroupsComponent', () => {
|
||||
)
|
||||
completeSetup(userService)
|
||||
fixture.detectChanges()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should show errors on load if load groups failure', () => {
|
||||
@@ -222,6 +222,6 @@ describe('UsersAndGroupsComponent', () => {
|
||||
)
|
||||
completeSetup(groupService)
|
||||
fixture.detectChanges()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
@@ -10,6 +10,7 @@ import { GroupService } from 'src/app/services/rest/group.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { setLocationHref } from 'src/app/utils/navigation'
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component'
|
||||
import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component'
|
||||
import { GroupEditDialogComponent } from '../../common/edit-dialog/group-edit-dialog/group-edit-dialog.component'
|
||||
@@ -93,7 +94,9 @@ export class UsersAndGroupsComponent
|
||||
$localize`Password has been changed, you will be logged out momentarily.`
|
||||
)
|
||||
setTimeout(() => {
|
||||
window.location.href = `${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
setLocationHref(
|
||||
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
)
|
||||
}, 2500)
|
||||
} else {
|
||||
this.toastService.showInfo(
|
||||
|
@@ -30,7 +30,7 @@
|
||||
}
|
||||
<div class="list-group-item">
|
||||
<div class="input-group input-group-sm">
|
||||
<input class="form-control" type="text" [(ngModel)]="filterText" [placeholder]="filterPlaceholder" (keyup.enter)="listFilterEnter()" #listFilterTextInput>
|
||||
<input class="form-control" type="text" spellcheck="false" [(ngModel)]="filterText" [placeholder]="filterPlaceholder" (keyup.enter)="listFilterEnter()" #listFilterTextInput>
|
||||
</div>
|
||||
</div>
|
||||
@if (selectionModel.items) {
|
||||
|
@@ -18,6 +18,7 @@ import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
|
||||
import { of, throwError } from 'rxjs'
|
||||
import { ProfileService } from 'src/app/services/profile.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import * as navUtils from 'src/app/utils/navigation'
|
||||
import { ConfirmButtonComponent } from '../confirm-button/confirm-button.component'
|
||||
import { PasswordComponent } from '../input/password/password.component'
|
||||
import { TextComponent } from '../input/text/text.component'
|
||||
@@ -205,16 +206,15 @@ describe('ProfileEditDialogComponent', () => {
|
||||
|
||||
const updateSpy = jest.spyOn(profileService, 'update')
|
||||
updateSpy.mockReturnValue(of(null))
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
href: 'http://localhost/',
|
||||
},
|
||||
writable: true, // possibility to override
|
||||
})
|
||||
const navSpy = jest
|
||||
.spyOn(navUtils, 'setLocationHref')
|
||||
.mockImplementation(() => {})
|
||||
component.save()
|
||||
expect(updateSpy).toHaveBeenCalled()
|
||||
tick(2600)
|
||||
expect(window.location.href).toContain('logout')
|
||||
expect(navSpy).toHaveBeenCalledWith(
|
||||
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
)
|
||||
}))
|
||||
|
||||
it('should support auth token copy', fakeAsync(() => {
|
||||
|
@@ -21,6 +21,7 @@ import {
|
||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||
import { ProfileService } from 'src/app/services/profile.service'
|
||||
import { ToastService } from 'src/app/services/toast.service'
|
||||
import { setLocationHref } from 'src/app/utils/navigation'
|
||||
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
|
||||
import { ConfirmButtonComponent } from '../confirm-button/confirm-button.component'
|
||||
import { PasswordComponent } from '../input/password/password.component'
|
||||
@@ -194,7 +195,9 @@ export class ProfileEditDialogComponent
|
||||
$localize`Password has been changed, you will be logged out momentarily.`
|
||||
)
|
||||
setTimeout(() => {
|
||||
window.location.href = `${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
setLocationHref(
|
||||
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
|
||||
)
|
||||
}, 2500)
|
||||
}
|
||||
this.activeModal.close()
|
||||
|
@@ -188,7 +188,7 @@ describe('MailComponent', () => {
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||
editDialog.failed.emit()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
editDialog.succeeded.emit(mailAccounts[0] as any)
|
||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||
`Saved account "${mailAccounts[0].name}".`
|
||||
@@ -211,7 +211,7 @@ describe('MailComponent', () => {
|
||||
throwError(() => new Error('error deleting mail account'))
|
||||
)
|
||||
deleteDialog.confirm()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
deleteSpy.mockReturnValueOnce(of(true))
|
||||
deleteDialog.confirm()
|
||||
expect(listAllSpy).toHaveBeenCalled()
|
||||
@@ -246,7 +246,7 @@ describe('MailComponent', () => {
|
||||
const toastErrorSpy = jest.spyOn(toastService, 'showError')
|
||||
const toastInfoSpy = jest.spyOn(toastService, 'showInfo')
|
||||
editDialog.failed.emit()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
editDialog.succeeded.emit(mailRules[0] as any)
|
||||
expect(toastInfoSpy).toHaveBeenCalledWith(
|
||||
`Saved rule "${mailRules[0].name}".`
|
||||
@@ -280,7 +280,7 @@ describe('MailComponent', () => {
|
||||
throwError(() => new Error('error deleting mail rule "rule1"'))
|
||||
)
|
||||
deleteDialog.confirm()
|
||||
expect(toastErrorSpy).toBeCalled()
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
deleteSpy.mockReturnValueOnce(of(true))
|
||||
deleteDialog.confirm()
|
||||
expect(listAllSpy).toHaveBeenCalled()
|
||||
|
@@ -1,4 +1,5 @@
|
||||
<pngx-page-header title="{{ typeNamePlural | titlecase }}">
|
||||
<pngx-page-header title="{{ typeNamePlural | titlecase }}" info="View, add, edit and delete {{ typeNamePlural }}." infoLink="usage/#terms-and-definitions">
|
||||
|
||||
<button class="btn btn-sm btn-outline-secondary" (click)="clearSelection()" [hidden]="selectedObjects.size === 0">
|
||||
<i-bs name="x"></i-bs> <ng-container i18n>Clear selection</ng-container>
|
||||
</button>
|
||||
|
8
src-ui/src/app/utils/navigation.ts
Normal file
8
src-ui/src/app/utils/navigation.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
export function setLocationHref(url: string) {
|
||||
window.location.href = url
|
||||
}
|
||||
|
||||
export function locationReload() {
|
||||
window.location.reload()
|
||||
}
|
@@ -3,7 +3,8 @@
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jest"
|
||||
"jest",
|
||||
"node",
|
||||
],
|
||||
"module": "commonjs",
|
||||
"emitDecoratorMetadata": true,
|
||||
|
@@ -12,11 +12,13 @@ from celery.signals import before_task_publish
|
||||
from celery.signals import task_failure
|
||||
from celery.signals import task_postrun
|
||||
from celery.signals import task_prerun
|
||||
from celery.signals import worker_process_init
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import DatabaseError
|
||||
from django.db import close_old_connections
|
||||
from django.db import connections
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.dispatch import receiver
|
||||
@@ -1439,3 +1441,18 @@ def task_failure_handler(
|
||||
task_instance.save()
|
||||
except Exception: # pragma: no cover
|
||||
logger.exception("Updating PaperlessTask failed")
|
||||
|
||||
|
||||
@worker_process_init.connect
|
||||
def close_connection_pool_on_worker_init(**kwargs):
|
||||
"""
|
||||
Close the DB connection pool for each Celery child process after it starts.
|
||||
|
||||
This is necessary because the parent process parse the Django configuration,
|
||||
initializes connection pools then forks.
|
||||
|
||||
Closing these pools after forking ensures child processes have a valid connection.
|
||||
"""
|
||||
for conn in connections.all(initialized_only=True):
|
||||
if conn.alias == "default" and hasattr(conn, "pool") and conn.pool:
|
||||
conn.close_pool()
|
||||
|
@@ -2,7 +2,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: paperless-ngx\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-07-08 21:14+0000\n"
|
||||
"POT-Creation-Date: 2025-08-02 12:55+0000\n"
|
||||
"PO-Revision-Date: 2022-02-17 04:17\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: English\n"
|
||||
@@ -1645,147 +1645,147 @@ msgstr ""
|
||||
msgid "paperless application settings"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:762
|
||||
#: paperless/settings.py:774
|
||||
msgid "English (US)"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:763
|
||||
#: paperless/settings.py:775
|
||||
msgid "Arabic"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:764
|
||||
#: paperless/settings.py:776
|
||||
msgid "Afrikaans"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:765
|
||||
#: paperless/settings.py:777
|
||||
msgid "Belarusian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:766
|
||||
#: paperless/settings.py:778
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:767
|
||||
#: paperless/settings.py:779
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:768
|
||||
#: paperless/settings.py:780
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:769
|
||||
#: paperless/settings.py:781
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:770
|
||||
#: paperless/settings.py:782
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:771
|
||||
#: paperless/settings.py:783
|
||||
msgid "Greek"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:772
|
||||
#: paperless/settings.py:784
|
||||
msgid "English (GB)"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:773
|
||||
#: paperless/settings.py:785
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:774
|
||||
#: paperless/settings.py:786
|
||||
msgid "Persian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:775
|
||||
#: paperless/settings.py:787
|
||||
msgid "Finnish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:776
|
||||
#: paperless/settings.py:788
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:777
|
||||
#: paperless/settings.py:789
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:778
|
||||
#: paperless/settings.py:790
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:779
|
||||
#: paperless/settings.py:791
|
||||
msgid "Japanese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:780
|
||||
#: paperless/settings.py:792
|
||||
msgid "Korean"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:781
|
||||
#: paperless/settings.py:793
|
||||
msgid "Luxembourgish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:782
|
||||
#: paperless/settings.py:794
|
||||
msgid "Norwegian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:783
|
||||
#: paperless/settings.py:795
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:784
|
||||
#: paperless/settings.py:796
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:785
|
||||
#: paperless/settings.py:797
|
||||
msgid "Portuguese (Brazil)"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:786
|
||||
#: paperless/settings.py:798
|
||||
msgid "Portuguese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:787
|
||||
#: paperless/settings.py:799
|
||||
msgid "Romanian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:788
|
||||
#: paperless/settings.py:800
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:789
|
||||
#: paperless/settings.py:801
|
||||
msgid "Slovak"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:790
|
||||
#: paperless/settings.py:802
|
||||
msgid "Slovenian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:791
|
||||
#: paperless/settings.py:803
|
||||
msgid "Serbian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:792
|
||||
#: paperless/settings.py:804
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:793
|
||||
#: paperless/settings.py:805
|
||||
msgid "Turkish"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:794
|
||||
#: paperless/settings.py:806
|
||||
msgid "Ukrainian"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:795
|
||||
#: paperless/settings.py:807
|
||||
msgid "Vietnamese"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:796
|
||||
#: paperless/settings.py:808
|
||||
msgid "Chinese Simplified"
|
||||
msgstr ""
|
||||
|
||||
#: paperless/settings.py:797
|
||||
#: paperless/settings.py:809
|
||||
msgid "Chinese Traditional"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -703,6 +703,9 @@ def _parse_db_settings() -> dict:
|
||||
# Leave room for future extensibility
|
||||
if os.getenv("PAPERLESS_DBENGINE") == "mariadb":
|
||||
engine = "django.db.backends.mysql"
|
||||
# Contrary to Postgres, Django does not natively support connection pooling for MariaDB.
|
||||
# However, since MariaDB uses threads instead of forks, establishing connections is significantly faster
|
||||
# compared to PostgreSQL, so the lack of pooling is not an issue
|
||||
options = {
|
||||
"read_default_file": "/etc/mysql/my.cnf",
|
||||
"charset": "utf8mb4",
|
||||
@@ -722,6 +725,15 @@ def _parse_db_settings() -> dict:
|
||||
"sslcert": os.getenv("PAPERLESS_DBSSLCERT", None),
|
||||
"sslkey": os.getenv("PAPERLESS_DBSSLKEY", None),
|
||||
}
|
||||
if int(os.getenv("PAPERLESS_DB_POOLSIZE", 0)) > 0:
|
||||
options.update(
|
||||
{
|
||||
"pool": {
|
||||
"min_size": 1,
|
||||
"max_size": int(os.getenv("PAPERLESS_DB_POOLSIZE")),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
databases["default"]["ENGINE"] = engine
|
||||
databases["default"]["OPTIONS"].update(options)
|
||||
|
@@ -16,7 +16,15 @@ class TextDocumentParser(DocumentParser):
|
||||
logging_name = "paperless.parsing.text"
|
||||
|
||||
def get_thumbnail(self, document_path: Path, mime_type, file_name=None) -> Path:
|
||||
text = self.read_file_handle_unicode_errors(document_path)
|
||||
# Avoid reading entire file into memory
|
||||
max_chars = 100_000
|
||||
file_size_limit = 50 * 1024 * 1024
|
||||
|
||||
if document_path.stat().st_size > file_size_limit:
|
||||
text = "[File too large to preview]"
|
||||
else:
|
||||
with Path(document_path).open("r", encoding="utf-8", errors="replace") as f:
|
||||
text = f.read(max_chars)
|
||||
|
||||
img = Image.new("RGB", (500, 700), color="white")
|
||||
draw = ImageDraw.Draw(img)
|
||||
@@ -25,7 +33,7 @@ class TextDocumentParser(DocumentParser):
|
||||
size=20,
|
||||
layout_engine=ImageFont.Layout.BASIC,
|
||||
)
|
||||
draw.text((5, 5), text, font=font, fill="black")
|
||||
draw.multiline_text((5, 5), text, font=font, fill="black", spacing=4)
|
||||
|
||||
out_path = self.tempdir / "thumb.webp"
|
||||
img.save(out_path, format="WEBP")
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from paperless_text.parsers import TextDocumentParser
|
||||
@@ -35,3 +36,26 @@ class TestTextParser:
|
||||
|
||||
assert text_parser.get_text() == "Pantothens<EFBFBD>ure\n"
|
||||
assert text_parser.get_archive_path() is None
|
||||
|
||||
def test_thumbnail_large_file(self, text_parser: TextDocumentParser):
|
||||
"""
|
||||
GIVEN:
|
||||
- A very large text file (>50MB)
|
||||
WHEN:
|
||||
- A thumbnail is requested
|
||||
THEN:
|
||||
- A thumbnail is created without reading the entire file into memory
|
||||
"""
|
||||
with tempfile.NamedTemporaryFile(
|
||||
delete=False,
|
||||
mode="w",
|
||||
encoding="utf-8",
|
||||
suffix=".txt",
|
||||
) as tmp:
|
||||
tmp.write("A" * (51 * 1024 * 1024)) # 51 MB of 'A'
|
||||
large_file = Path(tmp.name)
|
||||
|
||||
thumb = text_parser.get_thumbnail(large_file, "text/plain")
|
||||
assert thumb.exists()
|
||||
assert thumb.is_file()
|
||||
large_file.unlink()
|
||||
|
Reference in New Issue
Block a user