mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-24 18:04:39 -05:00
Compare commits
26 Commits
f8fb224968
...
6a1060f4d2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6a1060f4d2 | ||
![]() |
5881c70051 | ||
![]() |
1f549dcd52 | ||
![]() |
79323fcea6 | ||
![]() |
f42a71dc62 | ||
![]() |
e2888f12d7 | ||
![]() |
3ad2eaf1e3 | ||
![]() |
04504c3d42 | ||
![]() |
9de605d2e6 | ||
![]() |
6964b184d1 | ||
![]() |
0e76b86066 | ||
![]() |
89e5c08a1f | ||
![]() |
0faa9e8865 | ||
![]() |
f205c4d0e2 | ||
![]() |
344b2bc0eb | ||
![]() |
817aad7c8b | ||
![]() |
d82555e644 | ||
![]() |
f3e6ed56b9 | ||
![]() |
780d1c67e9 | ||
![]() |
2b72397a4d | ||
![]() |
6c13ffaa01 | ||
![]() |
eb8e124971 | ||
![]() |
1bc77546eb | ||
![]() |
5a453653e2 | ||
![]() |
16f17829b6 | ||
![]() |
3cf1c04a83 |
@ -76,18 +76,15 @@ RUN set -eux \
|
|||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES}
|
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES}
|
||||||
|
|
||||||
ARG PYTHON_PACKAGES="\
|
ARG PYTHON_PACKAGES="ca-certificates"
|
||||||
python3 \
|
|
||||||
python3-pip \
|
|
||||||
python3-wheel \
|
|
||||||
pipenv \
|
|
||||||
ca-certificates"
|
|
||||||
|
|
||||||
RUN set -eux \
|
RUN set -eux \
|
||||||
echo "Installing python packages" \
|
echo "Installing python packages" \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install --yes --quiet ${PYTHON_PACKAGES}
|
&& apt-get install --yes --quiet ${PYTHON_PACKAGES}
|
||||||
|
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
|
||||||
|
|
||||||
RUN set -eux \
|
RUN set -eux \
|
||||||
&& echo "Installing pre-built updates" \
|
&& echo "Installing pre-built updates" \
|
||||||
&& echo "Installing qpdf ${QPDF_VERSION}" \
|
&& echo "Installing qpdf ${QPDF_VERSION}" \
|
||||||
@ -131,6 +128,8 @@ RUN set -eux \
|
|||||||
&& echo "Configuring ImageMagick" \
|
&& echo "Configuring ImageMagick" \
|
||||||
&& mv paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
&& 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
|
# Packages needed only for building a few quick Python
|
||||||
# dependencies
|
# dependencies
|
||||||
ARG BUILD_PACKAGES="\
|
ARG BUILD_PACKAGES="\
|
||||||
@ -140,11 +139,10 @@ ARG BUILD_PACKAGES="\
|
|||||||
libpq-dev \
|
libpq-dev \
|
||||||
# https://github.com/PyMySQL/mysqlclient#linux
|
# https://github.com/PyMySQL/mysqlclient#linux
|
||||||
default-libmysqlclient-dev \
|
default-libmysqlclient-dev \
|
||||||
pkg-config \
|
pkg-config"
|
||||||
pre-commit"
|
|
||||||
|
|
||||||
# hadolint ignore=DL3042
|
# hadolint ignore=DL3042
|
||||||
RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
|
RUN --mount=type=cache,target=/root/.cache/uv,id=pip-cache \
|
||||||
set -eux \
|
set -eux \
|
||||||
&& echo "Installing build system packages" \
|
&& echo "Installing build system packages" \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
@ -169,9 +167,6 @@ RUN set -eux \
|
|||||||
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/.venv \
|
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/.venv \
|
||||||
&& echo "Adjusting all permissions" \
|
&& echo "Adjusting all permissions" \
|
||||||
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless
|
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless
|
||||||
# && echo "Collecting static files" \
|
|
||||||
# && gosu paperless python3 manage.py collectstatic --clear --no-input --link \
|
|
||||||
# && gosu paperless python3 manage.py compilemessages
|
|
||||||
|
|
||||||
VOLUME ["/usr/src/paperless/paperless-ngx/data", \
|
VOLUME ["/usr/src/paperless/paperless-ngx/data", \
|
||||||
"/usr/src/paperless/paperless-ngx/media", \
|
"/usr/src/paperless/paperless-ngx/media", \
|
||||||
|
117
.devcontainer/README.md
Normal file
117
.devcontainer/README.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# Paperless-ngx Development Environment
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Welcome to the Paperless-ngx development environment! This setup uses VSCode DevContainers to provide a consistent and seamless development experience.
|
||||||
|
|
||||||
|
### What are DevContainers?
|
||||||
|
|
||||||
|
DevContainers are a feature in VSCode that allows you to develop within a Docker container. This ensures that your development environment is consistent across different machines and setups. By defining a containerized environment, you can eliminate the "works on my machine" problem.
|
||||||
|
|
||||||
|
### Advantages of DevContainers
|
||||||
|
|
||||||
|
- **Consistency**: Same environment for all developers.
|
||||||
|
- **Isolation**: Separate development environment from your local machine.
|
||||||
|
- **Reproducibility**: Easily recreate the environment on any machine.
|
||||||
|
- **Pre-configured Tools**: Include all necessary tools and dependencies in the container.
|
||||||
|
|
||||||
|
## DevContainer Setup
|
||||||
|
|
||||||
|
The DevContainer configuration provides up all the necessary services for Paperless-ngx, including:
|
||||||
|
|
||||||
|
- Redis
|
||||||
|
- Gotenberg
|
||||||
|
- Tika
|
||||||
|
|
||||||
|
Data is stored using Docker volumes to ensure persistence across container restarts.
|
||||||
|
|
||||||
|
## Configuration Files
|
||||||
|
|
||||||
|
The setup includes debugging configurations (`launch.json`) and tasks (`tasks.json`) to help you manage and debug various parts of the project:
|
||||||
|
|
||||||
|
- **Backend Debugging:**
|
||||||
|
- `manage.py runserver`
|
||||||
|
- `manage.py document-consumer`
|
||||||
|
- `celery`
|
||||||
|
- **Maintenance Tasks:**
|
||||||
|
- Create superuser
|
||||||
|
- Run migrations
|
||||||
|
- Recreate virtual environment (`.venv` with `uv`)
|
||||||
|
- Compile frontend assets
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Step 1: Running the DevContainer
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
VSCode will build and start the DevContainer environment.
|
||||||
|
|
||||||
|
### Step 2: Initial Setup
|
||||||
|
|
||||||
|
Once the DevContainer is up and running, perform the following steps:
|
||||||
|
|
||||||
|
1. **Compile Frontend Assets**:
|
||||||
|
|
||||||
|
- 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`.
|
||||||
|
|
||||||
|
### Debugging and Running Services
|
||||||
|
|
||||||
|
You can start and debug backend services either as debugging sessions via `launch.json` or as tasks.
|
||||||
|
|
||||||
|
#### Using `launch.json`
|
||||||
|
|
||||||
|
1. Press `F5` or go to the **Run and Debug** view in VSCode.
|
||||||
|
2. Select the desired configuration:
|
||||||
|
- `Runserver`
|
||||||
|
- `Document Consumer`
|
||||||
|
- `Celery`
|
||||||
|
|
||||||
|
#### 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:
|
||||||
|
- `Runserver`
|
||||||
|
- `Document Consumer`
|
||||||
|
- `Celery`
|
||||||
|
|
||||||
|
### Additional Maintenance Tasks
|
||||||
|
|
||||||
|
Additional tasks are available for common maintenance operations:
|
||||||
|
|
||||||
|
- **Recreate .venv**: For setting up the virtual environment using `uv`.
|
||||||
|
- **Migrate Database**: To apply database migrations.
|
||||||
|
- **Create Superuser**: To create an admin user for the application.
|
||||||
|
|
||||||
|
## Let's Get Started!
|
||||||
|
|
||||||
|
Follow the steps above to get your development environment up and running. Happy coding!
|
@ -3,7 +3,7 @@
|
|||||||
"dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml",
|
"dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml",
|
||||||
"service": "paperless-development",
|
"service": "paperless-development",
|
||||||
"workspaceFolder": "/usr/src/paperless/paperless-ngx",
|
"workspaceFolder": "/usr/src/paperless/paperless-ngx",
|
||||||
"postCreateCommand": "pipenv install --dev && pipenv run pre-commit install",
|
"postCreateCommand": "/bin/bash -c uv sync --dev && uv run pre-commit install",
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
|
@ -43,7 +43,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ..:/usr/src/paperless/paperless-ngx:delegated
|
- ..:/usr/src/paperless/paperless-ngx:delegated
|
||||||
- ../.devcontainer/vscode:/usr/src/paperless/paperless-ngx/.vscode:delegated # VSCode config files
|
- ../.devcontainer/vscode:/usr/src/paperless/paperless-ngx/.vscode:delegated # VSCode config files
|
||||||
- pipenv:/usr/src/paperless/paperless-ngx/.venv
|
- virtualenv:/usr/src/paperless/paperless-ngx/.venv # Virtual environment persisted in volume
|
||||||
- /usr/src/paperless/paperless-ngx/src/documents/static/frontend # Static frontend files exist only in container
|
- /usr/src/paperless/paperless-ngx/src/documents/static/frontend # Static frontend files exist only in container
|
||||||
- /usr/src/paperless/paperless-ngx/src/.pytest_cache
|
- /usr/src/paperless/paperless-ngx/src/.pytest_cache
|
||||||
- /usr/src/paperless/paperless-ngx/.ruff_cache
|
- /usr/src/paperless/paperless-ngx/.ruff_cache
|
||||||
@ -80,4 +80,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pipenv:
|
data:
|
||||||
|
media:
|
||||||
|
redisdata:
|
||||||
|
virtualenv:
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"label": "Start: Celery Worker",
|
"label": "Start: Celery Worker",
|
||||||
"description": "Start the Celery Worker which processes background and consume tasks",
|
"description": "Start the Celery Worker which processes background and consume tasks",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run celery --app paperless worker -l DEBUG",
|
"command": "uv run celery --app paperless worker -l DEBUG",
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/src"
|
"cwd": "${workspaceFolder}/src"
|
||||||
@ -61,7 +61,7 @@
|
|||||||
"label": "Start: Consumer Service (manage.py document_consumer)",
|
"label": "Start: Consumer Service (manage.py document_consumer)",
|
||||||
"description": "Start the Consumer Service which processes files from a directory",
|
"description": "Start the Consumer Service which processes files from a directory",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run python manage.py document_consumer",
|
"command": "uv run python manage.py document_consumer",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -80,7 +80,7 @@
|
|||||||
"label": "Start: Backend Server (manage.py runserver)",
|
"label": "Start: Backend Server (manage.py runserver)",
|
||||||
"description": "Start the Backend Server which serves the Django API and the compiled Angular frontend",
|
"description": "Start the Backend Server which serves the Django API and the compiled Angular frontend",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run python manage.py runserver",
|
"command": "uv run python manage.py runserver",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -99,7 +99,7 @@
|
|||||||
"label": "Maintenance: manage.py migrate",
|
"label": "Maintenance: manage.py migrate",
|
||||||
"description": "Apply database migrations",
|
"description": "Apply database migrations",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run python manage.py migrate",
|
"command": "uv run python manage.py migrate",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -118,7 +118,7 @@
|
|||||||
"label": "Maintenance: Build Documentation",
|
"label": "Maintenance: Build Documentation",
|
||||||
"description": "Build the documentation with MkDocs",
|
"description": "Build the documentation with MkDocs",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run mkdocs build --config-file mkdocs.yml && pipenv run mkdocs serve",
|
"command": "uv run mkdocs build --config-file mkdocs.yml && uv run mkdocs serve",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -137,7 +137,7 @@
|
|||||||
"label": "Maintenance: manage.py createsuperuser",
|
"label": "Maintenance: manage.py createsuperuser",
|
||||||
"description": "Create a superuser",
|
"description": "Create a superuser",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "pipenv run python manage.py createsuperuser",
|
"command": "uv run python manage.py createsuperuser",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
@ -156,7 +156,7 @@
|
|||||||
"label": "Maintenance: recreate .venv",
|
"label": "Maintenance: recreate .venv",
|
||||||
"description": "Recreate the python virtual environment and install python dependencies",
|
"description": "Recreate the python virtual environment and install python dependencies",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "rm -R -v .venv/* || pipenv install --dev",
|
"command": "rm -R -v .venv/* || uv install --dev",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
|
@ -27,9 +27,6 @@ indent_style = space
|
|||||||
[*.md]
|
[*.md]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
||||||
[Pipfile.lock]
|
|
||||||
indent_style = space
|
|
||||||
|
|
||||||
# Tests don't get a line width restriction. It's still a good idea to follow
|
# Tests don't get a line width restriction. It's still a good idea to follow
|
||||||
# the 79 character rule, but in the interests of clarity, tests often need to
|
# the 79 character rule, but in the interests of clarity, tests often need to
|
||||||
# violate it.
|
# violate it.
|
||||||
|
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@ -1,6 +1,8 @@
|
|||||||
# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates#package-ecosystem
|
# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates#package-ecosystem
|
||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
|
# Required for uv support for now
|
||||||
|
enable-beta-ecosystems: true
|
||||||
updates:
|
updates:
|
||||||
|
|
||||||
# Enable version updates for npm
|
# Enable version updates for npm
|
||||||
@ -34,9 +36,8 @@ updates:
|
|||||||
- "eslint"
|
- "eslint"
|
||||||
|
|
||||||
# Enable version updates for Python
|
# Enable version updates for Python
|
||||||
- package-ecosystem: "pip"
|
- package-ecosystem: "uv"
|
||||||
target-branch: "dev"
|
target-branch: "dev"
|
||||||
# Look for a `Pipfile` in the `root` directory
|
|
||||||
directory: "/"
|
directory: "/"
|
||||||
# Check for updates once a week
|
# Check for updates once a week
|
||||||
schedule:
|
schedule:
|
||||||
@ -53,6 +54,7 @@ updates:
|
|||||||
- "*pytest*"
|
- "*pytest*"
|
||||||
- "ruff"
|
- "ruff"
|
||||||
- "mkdocs-material"
|
- "mkdocs-material"
|
||||||
|
- "pre-commit*"
|
||||||
django:
|
django:
|
||||||
patterns:
|
patterns:
|
||||||
- "*django*"
|
- "*django*"
|
||||||
@ -63,6 +65,10 @@ updates:
|
|||||||
update-types:
|
update-types:
|
||||||
- "minor"
|
- "minor"
|
||||||
- "patch"
|
- "patch"
|
||||||
|
pre-built:
|
||||||
|
patterns:
|
||||||
|
- psycopg*
|
||||||
|
- zxing-cpp
|
||||||
|
|
||||||
# Enable updates for GitHub Actions
|
# Enable updates for GitHub Actions
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
|
148
.github/workflows/ci.yml
vendored
148
.github/workflows/ci.yml
vendored
@ -14,9 +14,7 @@ on:
|
|||||||
- 'translations**'
|
- 'translations**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# This is the version of pipenv all the steps will use
|
DEFAULT_UV_VERSION: "0.6.x"
|
||||||
# If changing this, change Dockerfile
|
|
||||||
DEFAULT_PIP_ENV_VERSION: "2024.4.1"
|
|
||||||
# This is the default version of Python to use in most steps which aren't specific
|
# This is the default version of Python to use in most steps which aren't specific
|
||||||
DEFAULT_PYTHON_VERSION: "3.11"
|
DEFAULT_PYTHON_VERSION: "3.11"
|
||||||
|
|
||||||
@ -59,24 +57,25 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
cache: "pipenv"
|
|
||||||
cache-dependency-path: 'Pipfile.lock'
|
|
||||||
-
|
-
|
||||||
name: Install pipenv
|
name: Install uv
|
||||||
run: |
|
uses: astral-sh/setup-uv@v5
|
||||||
pip install --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }}
|
with:
|
||||||
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
|
enable-cache: true
|
||||||
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
-
|
-
|
||||||
name: Install dependencies
|
name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
|
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
||||||
-
|
|
||||||
name: List installed Python dependencies
|
|
||||||
run: |
|
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
|
|
||||||
-
|
-
|
||||||
name: Make documentation
|
name: Make documentation
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs build --config-file ./mkdocs.yml
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--dev \
|
||||||
|
--frozen \
|
||||||
|
mkdocs build --config-file ./mkdocs.yml
|
||||||
-
|
-
|
||||||
name: Deploy documentation
|
name: Deploy documentation
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
@ -84,7 +83,11 @@ jobs:
|
|||||||
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
|
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
|
||||||
git config --global user.name "${{ github.actor }}"
|
git config --global user.name "${{ github.actor }}"
|
||||||
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs gh-deploy --force --no-history
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--dev \
|
||||||
|
--frozen \
|
||||||
|
mkdocs gh-deploy --force --no-history
|
||||||
-
|
-
|
||||||
name: Upload artifact
|
name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@ -117,12 +120,13 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "${{ matrix.python-version }}"
|
python-version: "${{ matrix.python-version }}"
|
||||||
cache: "pipenv"
|
|
||||||
cache-dependency-path: 'Pipfile.lock'
|
|
||||||
-
|
-
|
||||||
name: Install pipenv
|
name: Install uv
|
||||||
run: |
|
uses: astral-sh/setup-uv@v5
|
||||||
pip install --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }}
|
with:
|
||||||
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
|
enable-cache: true
|
||||||
|
python-version: ${{ steps.setup-python.outputs.python-version }}
|
||||||
-
|
-
|
||||||
name: Install system dependencies
|
name: Install system dependencies
|
||||||
run: |
|
run: |
|
||||||
@ -135,12 +139,14 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Install Python dependencies
|
name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run python --version
|
uv sync \
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--group testing \
|
||||||
|
--frozen
|
||||||
-
|
-
|
||||||
name: List installed Python dependencies
|
name: List installed Python dependencies
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
|
uv pip list
|
||||||
-
|
-
|
||||||
name: Tests
|
name: Tests
|
||||||
env:
|
env:
|
||||||
@ -150,17 +156,22 @@ jobs:
|
|||||||
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
|
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
|
||||||
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
|
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
|
||||||
run: |
|
run: |
|
||||||
cd src/
|
uv run \
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pytest -ra
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--dev \
|
||||||
|
--frozen \
|
||||||
|
pytest
|
||||||
-
|
-
|
||||||
name: Upload coverage
|
name: Upload coverage
|
||||||
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
|
if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: backend-coverage-report
|
name: backend-coverage-report
|
||||||
path: src/coverage.xml
|
path: |
|
||||||
|
coverage.xml
|
||||||
|
junit.xml
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
if-no-files-found: warn
|
if-no-files-found: error
|
||||||
-
|
-
|
||||||
name: Stop containers
|
name: Stop containers
|
||||||
if: always()
|
if: always()
|
||||||
@ -234,6 +245,8 @@ jobs:
|
|||||||
run: cd src-ui && npm run lint
|
run: cd src-ui && npm run lint
|
||||||
-
|
-
|
||||||
name: Run Jest unit tests
|
name: Run Jest unit tests
|
||||||
|
env:
|
||||||
|
JEST_JUNIT_OUTPUT_FILE: junit-report-${{ matrix.shard-index }}.xml
|
||||||
run: cd src-ui && npm run test -- --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
run: cd src-ui && npm run test -- --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||||
-
|
-
|
||||||
name: Upload Jest coverage
|
name: Upload Jest coverage
|
||||||
@ -246,7 +259,7 @@ jobs:
|
|||||||
src-ui/coverage/lcov.info
|
src-ui/coverage/lcov.info
|
||||||
src-ui/coverage/clover.xml
|
src-ui/coverage/clover.xml
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
if-no-files-found: warn
|
if-no-files-found: error
|
||||||
-
|
-
|
||||||
name: Run Playwright e2e tests
|
name: Run Playwright e2e tests
|
||||||
run: cd src-ui && npx playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
run: cd src-ui && npx playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||||
@ -258,6 +271,16 @@ jobs:
|
|||||||
name: playwright-report-${{ matrix.shard-index }}
|
name: playwright-report-${{ matrix.shard-index }}
|
||||||
path: src-ui/playwright-report
|
path: src-ui/playwright-report
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
if-no-files-found: error
|
||||||
|
-
|
||||||
|
name: Upload frontend test results
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: junit-report-${{ matrix.shard-index }}
|
||||||
|
path: src-ui/junit-report-${{ matrix.shard-index }}.xml
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
tests-coverage-upload:
|
tests-coverage-upload:
|
||||||
name: "Upload to Codecov"
|
name: "Upload to Codecov"
|
||||||
@ -281,6 +304,13 @@ jobs:
|
|||||||
path: src-ui/coverage/
|
path: src-ui/coverage/
|
||||||
pattern: playwright-report-*
|
pattern: playwright-report-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
-
|
||||||
|
name: Download frontend test results
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: src-ui/junit/
|
||||||
|
pattern: junit-report-*
|
||||||
|
merge-multiple: true
|
||||||
-
|
-
|
||||||
name: Upload frontend coverage to Codecov
|
name: Upload frontend coverage to Codecov
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
@ -291,6 +321,14 @@ jobs:
|
|||||||
directory: src-ui/coverage/
|
directory: src-ui/coverage/
|
||||||
# dont include backend coverage files here
|
# dont include backend coverage files here
|
||||||
files: '!coverage.xml'
|
files: '!coverage.xml'
|
||||||
|
-
|
||||||
|
name: Upload frontend test results to Codecov
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
uses: codecov/test-results-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
flags: frontend
|
||||||
|
directory: src-ui/junit/
|
||||||
-
|
-
|
||||||
name: Download backend coverage
|
name: Download backend coverage
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@ -306,6 +344,14 @@ jobs:
|
|||||||
# future expansion
|
# future expansion
|
||||||
flags: backend
|
flags: backend
|
||||||
directory: src/
|
directory: src/
|
||||||
|
-
|
||||||
|
name: Upload backend test results to Codecov
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
uses: codecov/test-results-action@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
flags: backend
|
||||||
|
directory: src/
|
||||||
-
|
-
|
||||||
name: Use Node.js 20
|
name: Use Node.js 20
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
@ -472,16 +518,17 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
cache: "pipenv"
|
|
||||||
cache-dependency-path: 'Pipfile.lock'
|
|
||||||
-
|
-
|
||||||
name: Install pipenv + tools
|
name: Install uv
|
||||||
run: |
|
uses: astral-sh/setup-uv@v5
|
||||||
pip install --upgrade --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }} setuptools wheel
|
with:
|
||||||
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
|
enable-cache: true
|
||||||
|
python-version: ${{ steps.setup-python.outputs.python-version }}
|
||||||
-
|
-
|
||||||
name: Install Python dependencies
|
name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
|
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
||||||
-
|
-
|
||||||
name: Install system dependencies
|
name: Install system dependencies
|
||||||
run: |
|
run: |
|
||||||
@ -502,17 +549,21 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Generate requirements file
|
name: Generate requirements file
|
||||||
run: |
|
run: |
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} requirements > requirements.txt
|
uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt
|
||||||
-
|
-
|
||||||
name: Compile messages
|
name: Compile messages
|
||||||
run: |
|
run: |
|
||||||
cd src/
|
cd src/
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run python3 manage.py compilemessages
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
manage.py compilemessages
|
||||||
-
|
-
|
||||||
name: Collect static files
|
name: Collect static files
|
||||||
run: |
|
run: |
|
||||||
cd src/
|
cd src/
|
||||||
pipenv --python ${{ steps.setup-python.outputs.python-version }} run python3 manage.py collectstatic --no-input
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
manage.py collectstatic --no-input
|
||||||
-
|
-
|
||||||
name: Move files
|
name: Move files
|
||||||
run: |
|
run: |
|
||||||
@ -528,8 +579,8 @@ jobs:
|
|||||||
for file_name in .dockerignore \
|
for file_name in .dockerignore \
|
||||||
.env \
|
.env \
|
||||||
Dockerfile \
|
Dockerfile \
|
||||||
Pipfile \
|
pyproject.toml \
|
||||||
Pipfile.lock \
|
uv.lock \
|
||||||
requirements.txt \
|
requirements.txt \
|
||||||
LICENSE \
|
LICENSE \
|
||||||
README.md \
|
README.md \
|
||||||
@ -631,15 +682,17 @@ jobs:
|
|||||||
ref: main
|
ref: main
|
||||||
-
|
-
|
||||||
name: Set up Python
|
name: Set up Python
|
||||||
|
id: setup-python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
cache: "pipenv"
|
|
||||||
cache-dependency-path: 'Pipfile.lock'
|
|
||||||
-
|
-
|
||||||
name: Install pipenv + tools
|
name: Install uv
|
||||||
run: |
|
uses: astral-sh/setup-uv@v5
|
||||||
pip install --upgrade --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }} setuptools wheel
|
with:
|
||||||
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
|
enable-cache: true
|
||||||
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
-
|
-
|
||||||
name: Append Changelog to docs
|
name: Append Changelog to docs
|
||||||
id: append-Changelog
|
id: append-Changelog
|
||||||
@ -655,7 +708,10 @@ jobs:
|
|||||||
CURRENT_CHANGELOG=`tail --lines +2 changelog.md`
|
CURRENT_CHANGELOG=`tail --lines +2 changelog.md`
|
||||||
echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
|
echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
|
||||||
mv changelog-new.md changelog.md
|
mv changelog-new.md changelog.md
|
||||||
pipenv run pre-commit run --files changelog.md || true
|
uv run \
|
||||||
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
|
--dev \
|
||||||
|
pre-commit run --files changelog.md || true
|
||||||
git config --global user.name "github-actions"
|
git config --global user.name "github-actions"
|
||||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA"
|
git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA"
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -44,6 +44,7 @@ nosetests.xml
|
|||||||
coverage.xml
|
coverage.xml
|
||||||
*,cover
|
*,cover
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
|
junit.xml
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
*.mo
|
*.mo
|
||||||
|
@ -45,7 +45,6 @@ repos:
|
|||||||
- javascript
|
- javascript
|
||||||
- ts
|
- ts
|
||||||
- markdown
|
- markdown
|
||||||
exclude: "(^Pipfile\\.lock$)"
|
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- prettier@3.3.3
|
- prettier@3.3.3
|
||||||
- 'prettier-plugin-organize-imports@4.1.0'
|
- 'prettier-plugin-organize-imports@4.1.0'
|
||||||
@ -55,6 +54,10 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
||||||
|
rev: "v2.5.1"
|
||||||
|
hooks:
|
||||||
|
- id: pyproject-fmt
|
||||||
# Dockerfile hooks
|
# Dockerfile hooks
|
||||||
- repo: https://github.com/AleksaC/hadolint-py
|
- repo: https://github.com/AleksaC/hadolint-py
|
||||||
rev: v2.12.0.3
|
rev: v2.12.0.3
|
||||||
|
@ -1 +0,0 @@
|
|||||||
3.10.15
|
|
87
.ruff.toml
87
.ruff.toml
@ -1,87 +0,0 @@
|
|||||||
fix = true
|
|
||||||
line-length = 88
|
|
||||||
respect-gitignore = true
|
|
||||||
src = ["src"]
|
|
||||||
target-version = "py310"
|
|
||||||
output-format = "grouped"
|
|
||||||
show-fixes = true
|
|
||||||
|
|
||||||
# https://docs.astral.sh/ruff/settings/
|
|
||||||
# https://docs.astral.sh/ruff/rules/
|
|
||||||
[lint]
|
|
||||||
extend-select = [
|
|
||||||
"W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
|
|
||||||
"I", # https://docs.astral.sh/ruff/rules/#isort-i
|
|
||||||
"UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up
|
|
||||||
"COM", # https://docs.astral.sh/ruff/rules/#flake8-commas-com
|
|
||||||
"DJ", # https://docs.astral.sh/ruff/rules/#flake8-django-dj
|
|
||||||
"EXE", # https://docs.astral.sh/ruff/rules/#flake8-executable-exe
|
|
||||||
"ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc
|
|
||||||
"ICN", # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn
|
|
||||||
"G201", # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g
|
|
||||||
"INP", # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp
|
|
||||||
"PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie
|
|
||||||
"Q", # https://docs.astral.sh/ruff/rules/#flake8-quotes-q
|
|
||||||
"RSE", # https://docs.astral.sh/ruff/rules/#flake8-raise-rse
|
|
||||||
"T20", # https://docs.astral.sh/ruff/rules/#flake8-print-t20
|
|
||||||
"SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim
|
|
||||||
"TID", # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid
|
|
||||||
"TC", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc
|
|
||||||
"PLC", # https://docs.astral.sh/ruff/rules/#pylint-pl
|
|
||||||
"PLE", # https://docs.astral.sh/ruff/rules/#pylint-pl
|
|
||||||
"RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf
|
|
||||||
"FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly
|
|
||||||
"PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
|
|
||||||
"FBT", # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt
|
|
||||||
]
|
|
||||||
ignore = ["DJ001", "SIM105", "RUF012"]
|
|
||||||
|
|
||||||
[lint.per-file-ignores]
|
|
||||||
".github/scripts/*.py" = ["E501", "INP001", "SIM117"]
|
|
||||||
"docker/wait-for-redis.py" = ["INP001", "T201"]
|
|
||||||
"src/documents/file_handling.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/management/commands/document_consumer.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/management/commands/document_exporter.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/migrations/0012_auto_20160305_0040.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/migrations/0014_document_checksum.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/migrations/1003_mime_types.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/migrations/1012_fix_archive_files.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/models.py" = ["SIM115", "PTH"] # TODO PTH Enable & remove
|
|
||||||
"src/documents/parsers.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/signals/handlers.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tasks.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_api_app_config.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_classifier.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_consumer.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_file_handling.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_management.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_management_consumer.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_management_exporter.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_management_thumbnails.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_migration_archive_files.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_migration_document_pages_count.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_migration_mime_type.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_sanity_check.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_tasks.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/tests/test_views.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/documents/views.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/checks.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/settings.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/tests/test_checks.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/urls.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless/views.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_mail/mail.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_mail/preprocessor.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_tesseract/parsers.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_tesseract/tests/test_parser.py" = ["RUF001", "PTH"] # TODO PTH Enable & remove
|
|
||||||
"src/paperless_tika/tests/test_live_tika.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
"src/paperless_tika/tests/test_tika_parser.py" = ["PTH"] # TODO Enable & remove
|
|
||||||
# Testing
|
|
||||||
"*/tests/*.py" = ["E501", "SIM117"]
|
|
||||||
# Migrations
|
|
||||||
"*/migrations/*.py" = ["E501", "SIM", "T201"]
|
|
||||||
# Docker specific
|
|
||||||
"docker/rootfs/usr/local/bin/wait-for-redis.py" = ["INP001", "T201"]
|
|
||||||
|
|
||||||
[lint.isort]
|
|
||||||
force-single-line = true
|
|
@ -5,5 +5,6 @@
|
|||||||
/src-ui/ @paperless-ngx/frontend
|
/src-ui/ @paperless-ngx/frontend
|
||||||
|
|
||||||
/src/ @paperless-ngx/backend
|
/src/ @paperless-ngx/backend
|
||||||
Pipfile* @paperless-ngx/backend
|
pyproject.toml @paperless-ngx/backend
|
||||||
|
uv.lock @paperless-ngx/backend
|
||||||
*.py @paperless-ngx/backend
|
*.py @paperless-ngx/backend
|
||||||
|
45
Dockerfile
45
Dockerfile
@ -26,28 +26,11 @@ esac
|
|||||||
RUN set -eux \
|
RUN set -eux \
|
||||||
&& ./node_modules/.bin/ng build --configuration production
|
&& ./node_modules/.bin/ng build --configuration production
|
||||||
|
|
||||||
# Stage: pipenv-base
|
|
||||||
# Purpose: Generates a requirements.txt file for building
|
|
||||||
# Comments:
|
|
||||||
# - pipenv dependencies are not left in the final image
|
|
||||||
# - pipenv can't touch the final image somehow
|
|
||||||
FROM --platform=$BUILDPLATFORM docker.io/python:3.12-alpine AS pipenv-base
|
|
||||||
|
|
||||||
WORKDIR /usr/src/pipenv
|
|
||||||
|
|
||||||
COPY Pipfile* ./
|
|
||||||
|
|
||||||
RUN set -eux \
|
|
||||||
&& echo "Installing pipenv" \
|
|
||||||
&& python3 -m pip install --no-cache-dir --upgrade pipenv==2024.4.1 \
|
|
||||||
&& echo "Generating requirement.txt" \
|
|
||||||
&& pipenv requirements > requirements.txt
|
|
||||||
|
|
||||||
# Stage: s6-overlay-base
|
# Stage: s6-overlay-base
|
||||||
# Purpose: Installs s6-overlay and rootfs
|
# Purpose: Installs s6-overlay and rootfs
|
||||||
# Comments:
|
# Comments:
|
||||||
# - Don't leave anything extra in here either
|
# - Don't leave anything extra in here either
|
||||||
FROM docker.io/python:3.12-slim-bookworm AS s6-overlay-base
|
FROM ghcr.io/astral-sh/uv:0.6.3-python3.12-bookworm-slim AS s6-overlay-base
|
||||||
|
|
||||||
WORKDIR /usr/src/s6
|
WORKDIR /usr/src/s6
|
||||||
|
|
||||||
@ -123,9 +106,12 @@ ARG GS_VERSION=10.03.1
|
|||||||
# Set Python environment variables
|
# Set Python environment variables
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
PYTHONUNBUFFERED=1 \
|
PYTHONUNBUFFERED=1 \
|
||||||
# Ignore warning from Whitenoise
|
# Ignore warning from Whitenoise about async iterators
|
||||||
PYTHONWARNINGS="ignore:::django.http.response:517" \
|
PYTHONWARNINGS="ignore:::django.http.response:517" \
|
||||||
PNGX_CONTAINERIZED=1
|
PNGX_CONTAINERIZED=1 \
|
||||||
|
# https://docs.astral.sh/uv/reference/settings/#link-mode
|
||||||
|
UV_LINK_MODE=copy \
|
||||||
|
UV_CACHE_DIR=/cache/uv/
|
||||||
|
|
||||||
#
|
#
|
||||||
# Begin installation and configuration
|
# Begin installation and configuration
|
||||||
@ -213,36 +199,25 @@ WORKDIR /usr/src/paperless/src/
|
|||||||
|
|
||||||
# Python dependencies
|
# Python dependencies
|
||||||
# Change pretty frequently
|
# Change pretty frequently
|
||||||
COPY --chown=1000:1000 --from=pipenv-base /usr/src/pipenv/requirements.txt ./
|
COPY --chown=1000:1000 ["pyproject.toml", "uv.lock", "/usr/src/paperless/src/"]
|
||||||
|
|
||||||
# Packages needed only for building a few quick Python
|
# Packages needed only for building a few quick Python
|
||||||
# dependencies
|
# dependencies
|
||||||
ARG BUILD_PACKAGES="\
|
ARG BUILD_PACKAGES="\
|
||||||
build-essential \
|
build-essential \
|
||||||
git \
|
|
||||||
# https://www.psycopg.org/docs/install.html#prerequisites
|
|
||||||
libpq-dev \
|
|
||||||
# https://github.com/PyMySQL/mysqlclient#linux
|
# https://github.com/PyMySQL/mysqlclient#linux
|
||||||
default-libmysqlclient-dev \
|
default-libmysqlclient-dev \
|
||||||
pkg-config"
|
pkg-config"
|
||||||
|
|
||||||
ARG ZXING_VERSION=2.3.0
|
|
||||||
ARG PSYCOPG_VERSION=3.2.4
|
|
||||||
|
|
||||||
# hadolint ignore=DL3042
|
# hadolint ignore=DL3042
|
||||||
RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
|
RUN --mount=type=cache,target=${UV_CACHE_DIR},id=python-cache \
|
||||||
set -eux \
|
set -eux \
|
||||||
&& echo "Installing build system packages" \
|
&& echo "Installing build system packages" \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
|
||||||
&& python3 -m pip install --upgrade wheel \
|
|
||||||
&& echo "Installing Python requirements" \
|
&& echo "Installing Python requirements" \
|
||||||
&& curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \
|
&& uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt \
|
||||||
https://github.com/paperless-ngx/builder/releases/download/psycopg-${PSYCOPG_VERSION}/psycopg_c-${PSYCOPG_VERSION}-cp312-cp312-linux_x86_64.whl \
|
&& uv pip install --system --no-python-downloads --python-preference system --requirements requirements.txt \
|
||||||
https://github.com/paperless-ngx/builder/releases/download/psycopg-${PSYCOPG_VERSION}/psycopg_c-${PSYCOPG_VERSION}-cp312-cp312-linux_aarch64.whl \
|
|
||||||
https://github.com/paperless-ngx/builder/releases/download/zxing-${ZXING_VERSION}/zxing_cpp-${ZXING_VERSION}-cp312-cp312-linux_aarch64.whl \
|
|
||||||
https://github.com/paperless-ngx/builder/releases/download/zxing-${ZXING_VERSION}/zxing_cpp-${ZXING_VERSION}-cp312-cp312-linux_x86_64.whl \
|
|
||||||
&& python3 -m pip install --default-timeout=1000 --find-links . --requirement requirements.txt \
|
|
||||||
&& echo "Installing NLTK data" \
|
&& echo "Installing NLTK data" \
|
||||||
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
|
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
|
||||||
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \
|
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \
|
||||||
|
99
Pipfile
99
Pipfile
@ -1,99 +0,0 @@
|
|||||||
[[source]]
|
|
||||||
url = "https://pypi.python.org/simple"
|
|
||||||
verify_ssl = true
|
|
||||||
name = "pypi"
|
|
||||||
|
|
||||||
[packages]
|
|
||||||
dateparser = "~=1.2"
|
|
||||||
# WARNING: django does not use semver.
|
|
||||||
# Only patch versions are guaranteed to not introduce breaking changes.
|
|
||||||
django = "~=5.1.5"
|
|
||||||
django-allauth = {extras = ["mfa", "socialaccount"], version = "*"}
|
|
||||||
django-auditlog = "*"
|
|
||||||
django-celery-results = "*"
|
|
||||||
django-compression-middleware = "*"
|
|
||||||
django-cors-headers = "*"
|
|
||||||
django-extensions = "*"
|
|
||||||
django-filter = "~=25.1"
|
|
||||||
django-guardian = "*"
|
|
||||||
django-multiselectfield = "*"
|
|
||||||
django-soft-delete = "*"
|
|
||||||
djangorestframework = "~=3.15.2"
|
|
||||||
djangorestframework-guardian = "*"
|
|
||||||
drf-spectacular = "*"
|
|
||||||
drf-spectacular-sidecar = "*"
|
|
||||||
drf-writable-nested = "*"
|
|
||||||
bleach = "*"
|
|
||||||
celery = {extras = ["redis"], version = "*"}
|
|
||||||
channels = "~=4.2"
|
|
||||||
channels-redis = "*"
|
|
||||||
concurrent-log-handler = "*"
|
|
||||||
filelock = "*"
|
|
||||||
flower = "*"
|
|
||||||
gotenberg-client = "*"
|
|
||||||
granian = "*"
|
|
||||||
httpx-oauth = "*"
|
|
||||||
imap-tools = "*"
|
|
||||||
inotifyrecursive = "~=0.3"
|
|
||||||
jinja2 = "~=3.1"
|
|
||||||
langdetect = "*"
|
|
||||||
mysqlclient = "*"
|
|
||||||
nltk = "*"
|
|
||||||
ocrmypdf = "~=16.9"
|
|
||||||
pathvalidate = "*"
|
|
||||||
pdf2image = "*"
|
|
||||||
psycopg = {version = "*", extras = ["c"]}
|
|
||||||
python-dateutil = "*"
|
|
||||||
python-dotenv = "*"
|
|
||||||
python-gnupg = "*"
|
|
||||||
python-ipware = "*"
|
|
||||||
python-magic = "*"
|
|
||||||
pyzbar = "*"
|
|
||||||
rapidfuzz = "*"
|
|
||||||
redis = {extras = ["hiredis"], version = "*"}
|
|
||||||
scikit-learn = "~=1.6"
|
|
||||||
setproctitle = "*"
|
|
||||||
tika-client = "*"
|
|
||||||
tqdm = "*"
|
|
||||||
watchdog = "~=6.0"
|
|
||||||
whitenoise = "~=6.9"
|
|
||||||
whoosh = "~=2.7"
|
|
||||||
zxing-cpp = "*"
|
|
||||||
|
|
||||||
[dev-packages]
|
|
||||||
# Linting
|
|
||||||
pre-commit = "*"
|
|
||||||
ruff = "*"
|
|
||||||
# Testing
|
|
||||||
factory-boy = "*"
|
|
||||||
pytest = "*"
|
|
||||||
pytest-cov = "*"
|
|
||||||
pytest-django = "*"
|
|
||||||
pytest-httpx = "*"
|
|
||||||
pytest-env = "*"
|
|
||||||
pytest-sugar = "*"
|
|
||||||
pytest-xdist = "*"
|
|
||||||
pytest-mock = "*"
|
|
||||||
pytest-rerunfailures = "*"
|
|
||||||
imagehash = "*"
|
|
||||||
daphne = "*"
|
|
||||||
# Documentation
|
|
||||||
mkdocs-material = "*"
|
|
||||||
mkdocs-glightbox = "*"
|
|
||||||
|
|
||||||
[typing-dev]
|
|
||||||
mypy = "*"
|
|
||||||
types-Pillow = "*"
|
|
||||||
django-filter-stubs = "*"
|
|
||||||
types-python-dateutil = "*"
|
|
||||||
djangorestframework-stubs = {extras= ["compatible-mypy"], version="*"}
|
|
||||||
celery-types = "*"
|
|
||||||
django-stubs = {extras= ["compatible-mypy"], version="*"}
|
|
||||||
types-dateparser = "*"
|
|
||||||
types-bleach = "*"
|
|
||||||
types-redis = "*"
|
|
||||||
types-tqdm = "*"
|
|
||||||
types-Markdown = "*"
|
|
||||||
types-Pygments = "*"
|
|
||||||
types-colorama = "*"
|
|
||||||
types-setuptools = "*"
|
|
4812
Pipfile.lock
generated
4812
Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@ -509,6 +509,12 @@ Invoice_{{ custom_fields|get_cf_value("Select Field") }}_{{ custom_fields|get_cf
|
|||||||
|
|
||||||
This will create a path like `invoices/2022/01/01/Invoice_OptionTwo_20220101.pdf` if the custom field "Date Field" is set to January 1, 2022 and "Select Field" is set to `OptionTwo`.
|
This will create a path like `invoices/2022/01/01/Invoice_OptionTwo_20220101.pdf` if the custom field "Date Field" is set to January 1, 2022 and "Select Field" is set to `OptionTwo`.
|
||||||
|
|
||||||
|
You can also use a custom `slugify` filter to slufigy text:
|
||||||
|
|
||||||
|
```jinja
|
||||||
|
{{ title | slugify }}
|
||||||
|
```
|
||||||
|
|
||||||
## Automatic recovery of invalid PDFs {#pdf-recovery}
|
## Automatic recovery of invalid PDFs {#pdf-recovery}
|
||||||
|
|
||||||
Paperless will attempt to "clean" certain invalid PDFs with `qpdf` before processing if, for example, the mime_type
|
Paperless will attempt to "clean" certain invalid PDFs with `qpdf` before processing if, for example, the mime_type
|
||||||
|
@ -60,7 +60,7 @@ first-time setup.
|
|||||||
|
|
||||||
Every command is executed directly from the root folder of the project unless specified otherwise.
|
Every command is executed directly from the root folder of the project unless specified otherwise.
|
||||||
|
|
||||||
1. Install prerequisites + pipenv as mentioned in
|
1. Install prerequisites + [uv](https://github.com/astral-sh/uv) as mentioned in
|
||||||
[Bare metal route](setup.md#bare_metal).
|
[Bare metal route](setup.md#bare_metal).
|
||||||
|
|
||||||
2. Copy `paperless.conf.example` to `paperless.conf` and enable debug
|
2. Copy `paperless.conf.example` to `paperless.conf` and enable debug
|
||||||
@ -75,17 +75,13 @@ first-time setup.
|
|||||||
4. Install the Python dependencies:
|
4. Install the Python dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pipenv install --dev
|
$ uv sync --dev
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
|
||||||
|
|
||||||
Using a virtual environment is highly recommended. You can spawn one via `pipenv shell`.
|
|
||||||
|
|
||||||
5. Install pre-commit hooks:
|
5. Install pre-commit hooks:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pre-commit install
|
$ uv run pre-commit install
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Apply migrations and create a superuser for your development instance:
|
6. Apply migrations and create a superuser for your development instance:
|
||||||
@ -93,8 +89,8 @@ first-time setup.
|
|||||||
```bash
|
```bash
|
||||||
# src/
|
# src/
|
||||||
|
|
||||||
python3 manage.py migrate
|
$ uv run manage.py migrate
|
||||||
python3 manage.py createsuperuser
|
$ uv run manage.py createsuperuser
|
||||||
```
|
```
|
||||||
|
|
||||||
7. You can now either ...
|
7. You can now either ...
|
||||||
@ -164,6 +160,19 @@ $ ng build --configuration production
|
|||||||
complicated IF cases. Append `# noqa: E501` to disable this check
|
complicated IF cases. Append `# noqa: E501` to disable this check
|
||||||
for certain lines.
|
for certain lines.
|
||||||
|
|
||||||
|
### Package Management
|
||||||
|
|
||||||
|
Paperless uses `uv` to manage packages and virtual environments for both development and production.
|
||||||
|
To accomplish some common tasks using `uv`, follow the shortcuts below:
|
||||||
|
|
||||||
|
To upgrade all locked packages to the latest allowed versions: `uv lock --upgrade`
|
||||||
|
|
||||||
|
To upgrade a single locked package: `uv lock --upgrade-package <package>`
|
||||||
|
|
||||||
|
To add a new package: `uv add <package>`
|
||||||
|
|
||||||
|
To add a new development package `uv add --dev <package>`
|
||||||
|
|
||||||
## Front end development
|
## Front end development
|
||||||
|
|
||||||
The front end is built using AngularJS. In order to get started, you need Node.js (version 14.15+) and
|
The front end is built using AngularJS. In order to get started, you need Node.js (version 14.15+) and
|
||||||
@ -332,27 +341,21 @@ LANGUAGES = [
|
|||||||
The documentation is built using material-mkdocs, see their [documentation](https://squidfunk.github.io/mkdocs-material/reference/).
|
The documentation is built using material-mkdocs, see their [documentation](https://squidfunk.github.io/mkdocs-material/reference/).
|
||||||
If you want to build the documentation locally, this is how you do it:
|
If you want to build the documentation locally, this is how you do it:
|
||||||
|
|
||||||
1. Have an active pipenv shell (`pipenv shell`) and install Python dependencies:
|
1. Build the documentation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pipenv install --dev
|
$ uv run mkdocs build --config-file mkdocs.yml
|
||||||
```
|
|
||||||
|
|
||||||
2. Build the documentation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdocs build --config-file mkdocs.yml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
_alternatively..._
|
_alternatively..._
|
||||||
|
|
||||||
3. Serve the documentation. This will spin up a
|
2. Serve the documentation. This will spin up a
|
||||||
copy of the documentation at http://127.0.0.1:8000
|
copy of the documentation at http://127.0.0.1:8000
|
||||||
that will automatically refresh every time you change
|
that will automatically refresh every time you change
|
||||||
something.
|
something.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdocs serve
|
$ uv run mkdocs serve
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building the Docker image
|
## Building the Docker image
|
||||||
|
@ -380,6 +380,12 @@ are released, dependency support is confirmed, etc.
|
|||||||
dependencies. This is an alternative to the above and may require adjusting
|
dependencies. This is an alternative to the above and may require adjusting
|
||||||
the example scripts to utilize the virtual environment paths
|
the example scripts to utilize the virtual environment paths
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
If you use modern Python tooling, such as `uv`, installation will not include
|
||||||
|
dependencies for Postgres or Mariadb. You can select those extras with `--extra <EXTRA>`
|
||||||
|
or all with `--all-extras`
|
||||||
|
|
||||||
9. Go to `/opt/paperless/src`, and execute the following commands:
|
9. Go to `/opt/paperless/src`, and execute the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
401
pyproject.toml
Normal file
401
pyproject.toml
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
[project]
|
||||||
|
name = "paperless-ngx"
|
||||||
|
version = "2.14.7"
|
||||||
|
description = "A community-supported supercharged version of paperless: scan, index and archive all your physical documents"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
|
]
|
||||||
|
# TODO: Move certain things to groups and then utilize that further
|
||||||
|
# This will allow testing to not install a webserver, mysql, etc
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
"bleach~=6.2.0",
|
||||||
|
"celery[redis]~=5.4.0",
|
||||||
|
"channels~=4.2",
|
||||||
|
"channels-redis~=4.2",
|
||||||
|
"concurrent-log-handler~=0.9.25",
|
||||||
|
"dateparser~=1.2",
|
||||||
|
# WARNING: django does not use semver.
|
||||||
|
# Only patch versions are guaranteed to not introduce breaking changes.
|
||||||
|
"django~=5.1.6",
|
||||||
|
"django-allauth[socialaccount,mfa]~=65.4.0",
|
||||||
|
"django-auditlog~=3.0.0",
|
||||||
|
"django-celery-results~=2.5.1",
|
||||||
|
"django-compression-middleware~=0.5.0",
|
||||||
|
"django-cors-headers~=4.7.0",
|
||||||
|
"django-extensions~=3.2.3",
|
||||||
|
"django-filter~=25.1",
|
||||||
|
"django-guardian~=2.4.0",
|
||||||
|
"django-multiselectfield~=0.1.13",
|
||||||
|
"django-soft-delete~=1.0.18",
|
||||||
|
"djangorestframework~=3.15",
|
||||||
|
"djangorestframework-guardian~=0.3.0",
|
||||||
|
"drf-spectacular~=0.28",
|
||||||
|
"drf-spectacular-sidecar~=2025.2.1",
|
||||||
|
"drf-writable-nested~=0.7.1",
|
||||||
|
"filelock~=3.17.0",
|
||||||
|
"flower~=2.0.1",
|
||||||
|
"gotenberg-client~=0.9.0",
|
||||||
|
"httpx-oauth~=0.16",
|
||||||
|
"imap-tools~=1.10.0",
|
||||||
|
"inotifyrecursive~=0.3",
|
||||||
|
"jinja2~=3.1.5",
|
||||||
|
"langdetect~=1.0.9",
|
||||||
|
"nltk~=3.9.1",
|
||||||
|
"ocrmypdf~=16.9.0",
|
||||||
|
"pathvalidate~=3.2.3",
|
||||||
|
"pdf2image~=1.17.0",
|
||||||
|
"python-dateutil~=2.9.0",
|
||||||
|
"python-dotenv~=1.0.1",
|
||||||
|
"python-gnupg~=0.5.4",
|
||||||
|
"python-ipware~=3.0.0",
|
||||||
|
"python-magic~=0.4.27",
|
||||||
|
"pyzbar~=0.1.9",
|
||||||
|
"rapidfuzz~=3.12.1",
|
||||||
|
"redis[hiredis]~=5.2.1",
|
||||||
|
"scikit-learn~=1.6.1",
|
||||||
|
"setproctitle~=1.3.4",
|
||||||
|
"tika-client~=0.9.0",
|
||||||
|
"tqdm~=4.67.1",
|
||||||
|
"watchdog~=6.0",
|
||||||
|
"whitenoise~=6.9",
|
||||||
|
"whoosh~=2.7",
|
||||||
|
"zxing-cpp~=2.3.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
optional-dependencies.mariadb = [
|
||||||
|
"mysqlclient~=2.2.7",
|
||||||
|
]
|
||||||
|
optional-dependencies.postgres = [
|
||||||
|
"psycopg[c]==3.2.4",
|
||||||
|
# Direct dependency for proper resolution of the pre-built wheels
|
||||||
|
"psycopg-c==3.2.4",
|
||||||
|
]
|
||||||
|
optional-dependencies.webserver = [
|
||||||
|
"granian~=1.7.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
|
||||||
|
dev = [
|
||||||
|
{ "include-group" = "docs" },
|
||||||
|
{ "include-group" = "testing" },
|
||||||
|
{ "include-group" = "lint" },
|
||||||
|
]
|
||||||
|
|
||||||
|
docs = [
|
||||||
|
"mkdocs-glightbox~=0.4.0",
|
||||||
|
"mkdocs-material~=9.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
testing = [
|
||||||
|
"daphne",
|
||||||
|
"factory-boy~=3.3.1",
|
||||||
|
"imagehash",
|
||||||
|
"pytest~=8.3.3",
|
||||||
|
"pytest-cov~=6.0.0",
|
||||||
|
"pytest-django~=4.10.0",
|
||||||
|
"pytest-env",
|
||||||
|
"pytest-httpx",
|
||||||
|
"pytest-mock",
|
||||||
|
"pytest-rerunfailures",
|
||||||
|
"pytest-sugar",
|
||||||
|
"pytest-xdist",
|
||||||
|
]
|
||||||
|
|
||||||
|
lint = [
|
||||||
|
"pre-commit~=4.1.0",
|
||||||
|
"pre-commit-uv~=4.1.3",
|
||||||
|
"ruff~=0.9.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
typing = [
|
||||||
|
"celery-types",
|
||||||
|
"django-filter-stubs",
|
||||||
|
"django-stubs[compatible-mypy]",
|
||||||
|
"djangorestframework-stubs[compatible-mypy]",
|
||||||
|
"mypy",
|
||||||
|
"types-bleach",
|
||||||
|
"types-colorama",
|
||||||
|
"types-dateparser",
|
||||||
|
"types-markdown",
|
||||||
|
"types-pygments",
|
||||||
|
"types-python-dateutil",
|
||||||
|
"types-redis",
|
||||||
|
"types-setuptools",
|
||||||
|
"types-tqdm",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py310"
|
||||||
|
line-length = 88
|
||||||
|
src = [
|
||||||
|
"src",
|
||||||
|
]
|
||||||
|
respect-gitignore = true
|
||||||
|
# https://docs.astral.sh/ruff/settings/
|
||||||
|
fix = true
|
||||||
|
show-fixes = true
|
||||||
|
|
||||||
|
output-format = "grouped"
|
||||||
|
# https://docs.astral.sh/ruff/rules/
|
||||||
|
lint.extend-select = [
|
||||||
|
"COM", # https://docs.astral.sh/ruff/rules/#flake8-commas-com
|
||||||
|
"DJ", # https://docs.astral.sh/ruff/rules/#flake8-django-dj
|
||||||
|
"EXE", # https://docs.astral.sh/ruff/rules/#flake8-executable-exe
|
||||||
|
"FBT", # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt
|
||||||
|
"FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly
|
||||||
|
"G201", # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g
|
||||||
|
"I", # https://docs.astral.sh/ruff/rules/#isort-i
|
||||||
|
"ICN", # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn
|
||||||
|
"INP", # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp
|
||||||
|
"ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc
|
||||||
|
"PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie
|
||||||
|
"PLC", # https://docs.astral.sh/ruff/rules/#pylint-pl
|
||||||
|
"PLE", # https://docs.astral.sh/ruff/rules/#pylint-pl
|
||||||
|
"PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
|
||||||
|
"Q", # https://docs.astral.sh/ruff/rules/#flake8-quotes-q
|
||||||
|
"RSE", # https://docs.astral.sh/ruff/rules/#flake8-raise-rse
|
||||||
|
"RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf
|
||||||
|
"SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim
|
||||||
|
"T20", # https://docs.astral.sh/ruff/rules/#flake8-print-t20
|
||||||
|
"TC", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc
|
||||||
|
"TID", # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid
|
||||||
|
"UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up
|
||||||
|
"W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
|
||||||
|
]
|
||||||
|
lint.ignore = [
|
||||||
|
"DJ001",
|
||||||
|
"RUF012",
|
||||||
|
"SIM105",
|
||||||
|
]
|
||||||
|
# Migrations
|
||||||
|
lint.per-file-ignores."*/migrations/*.py" = [
|
||||||
|
"E501",
|
||||||
|
"SIM",
|
||||||
|
"T201",
|
||||||
|
]
|
||||||
|
# Testing
|
||||||
|
lint.per-file-ignores."*/tests/*.py" = [
|
||||||
|
"E501",
|
||||||
|
"SIM117",
|
||||||
|
]
|
||||||
|
lint.per-file-ignores.".github/scripts/*.py" = [
|
||||||
|
"E501",
|
||||||
|
"INP001",
|
||||||
|
"SIM117",
|
||||||
|
]
|
||||||
|
# Docker specific
|
||||||
|
lint.per-file-ignores."docker/rootfs/usr/local/bin/wait-for-redis.py" = [
|
||||||
|
"INP001",
|
||||||
|
"T201",
|
||||||
|
]
|
||||||
|
lint.per-file-ignores."docker/wait-for-redis.py" = [
|
||||||
|
"INP001",
|
||||||
|
"T201",
|
||||||
|
]
|
||||||
|
lint.per-file-ignores."src/documents/file_handling.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/management/commands/document_consumer.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/management/commands/document_exporter.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/migrations/0012_auto_20160305_0040.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/migrations/0014_document_checksum.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/migrations/1003_mime_types.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/migrations/1012_fix_archive_files.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/models.py" = [
|
||||||
|
"PTH",
|
||||||
|
"SIM115",
|
||||||
|
] # TODO PTH Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/parsers.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/signals/handlers.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tasks.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_api_app_config.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_classifier.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_consumer.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_file_handling.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_management.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_management_consumer.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_management_exporter.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_management_thumbnails.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_migration_archive_files.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_migration_document_pages_count.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_migration_mime_type.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_sanity_check.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_tasks.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/tests/test_views.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/documents/views.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/checks.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/settings.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/tests/test_checks.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/urls.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless/views.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_mail/mail.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_mail/preprocessor.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_tesseract/parsers.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [
|
||||||
|
"PTH",
|
||||||
|
"RUF001",
|
||||||
|
] # TODO PTH Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_tika/tests/test_live_tika.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.per-file-ignores."src/paperless_tika/tests/test_tika_parser.py" = [
|
||||||
|
"PTH",
|
||||||
|
] # TODO Enable & remove
|
||||||
|
lint.isort.force-single-line = true
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
minversion = "8.0"
|
||||||
|
pythonpath = [
|
||||||
|
"src",
|
||||||
|
]
|
||||||
|
testpaths = [
|
||||||
|
"src/documents/tests/",
|
||||||
|
"src/paperless/tests/",
|
||||||
|
"src/paperless_mail/tests/",
|
||||||
|
"src/paperless_tesseract/tests/",
|
||||||
|
"src/paperless_tika/tests",
|
||||||
|
]
|
||||||
|
addopts = [
|
||||||
|
"--pythonwarnings=all",
|
||||||
|
"--cov",
|
||||||
|
"--cov-report=html",
|
||||||
|
"--cov-report=xml",
|
||||||
|
"--numprocesses=auto",
|
||||||
|
"--maxprocesses=16",
|
||||||
|
"--quiet",
|
||||||
|
"--durations=50",
|
||||||
|
"--junitxml=junit.xml",
|
||||||
|
"-o junit_family=legacy",
|
||||||
|
]
|
||||||
|
norecursedirs = [ "src/locale/", ".venv/", "src-ui/" ]
|
||||||
|
|
||||||
|
DJANGO_SETTINGS_MODULE = "paperless.settings"
|
||||||
|
|
||||||
|
[tool.pytest_env]
|
||||||
|
PAPERLESS_DISABLE_DBHANDLER = "true"
|
||||||
|
PAPERLESS_CACHE_BACKEND = "django.core.cache.backends.locmem.LocMemCache"
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
source = [
|
||||||
|
"src/",
|
||||||
|
]
|
||||||
|
omit = [
|
||||||
|
"*/tests/*",
|
||||||
|
"manage.py",
|
||||||
|
"paperless/wsgi.py",
|
||||||
|
"paperless/auth.py",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.coverage.report]
|
||||||
|
exclude_also = [
|
||||||
|
"if settings.AUDIT_LOG_ENABLED:",
|
||||||
|
"if AUDIT_LOG_ENABLED:",
|
||||||
|
"if TYPE_CHECKING:",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
plugins = [
|
||||||
|
"mypy_django_plugin.main",
|
||||||
|
"mypy_drf_plugin.main",
|
||||||
|
"numpy.typing.mypy_plugin",
|
||||||
|
]
|
||||||
|
check_untyped_defs = true
|
||||||
|
disallow_any_generics = true
|
||||||
|
disallow_incomplete_defs = true
|
||||||
|
disallow_untyped_defs = true
|
||||||
|
warn_redundant_casts = true
|
||||||
|
warn_unused_ignores = true
|
||||||
|
|
||||||
|
[tool.uv]
|
||||||
|
required-version = ">=0.5.14"
|
||||||
|
package = false
|
||||||
|
environments = [
|
||||||
|
"sys_platform == 'darwin'",
|
||||||
|
"sys_platform == 'linux'",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
# Markers are chosen to select these almost exclusively when building the Docker image
|
||||||
|
psycopg-c = [
|
||||||
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||||
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.4/psycopg_c-3.2.4-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
||||||
|
]
|
||||||
|
zxing-cpp = [
|
||||||
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
|
||||||
|
{ url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.django-stubs]
|
||||||
|
django_settings_module = "paperless.settings"
|
@ -12,4 +12,13 @@ module.exports = {
|
|||||||
'^src/(.*)': '<rootDir>/src/$1',
|
'^src/(.*)': '<rootDir>/src/$1',
|
||||||
},
|
},
|
||||||
workerIdleMemoryLimit: '512MB',
|
workerIdleMemoryLimit: '512MB',
|
||||||
|
reporters: [
|
||||||
|
'default',
|
||||||
|
[
|
||||||
|
'jest-junit',
|
||||||
|
{
|
||||||
|
classNameTemplate: '{filepath}/{classname}: {title}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
@ -545,7 +545,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">364</context>
|
<context context-type="linenumber">360</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||||
@ -1034,8 +1034,8 @@
|
|||||||
<context context-type="linenumber">188</context>
|
<context context-type="linenumber">188</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7791089127901960679" datatype="html">
|
<trans-unit id="2678648946508279627" datatype="html">
|
||||||
<source>Default zoom:</source>
|
<source>Default zoom</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">194</context>
|
<context context-type="linenumber">194</context>
|
||||||
@ -1076,11 +1076,79 @@
|
|||||||
<context context-type="linenumber">213</context>
|
<context context-type="linenumber">213</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="6760166989231109310" datatype="html">
|
||||||
|
<source>Global search</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">217</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">120</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="2818183879511244335" datatype="html">
|
||||||
|
<source>Do not include advanced search results</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">220</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="3969258421469113318" datatype="html">
|
||||||
|
<source>Full search links to</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">226</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="6631288852577115923" datatype="html">
|
||||||
|
<source>Title and content search</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">230</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="1010505078885609376" datatype="html">
|
||||||
|
<source>Advanced search</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">231</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.html</context>
|
||||||
|
<context context-type="linenumber">24</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||||
|
<context context-type="linenumber">173</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="8508424367627989968" datatype="html">
|
||||||
|
<source>Bulk editing</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">236</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="8158899674926420054" datatype="html">
|
||||||
|
<source>Show confirmation dialogs</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">239</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="290238406234356122" datatype="html">
|
||||||
|
<source>Apply on close</source>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">240</context>
|
||||||
|
</context-group>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="8104421162933956065" datatype="html">
|
<trans-unit id="8104421162933956065" datatype="html">
|
||||||
<source>Notes</source>
|
<source>Notes</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">217</context>
|
<context context-type="linenumber">244</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||||
@ -1097,84 +1165,16 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="293524471897878391" datatype="html">
|
<trans-unit id="293524471897878391" datatype="html">
|
||||||
<source>Enable notes</source>
|
<source>Enable notes</source>
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">220</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="8508424367627989968" datatype="html">
|
|
||||||
<source>Bulk editing</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">224</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="8158899674926420054" datatype="html">
|
|
||||||
<source>Show confirmation dialogs</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">227</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="290238406234356122" datatype="html">
|
|
||||||
<source>Apply on close</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">228</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="6760166989231109310" datatype="html">
|
|
||||||
<source>Global search</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">232</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">120</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="2818183879511244335" datatype="html">
|
|
||||||
<source>Do not include advanced search results</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">235</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="3969258421469113318" datatype="html">
|
|
||||||
<source>Full search links to</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">243</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="6631288852577115923" datatype="html">
|
|
||||||
<source>Title and content search</source>
|
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">247</context>
|
<context context-type="linenumber">247</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1010505078885609376" datatype="html">
|
|
||||||
<source>Advanced search</source>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">248</context>
|
|
||||||
</context-group>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/global-search/global-search.component.html</context>
|
|
||||||
<context context-type="linenumber">24</context>
|
|
||||||
</context-group>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
|
||||||
<context context-type="linenumber">173</context>
|
|
||||||
</context-group>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="7314814725704332646" datatype="html">
|
<trans-unit id="7314814725704332646" datatype="html">
|
||||||
<source>Permissions</source>
|
<source>Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">262</context>
|
<context context-type="linenumber">258</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.html</context>
|
||||||
@ -1237,28 +1237,28 @@
|
|||||||
<source>Default Permissions</source>
|
<source>Default Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">265</context>
|
<context context-type="linenumber">261</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8222269449891326545" datatype="html">
|
<trans-unit id="6544153565064275581" datatype="html">
|
||||||
<source> Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI </source>
|
<source> Settings apply to this user account for objects (Tags, Mail Rules, etc. but not documents) created via the web UI. </source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">269,271</context>
|
<context context-type="linenumber">265,267</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4292903881380648974" datatype="html">
|
<trans-unit id="4292903881380648974" datatype="html">
|
||||||
<source>Default Owner</source>
|
<source>Default Owner</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">276</context>
|
<context context-type="linenumber">272</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="734147282056744882" datatype="html">
|
<trans-unit id="734147282056744882" datatype="html">
|
||||||
<source>Objects without an owner can be viewed and edited by all users</source>
|
<source>Objects without an owner can be viewed and edited by all users</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">280</context>
|
<context context-type="linenumber">276</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
||||||
@ -1269,34 +1269,34 @@
|
|||||||
<source>Default View Permissions</source>
|
<source>Default View Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">285</context>
|
<context context-type="linenumber">281</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2191775412581217688" datatype="html">
|
<trans-unit id="2191775412581217688" datatype="html">
|
||||||
<source>Users:</source>
|
<source>Users:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">290</context>
|
|
||||||
</context-group>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
|
||||||
<context context-type="linenumber">317</context>
|
|
||||||
</context-group>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
|
||||||
<context context-type="linenumber">200</context>
|
|
||||||
</context-group>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
|
||||||
<context context-type="linenumber">219</context>
|
|
||||||
</context-group>
|
|
||||||
<context-group purpose="location">
|
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
|
||||||
<context context-type="linenumber">286</context>
|
<context context-type="linenumber">286</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
|
<context context-type="linenumber">313</context>
|
||||||
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">305</context>
|
<context context-type="linenumber">201</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">220</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">287</context>
|
||||||
|
</context-group>
|
||||||
|
<context-group purpose="location">
|
||||||
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
|
<context context-type="linenumber">306</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
||||||
@ -1311,27 +1311,27 @@
|
|||||||
<source>Groups:</source>
|
<source>Groups:</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">300</context>
|
<context context-type="linenumber">296</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">327</context>
|
<context context-type="linenumber">323</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">208</context>
|
<context context-type="linenumber">209</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">227</context>
|
<context context-type="linenumber">228</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">294</context>
|
<context context-type="linenumber">295</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">313</context>
|
<context context-type="linenumber">314</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
||||||
@ -1346,22 +1346,22 @@
|
|||||||
<source>Default Edit Permissions</source>
|
<source>Default Edit Permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">312</context>
|
<context context-type="linenumber">308</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3728984448750213892" datatype="html">
|
<trans-unit id="3728984448750213892" datatype="html">
|
||||||
<source>Edit permissions also grant viewing permissions</source>
|
<source>Edit permissions also grant viewing permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">336</context>
|
<context context-type="linenumber">332</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">233</context>
|
<context context-type="linenumber">234</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">319</context>
|
<context context-type="linenumber">320</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/input/permissions/permissions-form/permissions-form.component.html</context>
|
||||||
@ -1372,7 +1372,7 @@
|
|||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">344</context>
|
<context context-type="linenumber">340</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.html</context>
|
<context context-type="sourcefile">src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.html</context>
|
||||||
@ -1383,49 +1383,49 @@
|
|||||||
<source>Document processing</source>
|
<source>Document processing</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">347</context>
|
<context context-type="linenumber">343</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3656786776644872398" datatype="html">
|
<trans-unit id="3656786776644872398" datatype="html">
|
||||||
<source>Show notifications when new documents are detected</source>
|
<source>Show notifications when new documents are detected</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">351</context>
|
<context context-type="linenumber">347</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6057053428592387613" datatype="html">
|
<trans-unit id="6057053428592387613" datatype="html">
|
||||||
<source>Show notifications when document processing completes successfully</source>
|
<source>Show notifications when document processing completes successfully</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">352</context>
|
<context context-type="linenumber">348</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="370315664367425513" datatype="html">
|
<trans-unit id="370315664367425513" datatype="html">
|
||||||
<source>Show notifications when document processing fails</source>
|
<source>Show notifications when document processing fails</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">353</context>
|
<context context-type="linenumber">349</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6838309441164918531" datatype="html">
|
<trans-unit id="6838309441164918531" datatype="html">
|
||||||
<source>Suppress notifications on dashboard</source>
|
<source>Suppress notifications on dashboard</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">354</context>
|
<context context-type="linenumber">350</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2741919327232918179" datatype="html">
|
<trans-unit id="2741919327232918179" datatype="html">
|
||||||
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
<source>This will suppress all messages about document processing status on the dashboard.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">354</context>
|
<context context-type="linenumber">350</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2159130950882492111" datatype="html">
|
<trans-unit id="2159130950882492111" datatype="html">
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">365</context>
|
<context context-type="linenumber">361</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/confirm-dialog/confirm-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/confirm-dialog/confirm-dialog.component.ts</context>
|
||||||
@ -1736,7 +1736,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">87</context>
|
<context context-type="linenumber">88</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||||
@ -3543,7 +3543,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">83</context>
|
<context context-type="linenumber">84</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||||
@ -4396,7 +4396,7 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">129</context>
|
<context context-type="linenumber">130</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html</context>
|
||||||
@ -4787,227 +4787,227 @@
|
|||||||
<source>Assign owner</source>
|
<source>Assign owner</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">194</context>
|
<context context-type="linenumber">195</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1749184201773078639" datatype="html">
|
<trans-unit id="1749184201773078639" datatype="html">
|
||||||
<source>Assign view permissions</source>
|
<source>Assign view permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">196</context>
|
<context context-type="linenumber">197</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1744964187586405039" datatype="html">
|
<trans-unit id="1744964187586405039" datatype="html">
|
||||||
<source>Assign edit permissions</source>
|
<source>Assign edit permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">215</context>
|
<context context-type="linenumber">216</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6236311670364192011" datatype="html">
|
<trans-unit id="6236311670364192011" datatype="html">
|
||||||
<source>Remove tags</source>
|
<source>Remove tags</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">242</context>
|
<context context-type="linenumber">243</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7890599006071681081" datatype="html">
|
<trans-unit id="7890599006071681081" datatype="html">
|
||||||
<source>Remove all</source>
|
<source>Remove all</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">243</context>
|
<context context-type="linenumber">244</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">249</context>
|
<context context-type="linenumber">250</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">255</context>
|
<context context-type="linenumber">256</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">261</context>
|
<context context-type="linenumber">262</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">267</context>
|
<context context-type="linenumber">268</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">274</context>
|
<context context-type="linenumber">275</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">280</context>
|
<context context-type="linenumber">281</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8636414563726517994" datatype="html">
|
<trans-unit id="8636414563726517994" datatype="html">
|
||||||
<source>Remove correspondents</source>
|
<source>Remove correspondents</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">248</context>
|
<context context-type="linenumber">249</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5305293055593064952" datatype="html">
|
<trans-unit id="5305293055593064952" datatype="html">
|
||||||
<source>Remove document types</source>
|
<source>Remove document types</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">254</context>
|
<context context-type="linenumber">255</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2400388879708187" datatype="html">
|
<trans-unit id="2400388879708187" datatype="html">
|
||||||
<source>Remove storage paths</source>
|
<source>Remove storage paths</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">260</context>
|
<context context-type="linenumber">261</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4324304327041955720" datatype="html">
|
<trans-unit id="4324304327041955720" datatype="html">
|
||||||
<source>Remove custom fields</source>
|
<source>Remove custom fields</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">266</context>
|
<context context-type="linenumber">267</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8367536502602515064" datatype="html">
|
<trans-unit id="8367536502602515064" datatype="html">
|
||||||
<source>Remove owners</source>
|
<source>Remove owners</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">273</context>
|
<context context-type="linenumber">274</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3393772184866313281" datatype="html">
|
<trans-unit id="3393772184866313281" datatype="html">
|
||||||
<source>Remove permissions</source>
|
<source>Remove permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">279</context>
|
<context context-type="linenumber">280</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3145629643370481114" datatype="html">
|
<trans-unit id="3145629643370481114" datatype="html">
|
||||||
<source>View permissions</source>
|
<source>View permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">282</context>
|
<context context-type="linenumber">283</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1946660694635960249" datatype="html">
|
<trans-unit id="1946660694635960249" datatype="html">
|
||||||
<source>Edit permissions</source>
|
<source>Edit permissions</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">301</context>
|
<context context-type="linenumber">302</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8987736563240025468" datatype="html">
|
<trans-unit id="8987736563240025468" datatype="html">
|
||||||
<source>Email subject</source>
|
<source>Email subject</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">329</context>
|
<context context-type="linenumber">330</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8239445959209739142" datatype="html">
|
<trans-unit id="8239445959209739142" datatype="html">
|
||||||
<source>Email body</source>
|
<source>Email body</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">330</context>
|
<context context-type="linenumber">331</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1222152280703048012" datatype="html">
|
<trans-unit id="1222152280703048012" datatype="html">
|
||||||
<source>Email recipients</source>
|
<source>Email recipients</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">331</context>
|
<context context-type="linenumber">332</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7916910101279824329" datatype="html">
|
<trans-unit id="7916910101279824329" datatype="html">
|
||||||
<source>Attach document</source>
|
<source>Attach document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">332</context>
|
<context context-type="linenumber">333</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5028001922785731600" datatype="html">
|
<trans-unit id="5028001922785731600" datatype="html">
|
||||||
<source>Webhook url</source>
|
<source>Webhook url</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">340</context>
|
<context context-type="linenumber">341</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7491983459027245019" datatype="html">
|
<trans-unit id="7491983459027245019" datatype="html">
|
||||||
<source>Use parameters for webhook body</source>
|
<source>Use parameters for webhook body</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">342</context>
|
<context context-type="linenumber">343</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4078214298308732810" datatype="html">
|
<trans-unit id="4078214298308732810" datatype="html">
|
||||||
<source>Send webhook payload as JSON</source>
|
<source>Send webhook payload as JSON</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">343</context>
|
<context context-type="linenumber">344</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6806149889743731985" datatype="html">
|
<trans-unit id="6806149889743731985" datatype="html">
|
||||||
<source>Webhook params</source>
|
<source>Webhook params</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">346</context>
|
<context context-type="linenumber">347</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7089924379374330" datatype="html">
|
<trans-unit id="7089924379374330" datatype="html">
|
||||||
<source>Webhook body</source>
|
<source>Webhook body</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">348</context>
|
<context context-type="linenumber">349</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3829826512656746316" datatype="html">
|
<trans-unit id="3829826512656746316" datatype="html">
|
||||||
<source>Webhook headers</source>
|
<source>Webhook headers</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">350</context>
|
<context context-type="linenumber">351</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2114525789021600887" datatype="html">
|
<trans-unit id="2114525789021600887" datatype="html">
|
||||||
<source>Include document</source>
|
<source>Include document</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html</context>
|
||||||
<context context-type="linenumber">351</context>
|
<context context-type="linenumber">352</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4626030417479279989" datatype="html">
|
<trans-unit id="4626030417479279989" datatype="html">
|
||||||
<source>Consume Folder</source>
|
<source>Consume Folder</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">64</context>
|
<context context-type="linenumber">65</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="526966086395145275" datatype="html">
|
<trans-unit id="526966086395145275" datatype="html">
|
||||||
<source>API Upload</source>
|
<source>API Upload</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">68</context>
|
<context context-type="linenumber">69</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7502272564743467653" datatype="html">
|
<trans-unit id="7502272564743467653" datatype="html">
|
||||||
<source>Mail Fetch</source>
|
<source>Mail Fetch</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">72</context>
|
<context context-type="linenumber">73</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="235571817610183244" datatype="html">
|
<trans-unit id="235571817610183244" datatype="html">
|
||||||
<source>Web UI</source>
|
<source>Web UI</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">76</context>
|
<context context-type="linenumber">77</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3553216189604488439" datatype="html">
|
<trans-unit id="3553216189604488439" datatype="html">
|
||||||
<source>Modified</source>
|
<source>Modified</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">91</context>
|
<context context-type="linenumber">92</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/data/document.ts</context>
|
<context context-type="sourcefile">src/app/data/document.ts</context>
|
||||||
@ -5018,70 +5018,70 @@
|
|||||||
<source>Custom Field</source>
|
<source>Custom Field</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">95</context>
|
<context context-type="linenumber">96</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8696908693776094667" datatype="html">
|
<trans-unit id="8696908693776094667" datatype="html">
|
||||||
<source>Consumption Started</source>
|
<source>Consumption Started</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">102</context>
|
<context context-type="linenumber">103</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7858311467093621703" datatype="html">
|
<trans-unit id="7858311467093621703" datatype="html">
|
||||||
<source>Document Added</source>
|
<source>Document Added</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">106</context>
|
<context context-type="linenumber">107</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7955486237346046731" datatype="html">
|
<trans-unit id="7955486237346046731" datatype="html">
|
||||||
<source>Document Updated</source>
|
<source>Document Updated</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">110</context>
|
<context context-type="linenumber">111</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="9172233176401579786" datatype="html">
|
<trans-unit id="9172233176401579786" datatype="html">
|
||||||
<source>Scheduled</source>
|
<source>Scheduled</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">114</context>
|
<context context-type="linenumber">115</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5502398334173581061" datatype="html">
|
<trans-unit id="5502398334173581061" datatype="html">
|
||||||
<source>Assignment</source>
|
<source>Assignment</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">121</context>
|
<context context-type="linenumber">122</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6234812824772766804" datatype="html">
|
<trans-unit id="6234812824772766804" datatype="html">
|
||||||
<source>Removal</source>
|
<source>Removal</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">125</context>
|
<context context-type="linenumber">126</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4206419737792796794" datatype="html">
|
<trans-unit id="4206419737792796794" datatype="html">
|
||||||
<source>Webhook</source>
|
<source>Webhook</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">133</context>
|
<context context-type="linenumber">134</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3138206142174978019" datatype="html">
|
<trans-unit id="3138206142174978019" datatype="html">
|
||||||
<source>Create new workflow</source>
|
<source>Create new workflow</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">229</context>
|
<context context-type="linenumber">231</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5996779210524133604" datatype="html">
|
<trans-unit id="5996779210524133604" datatype="html">
|
||||||
<source>Edit workflow</source>
|
<source>Edit workflow</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
|
||||||
<context context-type="linenumber">233</context>
|
<context context-type="linenumber">235</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7376342558017986274" datatype="html">
|
<trans-unit id="7376342558017986274" datatype="html">
|
||||||
|
245
src-ui/package-lock.json
generated
245
src-ui/package-lock.json
generated
@ -44,23 +44,24 @@
|
|||||||
"@angular-devkit/build-angular": "^19.0.4",
|
"@angular-devkit/build-angular": "^19.0.4",
|
||||||
"@angular-devkit/core": "^19.2.0",
|
"@angular-devkit/core": "^19.2.0",
|
||||||
"@angular-devkit/schematics": "^19.2.0",
|
"@angular-devkit/schematics": "^19.2.0",
|
||||||
"@angular-eslint/builder": "19.1.0",
|
"@angular-eslint/builder": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin": "19.1.0",
|
"@angular-eslint/eslint-plugin": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "19.1.0",
|
"@angular-eslint/eslint-plugin-template": "19.2.0",
|
||||||
"@angular-eslint/schematics": "19.1.0",
|
"@angular-eslint/schematics": "19.2.0",
|
||||||
"@angular-eslint/template-parser": "19.1.0",
|
"@angular-eslint/template-parser": "19.2.0",
|
||||||
"@angular/cli": "~19.2.0",
|
"@angular/cli": "~19.2.0",
|
||||||
"@angular/compiler-cli": "~19.2.0",
|
"@angular/compiler-cli": "~19.2.0",
|
||||||
"@codecov/webpack-plugin": "^1.9.0",
|
"@codecov/webpack-plugin": "^1.9.0",
|
||||||
"@playwright/test": "^1.50.1",
|
"@playwright/test": "^1.50.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.13.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.25.0",
|
"@typescript-eslint/eslint-plugin": "^8.26.0",
|
||||||
"@typescript-eslint/parser": "^8.25.0",
|
"@typescript-eslint/parser": "^8.26.0",
|
||||||
"@typescript-eslint/utils": "^8.0.0",
|
"@typescript-eslint/utils": "^8.0.0",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jest-junit": "^16.0.0",
|
||||||
"jest-preset-angular": "^14.5.3",
|
"jest-preset-angular": "^14.5.3",
|
||||||
"jest-websocket-mock": "^2.5.0",
|
"jest-websocket-mock": "^2.5.0",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
@ -1148,9 +1149,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/builder": {
|
"node_modules/@angular-eslint/builder": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-19.2.0.tgz",
|
||||||
"integrity": "sha512-LWdQMTES/7GySlpTNFJn3k33ZGmjjWlHI/+IHV7B3xHQ9hj4MPK4ACmE/PNOAIQ9LwQm7sKS+3cTMxOZQ/cvSg==",
|
"integrity": "sha512-8Lx24MrMJT8RlgDtwqfiLiJo4DzSaktjco6RmELUdWO2chJgRe9y+2iIgOeB2pmyD9UCsubwsfjBXlrnV/MPhQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1163,21 +1164,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/bundled-angular-compiler": {
|
"node_modules/@angular-eslint/bundled-angular-compiler": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.2.0.tgz",
|
||||||
"integrity": "sha512-HUJyukRvnh8Z9lIdxdblBRuBaPYEVv4iAYZMw3d+dn4rrM27Nt5oh3/zkwYrrPkt36tZdeXdDWrOuz9jgjVN5w==",
|
"integrity": "sha512-hmmAogTpYGbBvnJ0j7DNLi8YQ+YEEuwFdx0heU8XjTpZlRoSRIP7MJJVlaQCt+ZT5f5XwdGtqi9lOXqqcyGHLA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/eslint-plugin": {
|
"node_modules/@angular-eslint/eslint-plugin": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.2.0.tgz",
|
||||||
"integrity": "sha512-TDO0+Ry+oNkxnaLHogKp1k2aey6IkJef5d7hathE4UFT6owjRizltWaRoX6bGw7Qu1yagVLL8L2Se8SddxSPAQ==",
|
"integrity": "sha512-QQWWDrTdJ22tBd7RLFG/FdPwNyYEhg7YwWgn29z6XcdnV00ZFtf7FRbv/te1kqVNPvfjtht7bvtHcPQ432aUdQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-eslint/bundled-angular-compiler": "19.1.0",
|
"@angular-eslint/bundled-angular-compiler": "19.2.0",
|
||||||
"@angular-eslint/utils": "19.1.0"
|
"@angular-eslint/utils": "19.2.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
||||||
@ -1186,14 +1187,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/eslint-plugin-template": {
|
"node_modules/@angular-eslint/eslint-plugin-template": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.2.0.tgz",
|
||||||
"integrity": "sha512-bIUizkCY40mnU8oAO1tLV7uN2H/cHf1evLlhpqlb9JYwc5dT2moiEhNDo61OtOgkJmDGNuThAeO9Xk9hGQc7nA==",
|
"integrity": "sha512-lUSzmk5/Dr0bNc2Omb5CZDu3zQZh70bJyuXnN5MKd00V1b3u90eqvMSveFzWFJ6Eot8Hh8+FxtiozPwGqOE+Og==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-eslint/bundled-angular-compiler": "19.1.0",
|
"@angular-eslint/bundled-angular-compiler": "19.2.0",
|
||||||
"@angular-eslint/utils": "19.1.0",
|
"@angular-eslint/utils": "19.2.0",
|
||||||
"aria-query": "5.3.2",
|
"aria-query": "5.3.2",
|
||||||
"axobject-query": "4.1.0"
|
"axobject-query": "4.1.0"
|
||||||
},
|
},
|
||||||
@ -1204,17 +1205,47 @@
|
|||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular-eslint/eslint-plugin-template/node_modules/@angular-eslint/utils": {
|
||||||
|
"version": "19.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.2.0.tgz",
|
||||||
|
"integrity": "sha512-1XQXzIqYadKUxcAgW1DPev56SVbR8Uld6TthgolU7rfIX23RYMIIRtQlrQCk7zoXLXm5fzcGqjTR4wHfoD+iWg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular-eslint/bundled-angular-compiler": "19.2.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
||||||
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
|
"typescript": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@angular-eslint/eslint-plugin/node_modules/@angular-eslint/utils": {
|
||||||
|
"version": "19.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.2.0.tgz",
|
||||||
|
"integrity": "sha512-1XQXzIqYadKUxcAgW1DPev56SVbR8Uld6TthgolU7rfIX23RYMIIRtQlrQCk7zoXLXm5fzcGqjTR4wHfoD+iWg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular-eslint/bundled-angular-compiler": "19.2.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
||||||
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
|
"typescript": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular-eslint/schematics": {
|
"node_modules/@angular-eslint/schematics": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-19.2.0.tgz",
|
||||||
"integrity": "sha512-6S1FjmM7rZxc0u0W0KjqWYOkFQ0q89IGyjPkdUt1a8NwRnWg3VoXp4WYfeuZOjda/FEYuBS/E6rckLAMp0h6Aw==",
|
"integrity": "sha512-SQfbKgPEJNkK5TVXRsdnWp6TjvVZOczvf8lELF1n+I/Uwmp7ulUjTRgTo59ZQnXoPSs2qCPgS4gAOVR6CD91zQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-devkit/core": ">= 19.0.0 < 20.0.0",
|
"@angular-devkit/core": ">= 19.0.0 < 20.0.0",
|
||||||
"@angular-devkit/schematics": ">= 19.0.0 < 20.0.0",
|
"@angular-devkit/schematics": ">= 19.0.0 < 20.0.0",
|
||||||
"@angular-eslint/eslint-plugin": "19.1.0",
|
"@angular-eslint/eslint-plugin": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "19.1.0",
|
"@angular-eslint/eslint-plugin-template": "19.2.0",
|
||||||
"ignore": "7.0.3",
|
"ignore": "7.0.3",
|
||||||
"semver": "7.7.1",
|
"semver": "7.7.1",
|
||||||
"strip-json-comments": "3.1.1"
|
"strip-json-comments": "3.1.1"
|
||||||
@ -1231,13 +1262,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/template-parser": {
|
"node_modules/@angular-eslint/template-parser": {
|
||||||
"version": "19.1.0",
|
"version": "19.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.2.0.tgz",
|
||||||
"integrity": "sha512-wbMi7adlC+uYqZo7NHNBShpNhFJRZsXLqihqvFpAUt1Ei6uDX8HR6MyMEDZ9tUnlqtPVW5nmbedPyLVG7HkjAA==",
|
"integrity": "sha512-VqgvFrILhoMe0GHZrx+Bjy8kx7/LJfJTd+x/wzE/X1cCChSU81MBZFMVeFMnoI75OOQUf4fwaaKrtUhUvAkVyw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-eslint/bundled-angular-compiler": "19.1.0",
|
"@angular-eslint/bundled-angular-compiler": "19.2.0",
|
||||||
"eslint-scope": "^8.0.2"
|
"eslint-scope": "^8.0.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@ -1245,21 +1276,6 @@
|
|||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-eslint/utils": {
|
|
||||||
"version": "19.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.1.0.tgz",
|
|
||||||
"integrity": "sha512-mcb7hPMH/u6wwUwvsewrmgb9y9NWN6ZacvpUvKlTOxF/jOtTdsu0XfV4YB43sp2A8NWzYzX0Str4c8K1xSmuBQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@angular-eslint/bundled-angular-compiler": "19.1.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@typescript-eslint/utils": "^7.11.0 || ^8.0.0",
|
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
|
||||||
"typescript": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@angular/cdk": {
|
"node_modules/@angular/cdk": {
|
||||||
"version": "19.2.1",
|
"version": "19.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.1.tgz",
|
||||||
@ -7150,9 +7166,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.13.5",
|
"version": "22.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz",
|
||||||
"integrity": "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==",
|
"integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -7271,17 +7287,17 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.0.tgz",
|
||||||
"integrity": "sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==",
|
"integrity": "sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/regexpp": "^4.10.0",
|
"@eslint-community/regexpp": "^4.10.0",
|
||||||
"@typescript-eslint/scope-manager": "8.25.0",
|
"@typescript-eslint/scope-manager": "8.26.0",
|
||||||
"@typescript-eslint/type-utils": "8.25.0",
|
"@typescript-eslint/type-utils": "8.26.0",
|
||||||
"@typescript-eslint/utils": "8.25.0",
|
"@typescript-eslint/utils": "8.26.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.25.0",
|
"@typescript-eslint/visitor-keys": "8.26.0",
|
||||||
"graphemer": "^1.4.0",
|
"graphemer": "^1.4.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.1",
|
||||||
"natural-compare": "^1.4.0",
|
"natural-compare": "^1.4.0",
|
||||||
@ -7297,20 +7313,20 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
|
"@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/parser": {
|
"node_modules/@typescript-eslint/parser": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.0.tgz",
|
||||||
"integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==",
|
"integrity": "sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.25.0",
|
"@typescript-eslint/scope-manager": "8.26.0",
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.25.0",
|
"@typescript-eslint/typescript-estree": "8.26.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.25.0",
|
"@typescript-eslint/visitor-keys": "8.26.0",
|
||||||
"debug": "^4.3.4"
|
"debug": "^4.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -7322,18 +7338,18 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/scope-manager": {
|
"node_modules/@typescript-eslint/scope-manager": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.0.tgz",
|
||||||
"integrity": "sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg==",
|
"integrity": "sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.25.0"
|
"@typescript-eslint/visitor-keys": "8.26.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -7344,14 +7360,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/type-utils": {
|
"node_modules/@typescript-eslint/type-utils": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.0.tgz",
|
||||||
"integrity": "sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g==",
|
"integrity": "sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/typescript-estree": "8.25.0",
|
"@typescript-eslint/typescript-estree": "8.26.0",
|
||||||
"@typescript-eslint/utils": "8.25.0",
|
"@typescript-eslint/utils": "8.26.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"ts-api-utils": "^2.0.1"
|
"ts-api-utils": "^2.0.1"
|
||||||
},
|
},
|
||||||
@ -7364,13 +7380,13 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/types": {
|
"node_modules/@typescript-eslint/types": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.0.tgz",
|
||||||
"integrity": "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw==",
|
"integrity": "sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -7382,14 +7398,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree": {
|
"node_modules/@typescript-eslint/typescript-estree": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.0.tgz",
|
||||||
"integrity": "sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q==",
|
"integrity": "sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"@typescript-eslint/visitor-keys": "8.25.0",
|
"@typescript-eslint/visitor-keys": "8.26.0",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
@ -7405,7 +7421,7 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
|
||||||
@ -7435,16 +7451,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/utils": {
|
"node_modules/@typescript-eslint/utils": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.0.tgz",
|
||||||
"integrity": "sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA==",
|
"integrity": "sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.4.0",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"@typescript-eslint/scope-manager": "8.25.0",
|
"@typescript-eslint/scope-manager": "8.26.0",
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"@typescript-eslint/typescript-estree": "8.25.0"
|
"@typescript-eslint/typescript-estree": "8.26.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
@ -7455,17 +7471,17 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": "^8.57.0 || ^9.0.0",
|
"eslint": "^8.57.0 || ^9.0.0",
|
||||||
"typescript": ">=4.8.4 <5.8.0"
|
"typescript": ">=4.8.4 <5.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@typescript-eslint/visitor-keys": {
|
"node_modules/@typescript-eslint/visitor-keys": {
|
||||||
"version": "8.25.0",
|
"version": "8.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.0.tgz",
|
||||||
"integrity": "sha512-kCYXKAum9CecGVHGij7muybDfTS2sD3t0L4bJsEZLkyrXUImiCTq1M3LG2SRtOhiHFwMR9wAFplpT6XHYjTkwQ==",
|
"integrity": "sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/types": "8.25.0",
|
"@typescript-eslint/types": "8.26.0",
|
||||||
"eslint-visitor-keys": "^4.2.0"
|
"eslint-visitor-keys": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -12403,6 +12419,32 @@
|
|||||||
"fsevents": "^2.3.2"
|
"fsevents": "^2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-junit": {
|
||||||
|
"version": "16.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz",
|
||||||
|
"integrity": "sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": "^1.0.4",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"uuid": "^8.3.2",
|
||||||
|
"xml": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-junit/node_modules/uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-leak-detector": {
|
"node_modules/jest-leak-detector": {
|
||||||
"version": "29.7.0",
|
"version": "29.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
|
||||||
@ -19250,6 +19292,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xml": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/xml-name-validator": {
|
"node_modules/xml-name-validator": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
||||||
|
@ -46,23 +46,24 @@
|
|||||||
"@angular-devkit/build-angular": "^19.0.4",
|
"@angular-devkit/build-angular": "^19.0.4",
|
||||||
"@angular-devkit/core": "^19.2.0",
|
"@angular-devkit/core": "^19.2.0",
|
||||||
"@angular-devkit/schematics": "^19.2.0",
|
"@angular-devkit/schematics": "^19.2.0",
|
||||||
"@angular-eslint/builder": "19.1.0",
|
"@angular-eslint/builder": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin": "19.1.0",
|
"@angular-eslint/eslint-plugin": "19.2.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "19.1.0",
|
"@angular-eslint/eslint-plugin-template": "19.2.0",
|
||||||
"@angular-eslint/schematics": "19.1.0",
|
"@angular-eslint/schematics": "19.2.0",
|
||||||
"@angular-eslint/template-parser": "19.1.0",
|
"@angular-eslint/template-parser": "19.2.0",
|
||||||
"@angular/cli": "~19.2.0",
|
"@angular/cli": "~19.2.0",
|
||||||
"@angular/compiler-cli": "~19.2.0",
|
"@angular/compiler-cli": "~19.2.0",
|
||||||
"@codecov/webpack-plugin": "^1.9.0",
|
"@codecov/webpack-plugin": "^1.9.0",
|
||||||
"@playwright/test": "^1.50.1",
|
"@playwright/test": "^1.50.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.13.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.25.0",
|
"@typescript-eslint/eslint-plugin": "^8.26.0",
|
||||||
"@typescript-eslint/parser": "^8.25.0",
|
"@typescript-eslint/parser": "^8.26.0",
|
||||||
"@typescript-eslint/utils": "^8.0.0",
|
"@typescript-eslint/utils": "^8.0.0",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jest-junit": "^16.0.0",
|
||||||
"jest-preset-angular": "^14.5.3",
|
"jest-preset-angular": "^14.5.3",
|
||||||
"jest-websocket-mock": "^2.5.0",
|
"jest-websocket-mock": "^2.5.0",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
|
@ -118,7 +118,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
<span i18n>Sidebar</span>
|
<span i18n>Sidebar</span>
|
||||||
</div>
|
</div>
|
||||||
@ -129,7 +129,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
<span i18n>Dark mode</span>
|
<span i18n>Dark mode</span>
|
||||||
</div>
|
</div>
|
||||||
@ -165,7 +165,7 @@
|
|||||||
<p i18n>
|
<p i18n>
|
||||||
Update checking works by pinging the public GitHub API for the latest release to determine whether a new version is available. Actual updating of the app must still be performed manually.
|
Update checking works by pinging the public GitHub API for the latest release to determine whether a new version is available. Actual updating of the app must still be performed manually.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p class="mb-0">
|
||||||
<em i18n>No tracking data is collected by the app in any way.</em>
|
<em i18n>No tracking data is collected by the app in any way.</em>
|
||||||
</p>
|
</p>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
@ -173,7 +173,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-3" i18n>Saved Views</h5>
|
<h5 class="mt-3" i18n>Saved Views</h5>
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
@ -183,15 +183,15 @@
|
|||||||
<div class="col-xl-6 ps-xl-5">
|
<div class="col-xl-6 ps-xl-5">
|
||||||
<h5 class="mt-3 mt-md-0" i18n>Document editing</h5>
|
<h5 class="mt-3 mt-md-0" i18n>Document editing</h5>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Use PDF viewer provided by the browser" i18n-hint hint="This is usually faster for displaying large PDF documents, but it might not work on some browsers." formControlName="useNativePdfViewer"></pngx-input-check>
|
<pngx-input-check i18n-title title="Use PDF viewer provided by the browser" i18n-hint hint="This is usually faster for displaying large PDF documents, but it might not work on some browsers." formControlName="useNativePdfViewer"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col-2">
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
<span i18n>Default zoom:</span>
|
<span i18n>Default zoom</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<select class="form-select" formControlName="pdfViewerDefaultZoom">
|
<select class="form-select" formControlName="pdfViewerDefaultZoom">
|
||||||
@ -202,7 +202,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Automatically remove inbox tag(s) on save" formControlName="documentEditingRemoveInboxTags"></pngx-input-check>
|
<pngx-input-check i18n-title title="Automatically remove inbox tag(s) on save" formControlName="documentEditingRemoveInboxTags"></pngx-input-check>
|
||||||
</div>
|
</div>
|
||||||
@ -214,10 +214,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-3" i18n>Notes</h5>
|
<h5 class="mt-3" i18n>Global search</h5>
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Enable notes" formControlName="notesEnabled"></pngx-input-check>
|
<pngx-input-check i18n-title title="Do not include advanced search results" formControlName="searchDbOnly"></pngx-input-check>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
|
<span i18n>Full search links to</span>
|
||||||
|
</div>
|
||||||
|
<div class="col mb-3">
|
||||||
|
<select class="form-select" formControlName="searchLink">
|
||||||
|
<option [ngValue]="GlobalSearchType.TITLE_CONTENT" i18n>Title and content search</option>
|
||||||
|
<option [ngValue]="GlobalSearchType.ADVANCED" i18n>Advanced search</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -229,26 +241,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-3" i18n>Global search</h5>
|
<h5 class="mt-3" i18n>Notes</h5>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Do not include advanced search results" formControlName="searchDbOnly"></pngx-input-check>
|
<pngx-input-check i18n-title title="Enable notes" formControlName="notesEnabled"></pngx-input-check>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Full search links to</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<select class="form-select" formControlName="searchLink">
|
|
||||||
<option [ngValue]="GlobalSearchType.TITLE_CONTENT" i18n>Title and content search</option>
|
|
||||||
<option [ngValue]="GlobalSearchType.ADVANCED" i18n>Advanced search</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -267,8 +263,8 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<p i18n>
|
<p i18n>
|
||||||
Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI
|
Settings apply to this user account for objects (Tags, Mail Rules, etc. but not documents) created via the web UI.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
@ -307,7 +303,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
<span i18n>Default Edit Permissions</span>
|
<span i18n>Default Edit Permissions</span>
|
||||||
</div>
|
</div>
|
||||||
@ -346,7 +342,7 @@
|
|||||||
|
|
||||||
<h5 i18n>Document processing</h5>
|
<h5 i18n>Document processing</h5>
|
||||||
|
|
||||||
<div class="row mb-3">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-check i18n-title title="Show notifications when new documents are detected" formControlName="notificationsConsumerNewDocument"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show notifications when new documents are detected" formControlName="notificationsConsumerNewDocument"></pngx-input-check>
|
||||||
<pngx-input-check i18n-title title="Show notifications when document processing completes successfully" formControlName="notificationsConsumerSuccess"></pngx-input-check>
|
<pngx-input-check i18n-title title="Show notifications when document processing completes successfully" formControlName="notificationsConsumerSuccess"></pngx-input-check>
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
<div class="scroll-list">
|
<div class="scroll-list">
|
||||||
@for (toast of toasts; track toast.id) {
|
@for (toast of toasts; track toast.id) {
|
||||||
<pngx-toast [autohide]="false" [toast]="toast" (hidden)="onHidden(toast)" (close)="toastService.closeToast(toast)"></pngx-toast>
|
<pngx-toast [autohide]="false" [toast]="toast" (hidden)="onHidden(toast)" (closed)="toastService.closeToast(toast)"></pngx-toast>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -189,6 +189,7 @@
|
|||||||
<pngx-input-select i18n-title title="Assign correspondent" [items]="correspondents" [allowNull]="true" formControlName="assign_correspondent"></pngx-input-select>
|
<pngx-input-select i18n-title title="Assign correspondent" [items]="correspondents" [allowNull]="true" formControlName="assign_correspondent"></pngx-input-select>
|
||||||
<pngx-input-select i18n-title title="Assign storage path" [items]="storagePaths" [allowNull]="true" formControlName="assign_storage_path"></pngx-input-select>
|
<pngx-input-select i18n-title title="Assign storage path" [items]="storagePaths" [allowNull]="true" formControlName="assign_storage_path"></pngx-input-select>
|
||||||
<pngx-input-select i18n-title title="Assign custom fields" multiple="true" [items]="customFields" [allowNull]="true" formControlName="assign_custom_fields"></pngx-input-select>
|
<pngx-input-select i18n-title title="Assign custom fields" multiple="true" [items]="customFields" [allowNull]="true" formControlName="assign_custom_fields"></pngx-input-select>
|
||||||
|
<pngx-input-custom-fields-values formControlName="assign_custom_fields_values" [selectedFields]="formGroup.get('assign_custom_fields').value" (removeSelectedField)="removeSelectedCustomField($event, formGroup)"></pngx-input-custom-fields-values>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<pngx-input-select i18n-title title="Assign owner" [items]="users" bindLabel="username" formControlName="assign_owner" [allowNull]="true"></pngx-input-select>
|
<pngx-input-select i18n-title title="Assign owner" [items]="users" bindLabel="username" formControlName="assign_owner" [allowNull]="true"></pngx-input-select>
|
||||||
|
@ -2,7 +2,12 @@ import { CdkDragDrop } from '@angular/cdk/drag-drop'
|
|||||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import {
|
||||||
|
FormControl,
|
||||||
|
FormGroup,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
} from '@angular/forms'
|
||||||
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgSelectModule } from '@ng-select/ng-select'
|
import { NgSelectModule } from '@ng-select/ng-select'
|
||||||
import { of } from 'rxjs'
|
import { of } from 'rxjs'
|
||||||
@ -369,4 +374,19 @@ describe('WorkflowEditDialogComponent', () => {
|
|||||||
expect(component.objectForm.get('actions').value[0].email).toBeNull()
|
expect(component.objectForm.get('actions').value[0].email).toBeNull()
|
||||||
expect(component.objectForm.get('actions').value[0].webhook).toBeNull()
|
expect(component.objectForm.get('actions').value[0].webhook).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should remove selected custom field from the form group', () => {
|
||||||
|
const formGroup = new FormGroup({
|
||||||
|
assign_custom_fields: new FormControl([1, 2, 3]),
|
||||||
|
})
|
||||||
|
|
||||||
|
component.removeSelectedCustomField(2, formGroup)
|
||||||
|
expect(formGroup.get('assign_custom_fields').value).toEqual([1, 3])
|
||||||
|
|
||||||
|
component.removeSelectedCustomField(1, formGroup)
|
||||||
|
expect(formGroup.get('assign_custom_fields').value).toEqual([3])
|
||||||
|
|
||||||
|
component.removeSelectedCustomField(3, formGroup)
|
||||||
|
expect(formGroup.get('assign_custom_fields').value).toEqual([])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -47,6 +47,7 @@ import { WorkflowService } from 'src/app/services/rest/workflow.service'
|
|||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { ConfirmButtonComponent } from '../../confirm-button/confirm-button.component'
|
import { ConfirmButtonComponent } from '../../confirm-button/confirm-button.component'
|
||||||
import { CheckComponent } from '../../input/check/check.component'
|
import { CheckComponent } from '../../input/check/check.component'
|
||||||
|
import { CustomFieldsValuesComponent } from '../../input/custom-fields-values/custom-fields-values.component'
|
||||||
import { EntriesComponent } from '../../input/entries/entries.component'
|
import { EntriesComponent } from '../../input/entries/entries.component'
|
||||||
import { NumberComponent } from '../../input/number/number.component'
|
import { NumberComponent } from '../../input/number/number.component'
|
||||||
import { PermissionsGroupComponent } from '../../input/permissions/permissions-group/permissions-group.component'
|
import { PermissionsGroupComponent } from '../../input/permissions/permissions-group/permissions-group.component'
|
||||||
@ -151,6 +152,7 @@ const TRIGGER_MATCHING_ALGORITHMS = MATCHING_ALGORITHMS.filter(
|
|||||||
SelectComponent,
|
SelectComponent,
|
||||||
TextAreaComponent,
|
TextAreaComponent,
|
||||||
TagsComponent,
|
TagsComponent,
|
||||||
|
CustomFieldsValuesComponent,
|
||||||
PermissionsGroupComponent,
|
PermissionsGroupComponent,
|
||||||
PermissionsUserComponent,
|
PermissionsUserComponent,
|
||||||
ConfirmButtonComponent,
|
ConfirmButtonComponent,
|
||||||
@ -439,6 +441,9 @@ export class WorkflowEditDialogComponent
|
|||||||
assign_change_users: new FormControl(action.assign_change_users),
|
assign_change_users: new FormControl(action.assign_change_users),
|
||||||
assign_change_groups: new FormControl(action.assign_change_groups),
|
assign_change_groups: new FormControl(action.assign_change_groups),
|
||||||
assign_custom_fields: new FormControl(action.assign_custom_fields),
|
assign_custom_fields: new FormControl(action.assign_custom_fields),
|
||||||
|
assign_custom_fields_values: new FormControl(
|
||||||
|
action.assign_custom_fields_values
|
||||||
|
),
|
||||||
remove_tags: new FormControl(action.remove_tags),
|
remove_tags: new FormControl(action.remove_tags),
|
||||||
remove_all_tags: new FormControl(action.remove_all_tags),
|
remove_all_tags: new FormControl(action.remove_all_tags),
|
||||||
remove_document_types: new FormControl(action.remove_document_types),
|
remove_document_types: new FormControl(action.remove_document_types),
|
||||||
@ -565,6 +570,7 @@ export class WorkflowEditDialogComponent
|
|||||||
assign_change_users: [],
|
assign_change_users: [],
|
||||||
assign_change_groups: [],
|
assign_change_groups: [],
|
||||||
assign_custom_fields: [],
|
assign_custom_fields: [],
|
||||||
|
assign_custom_fields_values: {},
|
||||||
remove_tags: [],
|
remove_tags: [],
|
||||||
remove_all_tags: false,
|
remove_all_tags: false,
|
||||||
remove_document_types: [],
|
remove_document_types: [],
|
||||||
@ -643,4 +649,12 @@ export class WorkflowEditDialogComponent
|
|||||||
})
|
})
|
||||||
super.save()
|
super.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public removeSelectedCustomField(fieldId: number, group: FormGroup) {
|
||||||
|
group
|
||||||
|
.get('assign_custom_fields')
|
||||||
|
.setValue(
|
||||||
|
group.get('assign_custom_fields').value.filter((id) => id !== fieldId)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
<div class="list-group mt-3 selected-fields">
|
||||||
|
@for (fieldId of selectedFields; track fieldId) {
|
||||||
|
<div class="list-group-item
|
||||||
|
d-flex
|
||||||
|
justify-content-between
|
||||||
|
align-items-center">
|
||||||
|
@switch (getCustomField(fieldId)?.data_type) {
|
||||||
|
@case (CustomFieldDataType.String) {
|
||||||
|
<pngx-input-text [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
|
||||||
|
[title]="getCustomField(fieldId)?.name"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[horizontal]="true"></pngx-input-text>
|
||||||
|
}
|
||||||
|
@case (CustomFieldDataType.Date) {
|
||||||
|
<pngx-input-date [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
|
||||||
|
[title]="getCustomField(fieldId)?.name"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[horizontal]="true"></pngx-input-date>
|
||||||
|
}
|
||||||
|
@case (CustomFieldDataType.Integer) {
|
||||||
|
<pngx-input-number [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
|
||||||
|
[title]="getCustomField(fieldId)?.name"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[horizontal]="true"
|
||||||
|
[showAdd]="false"></pngx-input-number>
|
||||||
|
}
|
||||||
|
@case (CustomFieldDataType.Float) {
|
||||||
|
<pngx-input-number [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
|
||||||
|
[title]="getCustomField(fieldId)?.name"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[horizontal]="true"
|
||||||
|
[showAdd]="false"
|
||||||
|
[step]=".1"></pngx-input-number>
|
||||||
|
}
|
||||||
|
@case (CustomFieldDataType.Monetary) {
|
||||||
|
<pngx-input-monetary [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
|
||||||
|
[title]="getCustomField(fieldId)?.name"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[defaultCurrency]="getCustomField(fieldId)?.extra_data?.default_currency"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[horizontal]="true"></pngx-input-monetary>
|
||||||
|
}
|
||||||
|
@case (CustomFieldDataType.Boolean) {
|
||||||
|
<pngx-input-check [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
|
||||||
|
[title]="getCustomField(fieldId)?.name"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[horizontal]="true"></pngx-input-check>
|
||||||
|
}
|
||||||
|
@case (CustomFieldDataType.Url) {
|
||||||
|
<pngx-input-url [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
|
||||||
|
[title]="getCustomField(fieldId)?.name"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[horizontal]="true"></pngx-input-url>
|
||||||
|
}
|
||||||
|
@case (CustomFieldDataType.DocumentLink) {
|
||||||
|
<pngx-input-document-link [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
|
||||||
|
[title]="getCustomField(fieldId)?.name"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[horizontal]="true"></pngx-input-document-link>
|
||||||
|
}
|
||||||
|
@case (CustomFieldDataType.Select) {
|
||||||
|
<pngx-input-select [(ngModel)]="value[fieldId]" (ngModelChange)="onChange(value)"
|
||||||
|
[title]="getCustomField(fieldId)?.name"
|
||||||
|
class="flex-grow-1"
|
||||||
|
[items]="getCustomField(fieldId)?.extra_data.select_options"
|
||||||
|
class="flex-grow-1"
|
||||||
|
bindLabel="label"
|
||||||
|
[allowNull]="true"
|
||||||
|
[horizontal]="true"></pngx-input-select>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<button type="button" class="btn btn-link text-danger" (click)="removeSelectedField.next(fieldId)">
|
||||||
|
<i-bs name="trash"></i-bs>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
@ -0,0 +1,3 @@
|
|||||||
|
:host ::ng-deep .list-group-item .mb-3 {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||||
|
import { provideHttpClientTesting } from '@angular/common/http/testing'
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
import {
|
||||||
|
FormsModule,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
} from '@angular/forms'
|
||||||
|
import { of } from 'rxjs'
|
||||||
|
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
|
||||||
|
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||||
|
import { CustomFieldsValuesComponent } from './custom-fields-values.component'
|
||||||
|
|
||||||
|
describe('CustomFieldsValuesComponent', () => {
|
||||||
|
let component: CustomFieldsValuesComponent
|
||||||
|
let fixture: ComponentFixture<CustomFieldsValuesComponent>
|
||||||
|
let customFieldsService: CustomFieldsService
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [FormsModule, ReactiveFormsModule, CustomFieldsValuesComponent],
|
||||||
|
providers: [
|
||||||
|
provideHttpClient(withInterceptorsFromDi()),
|
||||||
|
provideHttpClientTesting(),
|
||||||
|
],
|
||||||
|
}).compileComponents()
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(CustomFieldsValuesComponent)
|
||||||
|
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
customFieldsService = TestBed.inject(CustomFieldsService)
|
||||||
|
jest.spyOn(customFieldsService, 'listAll').mockReturnValue(
|
||||||
|
of({
|
||||||
|
all: [1],
|
||||||
|
count: 1,
|
||||||
|
results: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Field 1',
|
||||||
|
data_type: CustomFieldDataType.String,
|
||||||
|
} as CustomField,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CustomFieldsValuesComponent)
|
||||||
|
component = fixture.componentInstance
|
||||||
|
fixture.detectChanges()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should set selectedFields and map values correctly', () => {
|
||||||
|
component.value = { 1: 'value1' }
|
||||||
|
component.selectedFields = [1, 2]
|
||||||
|
expect(component.selectedFields).toEqual([1, 2])
|
||||||
|
expect(component.value).toEqual({ 1: 'value1', 2: null })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return the correct custom field by id', () => {
|
||||||
|
const field = component.getCustomField(1)
|
||||||
|
expect(field).toEqual({
|
||||||
|
id: 1,
|
||||||
|
name: 'Field 1',
|
||||||
|
data_type: CustomFieldDataType.String,
|
||||||
|
} as CustomField)
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,90 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
forwardRef,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
} from '@angular/core'
|
||||||
|
import {
|
||||||
|
FormsModule,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
} from '@angular/forms'
|
||||||
|
import { RouterModule } from '@angular/router'
|
||||||
|
import { NgSelectModule } from '@ng-select/ng-select'
|
||||||
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
|
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
|
||||||
|
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||||
|
import { AbstractInputComponent } from '../abstract-input'
|
||||||
|
import { CheckComponent } from '../check/check.component'
|
||||||
|
import { DateComponent } from '../date/date.component'
|
||||||
|
import { DocumentLinkComponent } from '../document-link/document-link.component'
|
||||||
|
import { MonetaryComponent } from '../monetary/monetary.component'
|
||||||
|
import { NumberComponent } from '../number/number.component'
|
||||||
|
import { SelectComponent } from '../select/select.component'
|
||||||
|
import { TextComponent } from '../text/text.component'
|
||||||
|
import { UrlComponent } from '../url/url.component'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: forwardRef(() => CustomFieldsValuesComponent),
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selector: 'pngx-input-custom-fields-values',
|
||||||
|
templateUrl: './custom-fields-values.component.html',
|
||||||
|
styleUrl: './custom-fields-values.component.scss',
|
||||||
|
imports: [
|
||||||
|
TextComponent,
|
||||||
|
DateComponent,
|
||||||
|
NumberComponent,
|
||||||
|
DocumentLinkComponent,
|
||||||
|
UrlComponent,
|
||||||
|
SelectComponent,
|
||||||
|
MonetaryComponent,
|
||||||
|
CheckComponent,
|
||||||
|
NgSelectModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
RouterModule,
|
||||||
|
NgxBootstrapIconsModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CustomFieldsValuesComponent extends AbstractInputComponent<Object> {
|
||||||
|
public CustomFieldDataType = CustomFieldDataType
|
||||||
|
|
||||||
|
constructor(customFieldsService: CustomFieldsService) {
|
||||||
|
super()
|
||||||
|
customFieldsService.listAll().subscribe((items) => {
|
||||||
|
this.fields = items.results
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fields: CustomField[]
|
||||||
|
|
||||||
|
private _selectedFields: number[]
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set selectedFields(newFields: number[]) {
|
||||||
|
this._selectedFields = newFields
|
||||||
|
// map the selected fields to an object with field_id as key and value as value
|
||||||
|
this.value = newFields.reduce((acc, fieldId) => {
|
||||||
|
acc[fieldId] = this.value?.[fieldId] || null
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
this.onChange(this.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
get selectedFields(): number[] {
|
||||||
|
return this._selectedFields
|
||||||
|
}
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
public removeSelectedField: EventEmitter<number> = new EventEmitter<number>()
|
||||||
|
|
||||||
|
public getCustomField(id: number): CustomField {
|
||||||
|
return this.fields.find((field) => field.id === id)
|
||||||
|
}
|
||||||
|
}
|
@ -51,6 +51,6 @@
|
|||||||
<p class="mb-0 mt-2"><button class="btn btn-sm btn-outline-secondary" (click)="close.emit(toast); toast.action()">{{toast.actionName}}</button></p>
|
<p class="mb-0 mt-2"><button class="btn btn-sm btn-outline-secondary" (click)="close.emit(toast); toast.action()">{{toast.actionName}}</button></p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn-close ms-auto flex-shrink-0" data-bs-dismiss="toast" aria-label="Close" (click)="close.emit(toast);"></button>
|
<button type="button" class="btn-close ms-auto flex-shrink-0" data-bs-dismiss="toast" aria-label="Close" (click)="closed.emit(toast);"></button>
|
||||||
</div>
|
</div>
|
||||||
</ngb-toast>
|
</ngb-toast>
|
||||||
|
@ -27,7 +27,7 @@ export class ToastComponent {
|
|||||||
|
|
||||||
@Output() hidden: EventEmitter<Toast> = new EventEmitter<Toast>()
|
@Output() hidden: EventEmitter<Toast> = new EventEmitter<Toast>()
|
||||||
|
|
||||||
@Output() close: EventEmitter<Toast> = new EventEmitter<Toast>()
|
@Output() closed: EventEmitter<Toast> = new EventEmitter<Toast>()
|
||||||
|
|
||||||
public copied: boolean = false
|
public copied: boolean = false
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
@for (toast of toasts; track toast.id) {
|
@for (toast of toasts; track toast.id) {
|
||||||
<pngx-toast [toast]="toast" [autohide]="true" (close)="closeToast()"></pngx-toast>
|
<pngx-toast [toast]="toast" [autohide]="true" (closed)="closeToast()"></pngx-toast>
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,8 @@ export interface WorkflowAction extends ObjectWithId {
|
|||||||
|
|
||||||
assign_custom_fields?: number[] // [CustomField.id]
|
assign_custom_fields?: number[] // [CustomField.id]
|
||||||
|
|
||||||
|
assign_custom_fields_values?: object
|
||||||
|
|
||||||
remove_tags?: number[] // Tag.id
|
remove_tags?: number[] // Tag.id
|
||||||
|
|
||||||
remove_all_tags?: boolean
|
remove_all_tags?: boolean
|
||||||
|
@ -26,7 +26,6 @@ from documents.models import CustomField
|
|||||||
from documents.models import CustomFieldInstance
|
from documents.models import CustomFieldInstance
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import FileInfo
|
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
from documents.models import WorkflowTrigger
|
from documents.models import WorkflowTrigger
|
||||||
@ -705,8 +704,6 @@ class ConsumerPlugin(
|
|||||||
) -> Document:
|
) -> Document:
|
||||||
# If someone gave us the original filename, use it instead of doc.
|
# If someone gave us the original filename, use it instead of doc.
|
||||||
|
|
||||||
file_info = FileInfo.from_filename(self.filename)
|
|
||||||
|
|
||||||
self.log.debug("Saving record to database")
|
self.log.debug("Saving record to database")
|
||||||
|
|
||||||
if self.metadata.created is not None:
|
if self.metadata.created is not None:
|
||||||
@ -714,9 +711,6 @@ class ConsumerPlugin(
|
|||||||
self.log.debug(
|
self.log.debug(
|
||||||
f"Creation date from post_documents parameter: {create_date}",
|
f"Creation date from post_documents parameter: {create_date}",
|
||||||
)
|
)
|
||||||
elif file_info.created is not None:
|
|
||||||
create_date = file_info.created
|
|
||||||
self.log.debug(f"Creation date from FileInfo: {create_date}")
|
|
||||||
elif date is not None:
|
elif date is not None:
|
||||||
create_date = date
|
create_date = date
|
||||||
self.log.debug(f"Creation date from parse_date: {create_date}")
|
self.log.debug(f"Creation date from parse_date: {create_date}")
|
||||||
@ -729,7 +723,11 @@ class ConsumerPlugin(
|
|||||||
|
|
||||||
storage_type = Document.STORAGE_TYPE_UNENCRYPTED
|
storage_type = Document.STORAGE_TYPE_UNENCRYPTED
|
||||||
|
|
||||||
title = file_info.title
|
if self.metadata.filename:
|
||||||
|
title = Path(self.metadata.filename).stem
|
||||||
|
else:
|
||||||
|
title = self.input_doc.original_file.stem
|
||||||
|
|
||||||
if self.metadata.title is not None:
|
if self.metadata.title is not None:
|
||||||
try:
|
try:
|
||||||
title = self._parse_title_placeholders(self.metadata.title)
|
title = self._parse_title_placeholders(self.metadata.title)
|
||||||
@ -808,13 +806,19 @@ class ConsumerPlugin(
|
|||||||
}
|
}
|
||||||
set_permissions_for_object(permissions=permissions, object=document)
|
set_permissions_for_object(permissions=permissions, object=document)
|
||||||
|
|
||||||
if self.metadata.custom_field_ids:
|
if self.metadata.custom_fields:
|
||||||
for field_id in self.metadata.custom_field_ids:
|
for field in CustomField.objects.filter(
|
||||||
field = CustomField.objects.get(pk=field_id)
|
id__in=self.metadata.custom_fields.keys(),
|
||||||
CustomFieldInstance.objects.create(
|
).distinct():
|
||||||
field=field,
|
value_field_name = CustomFieldInstance.get_value_field_name(
|
||||||
document=document,
|
data_type=field.data_type,
|
||||||
) # adds to document
|
)
|
||||||
|
args = {
|
||||||
|
"field": field,
|
||||||
|
"document": document,
|
||||||
|
value_field_name: self.metadata.custom_fields.get(field.id, None),
|
||||||
|
}
|
||||||
|
CustomFieldInstance.objects.create(**args) # adds to document
|
||||||
|
|
||||||
def _write(self, storage_type, source, target):
|
def _write(self, storage_type, source, target):
|
||||||
with (
|
with (
|
||||||
|
@ -29,7 +29,7 @@ class DocumentMetadataOverrides:
|
|||||||
view_groups: list[int] | None = None
|
view_groups: list[int] | None = None
|
||||||
change_users: list[int] | None = None
|
change_users: list[int] | None = None
|
||||||
change_groups: list[int] | None = None
|
change_groups: list[int] | None = None
|
||||||
custom_field_ids: list[int] | None = None
|
custom_fields: dict | None = None
|
||||||
|
|
||||||
def update(self, other: "DocumentMetadataOverrides") -> "DocumentMetadataOverrides":
|
def update(self, other: "DocumentMetadataOverrides") -> "DocumentMetadataOverrides":
|
||||||
"""
|
"""
|
||||||
@ -81,11 +81,10 @@ class DocumentMetadataOverrides:
|
|||||||
self.change_groups.extend(other.change_groups)
|
self.change_groups.extend(other.change_groups)
|
||||||
self.change_groups = list(set(self.change_groups))
|
self.change_groups = list(set(self.change_groups))
|
||||||
|
|
||||||
if self.custom_field_ids is None:
|
if self.custom_fields is None:
|
||||||
self.custom_field_ids = other.custom_field_ids
|
self.custom_fields = other.custom_fields
|
||||||
elif other.custom_field_ids is not None:
|
elif other.custom_fields is not None:
|
||||||
self.custom_field_ids.extend(other.custom_field_ids)
|
self.custom_fields.update(other.custom_fields)
|
||||||
self.custom_field_ids = list(set(self.custom_field_ids))
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -114,9 +113,10 @@ class DocumentMetadataOverrides:
|
|||||||
only_with_perms_in=["change_document"],
|
only_with_perms_in=["change_document"],
|
||||||
).values_list("id", flat=True),
|
).values_list("id", flat=True),
|
||||||
)
|
)
|
||||||
overrides.custom_field_ids = list(
|
overrides.custom_fields = {
|
||||||
doc.custom_fields.values_list("field", flat=True),
|
custom_field.id: custom_field.value
|
||||||
)
|
for custom_field in doc.custom_fields.all()
|
||||||
|
}
|
||||||
|
|
||||||
groups_with_perms = get_groups_with_perms(
|
groups_with_perms = get_groups_with_perms(
|
||||||
doc,
|
doc,
|
||||||
|
@ -36,7 +36,6 @@ from documents.models import CustomField
|
|||||||
from documents.models import CustomFieldInstance
|
from documents.models import CustomFieldInstance
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import Log
|
|
||||||
from documents.models import PaperlessTask
|
from documents.models import PaperlessTask
|
||||||
from documents.models import ShareLink
|
from documents.models import ShareLink
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
@ -761,12 +760,6 @@ class DocumentFilterSet(FilterSet):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LogFilterSet(FilterSet):
|
|
||||||
class Meta:
|
|
||||||
model = Log
|
|
||||||
fields = {"level": INT_KWARGS, "created": DATE_KWARGS, "group": ID_KWARGS}
|
|
||||||
|
|
||||||
|
|
||||||
class ShareLinkFilterSet(FilterSet):
|
class ShareLinkFilterSet(FilterSet):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ShareLink
|
model = ShareLink
|
||||||
|
15
src/documents/migrations/1064_delete_log.py
Normal file
15
src/documents/migrations/1064_delete_log.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-28 15:19
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("documents", "1063_paperlesstask_type_alter_paperlesstask_task_name_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name="Log",
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-03-01 18:10
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("documents", "1064_delete_log"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="workflowaction",
|
||||||
|
name="assign_custom_fields_values",
|
||||||
|
field=models.JSONField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Optional values to assign to the custom fields.",
|
||||||
|
null=True,
|
||||||
|
verbose_name="custom field values",
|
||||||
|
default=dict,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -1,12 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
from collections import OrderedDict
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
import dateutil.parser
|
|
||||||
import pathvalidate
|
import pathvalidate
|
||||||
from celery import states
|
from celery import states
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -379,36 +374,6 @@ class Document(SoftDeleteModel, ModelWithOwner):
|
|||||||
return timezone.localdate(self.created)
|
return timezone.localdate(self.created)
|
||||||
|
|
||||||
|
|
||||||
class Log(models.Model):
|
|
||||||
LEVELS = (
|
|
||||||
(logging.DEBUG, _("debug")),
|
|
||||||
(logging.INFO, _("information")),
|
|
||||||
(logging.WARNING, _("warning")),
|
|
||||||
(logging.ERROR, _("error")),
|
|
||||||
(logging.CRITICAL, _("critical")),
|
|
||||||
)
|
|
||||||
|
|
||||||
group = models.UUIDField(_("group"), blank=True, null=True)
|
|
||||||
|
|
||||||
message = models.TextField(_("message"))
|
|
||||||
|
|
||||||
level = models.PositiveIntegerField(
|
|
||||||
_("level"),
|
|
||||||
choices=LEVELS,
|
|
||||||
default=logging.INFO,
|
|
||||||
)
|
|
||||||
|
|
||||||
created = models.DateTimeField(_("created"), auto_now_add=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
ordering = ("-created",)
|
|
||||||
verbose_name = _("log")
|
|
||||||
verbose_name_plural = _("logs")
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.message
|
|
||||||
|
|
||||||
|
|
||||||
class SavedView(ModelWithOwner):
|
class SavedView(ModelWithOwner):
|
||||||
class DisplayMode(models.TextChoices):
|
class DisplayMode(models.TextChoices):
|
||||||
TABLE = ("table", _("Table"))
|
TABLE = ("table", _("Table"))
|
||||||
@ -548,91 +513,6 @@ class SavedViewFilterRule(models.Model):
|
|||||||
return f"SavedViewFilterRule: {self.rule_type} : {self.value}"
|
return f"SavedViewFilterRule: {self.rule_type} : {self.value}"
|
||||||
|
|
||||||
|
|
||||||
# TODO: why is this in the models file?
|
|
||||||
# TODO: how about, what is this and where is it documented?
|
|
||||||
# It appears to parsing JSON from an environment variable to get a title and date from
|
|
||||||
# the filename, if possible, as a higher priority than either document filename or
|
|
||||||
# content parsing
|
|
||||||
class FileInfo:
|
|
||||||
REGEXES = OrderedDict(
|
|
||||||
[
|
|
||||||
(
|
|
||||||
"created-title",
|
|
||||||
re.compile(
|
|
||||||
r"^(?P<created>\d{8}(\d{6})?Z) - (?P<title>.*)$",
|
|
||||||
flags=re.IGNORECASE,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
("title", re.compile(r"(?P<title>.*)$", flags=re.IGNORECASE)),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
created=None,
|
|
||||||
correspondent=None,
|
|
||||||
title=None,
|
|
||||||
tags=(),
|
|
||||||
extension=None,
|
|
||||||
):
|
|
||||||
self.created = created
|
|
||||||
self.title = title
|
|
||||||
self.extension = extension
|
|
||||||
self.correspondent = correspondent
|
|
||||||
self.tags = tags
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_created(cls, created):
|
|
||||||
try:
|
|
||||||
return dateutil.parser.parse(f"{created[:-1]:0<14}Z")
|
|
||||||
except ValueError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_title(cls, title):
|
|
||||||
return title
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _mangle_property(cls, properties, name):
|
|
||||||
if name in properties:
|
|
||||||
properties[name] = getattr(cls, f"_get_{name}")(properties[name])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_filename(cls, filename) -> "FileInfo":
|
|
||||||
# Mutate filename in-place before parsing its components
|
|
||||||
# by applying at most one of the configured transformations.
|
|
||||||
for pattern, repl in settings.FILENAME_PARSE_TRANSFORMS:
|
|
||||||
(filename, count) = pattern.subn(repl, filename)
|
|
||||||
if count:
|
|
||||||
break
|
|
||||||
|
|
||||||
# do this after the transforms so that the transforms can do whatever
|
|
||||||
# with the file extension.
|
|
||||||
filename_no_ext = os.path.splitext(filename)[0]
|
|
||||||
|
|
||||||
if filename_no_ext == filename and filename.startswith("."):
|
|
||||||
# This is a very special case where there is no text before the
|
|
||||||
# file type.
|
|
||||||
# TODO: this should be handled better. The ext is not removed
|
|
||||||
# because usually, files like '.pdf' are just hidden files
|
|
||||||
# with the name pdf, but in our case, its more likely that
|
|
||||||
# there's just no name to begin with.
|
|
||||||
filename = ""
|
|
||||||
# This isn't too bad either, since we'll just not match anything
|
|
||||||
# and return an empty title. TODO: actually, this is kinda bad.
|
|
||||||
else:
|
|
||||||
filename = filename_no_ext
|
|
||||||
|
|
||||||
# Parse filename components.
|
|
||||||
for regex in cls.REGEXES.values():
|
|
||||||
m = regex.match(filename)
|
|
||||||
if m:
|
|
||||||
properties = m.groupdict()
|
|
||||||
cls._mangle_property(properties, "created")
|
|
||||||
cls._mangle_property(properties, "title")
|
|
||||||
return cls(**properties)
|
|
||||||
|
|
||||||
|
|
||||||
# Extending User Model Using a One-To-One Link
|
# Extending User Model Using a One-To-One Link
|
||||||
class UiSettings(models.Model):
|
class UiSettings(models.Model):
|
||||||
user = models.OneToOneField(
|
user = models.OneToOneField(
|
||||||
@ -1391,6 +1271,16 @@ class WorkflowAction(models.Model):
|
|||||||
verbose_name=_("assign these custom fields"),
|
verbose_name=_("assign these custom fields"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assign_custom_fields_values = models.JSONField(
|
||||||
|
_("custom field values"),
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text=_(
|
||||||
|
"Optional values to assign to the custom fields.",
|
||||||
|
),
|
||||||
|
default=dict,
|
||||||
|
)
|
||||||
|
|
||||||
remove_tags = models.ManyToManyField(
|
remove_tags = models.ManyToManyField(
|
||||||
Tag,
|
Tag,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
@ -2018,6 +2018,7 @@ class WorkflowActionSerializer(serializers.ModelSerializer):
|
|||||||
"assign_change_users",
|
"assign_change_users",
|
||||||
"assign_change_groups",
|
"assign_change_groups",
|
||||||
"assign_custom_fields",
|
"assign_custom_fields",
|
||||||
|
"assign_custom_fields_values",
|
||||||
"remove_all_tags",
|
"remove_all_tags",
|
||||||
"remove_tags",
|
"remove_tags",
|
||||||
"remove_all_correspondents",
|
"remove_all_correspondents",
|
||||||
|
@ -632,7 +632,7 @@ def send_webhook(
|
|||||||
else:
|
else:
|
||||||
httpx.post(
|
httpx.post(
|
||||||
url,
|
url,
|
||||||
data=data,
|
content=data,
|
||||||
files=files,
|
files=files,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
).raise_for_status()
|
).raise_for_status()
|
||||||
@ -770,23 +770,40 @@ def run_workflows(
|
|||||||
if action.assign_custom_fields.exists():
|
if action.assign_custom_fields.exists():
|
||||||
if not use_overrides:
|
if not use_overrides:
|
||||||
for field in action.assign_custom_fields.all():
|
for field in action.assign_custom_fields.all():
|
||||||
if not CustomFieldInstance.objects.filter(
|
value_field_name = CustomFieldInstance.get_value_field_name(
|
||||||
|
data_type=field.data_type,
|
||||||
|
)
|
||||||
|
args = {
|
||||||
|
value_field_name: action.assign_custom_fields_values.get(
|
||||||
|
str(field.pk),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
# for some reason update_or_create doesn't work here
|
||||||
|
instance = CustomFieldInstance.objects.filter(
|
||||||
field=field,
|
field=field,
|
||||||
document=document,
|
document=document,
|
||||||
).exists():
|
).first()
|
||||||
# can be triggered on existing docs, so only add the field if it doesn't already exist
|
if instance:
|
||||||
|
setattr(instance, value_field_name, args[value_field_name])
|
||||||
|
instance.save()
|
||||||
|
else:
|
||||||
CustomFieldInstance.objects.create(
|
CustomFieldInstance.objects.create(
|
||||||
|
**args,
|
||||||
field=field,
|
field=field,
|
||||||
document=document,
|
document=document,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
overrides.custom_field_ids = list(
|
if overrides.custom_fields is None:
|
||||||
set(
|
overrides.custom_fields = {}
|
||||||
(overrides.custom_field_ids or [])
|
overrides.custom_fields.update(
|
||||||
+ list(
|
{
|
||||||
action.assign_custom_fields.values_list("pk", flat=True),
|
field.pk: action.assign_custom_fields_values.get(
|
||||||
),
|
str(field.pk),
|
||||||
),
|
None,
|
||||||
|
)
|
||||||
|
for field in action.assign_custom_fields.all()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def removal_action():
|
def removal_action():
|
||||||
@ -944,18 +961,18 @@ def run_workflows(
|
|||||||
if not use_overrides:
|
if not use_overrides:
|
||||||
CustomFieldInstance.objects.filter(document=document).delete()
|
CustomFieldInstance.objects.filter(document=document).delete()
|
||||||
else:
|
else:
|
||||||
overrides.custom_field_ids = None
|
overrides.custom_fields = None
|
||||||
elif action.remove_custom_fields.exists():
|
elif action.remove_custom_fields.exists():
|
||||||
if not use_overrides:
|
if not use_overrides:
|
||||||
CustomFieldInstance.objects.filter(
|
CustomFieldInstance.objects.filter(
|
||||||
field__in=action.remove_custom_fields.all(),
|
field__in=action.remove_custom_fields.all(),
|
||||||
document=document,
|
document=document,
|
||||||
).delete()
|
).delete()
|
||||||
elif overrides.custom_field_ids:
|
elif overrides.custom_fields:
|
||||||
for field in action.remove_custom_fields.filter(
|
for field in action.remove_custom_fields.filter(
|
||||||
pk__in=overrides.custom_field_ids,
|
pk__in=overrides.custom_fields.keys(),
|
||||||
):
|
):
|
||||||
overrides.custom_field_ids.remove(field.pk)
|
overrides.custom_fields.pop(field.pk, None)
|
||||||
|
|
||||||
def email_action():
|
def email_action():
|
||||||
if not settings.EMAIL_ENABLED:
|
if not settings.EMAIL_ENABLED:
|
||||||
@ -1002,29 +1019,37 @@ def run_workflows(
|
|||||||
added = timezone.localtime(timezone.now())
|
added = timezone.localtime(timezone.now())
|
||||||
created = timezone.localtime(overrides.created)
|
created = timezone.localtime(overrides.created)
|
||||||
|
|
||||||
subject = parse_w_workflow_placeholders(
|
subject = (
|
||||||
action.email.subject,
|
parse_w_workflow_placeholders(
|
||||||
correspondent,
|
action.email.subject,
|
||||||
document_type,
|
correspondent,
|
||||||
owner_username,
|
document_type,
|
||||||
added,
|
owner_username,
|
||||||
filename,
|
added,
|
||||||
current_filename,
|
filename,
|
||||||
created,
|
current_filename,
|
||||||
title,
|
created,
|
||||||
doc_url,
|
title,
|
||||||
|
doc_url,
|
||||||
|
)
|
||||||
|
if action.email.subject
|
||||||
|
else ""
|
||||||
)
|
)
|
||||||
body = parse_w_workflow_placeholders(
|
body = (
|
||||||
action.email.body,
|
parse_w_workflow_placeholders(
|
||||||
correspondent,
|
action.email.body,
|
||||||
document_type,
|
correspondent,
|
||||||
owner_username,
|
document_type,
|
||||||
added,
|
owner_username,
|
||||||
filename,
|
added,
|
||||||
current_filename,
|
filename,
|
||||||
created,
|
current_filename,
|
||||||
title,
|
created,
|
||||||
doc_url,
|
title,
|
||||||
|
doc_url,
|
||||||
|
)
|
||||||
|
if action.email.body
|
||||||
|
else ""
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
n_messages = send_email(
|
n_messages = send_email(
|
||||||
@ -1105,7 +1130,7 @@ def run_workflows(
|
|||||||
f"Error occurred parsing webhook params: {e}",
|
f"Error occurred parsing webhook params: {e}",
|
||||||
extra={"group": logging_group},
|
extra={"group": logging_group},
|
||||||
)
|
)
|
||||||
else:
|
elif action.webhook.body:
|
||||||
data = parse_w_workflow_placeholders(
|
data = parse_w_workflow_placeholders(
|
||||||
action.webhook.body,
|
action.webhook.body,
|
||||||
correspondent,
|
correspondent,
|
||||||
|
@ -8,6 +8,7 @@ from pathlib import PurePath
|
|||||||
import pathvalidate
|
import pathvalidate
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.dateparse import parse_date
|
from django.utils.dateparse import parse_date
|
||||||
|
from django.utils.text import slugify as django_slugify
|
||||||
from jinja2 import StrictUndefined
|
from jinja2 import StrictUndefined
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
from jinja2 import TemplateSyntaxError
|
from jinja2 import TemplateSyntaxError
|
||||||
@ -100,6 +101,8 @@ def format_datetime(value: str | datetime, format: str) -> str:
|
|||||||
|
|
||||||
_template_environment.filters["datetime"] = format_datetime
|
_template_environment.filters["datetime"] = format_datetime
|
||||||
|
|
||||||
|
_template_environment.filters["slugify"] = django_slugify
|
||||||
|
|
||||||
|
|
||||||
def create_dummy_document():
|
def create_dummy_document():
|
||||||
"""
|
"""
|
||||||
|
@ -28,6 +28,7 @@ from documents.caching import CACHE_50_MINUTES
|
|||||||
from documents.caching import CLASSIFIER_HASH_KEY
|
from documents.caching import CLASSIFIER_HASH_KEY
|
||||||
from documents.caching import CLASSIFIER_MODIFIED_KEY
|
from documents.caching import CLASSIFIER_MODIFIED_KEY
|
||||||
from documents.caching import CLASSIFIER_VERSION_KEY
|
from documents.caching import CLASSIFIER_VERSION_KEY
|
||||||
|
from documents.data_models import DocumentSource
|
||||||
from documents.models import Correspondent
|
from documents.models import Correspondent
|
||||||
from documents.models import CustomField
|
from documents.models import CustomField
|
||||||
from documents.models import CustomFieldInstance
|
from documents.models import CustomFieldInstance
|
||||||
@ -39,7 +40,10 @@ from documents.models import SavedView
|
|||||||
from documents.models import ShareLink
|
from documents.models import ShareLink
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
|
from documents.models import Workflow
|
||||||
|
from documents.models import WorkflowAction
|
||||||
from documents.models import WorkflowTrigger
|
from documents.models import WorkflowTrigger
|
||||||
|
from documents.signals.handlers import run_workflows
|
||||||
from documents.tests.utils import DirectoriesMixin
|
from documents.tests.utils import DirectoriesMixin
|
||||||
from documents.tests.utils import DocumentConsumeDelayMixin
|
from documents.tests.utils import DocumentConsumeDelayMixin
|
||||||
|
|
||||||
@ -1362,7 +1366,69 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
|||||||
|
|
||||||
self.assertEqual(input_doc.original_file.name, "simple.pdf")
|
self.assertEqual(input_doc.original_file.name, "simple.pdf")
|
||||||
self.assertEqual(overrides.filename, "simple.pdf")
|
self.assertEqual(overrides.filename, "simple.pdf")
|
||||||
self.assertEqual(overrides.custom_field_ids, [custom_field.id])
|
self.assertEqual(overrides.custom_fields, {custom_field.id: None})
|
||||||
|
|
||||||
|
def test_upload_with_custom_fields_and_workflow(self):
|
||||||
|
"""
|
||||||
|
GIVEN: A document with a source file
|
||||||
|
WHEN: Upload the document with custom fields and a workflow
|
||||||
|
THEN: Metadata is set correctly, mimicking what happens in the real consumer plugin
|
||||||
|
"""
|
||||||
|
self.consume_file_mock.return_value = celery.result.AsyncResult(
|
||||||
|
id=str(uuid.uuid4()),
|
||||||
|
)
|
||||||
|
|
||||||
|
cf = CustomField.objects.create(
|
||||||
|
name="stringfield",
|
||||||
|
data_type=CustomField.FieldDataType.STRING,
|
||||||
|
)
|
||||||
|
cf2 = CustomField.objects.create(
|
||||||
|
name="intfield",
|
||||||
|
data_type=CustomField.FieldDataType.INT,
|
||||||
|
)
|
||||||
|
|
||||||
|
trigger1 = WorkflowTrigger.objects.create(
|
||||||
|
type=WorkflowTrigger.WorkflowTriggerType.CONSUMPTION,
|
||||||
|
sources=f"{DocumentSource.ApiUpload},{DocumentSource.ConsumeFolder},{DocumentSource.MailFetch}",
|
||||||
|
)
|
||||||
|
action1 = WorkflowAction.objects.create(
|
||||||
|
assign_title="Doc title",
|
||||||
|
)
|
||||||
|
action1.assign_custom_fields.add(cf2)
|
||||||
|
action1.assign_custom_fields_values = {cf2.id: 123}
|
||||||
|
action1.save()
|
||||||
|
|
||||||
|
w1 = Workflow.objects.create(
|
||||||
|
name="Workflow 1",
|
||||||
|
order=0,
|
||||||
|
)
|
||||||
|
w1.triggers.add(trigger1)
|
||||||
|
w1.actions.add(action1)
|
||||||
|
w1.save()
|
||||||
|
|
||||||
|
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
|
||||||
|
response = self.client.post(
|
||||||
|
"/api/documents/post_document/",
|
||||||
|
{
|
||||||
|
"document": f,
|
||||||
|
"custom_fields": [cf.id],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
self.consume_file_mock.assert_called_once()
|
||||||
|
|
||||||
|
input_doc, overrides = self.get_last_consume_delay_call_args()
|
||||||
|
|
||||||
|
new_overrides, msg = run_workflows(
|
||||||
|
trigger_type=WorkflowTrigger.WorkflowTriggerType.CONSUMPTION,
|
||||||
|
document=input_doc,
|
||||||
|
logging_group=None,
|
||||||
|
overrides=overrides,
|
||||||
|
)
|
||||||
|
overrides.update(new_overrides)
|
||||||
|
self.assertEqual(overrides.custom_fields, {cf.id: None, cf2.id: 123})
|
||||||
|
|
||||||
def test_upload_with_webui_source(self):
|
def test_upload_with_webui_source(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
import tempfile
|
import tempfile
|
||||||
import zoneinfo
|
import zoneinfo
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest import TestCase as UnittestTestCase
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
@ -26,7 +24,6 @@ from documents.models import Correspondent
|
|||||||
from documents.models import CustomField
|
from documents.models import CustomField
|
||||||
from documents.models import Document
|
from documents.models import Document
|
||||||
from documents.models import DocumentType
|
from documents.models import DocumentType
|
||||||
from documents.models import FileInfo
|
|
||||||
from documents.models import StoragePath
|
from documents.models import StoragePath
|
||||||
from documents.models import Tag
|
from documents.models import Tag
|
||||||
from documents.parsers import DocumentParser
|
from documents.parsers import DocumentParser
|
||||||
@ -40,143 +37,6 @@ from paperless_mail.models import MailRule
|
|||||||
from paperless_mail.parsers import MailDocumentParser
|
from paperless_mail.parsers import MailDocumentParser
|
||||||
|
|
||||||
|
|
||||||
class TestAttributes(UnittestTestCase):
|
|
||||||
TAGS = ("tag1", "tag2", "tag3")
|
|
||||||
|
|
||||||
def _test_guess_attributes_from_name(self, filename, sender, title, tags):
|
|
||||||
file_info = FileInfo.from_filename(filename)
|
|
||||||
|
|
||||||
if sender:
|
|
||||||
self.assertEqual(file_info.correspondent.name, sender, filename)
|
|
||||||
else:
|
|
||||||
self.assertIsNone(file_info.correspondent, filename)
|
|
||||||
|
|
||||||
self.assertEqual(file_info.title, title, filename)
|
|
||||||
|
|
||||||
self.assertEqual(tuple(t.name for t in file_info.tags), tags, filename)
|
|
||||||
|
|
||||||
def test_guess_attributes_from_name_when_title_starts_with_dash(self):
|
|
||||||
self._test_guess_attributes_from_name(
|
|
||||||
"- weird but should not break.pdf",
|
|
||||||
None,
|
|
||||||
"- weird but should not break",
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_guess_attributes_from_name_when_title_ends_with_dash(self):
|
|
||||||
self._test_guess_attributes_from_name(
|
|
||||||
"weird but should not break -.pdf",
|
|
||||||
None,
|
|
||||||
"weird but should not break -",
|
|
||||||
(),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestFieldPermutations(TestCase):
|
|
||||||
valid_dates = (
|
|
||||||
"20150102030405Z",
|
|
||||||
"20150102Z",
|
|
||||||
)
|
|
||||||
valid_correspondents = ["timmy", "Dr. McWheelie", "Dash Gor-don", "o Θεpμaoτής", ""]
|
|
||||||
valid_titles = ["title", "Title w Spaces", "Title a-dash", "Tίτλoς", ""]
|
|
||||||
valid_tags = ["tag", "tig,tag", "tag1,tag2,tag-3"]
|
|
||||||
|
|
||||||
def _test_guessed_attributes(
|
|
||||||
self,
|
|
||||||
filename,
|
|
||||||
created=None,
|
|
||||||
correspondent=None,
|
|
||||||
title=None,
|
|
||||||
tags=None,
|
|
||||||
):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
|
|
||||||
# Created
|
|
||||||
if created is None:
|
|
||||||
self.assertIsNone(info.created, filename)
|
|
||||||
else:
|
|
||||||
self.assertEqual(info.created.year, int(created[:4]), filename)
|
|
||||||
self.assertEqual(info.created.month, int(created[4:6]), filename)
|
|
||||||
self.assertEqual(info.created.day, int(created[6:8]), filename)
|
|
||||||
|
|
||||||
# Correspondent
|
|
||||||
if correspondent:
|
|
||||||
self.assertEqual(info.correspondent.name, correspondent, filename)
|
|
||||||
else:
|
|
||||||
self.assertEqual(info.correspondent, None, filename)
|
|
||||||
|
|
||||||
# Title
|
|
||||||
self.assertEqual(info.title, title, filename)
|
|
||||||
|
|
||||||
# Tags
|
|
||||||
if tags is None:
|
|
||||||
self.assertEqual(info.tags, (), filename)
|
|
||||||
else:
|
|
||||||
self.assertEqual([t.name for t in info.tags], tags.split(","), filename)
|
|
||||||
|
|
||||||
def test_just_title(self):
|
|
||||||
template = "{title}.pdf"
|
|
||||||
for title in self.valid_titles:
|
|
||||||
spec = dict(title=title)
|
|
||||||
filename = template.format(**spec)
|
|
||||||
self._test_guessed_attributes(filename, **spec)
|
|
||||||
|
|
||||||
def test_created_and_title(self):
|
|
||||||
template = "{created} - {title}.pdf"
|
|
||||||
|
|
||||||
for created in self.valid_dates:
|
|
||||||
for title in self.valid_titles:
|
|
||||||
spec = {"created": created, "title": title}
|
|
||||||
self._test_guessed_attributes(template.format(**spec), **spec)
|
|
||||||
|
|
||||||
def test_invalid_date_format(self):
|
|
||||||
info = FileInfo.from_filename("06112017Z - title.pdf")
|
|
||||||
self.assertEqual(info.title, "title")
|
|
||||||
self.assertIsNone(info.created)
|
|
||||||
|
|
||||||
def test_filename_parse_transforms(self):
|
|
||||||
filename = "tag1,tag2_20190908_180610_0001.pdf"
|
|
||||||
all_patt = re.compile("^.*$")
|
|
||||||
none_patt = re.compile("$a")
|
|
||||||
re.compile("^([a-z0-9,]+)_(\\d{8})_(\\d{6})_([0-9]+)\\.")
|
|
||||||
|
|
||||||
# No transformations configured (= default)
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "tag1,tag2_20190908_180610_0001")
|
|
||||||
self.assertEqual(info.tags, ())
|
|
||||||
self.assertIsNone(info.created)
|
|
||||||
|
|
||||||
# Pattern doesn't match (filename unaltered)
|
|
||||||
with self.settings(FILENAME_PARSE_TRANSFORMS=[(none_patt, "none.gif")]):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "tag1,tag2_20190908_180610_0001")
|
|
||||||
|
|
||||||
# Simple transformation (match all)
|
|
||||||
with self.settings(FILENAME_PARSE_TRANSFORMS=[(all_patt, "all.gif")]):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "all")
|
|
||||||
|
|
||||||
# Multiple transformations configured (first pattern matches)
|
|
||||||
with self.settings(
|
|
||||||
FILENAME_PARSE_TRANSFORMS=[
|
|
||||||
(all_patt, "all.gif"),
|
|
||||||
(all_patt, "anotherall.gif"),
|
|
||||||
],
|
|
||||||
):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "all")
|
|
||||||
|
|
||||||
# Multiple transformations configured (second pattern matches)
|
|
||||||
with self.settings(
|
|
||||||
FILENAME_PARSE_TRANSFORMS=[
|
|
||||||
(none_patt, "none.gif"),
|
|
||||||
(all_patt, "anotherall.gif"),
|
|
||||||
],
|
|
||||||
):
|
|
||||||
info = FileInfo.from_filename(filename)
|
|
||||||
self.assertEqual(info.title, "anotherall")
|
|
||||||
|
|
||||||
|
|
||||||
class _BaseTestParser(DocumentParser):
|
class _BaseTestParser(DocumentParser):
|
||||||
def get_settings(self):
|
def get_settings(self):
|
||||||
"""
|
"""
|
||||||
@ -548,7 +408,9 @@ class TestConsumer(
|
|||||||
|
|
||||||
with self.get_consumer(
|
with self.get_consumer(
|
||||||
self.get_test_file(),
|
self.get_test_file(),
|
||||||
DocumentMetadataOverrides(custom_field_ids=[cf1.id, cf3.id]),
|
DocumentMetadataOverrides(
|
||||||
|
custom_fields={cf1.id: "value1", cf3.id: "http://example.com"},
|
||||||
|
),
|
||||||
) as consumer:
|
) as consumer:
|
||||||
consumer.run()
|
consumer.run()
|
||||||
|
|
||||||
@ -560,6 +422,11 @@ class TestConsumer(
|
|||||||
self.assertIn(cf1, fields_used)
|
self.assertIn(cf1, fields_used)
|
||||||
self.assertNotIn(cf2, fields_used)
|
self.assertNotIn(cf2, fields_used)
|
||||||
self.assertIn(cf3, fields_used)
|
self.assertIn(cf3, fields_used)
|
||||||
|
self.assertEqual(document.custom_fields.get(field=cf1).value, "value1")
|
||||||
|
self.assertEqual(
|
||||||
|
document.custom_fields.get(field=cf3).value,
|
||||||
|
"http://example.com",
|
||||||
|
)
|
||||||
self._assert_first_last_send_progress()
|
self._assert_first_last_send_progress()
|
||||||
|
|
||||||
def testOverrideAsn(self):
|
def testOverrideAsn(self):
|
||||||
|
@ -1517,3 +1517,63 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
|
|||||||
generate_filename(doc_a),
|
generate_filename(doc_a),
|
||||||
"2024-10-01/Some Title.pdf",
|
"2024-10-01/Some Title.pdf",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_slugify_filter(self):
|
||||||
|
"""
|
||||||
|
GIVEN:
|
||||||
|
- Filename format with slugify filter
|
||||||
|
WHEN:
|
||||||
|
- Filepath for a document with this format is called
|
||||||
|
THEN:
|
||||||
|
- The slugify filter properly converts strings to URL-friendly slugs
|
||||||
|
"""
|
||||||
|
doc = Document.objects.create(
|
||||||
|
title="Some Title! With @ Special # Characters",
|
||||||
|
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
|
||||||
|
added=timezone.make_aware(datetime.datetime(2024, 10, 1, 7, 36, 51, 153)),
|
||||||
|
mime_type="application/pdf",
|
||||||
|
pk=2,
|
||||||
|
checksum="2",
|
||||||
|
archive_serial_number=25,
|
||||||
|
)
|
||||||
|
|
||||||
|
with override_settings(
|
||||||
|
FILENAME_FORMAT="{{ title | slugify }}",
|
||||||
|
):
|
||||||
|
self.assertEqual(
|
||||||
|
generate_filename(doc),
|
||||||
|
"some-title-with-special-characters.pdf",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with correspondent name containing spaces and special chars
|
||||||
|
doc.correspondent = Correspondent.objects.create(
|
||||||
|
name="John's @ Office / Workplace",
|
||||||
|
)
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
with override_settings(
|
||||||
|
FILENAME_FORMAT="{{ correspondent | slugify }}/{{ title | slugify }}",
|
||||||
|
):
|
||||||
|
self.assertEqual(
|
||||||
|
generate_filename(doc),
|
||||||
|
"johns-office-workplace/some-title-with-special-characters.pdf",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test with custom fields
|
||||||
|
cf = CustomField.objects.create(
|
||||||
|
name="Location",
|
||||||
|
data_type=CustomField.FieldDataType.STRING,
|
||||||
|
)
|
||||||
|
CustomFieldInstance.objects.create(
|
||||||
|
document=doc,
|
||||||
|
field=cf,
|
||||||
|
value_text="Brussels @ Belgium!",
|
||||||
|
)
|
||||||
|
|
||||||
|
with override_settings(
|
||||||
|
FILENAME_FORMAT="{{ custom_fields | get_cf_value('Location') | slugify }}/{{ title | slugify }}",
|
||||||
|
):
|
||||||
|
self.assertEqual(
|
||||||
|
generate_filename(doc),
|
||||||
|
"brussels-belgium/some-title-with-special-characters.pdf",
|
||||||
|
)
|
||||||
|
@ -133,6 +133,9 @@ class TestWorkflows(
|
|||||||
action.assign_change_groups.add(self.group1.pk)
|
action.assign_change_groups.add(self.group1.pk)
|
||||||
action.assign_custom_fields.add(self.cf1.pk)
|
action.assign_custom_fields.add(self.cf1.pk)
|
||||||
action.assign_custom_fields.add(self.cf2.pk)
|
action.assign_custom_fields.add(self.cf2.pk)
|
||||||
|
action.assign_custom_fields_values = {
|
||||||
|
self.cf2.pk: 42,
|
||||||
|
}
|
||||||
action.save()
|
action.save()
|
||||||
w = Workflow.objects.create(
|
w = Workflow.objects.create(
|
||||||
name="Workflow 1",
|
name="Workflow 1",
|
||||||
@ -209,6 +212,10 @@ class TestWorkflows(
|
|||||||
list(document.custom_fields.all().values_list("field", flat=True)),
|
list(document.custom_fields.all().values_list("field", flat=True)),
|
||||||
[self.cf1.pk, self.cf2.pk],
|
[self.cf1.pk, self.cf2.pk],
|
||||||
)
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
document.custom_fields.get(field=self.cf2.pk).value,
|
||||||
|
42,
|
||||||
|
)
|
||||||
|
|
||||||
info = cm.output[0]
|
info = cm.output[0]
|
||||||
expected_str = f"Document matched {trigger} from {w}"
|
expected_str = f"Document matched {trigger} from {w}"
|
||||||
@ -1215,11 +1222,11 @@ class TestWorkflows(
|
|||||||
def test_document_updated_workflow_existing_custom_field(self):
|
def test_document_updated_workflow_existing_custom_field(self):
|
||||||
"""
|
"""
|
||||||
GIVEN:
|
GIVEN:
|
||||||
- Existing workflow with UPDATED trigger and action that adds a custom field
|
- Existing workflow with UPDATED trigger and action that assigns a custom field with a value
|
||||||
WHEN:
|
WHEN:
|
||||||
- Document is updated that already contains the field
|
- Document is updated that already contains the field
|
||||||
THEN:
|
THEN:
|
||||||
- Document update succeeds without trying to re-create the field
|
- Document update succeeds and updates the field
|
||||||
"""
|
"""
|
||||||
trigger = WorkflowTrigger.objects.create(
|
trigger = WorkflowTrigger.objects.create(
|
||||||
type=WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED,
|
type=WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED,
|
||||||
@ -1227,6 +1234,8 @@ class TestWorkflows(
|
|||||||
)
|
)
|
||||||
action = WorkflowAction.objects.create()
|
action = WorkflowAction.objects.create()
|
||||||
action.assign_custom_fields.add(self.cf1)
|
action.assign_custom_fields.add(self.cf1)
|
||||||
|
action.assign_custom_fields_values = {self.cf1.pk: "new value"}
|
||||||
|
action.save()
|
||||||
w = Workflow.objects.create(
|
w = Workflow.objects.create(
|
||||||
name="Workflow 1",
|
name="Workflow 1",
|
||||||
order=0,
|
order=0,
|
||||||
@ -1251,6 +1260,9 @@ class TestWorkflows(
|
|||||||
format="json",
|
format="json",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
doc.refresh_from_db()
|
||||||
|
self.assertEqual(doc.custom_fields.get(field=self.cf1).value, "new value")
|
||||||
|
|
||||||
def test_document_updated_workflow_merge_permissions(self):
|
def test_document_updated_workflow_merge_permissions(self):
|
||||||
"""
|
"""
|
||||||
GIVEN:
|
GIVEN:
|
||||||
@ -2603,7 +2615,7 @@ class TestWorkflows(
|
|||||||
|
|
||||||
mock_post.assert_called_once_with(
|
mock_post.assert_called_once_with(
|
||||||
"http://paperless-ngx.com",
|
"http://paperless-ngx.com",
|
||||||
data="Test message",
|
content="Test message",
|
||||||
headers={},
|
headers={},
|
||||||
files=None,
|
files=None,
|
||||||
)
|
)
|
||||||
|
@ -1471,7 +1471,10 @@ class PostDocumentView(GenericAPIView):
|
|||||||
created=created,
|
created=created,
|
||||||
asn=archive_serial_number,
|
asn=archive_serial_number,
|
||||||
owner_id=request.user.id,
|
owner_id=request.user.id,
|
||||||
custom_field_ids=custom_field_ids,
|
# TODO: set values
|
||||||
|
custom_fields={cf_id: None for cf_id in custom_field_ids}
|
||||||
|
if custom_field_ids
|
||||||
|
else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
async_task = consume_file.delay(
|
async_task = consume_file.delay(
|
||||||
|
@ -2,7 +2,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: paperless-ngx\n"
|
"Project-Id-Version: paperless-ngx\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-02-25 11:07-0800\n"
|
"POT-Creation-Date: 2025-03-01 21:03-0800\n"
|
||||||
"PO-Revision-Date: 2022-02-17 04:17\n"
|
"PO-Revision-Date: 2022-02-17 04:17\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: English\n"
|
"Language-Team: English\n"
|
||||||
@ -21,39 +21,39 @@ msgstr ""
|
|||||||
msgid "Documents"
|
msgid "Documents"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:370
|
#: documents/filters.py:375
|
||||||
msgid "Value must be valid JSON."
|
msgid "Value must be valid JSON."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:389
|
#: documents/filters.py:394
|
||||||
msgid "Invalid custom field query expression"
|
msgid "Invalid custom field query expression"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:399
|
#: documents/filters.py:404
|
||||||
msgid "Invalid expression list. Must be nonempty."
|
msgid "Invalid expression list. Must be nonempty."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:420
|
#: documents/filters.py:425
|
||||||
msgid "Invalid logical operator {op!r}"
|
msgid "Invalid logical operator {op!r}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:434
|
#: documents/filters.py:439
|
||||||
msgid "Maximum number of query conditions exceeded."
|
msgid "Maximum number of query conditions exceeded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:499
|
#: documents/filters.py:504
|
||||||
msgid "{name!r} is not a valid custom field."
|
msgid "{name!r} is not a valid custom field."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:536
|
#: documents/filters.py:541
|
||||||
msgid "{data_type} does not support query expr {expr!r}."
|
msgid "{data_type} does not support query expr {expr!r}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:644
|
#: documents/filters.py:649
|
||||||
msgid "Maximum nesting depth exceeded."
|
msgid "Maximum nesting depth exceeded."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/filters.py:829
|
#: documents/filters.py:834
|
||||||
msgid "Custom field not found"
|
msgid "Custom field not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ msgstr ""
|
|||||||
msgid "Automatic"
|
msgid "Automatic"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:67 documents/models.py:433 documents/models.py:1526
|
#: documents/models.py:67 documents/models.py:433 documents/models.py:1536
|
||||||
#: paperless_mail/models.py:23 paperless_mail/models.py:143
|
#: paperless_mail/models.py:23 paperless_mail/models.py:143
|
||||||
msgid "name"
|
msgid "name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -256,7 +256,7 @@ msgid "The position of this document in your physical document archive."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:295 documents/models.py:761 documents/models.py:815
|
#: documents/models.py:295 documents/models.py:761 documents/models.py:815
|
||||||
#: documents/models.py:1569
|
#: documents/models.py:1579
|
||||||
msgid "document"
|
msgid "document"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1088,141 +1088,149 @@ msgstr ""
|
|||||||
msgid "assign these custom fields"
|
msgid "assign these custom fields"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1398
|
#: documents/models.py:1395
|
||||||
|
msgid "custom field values"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: documents/models.py:1399
|
||||||
|
msgid "Optional values to assign to the custom fields."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: documents/models.py:1408
|
||||||
msgid "remove these tag(s)"
|
msgid "remove these tag(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1403
|
#: documents/models.py:1413
|
||||||
msgid "remove all tags"
|
msgid "remove all tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1410
|
#: documents/models.py:1420
|
||||||
msgid "remove these document type(s)"
|
msgid "remove these document type(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1415
|
#: documents/models.py:1425
|
||||||
msgid "remove all document types"
|
msgid "remove all document types"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1422
|
#: documents/models.py:1432
|
||||||
msgid "remove these correspondent(s)"
|
msgid "remove these correspondent(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1427
|
#: documents/models.py:1437
|
||||||
msgid "remove all correspondents"
|
msgid "remove all correspondents"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1434
|
#: documents/models.py:1444
|
||||||
msgid "remove these storage path(s)"
|
msgid "remove these storage path(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1439
|
#: documents/models.py:1449
|
||||||
msgid "remove all storage paths"
|
msgid "remove all storage paths"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1446
|
#: documents/models.py:1456
|
||||||
msgid "remove these owner(s)"
|
msgid "remove these owner(s)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1451
|
#: documents/models.py:1461
|
||||||
msgid "remove all owners"
|
msgid "remove all owners"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1458
|
#: documents/models.py:1468
|
||||||
msgid "remove view permissions for these users"
|
msgid "remove view permissions for these users"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1465
|
#: documents/models.py:1475
|
||||||
msgid "remove view permissions for these groups"
|
msgid "remove view permissions for these groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1472
|
#: documents/models.py:1482
|
||||||
msgid "remove change permissions for these users"
|
msgid "remove change permissions for these users"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1479
|
#: documents/models.py:1489
|
||||||
msgid "remove change permissions for these groups"
|
msgid "remove change permissions for these groups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1484
|
#: documents/models.py:1494
|
||||||
msgid "remove all permissions"
|
msgid "remove all permissions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1491
|
#: documents/models.py:1501
|
||||||
msgid "remove these custom fields"
|
msgid "remove these custom fields"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1496
|
#: documents/models.py:1506
|
||||||
msgid "remove all custom fields"
|
msgid "remove all custom fields"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1505
|
#: documents/models.py:1515
|
||||||
msgid "email"
|
msgid "email"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1514
|
#: documents/models.py:1524
|
||||||
msgid "webhook"
|
msgid "webhook"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1518
|
#: documents/models.py:1528
|
||||||
msgid "workflow action"
|
msgid "workflow action"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1519
|
#: documents/models.py:1529
|
||||||
msgid "workflow actions"
|
msgid "workflow actions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1528 paperless_mail/models.py:145
|
#: documents/models.py:1538 paperless_mail/models.py:145
|
||||||
msgid "order"
|
msgid "order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1534
|
#: documents/models.py:1544
|
||||||
msgid "triggers"
|
msgid "triggers"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1541
|
#: documents/models.py:1551
|
||||||
msgid "actions"
|
msgid "actions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1544 paperless_mail/models.py:154
|
#: documents/models.py:1554 paperless_mail/models.py:154
|
||||||
msgid "enabled"
|
msgid "enabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1555
|
#: documents/models.py:1565
|
||||||
msgid "workflow"
|
msgid "workflow"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1559
|
#: documents/models.py:1569
|
||||||
msgid "workflow trigger type"
|
msgid "workflow trigger type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1573
|
#: documents/models.py:1583
|
||||||
msgid "date run"
|
msgid "date run"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1579
|
#: documents/models.py:1589
|
||||||
msgid "workflow run"
|
msgid "workflow run"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/models.py:1580
|
#: documents/models.py:1590
|
||||||
msgid "workflow runs"
|
msgid "workflow runs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:128
|
#: documents/serialisers.py:134
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Invalid regular expression: %(error)s"
|
msgid "Invalid regular expression: %(error)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:554
|
#: documents/serialisers.py:560
|
||||||
msgid "Invalid color."
|
msgid "Invalid color."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:1570
|
#: documents/serialisers.py:1576
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "File type %(type)s not supported"
|
msgid "File type %(type)s not supported"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: documents/serialisers.py:1659
|
#: documents/serialisers.py:1665
|
||||||
msgid "Invalid variable detected."
|
msgid "Invalid variable detected."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1463,7 +1471,7 @@ msgstr ""
|
|||||||
msgid "Unable to parse URI {value}"
|
msgid "Unable to parse URI {value}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/apps.py:10
|
#: paperless/apps.py:11
|
||||||
msgid "Paperless"
|
msgid "Paperless"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -1611,139 +1619,139 @@ msgstr ""
|
|||||||
msgid "paperless application settings"
|
msgid "paperless application settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:721
|
#: paperless/settings.py:724
|
||||||
msgid "English (US)"
|
msgid "English (US)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:722
|
#: paperless/settings.py:725
|
||||||
msgid "Arabic"
|
msgid "Arabic"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:723
|
#: paperless/settings.py:726
|
||||||
msgid "Afrikaans"
|
msgid "Afrikaans"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:724
|
#: paperless/settings.py:727
|
||||||
msgid "Belarusian"
|
msgid "Belarusian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:725
|
#: paperless/settings.py:728
|
||||||
msgid "Bulgarian"
|
msgid "Bulgarian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:726
|
#: paperless/settings.py:729
|
||||||
msgid "Catalan"
|
msgid "Catalan"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:727
|
#: paperless/settings.py:730
|
||||||
msgid "Czech"
|
msgid "Czech"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:728
|
#: paperless/settings.py:731
|
||||||
msgid "Danish"
|
msgid "Danish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:729
|
#: paperless/settings.py:732
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:730
|
#: paperless/settings.py:733
|
||||||
msgid "Greek"
|
msgid "Greek"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:731
|
#: paperless/settings.py:734
|
||||||
msgid "English (GB)"
|
msgid "English (GB)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:732
|
#: paperless/settings.py:735
|
||||||
msgid "Spanish"
|
msgid "Spanish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:733
|
#: paperless/settings.py:736
|
||||||
msgid "Finnish"
|
msgid "Finnish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:734
|
#: paperless/settings.py:737
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:735
|
#: paperless/settings.py:738
|
||||||
msgid "Hungarian"
|
msgid "Hungarian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:736
|
#: paperless/settings.py:739
|
||||||
msgid "Italian"
|
msgid "Italian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:737
|
#: paperless/settings.py:740
|
||||||
msgid "Japanese"
|
msgid "Japanese"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:738
|
#: paperless/settings.py:741
|
||||||
msgid "Korean"
|
msgid "Korean"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:739
|
#: paperless/settings.py:742
|
||||||
msgid "Luxembourgish"
|
msgid "Luxembourgish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:740
|
#: paperless/settings.py:743
|
||||||
msgid "Norwegian"
|
msgid "Norwegian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:741
|
#: paperless/settings.py:744
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:742
|
#: paperless/settings.py:745
|
||||||
msgid "Polish"
|
msgid "Polish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:743
|
#: paperless/settings.py:746
|
||||||
msgid "Portuguese (Brazil)"
|
msgid "Portuguese (Brazil)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:744
|
#: paperless/settings.py:747
|
||||||
msgid "Portuguese"
|
msgid "Portuguese"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:745
|
#: paperless/settings.py:748
|
||||||
msgid "Romanian"
|
msgid "Romanian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:746
|
#: paperless/settings.py:749
|
||||||
msgid "Russian"
|
msgid "Russian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:747
|
#: paperless/settings.py:750
|
||||||
msgid "Slovak"
|
msgid "Slovak"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:748
|
#: paperless/settings.py:751
|
||||||
msgid "Slovenian"
|
msgid "Slovenian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:749
|
#: paperless/settings.py:752
|
||||||
msgid "Serbian"
|
msgid "Serbian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:750
|
#: paperless/settings.py:753
|
||||||
msgid "Swedish"
|
msgid "Swedish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:751
|
#: paperless/settings.py:754
|
||||||
msgid "Turkish"
|
msgid "Turkish"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:752
|
#: paperless/settings.py:755
|
||||||
msgid "Ukrainian"
|
msgid "Ukrainian"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:753
|
#: paperless/settings.py:756
|
||||||
msgid "Chinese Simplified"
|
msgid "Chinese Simplified"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: paperless/settings.py:754
|
#: paperless/settings.py:757
|
||||||
msgid "Chinese Traditional"
|
msgid "Chinese Traditional"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import json
|
|||||||
import math
|
import math
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import tempfile
|
import tempfile
|
||||||
from os import PathLike
|
from os import PathLike
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -1089,11 +1088,6 @@ FILENAME_DATE_ORDER = os.getenv("PAPERLESS_FILENAME_DATE_ORDER")
|
|||||||
# fewer dates shown.
|
# fewer dates shown.
|
||||||
NUMBER_OF_SUGGESTED_DATES = __get_int("PAPERLESS_NUMBER_OF_SUGGESTED_DATES", 3)
|
NUMBER_OF_SUGGESTED_DATES = __get_int("PAPERLESS_NUMBER_OF_SUGGESTED_DATES", 3)
|
||||||
|
|
||||||
# Transformations applied before filename parsing
|
|
||||||
FILENAME_PARSE_TRANSFORMS = []
|
|
||||||
for t in json.loads(os.getenv("PAPERLESS_FILENAME_PARSE_TRANSFORMS", "[]")):
|
|
||||||
FILENAME_PARSE_TRANSFORMS.append((re.compile(t["pattern"]), t["repl"]))
|
|
||||||
|
|
||||||
# Specify the filename format for out files
|
# Specify the filename format for out files
|
||||||
FILENAME_FORMAT = os.getenv("PAPERLESS_FILENAME_FORMAT")
|
FILENAME_FORMAT = os.getenv("PAPERLESS_FILENAME_FORMAT")
|
||||||
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
[tool:pytest]
|
|
||||||
DJANGO_SETTINGS_MODULE = paperless.settings
|
|
||||||
addopts = --pythonwarnings=all --cov --cov-report=html --cov-report=xml --numprocesses auto --maxprocesses=16 --quiet --durations=50
|
|
||||||
env =
|
|
||||||
PAPERLESS_DISABLE_DBHANDLER=true
|
|
||||||
PAPERLESS_CACHE_BACKEND=django.core.cache.backends.locmem.LocMemCache
|
|
||||||
norecursedirs = locale/*
|
|
||||||
|
|
||||||
[coverage:run]
|
|
||||||
source =
|
|
||||||
./
|
|
||||||
omit =
|
|
||||||
*/tests/*
|
|
||||||
manage.py
|
|
||||||
paperless/workers.py
|
|
||||||
paperless/wsgi.py
|
|
||||||
paperless/auth.py
|
|
||||||
|
|
||||||
[coverage:report]
|
|
||||||
exclude_also =
|
|
||||||
if settings.AUDIT_LOG_ENABLED:
|
|
||||||
if AUDIT_LOG_ENABLED:
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
|
|
||||||
[mypy]
|
|
||||||
plugins = mypy_django_plugin.main, mypy_drf_plugin.main, numpy.typing.mypy_plugin
|
|
||||||
check_untyped_defs = true
|
|
||||||
disallow_any_generics = true
|
|
||||||
disallow_incomplete_defs = true
|
|
||||||
disallow_untyped_defs = true
|
|
||||||
warn_redundant_casts = true
|
|
||||||
warn_unused_ignores = true
|
|
||||||
|
|
||||||
[mypy.plugins.django-stubs]
|
|
||||||
django_settings_module = "paperless.settings"
|
|
@ -6,7 +6,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
Granian(
|
Granian(
|
||||||
"paperless.asgi:application",
|
"paperless.asgi:application",
|
||||||
interface=Interfaces.ASGI,
|
interface=Interfaces.ASGINL,
|
||||||
address=os.getenv("GRANIAN_HOST") or os.getenv("PAPERLESS_BIND_ADDR", "::"),
|
address=os.getenv("GRANIAN_HOST") or os.getenv("PAPERLESS_BIND_ADDR", "::"),
|
||||||
port=int(os.getenv("GRANIAN_PORT") or os.getenv("PAPERLESS_PORT") or 8000),
|
port=int(os.getenv("GRANIAN_PORT") or os.getenv("PAPERLESS_PORT") or 8000),
|
||||||
workers=int(
|
workers=int(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user