mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-12-29 13:48:09 -06:00
Compare commits
39 Commits
v2.16.2
...
67b6eec51c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67b6eec51c | ||
|
|
64528a0d97 | ||
|
|
65cc6338cd | ||
|
|
dc68a2aaf9 | ||
|
|
0451146c1d | ||
|
|
42e60eb457 | ||
|
|
a268f969ce | ||
|
|
00dc30a507 | ||
|
|
06cb71490b | ||
|
|
0811b38f82 | ||
|
|
40839dc222 | ||
|
|
0cefbac6c6 | ||
|
|
b7797ba5b1 | ||
|
|
0ae00bacce | ||
|
|
9cc909f9ba | ||
|
|
9bd02e2bc9 | ||
|
|
419ee9d6e7 | ||
|
|
34b649aa01 | ||
|
|
a9982abde8 | ||
|
|
a94cc62207 | ||
|
|
fc1b3e674b | ||
|
|
603ad6c817 | ||
|
|
9c9a0e4496 | ||
|
|
7c33785c07 | ||
|
|
9c32d931bc | ||
|
|
d7b2e002ce | ||
|
|
51e70f0a20 | ||
|
|
bd257925bd | ||
|
|
15b1b83c66 | ||
|
|
b06c0a0eba | ||
|
|
e9746aa0e3 | ||
|
|
bfaab21589 | ||
|
|
3849569bd1 | ||
|
|
c40a7751b9 | ||
|
|
f39463ff4e | ||
|
|
2ada8ec681 | ||
|
|
4c6fdbb21f | ||
|
|
889c4378a9 | ||
|
|
06dd039083 |
@@ -83,7 +83,8 @@ RUN set -eux \
|
||||
&& apt-get update \
|
||||
&& apt-get install --yes --quiet ${PYTHON_PACKAGES}
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.7.8 /uv /bin/uv
|
||||
|
||||
|
||||
RUN set -eux \
|
||||
&& echo "Installing pre-built updates" \
|
||||
@@ -128,7 +129,6 @@ RUN set -eux \
|
||||
&& echo "Configuring ImageMagick" \
|
||||
&& mv paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
|
||||
|
||||
# Packages needed only for building a few quick Python
|
||||
# dependencies
|
||||
|
||||
@@ -47,39 +47,19 @@ To start the DevContainer:
|
||||
|
||||
1. Open VSCode.
|
||||
2. Open the project folder.
|
||||
3. Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
4. Type and select `Dev Containers: Rebuild and Reopen in Container`.
|
||||
3. Open the command palette and choose `Dev Containers: Rebuild and Reopen in Container`.
|
||||
|
||||
VSCode will build and start the DevContainer environment.
|
||||
|
||||
### Step 2: Initial Setup
|
||||
|
||||
Once the DevContainer is up and running, perform the following steps:
|
||||
Once the DevContainer is up and running, run the `Project Setup: Run all Init Tasks` task to initialize the project.
|
||||
|
||||
1. **Compile Frontend Assets**:
|
||||
Alternatively, the Project Setup can be done with individual tasks:
|
||||
|
||||
- Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
- Select `Tasks: Run Task`.
|
||||
- Choose `Frontend Compile`.
|
||||
|
||||
2. **Run Database Migrations**:
|
||||
|
||||
- Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
- Select `Tasks: Run Task`.
|
||||
- Choose `Migrate Database`.
|
||||
|
||||
3. **Create Superuser**:
|
||||
- Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
- Select `Tasks: Run Task`.
|
||||
- Choose `Create Superuser`.
|
||||
1. **Compile Frontend Assets**: `Maintenance: Compile frontend for production`.
|
||||
2. **Run Database Migrations**: `Maintenance: manage.py migrate`.
|
||||
3. **Create Superuser**: `Maintenance: manage.py createsuperuser`.
|
||||
|
||||
### Debugging and Running Services
|
||||
|
||||
@@ -95,11 +75,8 @@ You can start and debug backend services either as debugging sessions via `launc
|
||||
|
||||
#### Using Tasks
|
||||
|
||||
1. Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
2. Select `Tasks: Run Task`.
|
||||
3. Choose the desired task:
|
||||
1. Open the command palette and select `Tasks: Run Task`.
|
||||
2. Choose the desired task:
|
||||
- `Runserver`
|
||||
- `Document Consumer`
|
||||
- `Celery`
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
"label": "Maintenance: recreate .venv",
|
||||
"description": "Recreate the python virtual environment and install python dependencies",
|
||||
"type": "shell",
|
||||
"command": "rm -R -v .venv/* || uv install --dev",
|
||||
"command": "rm -rf .venv && uv venv && uv sync --dev",
|
||||
"group": "none",
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
|
||||
20
.github/dependabot.yml
vendored
20
.github/dependabot.yml
vendored
@@ -16,9 +16,6 @@ updates:
|
||||
labels:
|
||||
- "frontend"
|
||||
- "dependencies"
|
||||
# Add reviewers
|
||||
reviewers:
|
||||
- "paperless-ngx/frontend"
|
||||
groups:
|
||||
frontend-angular-dependencies:
|
||||
patterns:
|
||||
@@ -44,9 +41,6 @@ updates:
|
||||
labels:
|
||||
- "backend"
|
||||
- "dependencies"
|
||||
# Add reviewers
|
||||
reviewers:
|
||||
- "paperless-ngx/backend"
|
||||
groups:
|
||||
development:
|
||||
patterns:
|
||||
@@ -65,6 +59,9 @@ updates:
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
exclude-patterns:
|
||||
- "*django*"
|
||||
- "drf-*"
|
||||
pre-built:
|
||||
patterns:
|
||||
- psycopg*
|
||||
@@ -79,9 +76,6 @@ updates:
|
||||
labels:
|
||||
- "ci-cd"
|
||||
- "dependencies"
|
||||
# Add reviewers
|
||||
reviewers:
|
||||
- "paperless-ngx/ci-cd"
|
||||
groups:
|
||||
actions:
|
||||
update-types:
|
||||
@@ -90,12 +84,12 @@ updates:
|
||||
- "patch"
|
||||
# Update Dockerfile in root directory
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
directories:
|
||||
- "/"
|
||||
- "/.devcontainer/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- "paperless-ngx/ci-cd"
|
||||
labels:
|
||||
- "dependencies"
|
||||
commit-message:
|
||||
@@ -107,8 +101,6 @@ updates:
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- "paperless-ngx/ci-cd"
|
||||
labels:
|
||||
- "dependencies"
|
||||
commit-message:
|
||||
|
||||
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@@ -6,9 +6,13 @@ on:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
# https://semver.org/#spec-item-9
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-beta.rc[0-9]+'
|
||||
branches-ignore:
|
||||
- 'translations**'
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- 'translations**'
|
||||
env:
|
||||
DEFAULT_UV_VERSION: "0.6.x"
|
||||
DEFAULT_UV_VERSION: "0.7.x"
|
||||
# This is the default version of Python to use in most steps which aren't specific
|
||||
DEFAULT_PYTHON_VERSION: "3.11"
|
||||
jobs:
|
||||
@@ -158,7 +162,7 @@ jobs:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
package_json_file: 'src-ui/package.json'
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -191,7 +195,7 @@ jobs:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
package_json_file: 'src-ui/package.json'
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -241,7 +245,7 @@ jobs:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
package_json_file: 'src-ui/package.json'
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -284,7 +288,7 @@ jobs:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
package_json_file: 'src-ui/package.json'
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
|
||||
2
.github/workflows/pr-bot.yml
vendored
2
.github/workflows/pr-bot.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
labels.push('bug');
|
||||
} else if (/^feature/i.test(title)) {
|
||||
labels.push('enhancement');
|
||||
} else {
|
||||
} else if (!/^(dependabot)/i.test(title)) {
|
||||
labels.push('enhancement'); // Default fallback
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ RUN set -eux \
|
||||
# Purpose: Installs s6-overlay and rootfs
|
||||
# Comments:
|
||||
# - Don't leave anything extra in here either
|
||||
FROM ghcr.io/astral-sh/uv:0.6.16-python3.12-bookworm-slim AS s6-overlay-base
|
||||
FROM ghcr.io/astral-sh/uv:0.7.9-python3.12-bookworm-slim AS s6-overlay-base
|
||||
|
||||
WORKDIR /usr/src/s6
|
||||
|
||||
|
||||
@@ -179,6 +179,7 @@ variables:
|
||||
| ---------------------------- | ---------------------------------------------- |
|
||||
| `DOCUMENT_ID` | Database primary key of the document |
|
||||
| `DOCUMENT_FILE_NAME` | Formatted filename, not including paths |
|
||||
| `DOCUMENT_TYPE` | The document type (if any) |
|
||||
| `DOCUMENT_CREATED` | Date & time when document created |
|
||||
| `DOCUMENT_MODIFIED` | Date & time when document was last modified |
|
||||
| `DOCUMENT_ADDED` | Date & time when document was added |
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# Changelog
|
||||
|
||||
## paperless-ngx 2.16.2
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: accept datetime for created [@shamoon](https://github.com/shamoon) ([#10021](https://github.com/paperless-ngx/paperless-ngx/pull/10021))
|
||||
- Fix: created date fixes in v2.16 [@shamoon](https://github.com/shamoon) ([#10026](https://github.com/paperless-ngx/paperless-ngx/pull/10026))
|
||||
- Fix: mark fields for created objects as dirty [@shamoon](https://github.com/shamoon) ([#10022](https://github.com/paperless-ngx/paperless-ngx/pull/10022))
|
||||
- Fix: add fallback to copyfile on PermissionError @samuel-kosmann ([#10023](https://github.com/paperless-ngx/paperless-ngx/pull/10023))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Chore: warn users about removal of postgres v13 support [@shamoon](https://github.com/shamoon) ([#9980](https://github.com/paperless-ngx/paperless-ngx/pull/9980))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>5 changes</summary>
|
||||
|
||||
- Fix: accept datetime for created [@shamoon](https://github.com/shamoon) ([#10021](https://github.com/paperless-ngx/paperless-ngx/pull/10021))
|
||||
- Fix: add fallback to copyfile on PermissionError @samuel-kosmann ([#10023](https://github.com/paperless-ngx/paperless-ngx/pull/10023))
|
||||
- Fix: created date fixes in v2.16 [@shamoon](https://github.com/shamoon) ([#10026](https://github.com/paperless-ngx/paperless-ngx/pull/10026))
|
||||
- Fix: mark fields for created objects as dirty [@shamoon](https://github.com/shamoon) ([#10022](https://github.com/paperless-ngx/paperless-ngx/pull/10022))
|
||||
- Chore: warn users about removal of postgres v13 support [@shamoon](https://github.com/shamoon) ([#9980](https://github.com/paperless-ngx/paperless-ngx/pull/9980))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.16.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -200,7 +200,7 @@ and watch out for indentation if editing the YAML file.
|
||||
|
||||
### Email Parsing
|
||||
|
||||
#### [`PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT=<int>`(#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT) {#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT}
|
||||
#### [`PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT=<int>`](#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT) {#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT}
|
||||
|
||||
: The default layout to use for emails that are consumed as documents. Must be one of the integer choices below. Note that mail
|
||||
rules can specify this setting, thus this fallback is used for the default selection and for .eml files consumed by other means.
|
||||
|
||||
@@ -6,6 +6,7 @@ A document with an id of ${DOCUMENT_ID} was just consumed. I know the
|
||||
following additional information about it:
|
||||
|
||||
* Generated File Name: ${DOCUMENT_FILE_NAME}
|
||||
* Document type: ${DOCUMENT_TYPE}
|
||||
* Archive Path: ${DOCUMENT_ARCHIVE_PATH}
|
||||
* Source Path: ${DOCUMENT_SOURCE_PATH}
|
||||
* Created: ${DOCUMENT_CREATED}
|
||||
|
||||
@@ -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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/alert/alert.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/node_modules/src/alert/alert.ts</context>
|
||||
<context context-type="linenumber">51</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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="linenumber">132,136</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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="linenumber">158,160</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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/node_modules/src/carousel/carousel.ts</context>
|
||||
<context context-type="linenumber">199</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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="linenumber">77,79</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="linenumber">102</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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="linenumber">102</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/node_modules/src/datepicker/datepicker-navigation.ts</context>
|
||||
<context context-type="linenumber">102</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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/ngb-config.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/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@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._9d4942db4d21b78bcb41ee657ee833ea/node_modules/src/progressbar/progressbar.ts</context>
|
||||
<context context-type="sourcefile">node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@18.0.0_@angular+common@19.2.9_@angular+core@19.2.9_rxjs@7.8._86f32aa280b87d913a06e5a1e90ee24c/node_modules/src/progressbar/progressbar.ts</context>
|
||||
<context context-type="linenumber">41,42</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
@@ -1084,7 +1084,7 @@
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.ts</context>
|
||||
<context context-type="linenumber">120</context>
|
||||
<context context-type="linenumber">121</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2818183879511244335" datatype="html">
|
||||
@@ -3092,22 +3092,22 @@
|
||||
<source>Successfully updated object.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.ts</context>
|
||||
<context context-type="linenumber">209</context>
|
||||
<context context-type="linenumber">210</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.ts</context>
|
||||
<context context-type="linenumber">247</context>
|
||||
<context context-type="linenumber">248</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1801333259018423190" datatype="html">
|
||||
<source>Error occurred saving object.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.ts</context>
|
||||
<context context-type="linenumber">212</context>
|
||||
<context context-type="linenumber">213</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.ts</context>
|
||||
<context context-type="linenumber">250</context>
|
||||
<context context-type="linenumber">251</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8193912662253833654" datatype="html">
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/cdk": "^19.2.14",
|
||||
"@angular/common": "~19.2.9",
|
||||
"@angular/compiler": "~19.2.9",
|
||||
"@angular/core": "~19.2.9",
|
||||
"@angular/forms": "~19.2.9",
|
||||
"@angular/localize": "~19.2.9",
|
||||
"@angular/platform-browser": "~19.2.9",
|
||||
"@angular/platform-browser-dynamic": "~19.2.9",
|
||||
"@angular/router": "~19.2.9",
|
||||
"@angular/common": "~19.2.13",
|
||||
"@angular/compiler": "~19.2.14",
|
||||
"@angular/core": "~19.2.14",
|
||||
"@angular/forms": "~19.2.14",
|
||||
"@angular/localize": "~19.2.14",
|
||||
"@angular/platform-browser": "~19.2.14",
|
||||
"@angular/platform-browser-dynamic": "~19.2.14",
|
||||
"@angular/router": "~19.2.14",
|
||||
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
|
||||
"@ng-select/ng-select": "^14.7.0",
|
||||
"@ng-select/ng-select": "^14.9.0",
|
||||
"@ngneat/dirty-check-forms": "^3.0.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"bootstrap": "^5.3.3",
|
||||
"bootstrap": "^5.3.6",
|
||||
"file-saver": "^2.0.5",
|
||||
"mime-names": "^1.0.0",
|
||||
"ng2-pdf-viewer": "^10.4.0",
|
||||
@@ -37,29 +37,29 @@
|
||||
"tslib": "^2.8.1",
|
||||
"utif": "^3.1.0",
|
||||
"uuid": "^11.1.0",
|
||||
"zone.js": "^0.15.0"
|
||||
"zone.js": "^0.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "^19.0.1",
|
||||
"@angular-builders/jest": "^19.0.1",
|
||||
"@angular-devkit/build-angular": "^19.2.10",
|
||||
"@angular-devkit/core": "^19.2.10",
|
||||
"@angular-devkit/schematics": "^19.2.10",
|
||||
"@angular-eslint/builder": "19.3.0",
|
||||
"@angular-eslint/eslint-plugin": "19.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "19.3.0",
|
||||
"@angular-eslint/schematics": "19.3.0",
|
||||
"@angular-eslint/template-parser": "19.3.0",
|
||||
"@angular/cli": "~19.2.10",
|
||||
"@angular/compiler-cli": "~19.2.9",
|
||||
"@codecov/webpack-plugin": "^1.9.0",
|
||||
"@angular-devkit/build-angular": "^19.2.14",
|
||||
"@angular-devkit/core": "^19.2.14",
|
||||
"@angular-devkit/schematics": "^19.2.14",
|
||||
"@angular-eslint/builder": "19.7.0",
|
||||
"@angular-eslint/eslint-plugin": "19.7.0",
|
||||
"@angular-eslint/eslint-plugin-template": "19.7.0",
|
||||
"@angular-eslint/schematics": "19.7.0",
|
||||
"@angular-eslint/template-parser": "19.7.0",
|
||||
"@angular/cli": "~19.2.14",
|
||||
"@angular/compiler-cli": "~19.2.14",
|
||||
"@codecov/webpack-plugin": "^1.9.1",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.15.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
||||
"@typescript-eslint/parser": "^8.31.1",
|
||||
"@typescript-eslint/utils": "^8.31.1",
|
||||
"eslint": "^9.25.1",
|
||||
"@types/node": "^22.15.29",
|
||||
"@typescript-eslint/eslint-plugin": "^8.33.0",
|
||||
"@typescript-eslint/parser": "^8.33.0",
|
||||
"@typescript-eslint/utils": "^8.33.0",
|
||||
"eslint": "^9.28.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
@@ -69,6 +69,13 @@
|
||||
"ts-node": "~10.9.1",
|
||||
"typescript": "^5.5.4"
|
||||
},
|
||||
"packageManager": "pnpm@10.11.1",
|
||||
"devEngines": {
|
||||
"packageManager": {
|
||||
"name": "pnpm",
|
||||
"version": "10.11.1"
|
||||
}
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@parcel/watcher",
|
||||
|
||||
2376
src-ui/pnpm-lock.yaml
generated
2376
src-ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -529,6 +529,17 @@ describe('GlobalSearchComponent', () => {
|
||||
expect(dispatchSpy).toHaveBeenCalledTimes(2) // once for keydown, second for click
|
||||
})
|
||||
|
||||
it('should support using base href in navigateOrOpenInNewWindow', () => {
|
||||
jest
|
||||
.spyOn(component['locationStrategy'], 'getBaseHref')
|
||||
.mockReturnValue('/base/')
|
||||
const openSpy = jest.spyOn(window, 'open')
|
||||
const event = new Event('click')
|
||||
event['ctrlKey'] = true
|
||||
component.primaryAction(DataType.Document, { id: 1 }, event as any)
|
||||
expect(openSpy).toHaveBeenCalledWith('/base/documents/1', '_blank')
|
||||
})
|
||||
|
||||
it('should support title content search and advanced search', () => {
|
||||
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
|
||||
component.query = 'test'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NgTemplateOutlet } from '@angular/common'
|
||||
import { LocationStrategy, NgTemplateOutlet } from '@angular/common'
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
@@ -99,7 +99,8 @@ export class GlobalSearchComponent implements OnInit {
|
||||
private permissionsService: PermissionsService,
|
||||
private toastService: ToastService,
|
||||
private hotkeyService: HotKeyService,
|
||||
private settingsService: SettingsService
|
||||
private settingsService: SettingsService,
|
||||
private locationStrategy: LocationStrategy
|
||||
) {
|
||||
this.queryDebounce = new Subject<string>()
|
||||
|
||||
@@ -421,10 +422,13 @@ export class GlobalSearchComponent implements OnInit {
|
||||
extras: Object = {}
|
||||
) {
|
||||
if (newWindow) {
|
||||
const url = this.router.serializeUrl(
|
||||
const serializedUrl = this.router.serializeUrl(
|
||||
this.router.createUrlTree(commands, extras)
|
||||
)
|
||||
window.open(url, '_blank')
|
||||
const baseHref = this.locationStrategy.getBaseHref()
|
||||
const fullUrl =
|
||||
baseHref.replace(/\/+$/, '') + '/' + serializedUrl.replace(/^\/+/, '')
|
||||
window.open(fullUrl, '_blank')
|
||||
} else {
|
||||
this.router.navigate(commands, extras)
|
||||
}
|
||||
|
||||
@@ -303,6 +303,7 @@ class ConsumerPlugin(
|
||||
script_env = os.environ.copy()
|
||||
|
||||
script_env["DOCUMENT_ID"] = str(document.pk)
|
||||
script_env["DOCUMENT_TYPE"] = str(document.document_type)
|
||||
script_env["DOCUMENT_CREATED"] = str(document.created)
|
||||
script_env["DOCUMENT_MODIFIED"] = str(document.modified)
|
||||
script_env["DOCUMENT_ADDED"] = str(document.added)
|
||||
|
||||
@@ -39,7 +39,8 @@ def migrate_date(apps, schema_editor):
|
||||
f"[1067_alter_document_created] {total_updated} of {total_checked} processed...",
|
||||
)
|
||||
|
||||
print(f"[1067_alter_document_created] completed for {total_checked} documents.")
|
||||
if total_checked > 0:
|
||||
print(f"[1067_alter_document_created] completed for {total_checked} documents.")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -13,6 +13,7 @@ from celery.signals import task_failure
|
||||
from celery.signals import task_postrun
|
||||
from celery.signals import task_prerun
|
||||
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
|
||||
@@ -38,6 +39,7 @@ from documents.models import MatchingModel
|
||||
from documents.models import PaperlessTask
|
||||
from documents.models import SavedView
|
||||
from documents.models import Tag
|
||||
from documents.models import UiSettings
|
||||
from documents.models import Workflow
|
||||
from documents.models import WorkflowAction
|
||||
from documents.models import WorkflowRun
|
||||
@@ -581,6 +583,51 @@ def cleanup_custom_field_deletion(sender, instance: CustomField, **kwargs):
|
||||
)
|
||||
|
||||
|
||||
@receiver(models.signals.post_delete, sender=User)
|
||||
@receiver(models.signals.post_delete, sender=Group)
|
||||
def cleanup_user_deletion(sender, instance: User | Group, **kwargs):
|
||||
"""
|
||||
When a user or group is deleted, remove non-cascading references.
|
||||
At the moment, just the default permission settings in UiSettings.
|
||||
"""
|
||||
# Remove the user permission settings e.g.
|
||||
# DEFAULT_PERMS_OWNER: 'general-settings:permissions:default-owner',
|
||||
# DEFAULT_PERMS_VIEW_USERS: 'general-settings:permissions:default-view-users',
|
||||
# DEFAULT_PERMS_VIEW_GROUPS: 'general-settings:permissions:default-view-groups',
|
||||
# DEFAULT_PERMS_EDIT_USERS: 'general-settings:permissions:default-edit-users',
|
||||
# DEFAULT_PERMS_EDIT_GROUPS: 'general-settings:permissions:default-edit-groups',
|
||||
for ui_settings in UiSettings.objects.all():
|
||||
try:
|
||||
permissions = ui_settings.settings.get("permissions", {})
|
||||
updated = False
|
||||
if isinstance(instance, User):
|
||||
if permissions.get("default_owner") == instance.pk:
|
||||
permissions["default_owner"] = None
|
||||
updated = True
|
||||
if instance.pk in permissions.get("default_view_users", []):
|
||||
permissions["default_view_users"].remove(instance.pk)
|
||||
updated = True
|
||||
if instance.pk in permissions.get("default_change_users", []):
|
||||
permissions["default_change_users"].remove(instance.pk)
|
||||
updated = True
|
||||
elif isinstance(instance, Group):
|
||||
if instance.pk in permissions.get("default_view_groups", []):
|
||||
permissions["default_view_groups"].remove(instance.pk)
|
||||
updated = True
|
||||
if instance.pk in permissions.get("default_change_groups", []):
|
||||
permissions["default_change_groups"].remove(instance.pk)
|
||||
updated = True
|
||||
if updated:
|
||||
ui_settings.settings["permissions"] = permissions
|
||||
ui_settings.save(update_fields=["settings"])
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Error while cleaning up user {instance.pk} ({instance.username}) from ui_settings: {e}"
|
||||
if isinstance(instance, User)
|
||||
else f"Error while cleaning up group {instance.pk} ({instance.name}) from ui_settings: {e}",
|
||||
)
|
||||
|
||||
|
||||
def add_to_index(sender, document, **kwargs):
|
||||
from documents import index
|
||||
|
||||
|
||||
@@ -387,6 +387,18 @@ def empty_trash(doc_ids=None):
|
||||
|
||||
@shared_task
|
||||
def check_scheduled_workflows():
|
||||
"""
|
||||
Check and run all enabled scheduled workflows.
|
||||
|
||||
Scheduled triggers are evaluated based on a target date field (e.g. added, created, modified, or a custom date field),
|
||||
combined with a day offset.
|
||||
|
||||
The offset is mathematically negated resulting in the following behavior:
|
||||
- Positive offsets mean the workflow should trigger BEFORE the specified date (e.g., offset = +7 → trigger 7 days before)
|
||||
- Negative offsets mean the workflow should trigger AFTER the specified date (e.g., offset = -7 → trigger 7 days after)
|
||||
|
||||
Once a document satisfies this condition, and recurring/non-recurring constraints are met, the workflow is run.
|
||||
"""
|
||||
scheduled_workflows: list[Workflow] = (
|
||||
Workflow.objects.filter(
|
||||
triggers__type=WorkflowTrigger.WorkflowTriggerType.SCHEDULED,
|
||||
|
||||
@@ -1174,12 +1174,16 @@ class PostConsumeTestCase(DirectoriesMixin, GetConsumerMixin, TestCase):
|
||||
m.assert_called_once()
|
||||
|
||||
@mock.patch("documents.consumer.run_subprocess")
|
||||
def test_post_consume_script_with_correspondent(self, m):
|
||||
def test_post_consume_script_with_correspondent_and_type(self, m):
|
||||
with tempfile.NamedTemporaryFile() as script:
|
||||
with override_settings(POST_CONSUME_SCRIPT=script.name):
|
||||
c = Correspondent.objects.create(name="my_bank")
|
||||
t = DocumentType.objects.create(
|
||||
name="Test type",
|
||||
)
|
||||
doc = Document.objects.create(
|
||||
title="Test",
|
||||
document_type=t,
|
||||
mime_type="application/pdf",
|
||||
correspondent=c,
|
||||
)
|
||||
@@ -1207,6 +1211,7 @@ class PostConsumeTestCase(DirectoriesMixin, GetConsumerMixin, TestCase):
|
||||
|
||||
subset = {
|
||||
"DOCUMENT_ID": str(doc.pk),
|
||||
"DOCUMENT_TYPE": "Test type",
|
||||
"DOCUMENT_DOWNLOAD_URL": f"/api/documents/{doc.pk}/download/",
|
||||
"DOCUMENT_THUMBNAIL_URL": f"/api/documents/{doc.pk}/thumb/",
|
||||
"DOCUMENT_CORRESPONDENT": "my_bank",
|
||||
|
||||
@@ -1610,7 +1610,7 @@ class TestWorkflows(
|
||||
doc.refresh_from_db()
|
||||
self.assertIsNone(doc.owner)
|
||||
|
||||
def test_workflow_scheduled_trigger_negative_offset(self):
|
||||
def test_workflow_scheduled_trigger_negative_offset_customfield(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- Existing workflow with SCHEDULED trigger and negative offset of -7 days (so 7 days after date)
|
||||
@@ -1662,6 +1662,55 @@ class TestWorkflows(
|
||||
doc.refresh_from_db()
|
||||
self.assertEqual(doc.owner, self.user2)
|
||||
|
||||
def test_workflow_scheduled_trigger_negative_offset_created(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- Existing workflow with SCHEDULED trigger and negative offset of -7 days (so 7 days after date)
|
||||
- Created date set to 8 days ago → trigger time = 1 day ago and 5 days ago
|
||||
WHEN:
|
||||
- Scheduled workflows are checked for document
|
||||
THEN:
|
||||
- Workflow runs and document owner is updated for the first document, not the second
|
||||
"""
|
||||
trigger = WorkflowTrigger.objects.create(
|
||||
type=WorkflowTrigger.WorkflowTriggerType.SCHEDULED,
|
||||
schedule_offset_days=-7,
|
||||
schedule_date_field=WorkflowTrigger.ScheduleDateField.CREATED,
|
||||
)
|
||||
action = WorkflowAction.objects.create(
|
||||
assign_title="Doc assign owner",
|
||||
assign_owner=self.user2,
|
||||
)
|
||||
w = Workflow.objects.create(
|
||||
name="Workflow 1",
|
||||
order=0,
|
||||
)
|
||||
w.triggers.add(trigger)
|
||||
w.actions.add(action)
|
||||
w.save()
|
||||
|
||||
doc = Document.objects.create(
|
||||
title="sample test",
|
||||
correspondent=self.c,
|
||||
original_filename="sample.pdf",
|
||||
checksum="1",
|
||||
created=timezone.now().date() - timedelta(days=8),
|
||||
)
|
||||
|
||||
doc2 = Document.objects.create(
|
||||
title="sample test 2",
|
||||
correspondent=self.c,
|
||||
original_filename="sample2.pdf",
|
||||
checksum="2",
|
||||
created=timezone.now().date() - timedelta(days=5),
|
||||
)
|
||||
|
||||
tasks.check_scheduled_workflows()
|
||||
doc.refresh_from_db()
|
||||
self.assertEqual(doc.owner, self.user2)
|
||||
doc2.refresh_from_db()
|
||||
self.assertIsNone(doc2.owner) # has not triggered yet
|
||||
|
||||
def test_workflow_enabled_disabled(self):
|
||||
trigger = WorkflowTrigger.objects.create(
|
||||
type=WorkflowTrigger.WorkflowTriggerType.DOCUMENT_ADDED,
|
||||
|
||||
@@ -23,11 +23,17 @@ def copy_basic_file_stats(source: Path | str, dest: Path | str) -> None:
|
||||
|
||||
The extended attribute copy does weird things with SELinux and files
|
||||
copied from temporary directories and copystat doesn't allow disabling
|
||||
these copies
|
||||
these copies.
|
||||
|
||||
If there is a PermissionError, skip copying file stats.
|
||||
"""
|
||||
source, dest = _coerce_to_path(source, dest)
|
||||
src_stat = source.stat()
|
||||
utime(dest, ns=(src_stat.st_atime_ns, src_stat.st_mtime_ns))
|
||||
|
||||
try:
|
||||
utime(dest, ns=(src_stat.st_atime_ns, src_stat.st_mtime_ns))
|
||||
except PermissionError:
|
||||
pass
|
||||
|
||||
|
||||
def copy_file_with_basic_stats(
|
||||
|
||||
@@ -6,6 +6,7 @@ from django.http import HttpRequest
|
||||
from django.test import TestCase
|
||||
from django.test import override_settings
|
||||
|
||||
from documents.models import UiSettings
|
||||
from paperless.signals import handle_failed_login
|
||||
from paperless.signals import handle_social_account_updated
|
||||
|
||||
@@ -190,3 +191,76 @@ class TestSyncSocialLoginGroups(TestCase):
|
||||
sociallogin=sociallogin,
|
||||
)
|
||||
self.assertEqual(list(user.groups.all()), [])
|
||||
|
||||
|
||||
class TestUserGroupDeletionCleanup(TestCase):
|
||||
"""
|
||||
Test that when a user or group is deleted, references are cleaned up properly
|
||||
from ui_settings
|
||||
"""
|
||||
|
||||
def test_user_group_deletion_cleanup(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- Existing user
|
||||
- Existing group
|
||||
WHEN:
|
||||
- The user is deleted
|
||||
- The group is deleted
|
||||
THEN:
|
||||
- References in ui_settings are cleaned up
|
||||
"""
|
||||
user = User.objects.create_user(username="testuser")
|
||||
user2 = User.objects.create_user(username="testuser2")
|
||||
group = Group.objects.create(name="testgroup")
|
||||
|
||||
ui_settings = UiSettings.objects.create(
|
||||
user=user,
|
||||
settings={
|
||||
"permissions": {
|
||||
"default_owner": user2.id,
|
||||
"default_view_users": [user2.id],
|
||||
"default_change_users": [user2.id],
|
||||
"default_view_groups": [group.id],
|
||||
"default_change_groups": [group.id],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
user2.delete()
|
||||
ui_settings.refresh_from_db()
|
||||
permissions = ui_settings.settings.get("permissions", {})
|
||||
self.assertIsNone(permissions.get("default_owner"))
|
||||
self.assertEqual(permissions.get("default_view_users"), [])
|
||||
self.assertEqual(permissions.get("default_change_users"), [])
|
||||
|
||||
group.delete()
|
||||
ui_settings.refresh_from_db()
|
||||
permissions = ui_settings.settings.get("permissions", {})
|
||||
self.assertEqual(permissions.get("default_view_groups"), [])
|
||||
self.assertEqual(permissions.get("default_change_groups"), [])
|
||||
|
||||
def test_user_group_deletion_error_handling(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- Existing user and group
|
||||
WHEN:
|
||||
- The user is deleted and an error occurs during the signal handling
|
||||
THEN:
|
||||
- Error is logged and the system remains stable
|
||||
"""
|
||||
user = User.objects.create_user(username="testuser")
|
||||
user2 = User.objects.create_user(username="testuser2")
|
||||
user2_id = user2.id
|
||||
Group.objects.create(name="testgroup")
|
||||
|
||||
UiSettings.objects.create(
|
||||
user=user,
|
||||
) # invalid, no settings, this probably should not happen in production
|
||||
|
||||
with self.assertLogs("paperless.handlers", level="ERROR") as cm:
|
||||
user2.delete()
|
||||
self.assertIn(
|
||||
f"Error while cleaning up user {user2_id}",
|
||||
cm.output[0],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user