mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-02 16:14:39 -05:00
Compare commits
No commits in common. "dev" and "v2.15.3" have entirely different histories.
@ -83,8 +83,7 @@ RUN set -eux \
|
|||||||
&& 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.7.8 /uv /bin/uv
|
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" \
|
||||||
@ -129,6 +128,7 @@ 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
|
||||||
|
@ -47,19 +47,39 @@ To start the DevContainer:
|
|||||||
|
|
||||||
1. Open VSCode.
|
1. Open VSCode.
|
||||||
2. Open the project folder.
|
2. Open the project folder.
|
||||||
3. Open the command palette and choose `Dev Containers: Rebuild and Reopen in Container`.
|
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.
|
VSCode will build and start the DevContainer environment.
|
||||||
|
|
||||||
### Step 2: Initial Setup
|
### Step 2: Initial Setup
|
||||||
|
|
||||||
Once the DevContainer is up and running, run the `Project Setup: Run all Init Tasks` task to initialize the project.
|
Once the DevContainer is up and running, perform the following steps:
|
||||||
|
|
||||||
Alternatively, the Project Setup can be done with individual tasks:
|
1. **Compile Frontend Assets**:
|
||||||
|
|
||||||
1. **Compile Frontend Assets**: `Maintenance: Compile frontend for production`.
|
- Open the command palette:
|
||||||
2. **Run Database Migrations**: `Maintenance: manage.py migrate`.
|
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||||
3. **Create Superuser**: `Maintenance: manage.py createsuperuser`.
|
- **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
|
### Debugging and Running Services
|
||||||
|
|
||||||
@ -75,8 +95,11 @@ You can start and debug backend services either as debugging sessions via `launc
|
|||||||
|
|
||||||
#### Using Tasks
|
#### Using Tasks
|
||||||
|
|
||||||
1. Open the command palette and select `Tasks: Run Task`.
|
1. Open the command palette:
|
||||||
2. Choose the desired task:
|
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||||
|
- **Mac**: `Cmd+Shift+P`
|
||||||
|
2. Select `Tasks: Run Task`.
|
||||||
|
3. Choose the desired task:
|
||||||
- `Runserver`
|
- `Runserver`
|
||||||
- `Document Consumer`
|
- `Document Consumer`
|
||||||
- `Celery`
|
- `Celery`
|
||||||
|
@ -21,17 +21,19 @@
|
|||||||
# This file is intended only to be used through VSCOde devcontainers. See README.md
|
# This file is intended only to be used through VSCOde devcontainers. See README.md
|
||||||
# in the folder .devcontainer.
|
# in the folder .devcontainer.
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:7
|
image: docker.io/library/redis:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- ./redisdata:/data
|
- ./redisdata:/data
|
||||||
|
|
||||||
# No ports need to be exposed; the VSCode DevContainer plugin manages them.
|
# No ports need to be exposed; the VSCode DevContainer plugin manages them.
|
||||||
paperless-development:
|
paperless-development:
|
||||||
image: paperless-ngx
|
image: paperless-ngx
|
||||||
build:
|
build:
|
||||||
context: ../ # Dockerfile cannot access files from parent directories if context is not set.
|
context: ../ # Dockerfile cannot access files from parent directories if context is not set.
|
||||||
dockerfile: ./.devcontainer/Dockerfile
|
dockerfile: ./.devcontainer/Dockerfile
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
depends_on:
|
depends_on:
|
||||||
@ -58,20 +60,25 @@ services:
|
|||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||||
PAPERLESS_STATICDIR: ./src/documents/static
|
PAPERLESS_STATICDIR: ./src/documents/static
|
||||||
PAPERLESS_DEBUG: true
|
PAPERLESS_DEBUG: true
|
||||||
|
|
||||||
# Overrides default command so things don't shut down after the process ends.
|
# Overrides default command so things don't shut down after the process ends.
|
||||||
command: /bin/sh -c "chown -R paperless:paperless /usr/src/paperless/paperless-ngx/src/documents/static/frontend && chown -R paperless:paperless /usr/src/paperless/paperless-ngx/.ruff_cache && while sleep 1000; do :; done"
|
command: /bin/sh -c "chown -R paperless:paperless /usr/src/paperless/paperless-ngx/src/documents/static/frontend && chown -R paperless:paperless /usr/src/paperless/paperless-ngx/.ruff_cache && while sleep 1000; do :; done"
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.17
|
image: docker.io/gotenberg/gotenberg:8.17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# The Gotenberg Chromium route is used to convert .eml files. We do not
|
# The Gotenberg Chromium route is used to convert .eml files. We do not
|
||||||
# want to allow external content like tracking pixels or even JavaScript.
|
# want to allow external content like tracking pixels or even JavaScript.
|
||||||
command:
|
command:
|
||||||
- "gotenberg"
|
- "gotenberg"
|
||||||
- "--chromium-disable-javascript=true"
|
- "--chromium-disable-javascript=true"
|
||||||
- "--chromium-allow-list=file:///tmp/.*"
|
- "--chromium-allow-list=file:///tmp/.*"
|
||||||
|
|
||||||
tika:
|
tika:
|
||||||
image: docker.io/apache/tika:latest
|
image: docker.io/apache/tika:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -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 -rf .venv && uv venv && uv sync --dev",
|
"command": "rm -R -v .venv/* || uv install --dev",
|
||||||
"group": "none",
|
"group": "none",
|
||||||
"presentation": {
|
"presentation": {
|
||||||
"echo": true,
|
"echo": true,
|
||||||
|
55
.github/DISCUSSION_TEMPLATE/support.yml
vendored
55
.github/DISCUSSION_TEMPLATE/support.yml
vendored
@ -1,55 +0,0 @@
|
|||||||
title: "[Support] "
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: What's your question or issue?
|
|
||||||
description: Provide a clear and concise description of what you're trying to do, and what's going wrong.
|
|
||||||
placeholder: |
|
|
||||||
I'm trying to...
|
|
||||||
|
|
||||||
[Include screenshots if helpful]
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: steps
|
|
||||||
attributes:
|
|
||||||
label: What have you tried?
|
|
||||||
description: Describe any steps you've already taken to troubleshoot or solve the issue.
|
|
||||||
placeholder: |
|
|
||||||
- I checked the logs and saw...
|
|
||||||
- I followed the install guide and tried...
|
|
||||||
- type: input
|
|
||||||
id: version
|
|
||||||
attributes:
|
|
||||||
label: Paperless-ngx version
|
|
||||||
placeholder: e.g. 1.14.0
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: host-os
|
|
||||||
attributes:
|
|
||||||
label: Host OS
|
|
||||||
description: Include architecture if relevant.
|
|
||||||
placeholder: e.g. Ubuntu 22.04 / Raspberry Pi arm64
|
|
||||||
- type: dropdown
|
|
||||||
id: install-method
|
|
||||||
attributes:
|
|
||||||
label: Installation method
|
|
||||||
options:
|
|
||||||
- Docker - official image
|
|
||||||
- Docker - linuxserver.io image
|
|
||||||
- Bare metal
|
|
||||||
- Other (please describe above)
|
|
||||||
- type: textarea
|
|
||||||
id: system-status
|
|
||||||
attributes:
|
|
||||||
label: System status
|
|
||||||
description: If available, copy & paste the system status output from Settings > System Status > Copy
|
|
||||||
render: json
|
|
||||||
- type: textarea
|
|
||||||
id: logs
|
|
||||||
attributes:
|
|
||||||
label: Relevant logs or output
|
|
||||||
description: If you have logs, errors that might help, paste it here.
|
|
||||||
render: bash
|
|
28
.github/dependabot.yml
vendored
28
.github/dependabot.yml
vendored
@ -5,6 +5,7 @@ version: 2
|
|||||||
# Required for uv support for now
|
# Required for uv support for now
|
||||||
enable-beta-ecosystems: true
|
enable-beta-ecosystems: true
|
||||||
updates:
|
updates:
|
||||||
|
|
||||||
# Enable version updates for pnpm
|
# Enable version updates for pnpm
|
||||||
- package-ecosystem: "npm"
|
- package-ecosystem: "npm"
|
||||||
target-branch: "dev"
|
target-branch: "dev"
|
||||||
@ -16,6 +17,9 @@ updates:
|
|||||||
labels:
|
labels:
|
||||||
- "frontend"
|
- "frontend"
|
||||||
- "dependencies"
|
- "dependencies"
|
||||||
|
# Add reviewers
|
||||||
|
reviewers:
|
||||||
|
- "paperless-ngx/frontend"
|
||||||
groups:
|
groups:
|
||||||
frontend-angular-dependencies:
|
frontend-angular-dependencies:
|
||||||
patterns:
|
patterns:
|
||||||
@ -31,6 +35,7 @@ updates:
|
|||||||
patterns:
|
patterns:
|
||||||
- "@typescript-eslint*"
|
- "@typescript-eslint*"
|
||||||
- "eslint"
|
- "eslint"
|
||||||
|
|
||||||
# Enable version updates for Python
|
# Enable version updates for Python
|
||||||
- package-ecosystem: "uv"
|
- package-ecosystem: "uv"
|
||||||
target-branch: "dev"
|
target-branch: "dev"
|
||||||
@ -41,6 +46,9 @@ updates:
|
|||||||
labels:
|
labels:
|
||||||
- "backend"
|
- "backend"
|
||||||
- "dependencies"
|
- "dependencies"
|
||||||
|
# Add reviewers
|
||||||
|
reviewers:
|
||||||
|
- "paperless-ngx/backend"
|
||||||
groups:
|
groups:
|
||||||
development:
|
development:
|
||||||
patterns:
|
patterns:
|
||||||
@ -51,7 +59,6 @@ updates:
|
|||||||
django:
|
django:
|
||||||
patterns:
|
patterns:
|
||||||
- "*django*"
|
- "*django*"
|
||||||
- "drf-*"
|
|
||||||
major-versions:
|
major-versions:
|
||||||
update-types:
|
update-types:
|
||||||
- "major"
|
- "major"
|
||||||
@ -59,13 +66,11 @@ updates:
|
|||||||
update-types:
|
update-types:
|
||||||
- "minor"
|
- "minor"
|
||||||
- "patch"
|
- "patch"
|
||||||
exclude-patterns:
|
|
||||||
- "*django*"
|
|
||||||
- "drf-*"
|
|
||||||
pre-built:
|
pre-built:
|
||||||
patterns:
|
patterns:
|
||||||
- psycopg*
|
- psycopg*
|
||||||
- zxing-cpp
|
- zxing-cpp
|
||||||
|
|
||||||
# Enable updates for GitHub Actions
|
# Enable updates for GitHub Actions
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
target-branch: "dev"
|
target-branch: "dev"
|
||||||
@ -76,32 +81,41 @@ updates:
|
|||||||
labels:
|
labels:
|
||||||
- "ci-cd"
|
- "ci-cd"
|
||||||
- "dependencies"
|
- "dependencies"
|
||||||
|
# Add reviewers
|
||||||
|
reviewers:
|
||||||
|
- "paperless-ngx/ci-cd"
|
||||||
groups:
|
groups:
|
||||||
actions:
|
actions:
|
||||||
update-types:
|
update-types:
|
||||||
- "major"
|
- "major"
|
||||||
- "minor"
|
- "minor"
|
||||||
- "patch"
|
- "patch"
|
||||||
|
|
||||||
# Update Dockerfile in root directory
|
# Update Dockerfile in root directory
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directories:
|
directory: "/"
|
||||||
- "/"
|
|
||||||
- "/.devcontainer/"
|
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
open-pull-requests-limit: 5
|
open-pull-requests-limit: 5
|
||||||
|
reviewers:
|
||||||
|
- "paperless-ngx/ci-cd"
|
||||||
labels:
|
labels:
|
||||||
|
- "ci-cd"
|
||||||
- "dependencies"
|
- "dependencies"
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "docker"
|
prefix: "docker"
|
||||||
include: "scope"
|
include: "scope"
|
||||||
|
|
||||||
# Update Docker Compose files in docker/compose directory
|
# Update Docker Compose files in docker/compose directory
|
||||||
- package-ecosystem: "docker-compose"
|
- package-ecosystem: "docker-compose"
|
||||||
directory: "/docker/compose/"
|
directory: "/docker/compose/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
open-pull-requests-limit: 5
|
open-pull-requests-limit: 5
|
||||||
|
reviewers:
|
||||||
|
- "paperless-ngx/ci-cd"
|
||||||
labels:
|
labels:
|
||||||
|
- "ci-cd"
|
||||||
- "dependencies"
|
- "dependencies"
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "docker-compose"
|
prefix: "docker-compose"
|
||||||
|
26
.github/labeler.yml
vendored
26
.github/labeler.yml
vendored
@ -1,26 +0,0 @@
|
|||||||
backend:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- 'src/**'
|
|
||||||
- 'pyproject.toml'
|
|
||||||
- 'uv.lock'
|
|
||||||
- 'requirements.txt'
|
|
||||||
frontend:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- 'src-ui/**'
|
|
||||||
documentation:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- 'docs/**'
|
|
||||||
ci-cd:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- '.github/**'
|
|
||||||
# pr types
|
|
||||||
bug:
|
|
||||||
- head-branch:
|
|
||||||
- ['^fix']
|
|
||||||
enhancement:
|
|
||||||
- head-branch:
|
|
||||||
- ['^feature']
|
|
14
.github/release-drafter.yml
vendored
14
.github/release-drafter.yml
vendored
@ -1,3 +1,15 @@
|
|||||||
|
autolabeler:
|
||||||
|
- label: "bug"
|
||||||
|
branch:
|
||||||
|
- '/^fix/'
|
||||||
|
title:
|
||||||
|
- "/^fix/i"
|
||||||
|
- "/^Bugfix/i"
|
||||||
|
- label: "enhancement"
|
||||||
|
branch:
|
||||||
|
- '/^feature/'
|
||||||
|
title:
|
||||||
|
- "/^feature/i"
|
||||||
categories:
|
categories:
|
||||||
- title: 'Breaking Changes'
|
- title: 'Breaking Changes'
|
||||||
labels:
|
labels:
|
||||||
@ -5,7 +17,7 @@ categories:
|
|||||||
- title: 'Notable Changes'
|
- title: 'Notable Changes'
|
||||||
labels:
|
labels:
|
||||||
- 'notable'
|
- 'notable'
|
||||||
- title: 'Features / Enhancements'
|
- title: 'Features'
|
||||||
labels:
|
labels:
|
||||||
- 'enhancement'
|
- 'enhancement'
|
||||||
- title: 'Bug Fixes'
|
- title: 'Bug Fixes'
|
||||||
|
296
.github/workflows/ci.yml
vendored
296
.github/workflows/ci.yml
vendored
@ -1,4 +1,5 @@
|
|||||||
name: ci
|
name: ci
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
@ -11,57 +12,72 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'translations**'
|
- 'translations**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEFAULT_UV_VERSION: "0.7.x"
|
DEFAULT_UV_VERSION: "0.6.x"
|
||||||
# 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"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-commit:
|
pre-commit:
|
||||||
# We want to run on external PRs, but not on our own internal PRs as they'll be run
|
# We want to run on external PRs, but not on our own internal PRs as they'll be run
|
||||||
# by the push to the branch. Without this if check, checks are duplicated since
|
# by the push to the branch. Without this if check, checks are duplicated since
|
||||||
# internal PRs match both the push and pull_request events.
|
# internal PRs match both the push and pull_request events.
|
||||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
|
if:
|
||||||
|
github.event_name == 'push' || github.event.pull_request.head.repo.full_name !=
|
||||||
|
github.repository
|
||||||
|
|
||||||
name: Linting Checks
|
name: Linting Checks
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
-
|
||||||
|
name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install python
|
-
|
||||||
|
name: Install 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 }}
|
||||||
- name: Check files
|
-
|
||||||
|
name: Check files
|
||||||
uses: pre-commit/action@v3.0.1
|
uses: pre-commit/action@v3.0.1
|
||||||
|
|
||||||
documentation:
|
documentation:
|
||||||
name: "Build & Deploy Documentation"
|
name: "Build & Deploy Documentation"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- pre-commit
|
- pre-commit
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
-
|
||||||
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
-
|
||||||
|
name: Set up Python
|
||||||
id: setup-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 }}
|
||||||
- name: Install uv
|
-
|
||||||
uses: astral-sh/setup-uv@v6
|
name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v5
|
||||||
with:
|
with:
|
||||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
- name: Install Python dependencies
|
-
|
||||||
|
name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
||||||
- name: Make documentation
|
-
|
||||||
|
name: Make documentation
|
||||||
run: |
|
run: |
|
||||||
uv run \
|
uv run \
|
||||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
--dev \
|
--dev \
|
||||||
--frozen \
|
--frozen \
|
||||||
mkdocs build --config-file ./mkdocs.yml
|
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'
|
||||||
run: |
|
run: |
|
||||||
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
|
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
|
||||||
@ -72,12 +88,14 @@ jobs:
|
|||||||
--dev \
|
--dev \
|
||||||
--frozen \
|
--frozen \
|
||||||
mkdocs gh-deploy --force --no-history
|
mkdocs gh-deploy --force --no-history
|
||||||
- name: Upload artifact
|
-
|
||||||
|
name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: documentation
|
name: documentation
|
||||||
path: site/
|
path: site/
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
tests-backend:
|
tests-backend:
|
||||||
name: "Backend Tests (Python ${{ matrix.python-version }})"
|
name: "Backend Tests (Python ${{ matrix.python-version }})"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -88,40 +106,49 @@ jobs:
|
|||||||
python-version: ['3.10', '3.11', '3.12']
|
python-version: ['3.10', '3.11', '3.12']
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
-
|
||||||
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Start containers
|
-
|
||||||
|
name: Start containers
|
||||||
run: |
|
run: |
|
||||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml pull --quiet
|
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml pull --quiet
|
||||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml up --detach
|
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml up --detach
|
||||||
- name: Set up Python
|
-
|
||||||
|
name: Set up Python
|
||||||
id: setup-python
|
id: setup-python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "${{ matrix.python-version }}"
|
python-version: "${{ matrix.python-version }}"
|
||||||
- name: Install uv
|
-
|
||||||
uses: astral-sh/setup-uv@v6
|
name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v5
|
||||||
with:
|
with:
|
||||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ steps.setup-python.outputs.python-version }}
|
python-version: ${{ steps.setup-python.outputs.python-version }}
|
||||||
- name: Install system dependencies
|
-
|
||||||
|
name: Install system dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -qq
|
sudo apt-get update -qq
|
||||||
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript libzbar0 poppler-utils
|
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript libzbar0 poppler-utils
|
||||||
- name: Configure ImageMagick
|
-
|
||||||
|
name: Configure ImageMagick
|
||||||
run: |
|
run: |
|
||||||
sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
||||||
- name: Install Python dependencies
|
-
|
||||||
|
name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
uv sync \
|
uv sync \
|
||||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
--group testing \
|
--group testing \
|
||||||
--frozen
|
--frozen
|
||||||
- name: List installed Python dependencies
|
-
|
||||||
|
name: List installed Python dependencies
|
||||||
run: |
|
run: |
|
||||||
uv pip list
|
uv pip list
|
||||||
- name: Tests
|
-
|
||||||
|
name: Tests
|
||||||
env:
|
env:
|
||||||
PAPERLESS_CI_TEST: 1
|
PAPERLESS_CI_TEST: 1
|
||||||
# Enable paperless_mail testing against real server
|
# Enable paperless_mail testing against real server
|
||||||
@ -134,24 +161,28 @@ jobs:
|
|||||||
--dev \
|
--dev \
|
||||||
--frozen \
|
--frozen \
|
||||||
pytest
|
pytest
|
||||||
- name: Upload backend test results to Codecov
|
-
|
||||||
|
name: Upload backend test results to Codecov
|
||||||
if: always()
|
if: always()
|
||||||
uses: codecov/test-results-action@v1
|
uses: codecov/test-results-action@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
flags: backend-python-${{ matrix.python-version }}
|
flags: backend-python-${{ matrix.python-version }}
|
||||||
files: junit.xml
|
files: junit.xml
|
||||||
- name: Upload backend coverage to Codecov
|
-
|
||||||
|
name: Upload backend coverage to Codecov
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
flags: backend-python-${{ matrix.python-version }}
|
flags: backend-python-${{ matrix.python-version }}
|
||||||
files: coverage.xml
|
files: coverage.xml
|
||||||
- name: Stop containers
|
-
|
||||||
|
name: Stop containers
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs
|
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs
|
||||||
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down
|
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down
|
||||||
|
|
||||||
install-frontend-dependencies:
|
install-frontend-dependencies:
|
||||||
name: "Install Frontend Dependencies"
|
name: "Install Frontend Dependencies"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -163,7 +194,8 @@ jobs:
|
|||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
- name: Use Node.js 20
|
-
|
||||||
|
name: Use Node.js 20
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
@ -177,10 +209,17 @@ jobs:
|
|||||||
~/.pnpm-store
|
~/.pnpm-store
|
||||||
~/.cache
|
~/.cache
|
||||||
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
||||||
- name: Install dependencies
|
-
|
||||||
|
name: Install dependencies
|
||||||
|
if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
|
||||||
run: cd src-ui && pnpm install
|
run: cd src-ui && pnpm install
|
||||||
|
-
|
||||||
|
name: Install Playwright
|
||||||
|
if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
|
||||||
|
run: cd src-ui && pnpm playwright install --with-deps
|
||||||
|
|
||||||
tests-frontend:
|
tests-frontend:
|
||||||
name: "Frontend Unit Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
|
name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- install-frontend-dependencies
|
- install-frontend-dependencies
|
||||||
@ -196,7 +235,8 @@ jobs:
|
|||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
- name: Use Node.js 20
|
-
|
||||||
|
name: Use Node.js 20
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
@ -212,90 +252,52 @@ jobs:
|
|||||||
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
||||||
- name: Re-link Angular cli
|
- name: Re-link Angular cli
|
||||||
run: cd src-ui && pnpm link @angular/cli
|
run: cd src-ui && pnpm link @angular/cli
|
||||||
- name: Linting checks
|
-
|
||||||
|
name: Linting checks
|
||||||
run: cd src-ui && pnpm run lint
|
run: cd src-ui && pnpm run lint
|
||||||
- name: Run Jest unit tests
|
-
|
||||||
|
name: Run Jest unit tests
|
||||||
run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||||
- name: Upload frontend test results to Codecov
|
-
|
||||||
|
name: Run Playwright e2e tests
|
||||||
|
run: cd src-ui && pnpm exec playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
||||||
|
-
|
||||||
|
name: Upload frontend test results to Codecov
|
||||||
uses: codecov/test-results-action@v1
|
uses: codecov/test-results-action@v1
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
flags: frontend-node-${{ matrix.node-version }}
|
flags: frontend-node-${{ matrix.node-version }}
|
||||||
directory: src-ui/
|
directory: src-ui/
|
||||||
- name: Upload frontend coverage to Codecov
|
-
|
||||||
|
name: Upload frontend coverage to Codecov
|
||||||
uses: codecov/codecov-action@v5
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
flags: frontend-node-${{ matrix.node-version }}
|
flags: frontend-node-${{ matrix.node-version }}
|
||||||
directory: src-ui/coverage/
|
directory: src-ui/coverage/
|
||||||
tests-frontend-e2e:
|
|
||||||
name: "Frontend E2E Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs:
|
|
||||||
- install-frontend-dependencies
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
node-version: [20.x]
|
|
||||||
shard-index: [1, 2]
|
|
||||||
shard-count: [2]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
version: 10
|
|
||||||
- name: Use Node.js 20
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 20.x
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: 'src-ui/pnpm-lock.yaml'
|
|
||||||
- name: Cache frontend dependencies
|
|
||||||
id: cache-frontend-deps
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.pnpm-store
|
|
||||||
~/.cache
|
|
||||||
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
|
||||||
- name: Re-link Angular cli
|
|
||||||
run: cd src-ui && pnpm link @angular/cli
|
|
||||||
- name: Cache Playwright browsers
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.cache/ms-playwright
|
|
||||||
key: ${{ runner.os }}-playwright-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-playwright-
|
|
||||||
- name: Install Playwright system dependencies
|
|
||||||
run: npx playwright install-deps
|
|
||||||
- name: Install dependencies
|
|
||||||
run: cd src-ui && pnpm install --no-frozen-lockfile
|
|
||||||
- name: Install Playwright
|
|
||||||
run: cd src-ui && pnpm exec playwright install
|
|
||||||
- name: Run Playwright e2e tests
|
|
||||||
run: cd src-ui && pnpm exec playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
|
|
||||||
frontend-bundle-analysis:
|
frontend-bundle-analysis:
|
||||||
name: "Frontend Bundle Analysis"
|
name: "Frontend Bundle Analysis"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- tests-frontend
|
- tests-frontend
|
||||||
- tests-frontend-e2e
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install pnpm
|
-
|
||||||
|
name: Install pnpm
|
||||||
uses: pnpm/action-setup@v4
|
uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
version: 10
|
version: 10
|
||||||
- name: Use Node.js 20
|
-
|
||||||
|
name: Use Node.js 20
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20.x
|
node-version: 20.x
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
cache-dependency-path: 'src-ui/pnpm-lock.yaml'
|
cache-dependency-path: 'src-ui/pnpm-lock.yaml'
|
||||||
- name: Cache frontend dependencies
|
-
|
||||||
|
name: Cache frontend dependencies
|
||||||
id: cache-frontend-deps
|
id: cache-frontend-deps
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
@ -303,12 +305,15 @@ jobs:
|
|||||||
~/.pnpm-store
|
~/.pnpm-store
|
||||||
~/.cache
|
~/.cache
|
||||||
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
|
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
|
||||||
- name: Re-link Angular cli
|
-
|
||||||
|
name: Re-link Angular cli
|
||||||
run: cd src-ui && pnpm link @angular/cli
|
run: cd src-ui && pnpm link @angular/cli
|
||||||
- name: Build frontend and upload analysis
|
-
|
||||||
|
name: Build frontend and upload analysis
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
run: cd src-ui && pnpm run build --configuration=production
|
run: cd src-ui && pnpm run build --configuration=production
|
||||||
|
|
||||||
build-docker-image:
|
build-docker-image:
|
||||||
name: Build Docker image for ${{ github.ref_name }}
|
name: Build Docker image for ${{ github.ref_name }}
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -319,9 +324,9 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- tests-backend
|
- tests-backend
|
||||||
- tests-frontend
|
- tests-frontend
|
||||||
- tests-frontend-e2e
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check pushing to Docker Hub
|
-
|
||||||
|
name: Check pushing to Docker Hub
|
||||||
id: push-other-places
|
id: push-other-places
|
||||||
# Only push to Dockerhub from the main repo AND the ref is either:
|
# Only push to Dockerhub from the main repo AND the ref is either:
|
||||||
# main
|
# main
|
||||||
@ -337,13 +342,15 @@ jobs:
|
|||||||
echo "Not pushing to DockerHub"
|
echo "Not pushing to DockerHub"
|
||||||
echo "enable=false" >> $GITHUB_OUTPUT
|
echo "enable=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
- name: Set ghcr repository name
|
-
|
||||||
|
name: Set ghcr repository name
|
||||||
id: set-ghcr-repository
|
id: set-ghcr-repository
|
||||||
run: |
|
run: |
|
||||||
ghcr_name=$(echo "${{ github.repository }}" | awk '{ print tolower($0) }')
|
ghcr_name=$(echo "${{ github.repository }}" | awk '{ print tolower($0) }')
|
||||||
echo "Name is ${ghcr_name}"
|
echo "Name is ${ghcr_name}"
|
||||||
echo "ghcr-repository=${ghcr_name}" >> $GITHUB_OUTPUT
|
echo "ghcr-repository=${ghcr_name}" >> $GITHUB_OUTPUT
|
||||||
- name: Gather Docker metadata
|
-
|
||||||
|
name: Gather Docker metadata
|
||||||
id: docker-meta
|
id: docker-meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
@ -358,31 +365,37 @@ jobs:
|
|||||||
# For a tag x.y.z or vX.Y.Z, output an x.y.z and x.y image tag
|
# For a tag x.y.z or vX.Y.Z, output an x.y.z and x.y image tag
|
||||||
type=semver,pattern={{version}}
|
type=semver,pattern={{version}}
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
- name: Checkout
|
-
|
||||||
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
# If https://github.com/docker/buildx/issues/1044 is resolved,
|
# If https://github.com/docker/buildx/issues/1044 is resolved,
|
||||||
# the append input with a native arm64 arch could be used to
|
# the append input with a native arm64 arch could be used to
|
||||||
# significantly speed up building
|
# significantly speed up building
|
||||||
- name: Set up Docker Buildx
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Set up QEMU
|
-
|
||||||
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
with:
|
with:
|
||||||
platforms: arm64
|
platforms: arm64
|
||||||
- name: Login to GitHub Container Registry
|
-
|
||||||
|
name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Login to Docker Hub
|
-
|
||||||
|
name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
# Don't attempt to login if not pushing to Docker Hub
|
# Don't attempt to login if not pushing to Docker Hub
|
||||||
if: steps.push-other-places.outputs.enable == 'true'
|
if: steps.push-other-places.outputs.enable == 'true'
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- name: Login to Quay.io
|
-
|
||||||
|
name: Login to Quay.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
# Don't attempt to login if not pushing to Quay.io
|
# Don't attempt to login if not pushing to Quay.io
|
||||||
if: steps.push-other-places.outputs.enable == 'true'
|
if: steps.push-other-places.outputs.enable == 'true'
|
||||||
@ -390,7 +403,8 @@ jobs:
|
|||||||
registry: quay.io
|
registry: quay.io
|
||||||
username: ${{ secrets.QUAY_USERNAME }}
|
username: ${{ secrets.QUAY_USERNAME }}
|
||||||
password: ${{ secrets.QUAY_ROBOT_TOKEN }}
|
password: ${{ secrets.QUAY_ROBOT_TOKEN }}
|
||||||
- name: Build and push
|
-
|
||||||
|
name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
@ -408,19 +422,23 @@ jobs:
|
|||||||
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:dev
|
type=registry,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:dev
|
||||||
cache-to: |
|
cache-to: |
|
||||||
type=registry,mode=max,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
type=registry,mode=max,ref=ghcr.io/${{ steps.set-ghcr-repository.outputs.ghcr-repository }}/builder/cache/app:${{ github.ref_name }}
|
||||||
- name: Inspect image
|
-
|
||||||
|
name: Inspect image
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools inspect ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
docker buildx imagetools inspect ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
||||||
- name: Export frontend artifact from docker
|
-
|
||||||
|
name: Export frontend artifact from docker
|
||||||
run: |
|
run: |
|
||||||
docker create --name frontend-extract ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
docker create --name frontend-extract ${{ fromJSON(steps.docker-meta.outputs.json).tags[0] }}
|
||||||
docker cp frontend-extract:/usr/src/paperless/src/documents/static/frontend src/documents/static/frontend/
|
docker cp frontend-extract:/usr/src/paperless/src/documents/static/frontend src/documents/static/frontend/
|
||||||
- name: Upload frontend artifact
|
-
|
||||||
|
name: Upload frontend artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: frontend-compiled
|
name: frontend-compiled
|
||||||
path: src/documents/static/frontend/
|
path: src/documents/static/frontend/
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
build-release:
|
build-release:
|
||||||
name: "Build Release"
|
name: "Build Release"
|
||||||
needs:
|
needs:
|
||||||
@ -428,52 +446,63 @@ jobs:
|
|||||||
- documentation
|
- documentation
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
-
|
||||||
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
-
|
||||||
|
name: Set up Python
|
||||||
id: setup-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 }}
|
||||||
- name: Install uv
|
-
|
||||||
uses: astral-sh/setup-uv@v6
|
name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v5
|
||||||
with:
|
with:
|
||||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ steps.setup-python.outputs.python-version }}
|
python-version: ${{ steps.setup-python.outputs.python-version }}
|
||||||
- name: Install Python dependencies
|
-
|
||||||
|
name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen
|
||||||
- name: Install system dependencies
|
-
|
||||||
|
name: Install system dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -qq
|
sudo apt-get update -qq
|
||||||
sudo apt-get install -qq --no-install-recommends gettext liblept5
|
sudo apt-get install -qq --no-install-recommends gettext liblept5
|
||||||
- name: Download frontend artifact
|
-
|
||||||
|
name: Download frontend artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: frontend-compiled
|
name: frontend-compiled
|
||||||
path: src/documents/static/frontend/
|
path: src/documents/static/frontend/
|
||||||
- name: Download documentation artifact
|
-
|
||||||
|
name: Download documentation artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: documentation
|
name: documentation
|
||||||
path: docs/_build/html/
|
path: docs/_build/html/
|
||||||
- name: Generate requirements file
|
-
|
||||||
|
name: Generate requirements file
|
||||||
run: |
|
run: |
|
||||||
uv export --quiet --no-dev --all-extras --format requirements-txt --output-file 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/
|
||||||
uv run \
|
uv run \
|
||||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
manage.py compilemessages
|
manage.py compilemessages
|
||||||
- name: Collect static files
|
-
|
||||||
|
name: Collect static files
|
||||||
run: |
|
run: |
|
||||||
cd src/
|
cd src/
|
||||||
uv run \
|
uv run \
|
||||||
--python ${{ steps.setup-python.outputs.python-version }} \
|
--python ${{ steps.setup-python.outputs.python-version }} \
|
||||||
manage.py collectstatic --no-input
|
manage.py collectstatic --no-input
|
||||||
- name: Move files
|
-
|
||||||
|
name: Move files
|
||||||
run: |
|
run: |
|
||||||
echo "Making dist folders"
|
echo "Making dist folders"
|
||||||
for directory in dist \
|
for directory in dist \
|
||||||
@ -510,18 +539,21 @@ jobs:
|
|||||||
cp --recursive docs/_build/html/ dist/paperless-ngx/docs
|
cp --recursive docs/_build/html/ dist/paperless-ngx/docs
|
||||||
|
|
||||||
mv --verbose static dist/paperless-ngx
|
mv --verbose static dist/paperless-ngx
|
||||||
- name: Make release package
|
-
|
||||||
|
name: Make release package
|
||||||
run: |
|
run: |
|
||||||
echo "Creating release archive"
|
echo "Creating release archive"
|
||||||
cd dist
|
cd dist
|
||||||
sudo chown -R 1000:1000 paperless-ngx/
|
sudo chown -R 1000:1000 paperless-ngx/
|
||||||
tar -cJf paperless-ngx.tar.xz paperless-ngx/
|
tar -cJf paperless-ngx.tar.xz paperless-ngx/
|
||||||
- name: Upload release artifact
|
-
|
||||||
|
name: Upload release artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release
|
name: release
|
||||||
path: dist/paperless-ngx.tar.xz
|
path: dist/paperless-ngx.tar.xz
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
publish-release:
|
publish-release:
|
||||||
name: "Publish Release"
|
name: "Publish Release"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -533,12 +565,14 @@ jobs:
|
|||||||
- build-release
|
- build-release
|
||||||
if: github.ref_type == 'tag' && (startsWith(github.ref_name, 'v') || contains(github.ref_name, '-beta.rc'))
|
if: github.ref_type == 'tag' && (startsWith(github.ref_name, 'v') || contains(github.ref_name, '-beta.rc'))
|
||||||
steps:
|
steps:
|
||||||
- name: Download release artifact
|
-
|
||||||
|
name: Download release artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release
|
name: release
|
||||||
path: ./
|
path: ./
|
||||||
- name: Get version
|
-
|
||||||
|
name: Get version
|
||||||
id: get_version
|
id: get_version
|
||||||
run: |
|
run: |
|
||||||
echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
|
||||||
@ -547,7 +581,8 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo "prerelease=false" >> $GITHUB_OUTPUT
|
echo "prerelease=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
- name: Create Release and Changelog
|
-
|
||||||
|
name: Create Release and Changelog
|
||||||
id: create-release
|
id: create-release
|
||||||
uses: release-drafter/release-drafter@v6
|
uses: release-drafter/release-drafter@v6
|
||||||
with:
|
with:
|
||||||
@ -558,7 +593,8 @@ jobs:
|
|||||||
publish: true # ensures release is not marked as draft
|
publish: true # ensures release is not marked as draft
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Upload release archive
|
-
|
||||||
|
name: Upload release archive
|
||||||
id: upload-release-asset
|
id: upload-release-asset
|
||||||
uses: shogo82148/actions-upload-release-asset@v1
|
uses: shogo82148/actions-upload-release-asset@v1
|
||||||
with:
|
with:
|
||||||
@ -567,6 +603,7 @@ jobs:
|
|||||||
asset_path: ./paperless-ngx.tar.xz
|
asset_path: ./paperless-ngx.tar.xz
|
||||||
asset_name: paperless-ngx-${{ steps.get_version.outputs.version }}.tar.xz
|
asset_name: paperless-ngx-${{ steps.get_version.outputs.version }}.tar.xz
|
||||||
asset_content_type: application/x-xz
|
asset_content_type: application/x-xz
|
||||||
|
|
||||||
append-changelog:
|
append-changelog:
|
||||||
name: "Append Changelog"
|
name: "Append Changelog"
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
@ -574,22 +611,26 @@ jobs:
|
|||||||
- publish-release
|
- publish-release
|
||||||
if: needs.publish-release.outputs.prerelease == 'false'
|
if: needs.publish-release.outputs.prerelease == 'false'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
-
|
||||||
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
- name: Set up Python
|
-
|
||||||
|
name: Set up Python
|
||||||
id: setup-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 }}
|
||||||
- name: Install uv
|
-
|
||||||
uses: astral-sh/setup-uv@v6
|
name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v5
|
||||||
with:
|
with:
|
||||||
version: ${{ env.DEFAULT_UV_VERSION }}
|
version: ${{ env.DEFAULT_UV_VERSION }}
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
|
||||||
- name: Append Changelog to docs
|
-
|
||||||
|
name: Append Changelog to docs
|
||||||
id: append-Changelog
|
id: append-Changelog
|
||||||
working-directory: docs
|
working-directory: docs
|
||||||
run: |
|
run: |
|
||||||
@ -611,7 +652,8 @@ jobs:
|
|||||||
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"
|
||||||
git push origin ${{ needs.publish-release.outputs.version }}-changelog
|
git push origin ${{ needs.publish-release.outputs.version }}-changelog
|
||||||
- name: Create Pull Request
|
-
|
||||||
|
name: Create Pull Request
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
|
10
.github/workflows/cleanup-tags.yml
vendored
10
.github/workflows/cleanup-tags.yml
vendored
@ -6,14 +6,17 @@
|
|||||||
# This workflow will not trigger runs on forked repos.
|
# This workflow will not trigger runs on forked repos.
|
||||||
|
|
||||||
name: Cleanup Image Tags
|
name: Cleanup Image Tags
|
||||||
|
|
||||||
on:
|
on:
|
||||||
delete:
|
delete:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- ".github/workflows/cleanup-tags.yml"
|
- ".github/workflows/cleanup-tags.yml"
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: registry-tags-cleanup
|
group: registry-tags-cleanup
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cleanup-images:
|
cleanup-images:
|
||||||
name: Cleanup Image Tags for ${{ matrix.primary-name }}
|
name: Cleanup Image Tags for ${{ matrix.primary-name }}
|
||||||
@ -27,7 +30,8 @@ jobs:
|
|||||||
# Requires a personal access token with the OAuth scope delete:packages
|
# Requires a personal access token with the OAuth scope delete:packages
|
||||||
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- name: Clean temporary images
|
-
|
||||||
|
name: Clean temporary images
|
||||||
if: "${{ env.TOKEN != '' }}"
|
if: "${{ env.TOKEN != '' }}"
|
||||||
uses: stumpylog/image-cleaner-action/ephemeral@v0.10.0
|
uses: stumpylog/image-cleaner-action/ephemeral@v0.10.0
|
||||||
with:
|
with:
|
||||||
@ -39,6 +43,7 @@ jobs:
|
|||||||
repo_name: "paperless-ngx"
|
repo_name: "paperless-ngx"
|
||||||
match_regex: "(feature|fix)"
|
match_regex: "(feature|fix)"
|
||||||
do_delete: "true"
|
do_delete: "true"
|
||||||
|
|
||||||
cleanup-untagged-images:
|
cleanup-untagged-images:
|
||||||
name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
|
name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
@ -53,7 +58,8 @@ jobs:
|
|||||||
# Requires a personal access token with the OAuth scope delete:packages
|
# Requires a personal access token with the OAuth scope delete:packages
|
||||||
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
TOKEN: ${{ secrets.GHA_CONTAINER_DELETE_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
- name: Clean untagged images
|
-
|
||||||
|
name: Clean untagged images
|
||||||
if: "${{ env.TOKEN != '' }}"
|
if: "${{ env.TOKEN != '' }}"
|
||||||
uses: stumpylog/image-cleaner-action/untagged@v0.10.0
|
uses: stumpylog/image-cleaner-action/untagged@v0.10.0
|
||||||
with:
|
with:
|
||||||
|
38
.github/workflows/codeql-analysis.yml
vendored
38
.github/workflows/codeql-analysis.yml
vendored
@ -10,14 +10,16 @@
|
|||||||
# supported CodeQL languages.
|
# supported CodeQL languages.
|
||||||
#
|
#
|
||||||
name: "CodeQL"
|
name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, dev]
|
branches: [ main, dev ]
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [dev]
|
branches: [ dev ]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '28 13 * * 5'
|
- cron: '28 13 * * 5'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
@ -26,23 +28,27 @@ jobs:
|
|||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
language: ['javascript', 'python']
|
language: [ 'javascript', 'python' ]
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
# Initializes the CodeQL tools for scanning.
|
||||||
uses: github/codeql-action/init@v3
|
- name: Initialize CodeQL
|
||||||
with:
|
uses: github/codeql-action/init@v3
|
||||||
languages: ${{ matrix.language }}
|
with:
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
languages: ${{ matrix.language }}
|
||||||
# By default, queries listed here will override any specified in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
# By default, queries listed here will override any specified in a config file.
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
- name: Perform CodeQL Analysis
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
uses: github/codeql-action/analyze@v3
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v3
|
||||||
|
41
.github/workflows/crowdin.yml
vendored
41
.github/workflows/crowdin.yml
vendored
@ -1,30 +1,35 @@
|
|||||||
name: Crowdin Action
|
name: Crowdin Action
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '2 */12 * * *'
|
- cron: '2 */12 * * *'
|
||||||
push:
|
push:
|
||||||
paths: ['src/locale/**', 'src-ui/messages.xlf', 'src-ui/src/locale/**']
|
paths: [
|
||||||
branches: [dev]
|
'src/locale/**',
|
||||||
|
'src-ui/messages.xlf',
|
||||||
|
'src-ui/src/locale/**'
|
||||||
|
]
|
||||||
|
branches: [ dev ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
synchronize-with-crowdin:
|
synchronize-with-crowdin:
|
||||||
name: Crowdin Sync
|
name: Crowdin Sync
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
- name: crowdin action
|
||||||
token: ${{ secrets.PNGX_BOT_PAT }}
|
uses: crowdin/github-action@v2
|
||||||
- name: crowdin action
|
with:
|
||||||
uses: crowdin/github-action@v2
|
upload_translations: false
|
||||||
with:
|
download_translations: true
|
||||||
upload_translations: false
|
crowdin_branch_name: 'dev'
|
||||||
download_translations: true
|
localization_branch_name: l10n_dev
|
||||||
crowdin_branch_name: 'dev'
|
pull_request_labels: 'skip-changelog, translation'
|
||||||
localization_branch_name: l10n_dev
|
env:
|
||||||
pull_request_labels: 'skip-changelog, translation'
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
env:
|
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
|
||||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
|
||||||
|
112
.github/workflows/pr-bot.yml
vendored
112
.github/workflows/pr-bot.yml
vendored
@ -1,112 +0,0 @@
|
|||||||
name: PR Bot
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened]
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
jobs:
|
|
||||||
pr-bot:
|
|
||||||
name: Automated PR Bot
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Label PR by file path or branch name
|
|
||||||
# see .github/labeler.yml for the labeler config
|
|
||||||
uses: actions/labeler@v5
|
|
||||||
with:
|
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Label by size
|
|
||||||
uses: Gascon1/pr-size-labeler@v1.3.0
|
|
||||||
with:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
xs_label: 'small-change'
|
|
||||||
xs_diff: '9'
|
|
||||||
s_label: 'non-trivial'
|
|
||||||
s_diff: '99999'
|
|
||||||
fail_if_xl: 'false'
|
|
||||||
excluded_files: /\.lock$/ /\.txt$/ ^src-ui/pnpm-lock\.yaml$ ^src-ui/messages\.xlf$ ^src/locale/en_US/LC_MESSAGES/django\.po$
|
|
||||||
- name: Label by PR title
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const pr = context.payload.pull_request;
|
|
||||||
const title = pr.title.toLowerCase();
|
|
||||||
const labels = [];
|
|
||||||
|
|
||||||
if (/^(fix|bugfix)/i.test(title)) {
|
|
||||||
labels.push('bug');
|
|
||||||
} else if (/^feature/i.test(title)) {
|
|
||||||
labels.push('enhancement');
|
|
||||||
} else if (!/^(dependabot)/i.test(title)) {
|
|
||||||
labels.push('enhancement'); // Default fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
if (labels.length) {
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: pr.number,
|
|
||||||
labels,
|
|
||||||
});
|
|
||||||
core.info(`Added labels based on title: ${labels.join(', ')}`);
|
|
||||||
}
|
|
||||||
- name: Label bot-generated PRs
|
|
||||||
if: ${{ contains(github.actor, 'dependabot') || contains(github.actor, 'crowdin-bot') }}
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const pr = context.payload.pull_request;
|
|
||||||
const user = pr.user.login.toLowerCase();
|
|
||||||
const labels = [];
|
|
||||||
|
|
||||||
if (user.includes('dependabot')) {
|
|
||||||
labels.push('dependencies');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.includes('crowdin-bot')) {
|
|
||||||
labels.push('translation', 'skip-changelog');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (labels.length) {
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: pr.number,
|
|
||||||
labels,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
- name: Welcome comment
|
|
||||||
if: ${{ !contains(github.actor, 'bot') }}
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const pr = context.payload.pull_request;
|
|
||||||
const user = pr.user.login;
|
|
||||||
|
|
||||||
const { data: members } = await github.rest.orgs.listMembers({
|
|
||||||
org: 'paperless-ngx',
|
|
||||||
});
|
|
||||||
|
|
||||||
const memberLogins = members.map(m => m.login.toLowerCase());
|
|
||||||
if (memberLogins.includes(user.toLowerCase())) {
|
|
||||||
core.info('Skipping comment: user is org member');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const body =
|
|
||||||
"Hello @" + user + ",\n\n" +
|
|
||||||
"Thank you very much for submitting this PR to us!\n\n" +
|
|
||||||
"This is what will happen next:\n\n" +
|
|
||||||
"1. CI tests will run against your PR to ensure quality and consistency.\n" +
|
|
||||||
"2. Next, human contributors from paperless-ngx review your changes.\n" +
|
|
||||||
"3. Please address any issues that come up during the review as soon as you are able to.\n" +
|
|
||||||
"4. If accepted, your pull request will be merged into the `dev` branch and changes there will be tested further.\n" +
|
|
||||||
"5. Eventually, changes from you and other contributors will be merged into `main` and a new release will be made.\n\n" +
|
|
||||||
"You'll be hearing from us soon, and thank you again for contributing to our project.";
|
|
||||||
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
issue_number: pr.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
body,
|
|
||||||
});
|
|
3
.github/workflows/project-actions.yml
vendored
3
.github/workflows/project-actions.yml
vendored
@ -1,4 +1,5 @@
|
|||||||
name: Project Automations
|
name: Project Automations
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_target: #_target allows access to secrets
|
pull_request_target: #_target allows access to secrets
|
||||||
types:
|
types:
|
||||||
@ -7,8 +8,10 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- dev
|
- dev
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pr_opened_or_reopened:
|
pr_opened_or_reopened:
|
||||||
name: pr_opened_or_reopened
|
name: pr_opened_or_reopened
|
||||||
|
27
.github/workflows/repo-maintenance.yml
vendored
27
.github/workflows/repo-maintenance.yml
vendored
@ -1,14 +1,18 @@
|
|||||||
name: 'Repository Maintenance'
|
name: 'Repository Maintenance'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 3 * * *'
|
- cron: '0 3 * * *'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
discussions: write
|
discussions: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: lock
|
group: lock
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
name: 'Stale'
|
name: 'Stale'
|
||||||
@ -23,8 +27,9 @@ jobs:
|
|||||||
stale-issue-label: stale
|
stale-issue-label: stale
|
||||||
stale-pr-label: stale
|
stale-pr-label: stale
|
||||||
stale-issue-message: >
|
stale-issue-message: >
|
||||||
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||||
lock-threads:
|
lock-threads:
|
||||||
name: 'Lock Old Threads'
|
name: 'Lock Old Threads'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
@ -37,14 +42,20 @@ jobs:
|
|||||||
discussion-inactive-days: '30'
|
discussion-inactive-days: '30'
|
||||||
log-output: true
|
log-output: true
|
||||||
issue-comment: >
|
issue-comment: >
|
||||||
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new discussion or issue for related concerns. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
This issue has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion or issue for related concerns.
|
||||||
|
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||||
pr-comment: >
|
pr-comment: >
|
||||||
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new discussion or issue for related concerns. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
This pull request has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion or issue for related concerns.
|
||||||
|
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||||
discussion-comment: >
|
discussion-comment: >
|
||||||
This discussion has been automatically locked since there has not been any recent activity after it was closed. Please open a new discussion for related concerns. See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
This discussion has been automatically locked since there
|
||||||
|
has not been any recent activity after it was closed.
|
||||||
|
Please open a new discussion for related concerns.
|
||||||
|
See our [contributing guidelines](https://github.com/paperless-ngx/paperless-ngx/blob/dev/CONTRIBUTING.md#automatic-repository-maintenance) for more details.
|
||||||
close-answered-discussions:
|
close-answered-discussions:
|
||||||
name: 'Close Answered Discussions'
|
name: 'Close Answered Discussions'
|
||||||
if: github.repository_owner == 'paperless-ngx'
|
if: github.repository_owner == 'paperless-ngx'
|
||||||
|
69
.github/workflows/translate-strings.yml
vendored
69
.github/workflows/translate-strings.yml
vendored
@ -1,69 +0,0 @@
|
|||||||
name: Generate Translation Strings
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- dev
|
|
||||||
jobs:
|
|
||||||
generate-translate-strings:
|
|
||||||
name: Generate Translation Strings
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.PNGX_BOT_PAT }}
|
|
||||||
ref: ${{ github.head_ref }}
|
|
||||||
- name: Set up Python
|
|
||||||
id: setup-python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update -qq
|
|
||||||
sudo apt-get install -qq --no-install-recommends gettext
|
|
||||||
- name: Install uv
|
|
||||||
uses: astral-sh/setup-uv@v6
|
|
||||||
with:
|
|
||||||
enable-cache: true
|
|
||||||
- name: Install backend python dependencies
|
|
||||||
run: |
|
|
||||||
uv sync \
|
|
||||||
--group dev \
|
|
||||||
--frozen
|
|
||||||
- name: Generate backend translation strings
|
|
||||||
run: cd src/ && uv run manage.py makemessages -l en_US -i "samples*"
|
|
||||||
- name: Install pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
version: 10
|
|
||||||
- name: Use Node.js 20
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 20.x
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: 'src-ui/pnpm-lock.yaml'
|
|
||||||
- name: Cache frontend dependencies
|
|
||||||
id: cache-frontend-deps
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.pnpm-store
|
|
||||||
~/.cache
|
|
||||||
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
|
|
||||||
- name: Install frontend dependencies
|
|
||||||
if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
|
|
||||||
run: cd src-ui && pnpm install
|
|
||||||
- name: Re-link Angular cli
|
|
||||||
run: cd src-ui && pnpm link @angular/cli
|
|
||||||
- name: Generate frontend translation strings
|
|
||||||
run: |
|
|
||||||
cd src-ui
|
|
||||||
pnpm run ng extract-i18n
|
|
||||||
- name: Commit changes
|
|
||||||
uses: stefanzweifel/git-auto-commit-action@v6
|
|
||||||
with:
|
|
||||||
file_pattern: 'src-ui/messages.xlf src/locale/en_US/LC_MESSAGES/django.po'
|
|
||||||
commit_message: "Auto translate strings"
|
|
||||||
commit_user_name: "GitHub Actions"
|
|
||||||
commit_author: "GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>"
|
|
@ -76,8 +76,3 @@ repos:
|
|||||||
rev: "v0.10.0.1"
|
rev: "v0.10.0.1"
|
||||||
hooks:
|
hooks:
|
||||||
- id: shellcheck
|
- id: shellcheck
|
||||||
- repo: https://github.com/google/yamlfmt
|
|
||||||
rev: v0.14.0
|
|
||||||
hooks:
|
|
||||||
- id: yamlfmt
|
|
||||||
exclude: "^src-ui/pnpm-lock.yaml"
|
|
||||||
|
@ -21,7 +21,7 @@ ARG PNGX_TAG_VERSION=
|
|||||||
RUN set -eux && \
|
RUN set -eux && \
|
||||||
case "${PNGX_TAG_VERSION}" in \
|
case "${PNGX_TAG_VERSION}" in \
|
||||||
dev|beta|fix*|feature*) \
|
dev|beta|fix*|feature*) \
|
||||||
sed -i -E "s/tag: '([a-z\.]+)'/tag: '${PNGX_TAG_VERSION}'/g" /src/src-ui/src/environments/environment.prod.ts \
|
sed -i -E "s/version: '([0-9\.]+)'/version: '\1 #${PNGX_TAG_VERSION}'/g" /src/src-ui/src/environments/environment.prod.ts \
|
||||||
;; \
|
;; \
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ RUN set -eux \
|
|||||||
# 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 ghcr.io/astral-sh/uv:0.7.9-python3.12-bookworm-slim AS s6-overlay-base
|
FROM ghcr.io/astral-sh/uv:0.6.13-python3.12-bookworm-slim AS s6-overlay-base
|
||||||
|
|
||||||
WORKDIR /usr/src/s6
|
WORKDIR /usr/src/s6
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ ENV \
|
|||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG TARGETVARIANT
|
ARG TARGETVARIANT
|
||||||
# Lock this version
|
# Lock this version
|
||||||
ARG S6_OVERLAY_VERSION=3.2.1.0
|
ARG S6_OVERLAY_VERSION=3.2.0.2
|
||||||
|
|
||||||
ARG S6_BUILD_TIME_PKGS="curl \
|
ARG S6_BUILD_TIME_PKGS="curl \
|
||||||
xz-utils"
|
xz-utils"
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.20
|
image: docker.io/gotenberg/gotenberg:8.19
|
||||||
hostname: gotenberg
|
hostname: gotenberg
|
||||||
container_name: gotenberg
|
container_name: gotenberg
|
||||||
network_mode: host
|
network_mode: host
|
||||||
|
@ -32,10 +32,11 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/mariadb:11
|
image: docker.io/library/mariadb:11
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -47,6 +48,7 @@ services:
|
|||||||
MARIADB_USER: paperless
|
MARIADB_USER: paperless
|
||||||
MARIADB_PASSWORD: paperless
|
MARIADB_PASSWORD: paperless
|
||||||
MARIADB_ROOT_PASSWORD: paperless
|
MARIADB_ROOT_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -73,8 +75,9 @@ services:
|
|||||||
PAPERLESS_TIKA_ENABLED: 1
|
PAPERLESS_TIKA_ENABLED: 1
|
||||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.20
|
image: docker.io/gotenberg/gotenberg:8.19
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
# The gotenberg chromium route is used to convert .eml files. We do not
|
||||||
# want to allow external content like tracking pixels or even javascript.
|
# want to allow external content like tracking pixels or even javascript.
|
||||||
@ -82,9 +85,11 @@ services:
|
|||||||
- "gotenberg"
|
- "gotenberg"
|
||||||
- "--chromium-disable-javascript=true"
|
- "--chromium-disable-javascript=true"
|
||||||
- "--chromium-allow-list=file:///tmp/.*"
|
- "--chromium-allow-list=file:///tmp/.*"
|
||||||
|
|
||||||
tika:
|
tika:
|
||||||
image: docker.io/apache/tika:latest
|
image: docker.io/apache/tika:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -27,10 +27,11 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/mariadb:11
|
image: docker.io/library/mariadb:11
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -42,6 +43,7 @@ services:
|
|||||||
MARIADB_USER: paperless
|
MARIADB_USER: paperless
|
||||||
MARIADB_PASSWORD: paperless
|
MARIADB_PASSWORD: paperless
|
||||||
MARIADB_ROOT_PASSWORD: paperless
|
MARIADB_ROOT_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -63,6 +65,7 @@ services:
|
|||||||
PAPERLESS_DBUSER: paperless # only needed if non-default username
|
PAPERLESS_DBUSER: paperless # only needed if non-default username
|
||||||
PAPERLESS_DBPASS: paperless # only needed if non-default password
|
PAPERLESS_DBPASS: paperless # only needed if non-default password
|
||||||
PAPERLESS_DBPORT: 3306
|
PAPERLESS_DBPORT: 3306
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -28,10 +28,11 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/postgres:17
|
image: docker.io/library/postgres:17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -41,6 +42,7 @@ services:
|
|||||||
POSTGRES_DB: paperless
|
POSTGRES_DB: paperless
|
||||||
POSTGRES_USER: paperless
|
POSTGRES_USER: paperless
|
||||||
POSTGRES_PASSWORD: paperless
|
POSTGRES_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -59,6 +61,7 @@ services:
|
|||||||
PAPERLESS_DBHOST: db
|
PAPERLESS_DBHOST: db
|
||||||
env_file:
|
env_file:
|
||||||
- stack.env
|
- stack.env
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -31,10 +31,11 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/postgres:17
|
image: docker.io/library/postgres:17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -44,6 +45,7 @@ services:
|
|||||||
POSTGRES_DB: paperless
|
POSTGRES_DB: paperless
|
||||||
POSTGRES_USER: paperless
|
POSTGRES_USER: paperless
|
||||||
POSTGRES_PASSWORD: paperless
|
POSTGRES_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -66,18 +68,22 @@ services:
|
|||||||
PAPERLESS_TIKA_ENABLED: 1
|
PAPERLESS_TIKA_ENABLED: 1
|
||||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.20
|
image: docker.io/gotenberg/gotenberg:8.19
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
# The gotenberg chromium route is used to convert .eml files. We do not
|
||||||
# want to allow external content like tracking pixels or even javascript.
|
# want to allow external content like tracking pixels or even javascript.
|
||||||
command:
|
command:
|
||||||
- "gotenberg"
|
- "gotenberg"
|
||||||
- "--chromium-disable-javascript=true"
|
- "--chromium-disable-javascript=true"
|
||||||
- "--chromium-allow-list=file:///tmp/.*"
|
- "--chromium-allow-list=file:///tmp/.*"
|
||||||
|
|
||||||
tika:
|
tika:
|
||||||
image: docker.io/apache/tika:latest
|
image: docker.io/apache/tika:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -27,10 +27,11 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: docker.io/library/postgres:17
|
image: docker.io/library/postgres:17
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -40,6 +41,7 @@ services:
|
|||||||
POSTGRES_DB: paperless
|
POSTGRES_DB: paperless
|
||||||
POSTGRES_USER: paperless
|
POSTGRES_USER: paperless
|
||||||
POSTGRES_PASSWORD: paperless
|
POSTGRES_PASSWORD: paperless
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -57,6 +59,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
PAPERLESS_REDIS: redis://broker:6379
|
PAPERLESS_REDIS: redis://broker:6379
|
||||||
PAPERLESS_DBHOST: db
|
PAPERLESS_DBHOST: db
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -31,10 +31,11 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -55,18 +56,22 @@ services:
|
|||||||
PAPERLESS_TIKA_ENABLED: 1
|
PAPERLESS_TIKA_ENABLED: 1
|
||||||
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://gotenberg:3000
|
||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: docker.io/gotenberg/gotenberg:8.20
|
image: docker.io/gotenberg/gotenberg:8.19
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# The gotenberg chromium route is used to convert .eml files. We do not
|
# The gotenberg chromium route is used to convert .eml files. We do not
|
||||||
# want to allow external content like tracking pixels or even javascript.
|
# want to allow external content like tracking pixels or even javascript.
|
||||||
command:
|
command:
|
||||||
- "gotenberg"
|
- "gotenberg"
|
||||||
- "--chromium-disable-javascript=true"
|
- "--chromium-disable-javascript=true"
|
||||||
- "--chromium-allow-list=file:///tmp/.*"
|
- "--chromium-allow-list=file:///tmp/.*"
|
||||||
|
|
||||||
tika:
|
tika:
|
||||||
image: docker.io/apache/tika:latest
|
image: docker.io/apache/tika:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -24,10 +24,11 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
broker:
|
broker:
|
||||||
image: docker.io/library/redis:8
|
image: docker.io/library/redis:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- redisdata:/data
|
- redisdata:/data
|
||||||
|
|
||||||
webserver:
|
webserver:
|
||||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -43,6 +44,7 @@ services:
|
|||||||
env_file: docker-compose.env
|
env_file: docker-compose.env
|
||||||
environment:
|
environment:
|
||||||
PAPERLESS_REDIS: redis://broker:6379
|
PAPERLESS_REDIS: redis://broker:6379
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
data:
|
data:
|
||||||
media:
|
media:
|
||||||
|
@ -9,7 +9,7 @@ if find /run/s6/container_environment/*"_FILE" -maxdepth 1 > /dev/null 2>&1; the
|
|||||||
for FILENAME in /run/s6/container_environment/*; do
|
for FILENAME in /run/s6/container_environment/*; do
|
||||||
if [[ "${FILENAME##*/}" == PAPERLESS_*_FILE ]]; then
|
if [[ "${FILENAME##*/}" == PAPERLESS_*_FILE ]]; then
|
||||||
# This should have been named different..
|
# This should have been named different..
|
||||||
if [[ "${FILENAME##*/}" == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" || "${FILENAME##*/}" == "PAPERLESS_MODEL_FILE" ]]; then
|
if [[ ${FILENAME} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" || ${FILENAME} == "PAPERLESS_MODEL_FILE" ]]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
SECRETFILE=$(cat "${FILENAME}")
|
SECRETFILE=$(cat "${FILENAME}")
|
||||||
|
@ -13,8 +13,8 @@ echo $(date +%s) > /var/run/s6/container_environment/PAPERLESS_START_TIME_S
|
|||||||
# Check if we're starting as a non-root user
|
# Check if we're starting as a non-root user
|
||||||
if [ "$(id --user)" != "0" ]; then
|
if [ "$(id --user)" != "0" ]; then
|
||||||
printf "true" > /var/run/s6/container_environment/USER_IS_NON_ROOT
|
printf "true" > /var/run/s6/container_environment/USER_IS_NON_ROOT
|
||||||
echo "${log_prefix} paperless-ngx docker container running under a user ($(id --user):$(id --group))"
|
echo "${log_prefix} paperless-ngx docker container running under a user ($(id --user):$(id --group))"
|
||||||
else
|
else
|
||||||
printf "/usr/src/paperless" > /var/run/s6/container_environment/HOME
|
printf "/usr/src/paperless" > /var/run/s6/container_environment/HOME
|
||||||
echo "${log_prefix} paperless-ngx docker container starting init as root"
|
echo "${log_prefix} paperless-ngx docker container starting init as root"
|
||||||
fi
|
fi
|
||||||
|
@ -333,7 +333,7 @@ must be provided to import. If this value is lost, the export cannot be imported
|
|||||||
The document importer takes the export produced by the [Document
|
The document importer takes the export produced by the [Document
|
||||||
exporter](#exporter) and imports it into paperless.
|
exporter](#exporter) and imports it into paperless.
|
||||||
|
|
||||||
The importer works just like the exporter. You point it at a directory or the generated .zip file,
|
The importer works just like the exporter. You point it at a directory,
|
||||||
and the script does the rest of the work:
|
and the script does the rest of the work:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -351,6 +351,9 @@ When you use the provided docker compose script, put the export inside
|
|||||||
the `export` folder in your paperless source directory. Specify
|
the `export` folder in your paperless source directory. Specify
|
||||||
`../export` as the `source`.
|
`../export` as the `source`.
|
||||||
|
|
||||||
|
Note that .zip files (as can be generated from the exporter) are not supported. You must unzip them into
|
||||||
|
the target directory first.
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
Importing from a previous version of Paperless may work, but for best
|
Importing from a previous version of Paperless may work, but for best
|
||||||
@ -457,22 +460,6 @@ of the index and usually makes queries faster and also ensures that the
|
|||||||
autocompletion works properly. This command is regularly invoked by the
|
autocompletion works properly. This command is regularly invoked by the
|
||||||
task scheduler.
|
task scheduler.
|
||||||
|
|
||||||
### Clearing the database read cache
|
|
||||||
|
|
||||||
If the database read cache is enabled, **you must run this command** after making any changes to the database outside the application context.
|
|
||||||
This includes operations such as restoring a database backup or executing SQL statements like UPDATE, INSERT, DELETE, ALTER, CREATE, or DROP.
|
|
||||||
|
|
||||||
Failing to invalidate the cache after such modifications can lead to stale data being served from the cache, and **may cause data corruption** or inconsistent behavior in the application.
|
|
||||||
|
|
||||||
Use the following management command to clear the cache:
|
|
||||||
|
|
||||||
```
|
|
||||||
invalidate_cachalot
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! info
|
|
||||||
The database read cache is based on Django-Cachalot. You can refer to their [documentation](https://django-cachalot.readthedocs.io/en/latest/quickstart.html#manage-py-command).
|
|
||||||
|
|
||||||
### Managing filenames {#renamer}
|
### Managing filenames {#renamer}
|
||||||
|
|
||||||
If you use paperless' feature to
|
If you use paperless' feature to
|
||||||
|
@ -179,7 +179,6 @@ variables:
|
|||||||
| ---------------------------- | ---------------------------------------------- |
|
| ---------------------------- | ---------------------------------------------- |
|
||||||
| `DOCUMENT_ID` | Database primary key of the document |
|
| `DOCUMENT_ID` | Database primary key of the document |
|
||||||
| `DOCUMENT_FILE_NAME` | Formatted filename, not including paths |
|
| `DOCUMENT_FILE_NAME` | Formatted filename, not including paths |
|
||||||
| `DOCUMENT_TYPE` | The document type (if any) |
|
|
||||||
| `DOCUMENT_CREATED` | Date & time when document created |
|
| `DOCUMENT_CREATED` | Date & time when document created |
|
||||||
| `DOCUMENT_MODIFIED` | Date & time when document was last modified |
|
| `DOCUMENT_MODIFIED` | Date & time when document was last modified |
|
||||||
| `DOCUMENT_ADDED` | Date & time when document was added |
|
| `DOCUMENT_ADDED` | Date & time when document was added |
|
||||||
|
@ -132,7 +132,7 @@ use cases:
|
|||||||
|
|
||||||
5. Documents with a custom field "address" (text) that is empty:
|
5. Documents with a custom field "address" (text) that is empty:
|
||||||
|
|
||||||
`?custom_field_query=["OR", [["address", "isnull", true], ["address", "exact", ""]]]`
|
`?custom_field_query=["OR", ["address", "isnull", true], ["address", "exact", ""]]`
|
||||||
|
|
||||||
6. Documents that don't have a field called "foo":
|
6. Documents that don't have a field called "foo":
|
||||||
|
|
||||||
@ -418,9 +418,3 @@ Initial API version.
|
|||||||
|
|
||||||
- The user field of document notes now returns a simplified user object
|
- The user field of document notes now returns a simplified user object
|
||||||
rather than just the user ID.
|
rather than just the user ID.
|
||||||
|
|
||||||
#### Version 9
|
|
||||||
|
|
||||||
- The document `created` field is now a date, not a datetime. The
|
|
||||||
`created_date` field is considered deprecated and will be removed in a
|
|
||||||
future version.
|
|
||||||
|
@ -1,272 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## paperless-ngx 2.17.1
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- Fix: correct PAPERLESS_EMPTY_TRASH_DIR to Path [@shamoon](https://github.com/shamoon) ([#10227](https://github.com/paperless-ngx/paperless-ngx/pull/10227))
|
|
||||||
|
|
||||||
### All App Changes
|
|
||||||
|
|
||||||
- Fix: correct PAPERLESS_EMPTY_TRASH_DIR to Path [@shamoon](https://github.com/shamoon) ([#10227](https://github.com/paperless-ngx/paperless-ngx/pull/10227))
|
|
||||||
|
|
||||||
## paperless-ngx 2.17.0
|
|
||||||
|
|
||||||
### Breaking Changes
|
|
||||||
|
|
||||||
- Fix: restore expected pre-2.16 scheduled workflow offset behavior [@shamoon](https://github.com/shamoon) ([#10218](https://github.com/paperless-ngx/paperless-ngx/pull/10218))
|
|
||||||
|
|
||||||
### Features / Enhancements
|
|
||||||
|
|
||||||
- QoL: log version at startup, show backend vs frontend mismatch in system status [@shamoon](https://github.com/shamoon) ([#10214](https://github.com/paperless-ngx/paperless-ngx/pull/10214))
|
|
||||||
- Feature: add Persian translation [@shamoon](https://github.com/shamoon) ([#10183](https://github.com/paperless-ngx/paperless-ngx/pull/10183))
|
|
||||||
- Enhancement: support import of zipped export [@kaerbr](https://github.com/kaerbr) ([#10073](https://github.com/paperless-ngx/paperless-ngx/pull/10073))
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- Fix: more api fixes [@shamoon](https://github.com/shamoon) ([#10204](https://github.com/paperless-ngx/paperless-ngx/pull/10204))
|
|
||||||
- Fix: restore expected pre-2.16 scheduled workflow offset behavior [@shamoon](https://github.com/shamoon) ([#10218](https://github.com/paperless-ngx/paperless-ngx/pull/10218))
|
|
||||||
- Fix: fix some API crashes [@shamoon](https://github.com/shamoon) ([#10196](https://github.com/paperless-ngx/paperless-ngx/pull/10196))
|
|
||||||
- Fix: remove duplicate base path in websocket urls [@robertmx](https://github.com/robertmx) ([#10194](https://github.com/paperless-ngx/paperless-ngx/pull/10194))
|
|
||||||
- Fix: use hard delete for custom fields with workflow removal [@shamoon](https://github.com/shamoon) ([#10191](https://github.com/paperless-ngx/paperless-ngx/pull/10191))
|
|
||||||
- Fix: fix mail account test api schema [@shamoon](https://github.com/shamoon) ([#10164](https://github.com/paperless-ngx/paperless-ngx/pull/10164))
|
|
||||||
- Fix: correct api schema for mail_account process [@shamoon](https://github.com/shamoon) ([#10157](https://github.com/paperless-ngx/paperless-ngx/pull/10157))
|
|
||||||
- Fix: correct api schema for next_asn [@shamoon](https://github.com/shamoon) ([#10151](https://github.com/paperless-ngx/paperless-ngx/pull/10151))
|
|
||||||
- Fix: fix email and notes endpoints api spec [@shamoon](https://github.com/shamoon) ([#10148](https://github.com/paperless-ngx/paperless-ngx/pull/10148))
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
- Chore: bump angular/common to 19.12.14 [@shamoon](https://github.com/shamoon) ([#10212](https://github.com/paperless-ngx/paperless-ngx/pull/10212))
|
|
||||||
|
|
||||||
### All App Changes
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>14 changes</summary>
|
|
||||||
|
|
||||||
- QoL: log version at startup, show backend vs frontend mismatch in system status [@shamoon](https://github.com/shamoon) ([#10214](https://github.com/paperless-ngx/paperless-ngx/pull/10214))
|
|
||||||
- Fix: more api fixes [@shamoon](https://github.com/shamoon) ([#10204](https://github.com/paperless-ngx/paperless-ngx/pull/10204))
|
|
||||||
- Fix: restore expected pre-2.16 scheduled workflow offset behavior [@shamoon](https://github.com/shamoon) ([#10218](https://github.com/paperless-ngx/paperless-ngx/pull/10218))
|
|
||||||
- Chore: switch from os.path to pathlib.Path [@gothicVI](https://github.com/gothicVI) ([#9933](https://github.com/paperless-ngx/paperless-ngx/pull/9933))
|
|
||||||
- Chore: bump angular/common to 19.12.14 [@shamoon](https://github.com/shamoon) ([#10212](https://github.com/paperless-ngx/paperless-ngx/pull/10212))
|
|
||||||
- Fix: fix some API crashes [@shamoon](https://github.com/shamoon) ([#10196](https://github.com/paperless-ngx/paperless-ngx/pull/10196))
|
|
||||||
- Fix: remove duplicate base path in websocket urls [@robertmx](https://github.com/robertmx) ([#10194](https://github.com/paperless-ngx/paperless-ngx/pull/10194))
|
|
||||||
- Fix: use hard delete for custom fields with workflow removal [@shamoon](https://github.com/shamoon) ([#10191](https://github.com/paperless-ngx/paperless-ngx/pull/10191))
|
|
||||||
- Feature: add Persian translation [@shamoon](https://github.com/shamoon) ([#10183](https://github.com/paperless-ngx/paperless-ngx/pull/10183))
|
|
||||||
- Enhancement: support import of zipped export [@kaerbr](https://github.com/kaerbr) ([#10073](https://github.com/paperless-ngx/paperless-ngx/pull/10073))
|
|
||||||
- Fix: fix mail account test api schema [@shamoon](https://github.com/shamoon) ([#10164](https://github.com/paperless-ngx/paperless-ngx/pull/10164))
|
|
||||||
- Fix: correct api schema for mail_account process [@shamoon](https://github.com/shamoon) ([#10157](https://github.com/paperless-ngx/paperless-ngx/pull/10157))
|
|
||||||
- Fix: correct api schema for next_asn [@shamoon](https://github.com/shamoon) ([#10151](https://github.com/paperless-ngx/paperless-ngx/pull/10151))
|
|
||||||
- Fix: fix email and notes endpoints api spec [@shamoon](https://github.com/shamoon) ([#10148](https://github.com/paperless-ngx/paperless-ngx/pull/10148))
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## paperless-ngx 2.16.3
|
|
||||||
|
|
||||||
### Features / Enhancements
|
|
||||||
|
|
||||||
- Performance: pre-filter document list in scheduled workflow checks [@shamoon](https://github.com/shamoon) ([#10031](https://github.com/paperless-ngx/paperless-ngx/pull/10031))
|
|
||||||
- Chore: refactor consumer plugin checks to a pre-flight plugin [@shamoon](https://github.com/shamoon) ([#9994](https://github.com/paperless-ngx/paperless-ngx/pull/9994))
|
|
||||||
- Enhancement: include DOCUMENT_TYPE to post consume scripts [@matthesrieke](https://github.com/matthesrieke) ([#9977](https://github.com/paperless-ngx/paperless-ngx/pull/9977))
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- Fix: handle whoosh query correction errors [@shamoon](https://github.com/shamoon) ([#10121](https://github.com/paperless-ngx/paperless-ngx/pull/10121))
|
|
||||||
- Fix: handle favicon with non-default static dir [@shamoon](https://github.com/shamoon) ([#10107](https://github.com/paperless-ngx/paperless-ngx/pull/10107))
|
|
||||||
- Fixhancement: cleanup user or group references in settings upon deletion [@shamoon](https://github.com/shamoon) ([#10049](https://github.com/paperless-ngx/paperless-ngx/pull/10049))
|
|
||||||
- Fix: Add exception to in [@Freilichtbuehne](https://github.com/Freilichtbuehne) ([#10070](https://github.com/paperless-ngx/paperless-ngx/pull/10070))
|
|
||||||
- Fix: include base href when opening global search result in new window [@shamoon](https://github.com/shamoon) ([#10066](https://github.com/paperless-ngx/paperless-ngx/pull/10066))
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>10 changes</summary>
|
|
||||||
|
|
||||||
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10085](https://github.com/paperless-ngx/paperless-ngx/pull/10085))
|
|
||||||
- Chore(deps): Update granian[uvloop] requirement from ~=2.2.0 to ~=2.3.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10055](https://github.com/paperless-ngx/paperless-ngx/pull/10055))
|
|
||||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 18 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10099](https://github.com/paperless-ngx/paperless-ngx/pull/10099))
|
|
||||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10100](https://github.com/paperless-ngx/paperless-ngx/pull/10100))
|
|
||||||
- Chore(deps): Bump bootstrap from 5.3.3 to 5.3.6 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10091](https://github.com/paperless-ngx/paperless-ngx/pull/10091))
|
|
||||||
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.9.0 to 1.9.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10090](https://github.com/paperless-ngx/paperless-ngx/pull/10090))
|
|
||||||
- Chore(deps-dev): Bump @types/node from 22.15.3 to 22.15.29 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10089](https://github.com/paperless-ngx/paperless-ngx/pull/10089))
|
|
||||||
- Chore(deps): Bump zone.js from 0.15.0 to 0.15.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10088](https://github.com/paperless-ngx/paperless-ngx/pull/10088))
|
|
||||||
- docker(deps): bump astral-sh/uv from 0.7.8-python3.12-bookworm-slim to 0.7.9-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10084](https://github.com/paperless-ngx/paperless-ngx/pull/10084))
|
|
||||||
- docker(deps): Bump astral-sh/uv from 0.6.16-python3.12-bookworm-slim to 0.7.8-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10056](https://github.com/paperless-ngx/paperless-ngx/pull/10056))
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### All App Changes
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>16 changes</summary>
|
|
||||||
|
|
||||||
- Fix: handle whoosh query correction errors [@shamoon](https://github.com/shamoon) ([#10121](https://github.com/paperless-ngx/paperless-ngx/pull/10121))
|
|
||||||
- Performance: pre-filter document list in scheduled workflow checks [@shamoon](https://github.com/shamoon) ([#10031](https://github.com/paperless-ngx/paperless-ngx/pull/10031))
|
|
||||||
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10085](https://github.com/paperless-ngx/paperless-ngx/pull/10085))
|
|
||||||
- Chore: refactor consumer plugin checks to a pre-flight plugin [@shamoon](https://github.com/shamoon) ([#9994](https://github.com/paperless-ngx/paperless-ngx/pull/9994))
|
|
||||||
- Chore(deps): Update granian[uvloop] requirement from ~=2.2.0 to ~=2.3.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10055](https://github.com/paperless-ngx/paperless-ngx/pull/10055))
|
|
||||||
- Fix: handle favicon with non-default static dir [@shamoon](https://github.com/shamoon) ([#10107](https://github.com/paperless-ngx/paperless-ngx/pull/10107))
|
|
||||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 18 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10099](https://github.com/paperless-ngx/paperless-ngx/pull/10099))
|
|
||||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10100](https://github.com/paperless-ngx/paperless-ngx/pull/10100))
|
|
||||||
- Chore(deps): Bump bootstrap from 5.3.3 to 5.3.6 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10091](https://github.com/paperless-ngx/paperless-ngx/pull/10091))
|
|
||||||
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.9.0 to 1.9.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10090](https://github.com/paperless-ngx/paperless-ngx/pull/10090))
|
|
||||||
- Chore(deps-dev): Bump @types/node from 22.15.3 to 22.15.29 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10089](https://github.com/paperless-ngx/paperless-ngx/pull/10089))
|
|
||||||
- Chore(deps): Bump zone.js from 0.15.0 to 0.15.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10088](https://github.com/paperless-ngx/paperless-ngx/pull/10088))
|
|
||||||
- Fixhancement: cleanup user or group references in settings upon deletion [@shamoon](https://github.com/shamoon) ([#10049](https://github.com/paperless-ngx/paperless-ngx/pull/10049))
|
|
||||||
- Enhancement: include DOCUMENT_TYPE to post consume scripts [@matthesrieke](https://github.com/matthesrieke) ([#9977](https://github.com/paperless-ngx/paperless-ngx/pull/9977))
|
|
||||||
- Fix: Add exception to in [@Freilichtbuehne](https://github.com/Freilichtbuehne) ([#10070](https://github.com/paperless-ngx/paperless-ngx/pull/10070))
|
|
||||||
- Fix: include base href when opening global search result in new window [@shamoon](https://github.com/shamoon) ([#10066](https://github.com/paperless-ngx/paperless-ngx/pull/10066))
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## paperless-ngx 2.16.2
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- Fix: accept datetime for created [@shamoon](https://github.com/shamoon) ([#10021](https://github.com/paperless-ngx/paperless-ngx/pull/10021))
|
|
||||||
- Fix: created date fixes in v2.16 [@shamoon](https://github.com/shamoon) ([#10026](https://github.com/paperless-ngx/paperless-ngx/pull/10026))
|
|
||||||
- Fix: mark fields for created objects as dirty [@shamoon](https://github.com/shamoon) ([#10022](https://github.com/paperless-ngx/paperless-ngx/pull/10022))
|
|
||||||
- Fix: add fallback to copyfile on PermissionError @samuel-kosmann ([#10023](https://github.com/paperless-ngx/paperless-ngx/pull/10023))
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
- Chore: warn users about removal of postgres v13 support [@shamoon](https://github.com/shamoon) ([#9980](https://github.com/paperless-ngx/paperless-ngx/pull/9980))
|
|
||||||
|
|
||||||
### All App Changes
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>5 changes</summary>
|
|
||||||
|
|
||||||
- Fix: accept datetime for created [@shamoon](https://github.com/shamoon) ([#10021](https://github.com/paperless-ngx/paperless-ngx/pull/10021))
|
|
||||||
- Fix: add fallback to copyfile on PermissionError @samuel-kosmann ([#10023](https://github.com/paperless-ngx/paperless-ngx/pull/10023))
|
|
||||||
- Fix: created date fixes in v2.16 [@shamoon](https://github.com/shamoon) ([#10026](https://github.com/paperless-ngx/paperless-ngx/pull/10026))
|
|
||||||
- Fix: mark fields for created objects as dirty [@shamoon](https://github.com/shamoon) ([#10022](https://github.com/paperless-ngx/paperless-ngx/pull/10022))
|
|
||||||
- Chore: warn users about removal of postgres v13 support [@shamoon](https://github.com/shamoon) ([#9980](https://github.com/paperless-ngx/paperless-ngx/pull/9980))
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## paperless-ngx 2.16.1
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- Fix: fix created date filtering broken in 2.16.0 [@shamoon](https://github.com/shamoon) ([#9976](https://github.com/paperless-ngx/paperless-ngx/pull/9976))
|
|
||||||
|
|
||||||
### All App Changes
|
|
||||||
|
|
||||||
- Fix: fix created date filtering broken in 2.16.0 [@shamoon](https://github.com/shamoon) ([#9976](https://github.com/paperless-ngx/paperless-ngx/pull/9976))
|
|
||||||
|
|
||||||
## paperless-ngx 2.16.0
|
|
||||||
|
|
||||||
### Breaking Changes
|
|
||||||
|
|
||||||
- [BREAKING] Change: treat created as date not datetime [@shamoon](https://github.com/shamoon) ([#9793](https://github.com/paperless-ngx/paperless-ngx/pull/9793))
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- Enhancement: support negative offset in scheduled workflows [@shamoon](https://github.com/shamoon) ([#9746](https://github.com/paperless-ngx/paperless-ngx/pull/9746))
|
|
||||||
- Enhancement: support heic images [@shamoon](https://github.com/shamoon) ([#9771](https://github.com/paperless-ngx/paperless-ngx/pull/9771))
|
|
||||||
- Enhancement: use patch instead of put for frontend document changes [@shamoon](https://github.com/shamoon) ([#9744](https://github.com/paperless-ngx/paperless-ngx/pull/9744))
|
|
||||||
- Fixhancement: automatically disable email verification if no smtp setup [@shamoon](https://github.com/shamoon) ([#9949](https://github.com/paperless-ngx/paperless-ngx/pull/9949))
|
|
||||||
- Fixhancement: better handle removed social apps in profile [@shamoon](https://github.com/shamoon) ([#9876](https://github.com/paperless-ngx/paperless-ngx/pull/9876))
|
|
||||||
- Enhancement: add barcode frontend config [@shamoon](https://github.com/shamoon) ([#9742](https://github.com/paperless-ngx/paperless-ngx/pull/9742))
|
|
||||||
- Enhancement: support allauth disable unknown account emails [@shamoon](https://github.com/shamoon) ([#9743](https://github.com/paperless-ngx/paperless-ngx/pull/9743))
|
|
||||||
- Fixhancement: tag plus button should add tag to doc [@shamoon](https://github.com/shamoon) ([#9762](https://github.com/paperless-ngx/paperless-ngx/pull/9762))
|
|
||||||
- Fixhancement: check more permissions for status consumer messages [@shamoon](https://github.com/shamoon) ([#9804](https://github.com/paperless-ngx/paperless-ngx/pull/9804))
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- Fix: include subpath in drf-spectacular settings if set [@shamoon](https://github.com/shamoon) ([#9738](https://github.com/paperless-ngx/paperless-ngx/pull/9738))
|
|
||||||
- Fix: handle created change with api version increment, use created only on frontend, deprecate created_date [@shamoon](https://github.com/shamoon) ([#9962](https://github.com/paperless-ngx/paperless-ngx/pull/9962))
|
|
||||||
- Fix: ignore logo file from sanity checker [@shamoon](https://github.com/shamoon) ([#9946](https://github.com/paperless-ngx/paperless-ngx/pull/9946))
|
|
||||||
- Fix: correctly handle empty user for old notes api format, fix frontend API version [@shamoon](https://github.com/shamoon) ([#9846](https://github.com/paperless-ngx/paperless-ngx/pull/9846))
|
|
||||||
- Fix: fix single select in filterable dropdowns when editing [@shamoon](https://github.com/shamoon) ([#9834](https://github.com/paperless-ngx/paperless-ngx/pull/9834))
|
|
||||||
- Fix: always update classifier task result [@shamoon](https://github.com/shamoon) ([#9817](https://github.com/paperless-ngx/paperless-ngx/pull/9817))
|
|
||||||
- Fix: fix zoom increase/decrease buttons in FF [@shamoon](https://github.com/shamoon) ([#9761](https://github.com/paperless-ngx/paperless-ngx/pull/9761))
|
|
||||||
|
|
||||||
### Maintenance
|
|
||||||
|
|
||||||
- Chore(deps): Bump astral-sh/setup-uv from 5 to 6 in the actions group @[dependabot[bot]](https://github.com/apps/dependabot) ([#9842](https://github.com/paperless-ngx/paperless-ngx/pull/9842))
|
|
||||||
- Chore: split ci frontend e2e vs unit tests [@shamoon](https://github.com/shamoon) ([#9851](https://github.com/paperless-ngx/paperless-ngx/pull/9851))
|
|
||||||
- Chore: auto-generate translation strings [@shamoon](https://github.com/shamoon) ([#9462](https://github.com/paperless-ngx/paperless-ngx/pull/9462))
|
|
||||||
- Chore: add ymlfmt [@shamoon](https://github.com/shamoon) ([#9745](https://github.com/paperless-ngx/paperless-ngx/pull/9745))
|
|
||||||
- Chore: replace secretary with GHA [@shamoon](https://github.com/shamoon) ([#9723](https://github.com/paperless-ngx/paperless-ngx/pull/9723))
|
|
||||||
- Chore: resolve dynamic import warnings from pdfjs, again [@shamoon](https://github.com/shamoon) ([#9924](https://github.com/paperless-ngx/paperless-ngx/pull/9924))
|
|
||||||
- Fix/Chore: replace file drop package [@shamoon](https://github.com/shamoon) ([#9926](https://github.com/paperless-ngx/paperless-ngx/pull/9926))
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>14 changes</summary>
|
|
||||||
|
|
||||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9921](https://github.com/paperless-ngx/paperless-ngx/pull/9921))
|
|
||||||
- docker-compose(deps): Bump library/redis from 7 to 8 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#9879](https://github.com/paperless-ngx/paperless-ngx/pull/9879))
|
|
||||||
- Chore(deps): Bump astral-sh/setup-uv from 5 to 6 in the actions group @[dependabot[bot]](https://github.com/apps/dependabot) ([#9842](https://github.com/paperless-ngx/paperless-ngx/pull/9842))
|
|
||||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 14 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9848](https://github.com/paperless-ngx/paperless-ngx/pull/9848))
|
|
||||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9849](https://github.com/paperless-ngx/paperless-ngx/pull/9849))
|
|
||||||
- Chore(deps-dev): Bump @types/node from 22.13.17 to 22.15.3 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#9850](https://github.com/paperless-ngx/paperless-ngx/pull/9850))
|
|
||||||
- docker(deps): Bump astral-sh/uv from 0.6.14-python3.12-bookworm-slim to 0.6.16-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#9767](https://github.com/paperless-ngx/paperless-ngx/pull/9767))
|
|
||||||
- docker-compose(deps): bump gotenberg/gotenberg from 8.19 to 8.20 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#9661](https://github.com/paperless-ngx/paperless-ngx/pull/9661))
|
|
||||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 17 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9768](https://github.com/paperless-ngx/paperless-ngx/pull/9768))
|
|
||||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9770](https://github.com/paperless-ngx/paperless-ngx/pull/9770))
|
|
||||||
- Chore(deps-dev): Bump jest-preset-angular from 14.5.4 to 14.5.5 in /src-ui in the frontend-jest-dependencies group @[dependabot[bot]](https://github.com/apps/dependabot) ([#9769](https://github.com/paperless-ngx/paperless-ngx/pull/9769))
|
|
||||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9764](https://github.com/paperless-ngx/paperless-ngx/pull/9764))
|
|
||||||
- Chore(deps): Bump the django group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9753](https://github.com/paperless-ngx/paperless-ngx/pull/9753))
|
|
||||||
- docker(deps): bump astral-sh/uv from 0.6.13-python3.12-bookworm-slim to 0.6.14-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#9656](https://github.com/paperless-ngx/paperless-ngx/pull/9656))
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### All App Changes
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>29 changes</summary>
|
|
||||||
|
|
||||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9921](https://github.com/paperless-ngx/paperless-ngx/pull/9921))
|
|
||||||
- Fix: handle created change with api version increment, use created only on frontend, deprecate created_date [@shamoon](https://github.com/shamoon) ([#9962](https://github.com/paperless-ngx/paperless-ngx/pull/9962))
|
|
||||||
- Fixhancement: automatically disable email verification if no smtp setup [@shamoon](https://github.com/shamoon) ([#9949](https://github.com/paperless-ngx/paperless-ngx/pull/9949))
|
|
||||||
- Fix: ignore logo file from sanity checker [@shamoon](https://github.com/shamoon) ([#9946](https://github.com/paperless-ngx/paperless-ngx/pull/9946))
|
|
||||||
- [BREAKING] Change: treat created as date not datetime [@shamoon](https://github.com/shamoon) ([#9793](https://github.com/paperless-ngx/paperless-ngx/pull/9793))
|
|
||||||
- Fix/Chore: replace file drop package [@shamoon](https://github.com/shamoon) ([#9926](https://github.com/paperless-ngx/paperless-ngx/pull/9926))
|
|
||||||
- Chore: resolve dynamic import warnings from pdfjs, again [@shamoon](https://github.com/shamoon) ([#9924](https://github.com/paperless-ngx/paperless-ngx/pull/9924))
|
|
||||||
- Enhancement: support negative offset in scheduled workflows [@shamoon](https://github.com/shamoon) ([#9746](https://github.com/paperless-ngx/paperless-ngx/pull/9746))
|
|
||||||
- Fixhancement: better handle removed social apps in profile [@shamoon](https://github.com/shamoon) ([#9876](https://github.com/paperless-ngx/paperless-ngx/pull/9876))
|
|
||||||
- Enhancement: add barcode frontend config [@shamoon](https://github.com/shamoon) ([#9742](https://github.com/paperless-ngx/paperless-ngx/pull/9742))
|
|
||||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 14 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9848](https://github.com/paperless-ngx/paperless-ngx/pull/9848))
|
|
||||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9849](https://github.com/paperless-ngx/paperless-ngx/pull/9849))
|
|
||||||
- Chore(deps-dev): Bump @types/node from 22.13.17 to 22.15.3 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#9850](https://github.com/paperless-ngx/paperless-ngx/pull/9850))
|
|
||||||
- Fix: correctly handle empty user for old notes api format, fix frontend API version [@shamoon](https://github.com/shamoon) ([#9846](https://github.com/paperless-ngx/paperless-ngx/pull/9846))
|
|
||||||
- Fix: fix single select in filterable dropdowns when editing [@shamoon](https://github.com/shamoon) ([#9834](https://github.com/paperless-ngx/paperless-ngx/pull/9834))
|
|
||||||
- Fix: always update classifier task result [@shamoon](https://github.com/shamoon) ([#9817](https://github.com/paperless-ngx/paperless-ngx/pull/9817))
|
|
||||||
- Fixhancement: check more permissions for status consumer messages [@shamoon](https://github.com/shamoon) ([#9804](https://github.com/paperless-ngx/paperless-ngx/pull/9804))
|
|
||||||
- Enhancement: support heic images [@shamoon](https://github.com/shamoon) ([#9771](https://github.com/paperless-ngx/paperless-ngx/pull/9771))
|
|
||||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 17 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9768](https://github.com/paperless-ngx/paperless-ngx/pull/9768))
|
|
||||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9770](https://github.com/paperless-ngx/paperless-ngx/pull/9770))
|
|
||||||
- Chore(deps-dev): Bump jest-preset-angular from 14.5.4 to 14.5.5 in /src-ui in the frontend-jest-dependencies group @[dependabot[bot]](https://github.com/apps/dependabot) ([#9769](https://github.com/paperless-ngx/paperless-ngx/pull/9769))
|
|
||||||
- Enhancement: support allauth disable unknown account emails [@shamoon](https://github.com/shamoon) ([#9743](https://github.com/paperless-ngx/paperless-ngx/pull/9743))
|
|
||||||
- Enhancement: use patch instead of put for frontend document changes [@shamoon](https://github.com/shamoon) ([#9744](https://github.com/paperless-ngx/paperless-ngx/pull/9744))
|
|
||||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9764](https://github.com/paperless-ngx/paperless-ngx/pull/9764))
|
|
||||||
- Chore(deps): Bump the django group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9753](https://github.com/paperless-ngx/paperless-ngx/pull/9753))
|
|
||||||
- Fixhancement: tag plus button should add tag to doc [@shamoon](https://github.com/shamoon) ([#9762](https://github.com/paperless-ngx/paperless-ngx/pull/9762))
|
|
||||||
- Fix: fix zoom increase/decrease buttons in FF [@shamoon](https://github.com/shamoon) ([#9761](https://github.com/paperless-ngx/paperless-ngx/pull/9761))
|
|
||||||
- Chore: switch from os.path to pathlib.Path [@gothicVI](https://github.com/gothicVI) ([#9339](https://github.com/paperless-ngx/paperless-ngx/pull/9339))
|
|
||||||
- Fix: include subpath in drf-spectacular settings if set [@shamoon](https://github.com/shamoon) ([#9738](https://github.com/paperless-ngx/paperless-ngx/pull/9738))
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## paperless-ngx 2.15.3
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
- Fix: do not try deleting original file that was moved to trash dir [@shamoon](https://github.com/shamoon) ([#9684](https://github.com/paperless-ngx/paperless-ngx/pull/9684))
|
|
||||||
- Fix: preserve non-ASCII filenames in document downloads [@shamoon](https://github.com/shamoon) ([#9702](https://github.com/paperless-ngx/paperless-ngx/pull/9702))
|
|
||||||
- Fix: fix breaking api change to document notes user field [@shamoon](https://github.com/shamoon) ([#9714](https://github.com/paperless-ngx/paperless-ngx/pull/9714))
|
|
||||||
- Fix: another doc link fix [@shamoon](https://github.com/shamoon) ([#9700](https://github.com/paperless-ngx/paperless-ngx/pull/9700))
|
|
||||||
- Fix: correctly handle dict data with webhook [@shamoon](https://github.com/shamoon) ([#9674](https://github.com/paperless-ngx/paperless-ngx/pull/9674))
|
|
||||||
|
|
||||||
### All App Changes
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>5 changes</summary>
|
|
||||||
|
|
||||||
- Fix: do not try deleting original file that was moved to trash dir [@shamoon](https://github.com/shamoon) ([#9684](https://github.com/paperless-ngx/paperless-ngx/pull/9684))
|
|
||||||
- Fix: preserve non-ASCII filenames in document downloads [@shamoon](https://github.com/shamoon) ([#9702](https://github.com/paperless-ngx/paperless-ngx/pull/9702))
|
|
||||||
- Fix: fix breaking api change to document notes user field [@shamoon](https://github.com/shamoon) ([#9714](https://github.com/paperless-ngx/paperless-ngx/pull/9714))
|
|
||||||
- Fix: another doc link fix [@shamoon](https://github.com/shamoon) ([#9700](https://github.com/paperless-ngx/paperless-ngx/pull/9700))
|
|
||||||
- Fix: correctly handle dict data with webhook [@shamoon](https://github.com/shamoon) ([#9674](https://github.com/paperless-ngx/paperless-ngx/pull/9674))
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## paperless-ngx 2.15.2
|
## paperless-ngx 2.15.2
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
@ -6007,6 +5740,7 @@ primarily.
|
|||||||
a very good job at ocr'ing a document with the default
|
a very good job at ocr'ing a document with the default
|
||||||
language. Certain language specifics such as umlauts may not get
|
language. Certain language specifics such as umlauts may not get
|
||||||
picked up properly.
|
picked up properly.
|
||||||
|
- `PAPERLESS_DEBUG` defaults to `false`.
|
||||||
- The presence of `PAPERLESS_DBHOST` now determines whether to use
|
- The presence of `PAPERLESS_DBHOST` now determines whether to use
|
||||||
PostgreSQL or SQLite.
|
PostgreSQL or SQLite.
|
||||||
- `PAPERLESS_OCR_THREADS` is gone and replaced with
|
- `PAPERLESS_OCR_THREADS` is gone and replaced with
|
||||||
|
@ -50,48 +50,47 @@ matcher.
|
|||||||
|
|
||||||
### Database
|
### Database
|
||||||
|
|
||||||
By default, Paperless uses **SQLite** with a database stored at `data/db.sqlite3`.
|
|
||||||
To switch to **PostgreSQL** or **MariaDB**, set [`PAPERLESS_DBHOST`](#PAPERLESS_DBHOST) and optionally configure other
|
|
||||||
database-related environment variables.
|
|
||||||
|
|
||||||
#### [`PAPERLESS_DBHOST=<hostname>`](#PAPERLESS_DBHOST) {#PAPERLESS_DBHOST}
|
|
||||||
|
|
||||||
: If unset, Paperless uses **SQLite** by default.
|
|
||||||
|
|
||||||
Set `PAPERLESS_DBHOST` to switch to PostgreSQL or MariaDB instead.
|
|
||||||
|
|
||||||
#### [`PAPERLESS_DBENGINE=<engine_name>`](#PAPERLESS_DBENGINE) {#PAPERLESS_DBENGINE}
|
#### [`PAPERLESS_DBENGINE=<engine_name>`](#PAPERLESS_DBENGINE) {#PAPERLESS_DBENGINE}
|
||||||
|
|
||||||
: Optional. Specifies the database engine to use when connecting to a remote database.
|
: Optional, gives the ability to choose Postgres or MariaDB for
|
||||||
Available options are `postgresql` and `mariadb`.
|
database engine. Available options are `postgresql` and
|
||||||
|
`mariadb`.
|
||||||
|
|
||||||
Defaults to `postgresql` if `PAPERLESS_DBHOST` is set.
|
Default is `postgresql`.
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
|
|
||||||
Using MariaDB comes with some caveats. See [MySQL Caveats](advanced_usage.md#mysql-caveats).
|
Using MariaDB comes with some caveats. See [MySQL Caveats](advanced_usage.md#mysql-caveats).
|
||||||
|
|
||||||
|
#### [`PAPERLESS_DBHOST=<hostname>`](#PAPERLESS_DBHOST) {#PAPERLESS_DBHOST}
|
||||||
|
|
||||||
|
: By default, sqlite is used as the database backend. This can be
|
||||||
|
changed here.
|
||||||
|
|
||||||
|
Set PAPERLESS_DBHOST and another database will be used instead of
|
||||||
|
sqlite.
|
||||||
|
|
||||||
#### [`PAPERLESS_DBPORT=<port>`](#PAPERLESS_DBPORT) {#PAPERLESS_DBPORT}
|
#### [`PAPERLESS_DBPORT=<port>`](#PAPERLESS_DBPORT) {#PAPERLESS_DBPORT}
|
||||||
|
|
||||||
: Port to use when connecting to PostgreSQL or MariaDB.
|
: Adjust port if necessary.
|
||||||
|
|
||||||
Default is `5432` for PostgreSQL and `3306` for MariaDB.
|
Default is 5432.
|
||||||
|
|
||||||
#### [`PAPERLESS_DBNAME=<name>`](#PAPERLESS_DBNAME) {#PAPERLESS_DBNAME}
|
#### [`PAPERLESS_DBNAME=<name>`](#PAPERLESS_DBNAME) {#PAPERLESS_DBNAME}
|
||||||
|
|
||||||
: Name of the database to connect to when using PostgreSQL or MariaDB.
|
: Database name in PostgreSQL or MariaDB.
|
||||||
|
|
||||||
Defaults to "paperless".
|
Defaults to "paperless".
|
||||||
|
|
||||||
#### [`PAPERLESS_DBUSER=<name>`](#PAPERLESS_DBUSER) {#PAPERLESS_DBUSER}
|
#### [`PAPERLESS_DBUSER=<name>`](#PAPERLESS_DBUSER) {#PAPERLESS_DBUSER}
|
||||||
|
|
||||||
: Username for authenticating with the PostgreSQL or MariaDB database.
|
: Database user in PostgreSQL or MariaDB.
|
||||||
|
|
||||||
Defaults to "paperless".
|
Defaults to "paperless".
|
||||||
|
|
||||||
#### [`PAPERLESS_DBPASS=<password>`](#PAPERLESS_DBPASS) {#PAPERLESS_DBPASS}
|
#### [`PAPERLESS_DBPASS=<password>`](#PAPERLESS_DBPASS) {#PAPERLESS_DBPASS}
|
||||||
|
|
||||||
: Password for the PostgreSQL or MariaDB database user.
|
: Database password for PostgreSQL or MariaDB.
|
||||||
|
|
||||||
Defaults to "paperless".
|
Defaults to "paperless".
|
||||||
|
|
||||||
@ -111,20 +110,20 @@ Available options are `postgresql` and `mariadb`.
|
|||||||
|
|
||||||
#### [`PAPERLESS_DBSSLROOTCERT=<ca-path>`](#PAPERLESS_DBSSLROOTCERT) {#PAPERLESS_DBSSLROOTCERT}
|
#### [`PAPERLESS_DBSSLROOTCERT=<ca-path>`](#PAPERLESS_DBSSLROOTCERT) {#PAPERLESS_DBSSLROOTCERT}
|
||||||
|
|
||||||
: Path to the SSL root certificate used to verify the database server.
|
: SSL root certificate path
|
||||||
|
|
||||||
See [the official documentation about
|
See [the official documentation about
|
||||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||||
Changes the location of `root.crt`.
|
Changes path of `root.crt`.
|
||||||
|
|
||||||
See [the official documentation about
|
See [the official documentation about
|
||||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-ca).
|
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-ca).
|
||||||
|
|
||||||
Defaults to unset, using the standard location in the home directory.
|
Defaults to unset, using the documented path in the home directory.
|
||||||
|
|
||||||
#### [`PAPERLESS_DBSSLCERT=<client-cert-path>`](#PAPERLESS_DBSSLCERT) {#PAPERLESS_DBSSLCERT}
|
#### [`PAPERLESS_DBSSLCERT=<client-cert-path>`](#PAPERLESS_DBSSLCERT) {#PAPERLESS_DBSSLCERT}
|
||||||
|
|
||||||
: Path to the client SSL certificate used when connecting securely.
|
: SSL client certificate path
|
||||||
|
|
||||||
See [the official documentation about
|
See [the official documentation about
|
||||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||||
@ -132,13 +131,13 @@ Available options are `postgresql` and `mariadb`.
|
|||||||
See [the official documentation about
|
See [the official documentation about
|
||||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-cert).
|
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-cert).
|
||||||
|
|
||||||
Changes the location of `postgresql.crt`.
|
Changes path of `postgresql.crt`.
|
||||||
|
|
||||||
Defaults to unset, using the standard location in the home directory.
|
Defaults to unset, using the documented path in the home directory.
|
||||||
|
|
||||||
#### [`PAPERLESS_DBSSLKEY=<client-cert-key>`](#PAPERLESS_DBSSLKEY) {#PAPERLESS_DBSSLKEY}
|
#### [`PAPERLESS_DBSSLKEY=<client-cert-key>`](#PAPERLESS_DBSSLKEY) {#PAPERLESS_DBSSLKEY}
|
||||||
|
|
||||||
: Path to the client SSL private key used when connecting securely.
|
: SSL client key path
|
||||||
|
|
||||||
See [the official documentation about
|
See [the official documentation about
|
||||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||||
@ -146,53 +145,17 @@ Available options are `postgresql` and `mariadb`.
|
|||||||
See [the official documentation about
|
See [the official documentation about
|
||||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-key).
|
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-key).
|
||||||
|
|
||||||
Changes the location of `postgresql.key`.
|
Changes path of `postgresql.key`.
|
||||||
|
|
||||||
Defaults to unset, using the standard location in the home directory.
|
Defaults to unset, using the documented path in the home directory.
|
||||||
|
|
||||||
#### [`PAPERLESS_DB_TIMEOUT=<int>`](#PAPERLESS_DB_TIMEOUT) {#PAPERLESS_DB_TIMEOUT}
|
#### [`PAPERLESS_DB_TIMEOUT=<int>`](#PAPERLESS_DB_TIMEOUT) {#PAPERLESS_DB_TIMEOUT}
|
||||||
|
|
||||||
: Sets how long a database connection should wait before timing out.
|
: Amount of time for a database connection to wait for the database to
|
||||||
|
unlock. Mostly applicable for sqlite based installation. Consider changing
|
||||||
|
to postgresql if you are having concurrency problems with sqlite.
|
||||||
|
|
||||||
For SQLite, this sets how long to wait if the database is locked.
|
Defaults to unset, keeping the Django defaults.
|
||||||
For PostgreSQL or MariaDB, this sets the connection timeout.
|
|
||||||
|
|
||||||
Defaults to unset, which uses Django’s built-in defaults.
|
|
||||||
|
|
||||||
#### [`PAPERLESS_DB_READ_CACHE_ENABLED=<bool>`](#PAPERLESS_DB_READ_CACHE_ENABLED) {#PAPERLESS_DB_READ_CACHE_ENABLED}
|
|
||||||
|
|
||||||
: Caches the database read query results into Redis. This can significantly improve application response times by caching database queries, at the cost of slightly increased memory usage.
|
|
||||||
|
|
||||||
Defaults to `false`.
|
|
||||||
|
|
||||||
!!! danger
|
|
||||||
|
|
||||||
**Do not modify the database outside the application while it is running.**
|
|
||||||
This includes actions such as restoring a backup, upgrading the database, or performing manual inserts. All external modifications must be done **only when the application is stopped**.
|
|
||||||
After making any such changes, you **must invalidate the DB read cache** using the `invalidate_cachalot` management command.
|
|
||||||
|
|
||||||
#### [`PAPERLESS_READ_CACHE_TTL=<int>`](#PAPERLESS_READ_CACHE_TTL) {#PAPERLESS_READ_CACHE_TTL}
|
|
||||||
|
|
||||||
: Specifies how long (in seconds) read data should be cached.
|
|
||||||
|
|
||||||
Allowed values are between `1` (one second) and `31536000` (one year). Defaults to `3600` (one hour).
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
|
|
||||||
A high TTL increases memory usage over time. Memory may be used until end of TTL, even if the cache is invalidated with the `invalidate_cachalot` command.
|
|
||||||
|
|
||||||
In case of an out-of-memory (OOM) situation, Redis may stop accepting new data — including cache entries, scheduled tasks, and documents to consume.
|
|
||||||
If your system has limited RAM, consider configuring a dedicated Redis instance for the read cache, with a memory limit and the eviction policy set to `allkeys-lru`.
|
|
||||||
For more details, refer to the [Redis eviction policy documentation](https://redis.io/docs/latest/develop/reference/eviction/), and see the `PAPERLESS_READ_CACHE_REDIS_URL` setting to specify a separate Redis broker.
|
|
||||||
|
|
||||||
#### [`PAPERLESS_READ_CACHE_REDIS_URL=<url>`](#PAPERLESS_READ_CACHE_REDIS_URL) {#PAPERLESS_READ_CACHE_REDIS_URL}
|
|
||||||
|
|
||||||
: Defines the Redis instance used for the read cache.
|
|
||||||
|
|
||||||
Defaults to `None`.
|
|
||||||
|
|
||||||
!!! Note
|
|
||||||
If this value is not set, the same Redis instance used for scheduled tasks will be used for caching as well.
|
|
||||||
|
|
||||||
## Optional Services
|
## Optional Services
|
||||||
|
|
||||||
@ -237,7 +200,7 @@ and watch out for indentation if editing the YAML file.
|
|||||||
|
|
||||||
### Email Parsing
|
### Email Parsing
|
||||||
|
|
||||||
#### [`PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT=<int>`](#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT) {#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT}
|
#### [`PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT=<int>`(#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT) {#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT}
|
||||||
|
|
||||||
: The default layout to use for emails that are consumed as documents. Must be one of the integer choices below. Note that mail
|
: The default layout to use for emails that are consumed as documents. Must be one of the integer choices below. Note that mail
|
||||||
rules can specify this setting, thus this fallback is used for the default selection and for .eml files consumed by other means.
|
rules can specify this setting, thus this fallback is used for the default selection and for .eml files consumed by other means.
|
||||||
@ -666,13 +629,7 @@ If both the [PAPERLESS_ACCOUNT_DEFAULT_GROUPS](#PAPERLESS_ACCOUNT_DEFAULT_GROUPS
|
|||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
|
||||||
If you do not have a working email server set up this will be set to 'none'.
|
If you do not have a working email server set up you should set this to 'none'.
|
||||||
|
|
||||||
#### [`PAPERLESS_ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS=<bool>`](#PAPERLESS_ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS) {#PAPERLESS_ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS}
|
|
||||||
|
|
||||||
: See the relevant [django-allauth documentation](https://docs.allauth.org/en/latest/account/configuration.html)
|
|
||||||
|
|
||||||
Defaults to True (from allauth)
|
|
||||||
|
|
||||||
#### [`PAPERLESS_DISABLE_REGULAR_LOGIN=<bool>`](#PAPERLESS_DISABLE_REGULAR_LOGIN) {#PAPERLESS_DISABLE_REGULAR_LOGIN}
|
#### [`PAPERLESS_DISABLE_REGULAR_LOGIN=<bool>`](#PAPERLESS_DISABLE_REGULAR_LOGIN) {#PAPERLESS_DISABLE_REGULAR_LOGIN}
|
||||||
|
|
||||||
@ -1003,22 +960,6 @@ still perform some basic text pre-processing before matching.
|
|||||||
|
|
||||||
Defaults to 1.
|
Defaults to 1.
|
||||||
|
|
||||||
#### [`PAPERLESS_DATE_PARSER_LANGUAGES=<lang>`](#PAPERLESS_DATE_PARSER_LANGUAGES) {#PAPERLESS_DATE_PARSER_LANGUAGES}
|
|
||||||
|
|
||||||
Specifies which language Paperless should use when parsing dates from documents.
|
|
||||||
|
|
||||||
This should be a language code supported by the dateparser library,
|
|
||||||
for example: "en", or a combination such as "en+de".
|
|
||||||
Locales are also supported (e.g., "en-AU").
|
|
||||||
Multiple languages can be combined using "+", for example: "en+de" or "en-AU+de".
|
|
||||||
For valid values, refer to the list of supported languages and locales in the [dateparser documentation](https://dateparser.readthedocs.io/en/latest/supported_locales.html).
|
|
||||||
|
|
||||||
Set this to match the languages in which most of your documents are written.
|
|
||||||
If not set, Paperless will attempt to infer the language(s) from the OCR configuration (`PAPERLESS_OCR_LANGUAGE`).
|
|
||||||
|
|
||||||
!!! note
|
|
||||||
This format differs from the `PAPERLESS_OCR_LANGUAGE` setting, which uses ISO 639-2 codes (3 letters, e.g., "eng+deu" for Tesseract OCR).
|
|
||||||
|
|
||||||
#### [`PAPERLESS_EMAIL_TASK_CRON=<cron expression>`](#PAPERLESS_EMAIL_TASK_CRON) {#PAPERLESS_EMAIL_TASK_CRON}
|
#### [`PAPERLESS_EMAIL_TASK_CRON=<cron expression>`](#PAPERLESS_EMAIL_TASK_CRON) {#PAPERLESS_EMAIL_TASK_CRON}
|
||||||
|
|
||||||
: Configures the scheduled email fetching frequency. The value
|
: Configures the scheduled email fetching frequency. The value
|
||||||
@ -1116,9 +1057,9 @@ be used with caution!
|
|||||||
|
|
||||||
## Document Consumption {#consume_config}
|
## Document Consumption {#consume_config}
|
||||||
|
|
||||||
#### [`PAPERLESS_CONSUMER_DISABLE`](#PAPERLESS_CONSUMER_DISABLE) {#PAPERLESS_CONSUMER_DISABLE}
|
#### [`PAPERLESS_CONSUMER_DISABLE=<bool>`](#PAPERLESS_CONSUMER_DISABLE) {#PAPERLESS_CONSUMER_DISABLE}
|
||||||
|
|
||||||
: If set (to anything), this completely disables the directory-based consumer in docker. If you don't plan to consume documents
|
: Completely disable the directory-based consumer in docker. If you don't plan to consume documents
|
||||||
via the consumption directory, you can disable the consumer to save resources.
|
via the consumption directory, you can disable the consumer to save resources.
|
||||||
|
|
||||||
#### [`PAPERLESS_CONSUMER_DELETE_DUPLICATES=<bool>`](#PAPERLESS_CONSUMER_DELETE_DUPLICATES) {#PAPERLESS_CONSUMER_DELETE_DUPLICATES}
|
#### [`PAPERLESS_CONSUMER_DELETE_DUPLICATES=<bool>`](#PAPERLESS_CONSUMER_DELETE_DUPLICATES) {#PAPERLESS_CONSUMER_DELETE_DUPLICATES}
|
||||||
|
@ -130,7 +130,7 @@ command:
|
|||||||
- 'gotenberg'
|
- 'gotenberg'
|
||||||
- '--chromium-disable-javascript=true'
|
- '--chromium-disable-javascript=true'
|
||||||
- '--chromium-allow-list=file:///tmp/.*'
|
- '--chromium-allow-list=file:///tmp/.*'
|
||||||
- '--api-timeout=60s'
|
- '--api-timeout=60'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Permission denied errors in the consumption directory
|
## Permission denied errors in the consumption directory
|
||||||
|
@ -115,8 +115,7 @@ following operations on your documents:
|
|||||||
|
|
||||||
No matter which options you choose, Paperless will always store the
|
No matter which options you choose, Paperless will always store the
|
||||||
original document that it found in the consumption directory or in the
|
original document that it found in the consumption directory or in the
|
||||||
mail and will never overwrite that document (except when using certain
|
mail and will never overwrite that document. Archived versions are
|
||||||
document actions, which make that clear). Archived versions are
|
|
||||||
stored alongside the original versions. Any files found in the
|
stored alongside the original versions. Any files found in the
|
||||||
consumption directory will stored inside the Paperless-ngx file
|
consumption directory will stored inside the Paperless-ngx file
|
||||||
structure and will not be retained in the consumption directory.
|
structure and will not be retained in the consumption directory.
|
||||||
@ -338,25 +337,25 @@ Global permissions define what areas of the app and API endpoints users can acce
|
|||||||
determine if a user can create, edit, delete or view _any_ documents, but individual documents themselves
|
determine if a user can create, edit, delete or view _any_ documents, but individual documents themselves
|
||||||
still have "object-level" permissions.
|
still have "object-level" permissions.
|
||||||
|
|
||||||
| Type | Details |
|
| Type | Details |
|
||||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| AppConfig | _Change_ or higher permissions grants access to the "Application Configuration" area. |
|
| AppConfig | _Change_ or higher permissions grants access to the "Application Configuration" area. |
|
||||||
| Correspondent | Add, edit, delete or view Correspondents. |
|
| Correspondent | Add, edit, delete or view Correspondents. |
|
||||||
| CustomField | Add, edit, delete or view Custom Fields. |
|
| CustomField | Add, edit, delete or view Custom Fields. |
|
||||||
| Document | Add, edit, delete or view Documents. |
|
| Document | Add, edit, delete or view Documents. |
|
||||||
| DocumentType | Add, edit, delete or view Document Types. |
|
| DocumentType | Add, edit, delete or view Document Types. |
|
||||||
| Group | Add, edit, delete or view Groups. |
|
| Group | Add, edit, delete or view Groups. |
|
||||||
| MailAccount | Add, edit, delete or view Mail Accounts. |
|
| MailAccount | Add, edit, delete or view Mail Accounts. |
|
||||||
| MailRule | Add, edit, delete or view Mail Rules. |
|
| MailRule | Add, edit, delete or view Mail Rules. |
|
||||||
| Note | Add, edit, delete or view Notes. |
|
| Note | Add, edit, delete or view Notes. |
|
||||||
| PaperlessTask | View or dismiss (_Change_) File Tasks. |
|
| PaperlessTask | View or dismiss (_Change_) File Tasks. |
|
||||||
| SavedView | Add, edit, delete or view Saved Views. |
|
| SavedView | Add, edit, delete or view Saved Views. |
|
||||||
| ShareLink | Add, delete or view Share Links. |
|
| ShareLink | Add, delete or view Share Links. |
|
||||||
| StoragePath | Add, edit, delete or view Storage Paths. |
|
| StoragePath | Add, edit, delete or view Storage Paths. |
|
||||||
| Tag | Add, edit, delete or view Tags. |
|
| Tag | Add, edit, delete or view Tags. |
|
||||||
| UISettings | Add, edit, delete or view the UI settings that are used by the web app.<br/>:warning: **Users that will access the web UI must be granted at least _View_ permissions.** |
|
| UISettings | Add, edit, delete or view the UI settings that are used by the web app.<br/>:warning: **Users that will access the web UI must be granted at least _View_ permissions.** |
|
||||||
| User | Add, edit, delete or view Users. |
|
| User | Add, edit, delete or view Users. |
|
||||||
| Workflow | Add, edit, delete or view Workflows.<br/>Note that Workflows are global; all users who can access workflows see the same set. Workflows have other permission implications — see [Workflow permissions](#workflow-permissions). |
|
| Workflow | Add, edit, delete or view Workflows.<br/>Note that Workflows are global, in other words all users who can access workflows have access to the same set of them. |
|
||||||
|
|
||||||
#### Detailed Explanation of Object Permissions {#object-permissions}
|
#### Detailed Explanation of Object Permissions {#object-permissions}
|
||||||
|
|
||||||
@ -407,8 +406,7 @@ Currently, there are three events that correspond to workflow trigger 'types':
|
|||||||
3. **Document Updated**: when a document is updated. Similar to 'added' events, triggers can include filtering by content matching,
|
3. **Document Updated**: when a document is updated. Similar to 'added' events, triggers can include filtering by content matching,
|
||||||
tags, doc type, or correspondent.
|
tags, doc type, or correspondent.
|
||||||
4. **Scheduled**: a scheduled trigger that can be used to run workflows at a specific time. The date used can be either the document
|
4. **Scheduled**: a scheduled trigger that can be used to run workflows at a specific time. The date used can be either the document
|
||||||
added, created, updated date or you can specify a (date) custom field. You can also specify a day offset from the date (positive
|
added, created, updated date or you can specify a (date) custom field. You can also specify a day offset from the date.
|
||||||
offsets will trigger after the date, negative offsets will trigger before).
|
|
||||||
|
|
||||||
The following flow diagram illustrates the three document trigger types:
|
The following flow diagram illustrates the three document trigger types:
|
||||||
|
|
||||||
@ -533,7 +531,7 @@ The following placeholders are only available for "added" or "updated" triggers
|
|||||||
All users who have application permissions for editing workflows can see the same set
|
All users who have application permissions for editing workflows can see the same set
|
||||||
of workflows. In other words, workflows themselves intentionally do not have an owner or permissions.
|
of workflows. In other words, workflows themselves intentionally do not have an owner or permissions.
|
||||||
|
|
||||||
Given their potentially far-reaching capabilities, including changing the permissions of existing documents, you may want to restrict access to workflows.
|
Given their potentially far-reaching capabilities, you may want to restrict access to workflows.
|
||||||
|
|
||||||
Upon migration, existing installs will grant access to workflows to users who can add
|
Upon migration, existing installs will grant access to workflows to users who can add
|
||||||
documents (and superusers who can always access all parts of the app).
|
documents (and superusers who can always access all parts of the app).
|
||||||
|
28
mkdocs.yml
28
mkdocs.yml
@ -11,12 +11,14 @@ theme:
|
|||||||
toggle:
|
toggle:
|
||||||
icon: material/brightness-auto
|
icon: material/brightness-auto
|
||||||
name: Switch to light mode
|
name: Switch to light mode
|
||||||
|
|
||||||
# Palette toggle for light mode
|
# Palette toggle for light mode
|
||||||
- media: "(prefers-color-scheme: light)"
|
- media: "(prefers-color-scheme: light)"
|
||||||
scheme: default
|
scheme: default
|
||||||
toggle:
|
toggle:
|
||||||
icon: material/brightness-7
|
icon: material/brightness-7
|
||||||
name: Switch to dark mode
|
name: Switch to dark mode
|
||||||
|
|
||||||
# Palette toggle for dark mode
|
# Palette toggle for dark mode
|
||||||
- media: "(prefers-color-scheme: dark)"
|
- media: "(prefers-color-scheme: dark)"
|
||||||
scheme: slate
|
scheme: slate
|
||||||
@ -58,17 +60,17 @@ markdown_extensions:
|
|||||||
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
||||||
strict: true
|
strict: true
|
||||||
nav:
|
nav:
|
||||||
- index.md
|
- index.md
|
||||||
- setup.md
|
- setup.md
|
||||||
- 'Basic Usage': usage.md
|
- 'Basic Usage': usage.md
|
||||||
- configuration.md
|
- configuration.md
|
||||||
- administration.md
|
- administration.md
|
||||||
- advanced_usage.md
|
- advanced_usage.md
|
||||||
- 'REST API': api.md
|
- 'REST API': api.md
|
||||||
- development.md
|
- development.md
|
||||||
- 'FAQs': faq.md
|
- 'FAQs': faq.md
|
||||||
- troubleshooting.md
|
- troubleshooting.md
|
||||||
- changelog.md
|
- changelog.md
|
||||||
copyright: Copyright © 2016 - 2023 Daniel Quinn, Jonas Winkler, and the Paperless-ngx team
|
copyright: Copyright © 2016 - 2023 Daniel Quinn, Jonas Winkler, and the Paperless-ngx team
|
||||||
extra:
|
extra:
|
||||||
social:
|
social:
|
||||||
@ -81,5 +83,5 @@ extra:
|
|||||||
plugins:
|
plugins:
|
||||||
- search
|
- search
|
||||||
- glightbox:
|
- glightbox:
|
||||||
skip_classes:
|
skip_classes:
|
||||||
- no-lightbox
|
- no-lightbox
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
# Have a look at the docs for documentation.
|
# Have a look at the docs for documentation.
|
||||||
# https://docs.paperless-ngx.com/configuration/
|
# https://docs.paperless-ngx.com/configuration/
|
||||||
|
|
||||||
|
# Debug. Only enable this for development.
|
||||||
|
|
||||||
|
#PAPERLESS_DEBUG=false
|
||||||
|
|
||||||
# Required services
|
# Required services
|
||||||
|
|
||||||
#PAPERLESS_REDIS=redis://localhost:6379
|
#PAPERLESS_REDIS=redis://localhost:6379
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "paperless-ngx"
|
name = "paperless-ngx"
|
||||||
version = "2.17.1"
|
version = "2.15.3"
|
||||||
description = "A community-supported supercharged version of paperless: scan, index and archive all your physical documents"
|
description = "A community-supported supercharged version of paperless: scan, index and archive all your physical documents"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
@ -25,12 +25,11 @@ dependencies = [
|
|||||||
# Only patch versions are guaranteed to not introduce breaking changes.
|
# Only patch versions are guaranteed to not introduce breaking changes.
|
||||||
"django~=5.1.7",
|
"django~=5.1.7",
|
||||||
"django-allauth[socialaccount,mfa]~=65.4.0",
|
"django-allauth[socialaccount,mfa]~=65.4.0",
|
||||||
"django-auditlog~=3.1.2",
|
"django-auditlog~=3.0.0",
|
||||||
"django-cachalot~=2.8.0",
|
"django-celery-results~=2.5.1",
|
||||||
"django-celery-results~=2.6.0",
|
|
||||||
"django-compression-middleware~=0.5.0",
|
"django-compression-middleware~=0.5.0",
|
||||||
"django-cors-headers~=4.7.0",
|
"django-cors-headers~=4.7.0",
|
||||||
"django-extensions~=4.1",
|
"django-extensions~=3.2.3",
|
||||||
"django-filter~=25.1",
|
"django-filter~=25.1",
|
||||||
"django-guardian~=2.4.0",
|
"django-guardian~=2.4.0",
|
||||||
"django-multiselectfield~=0.1.13",
|
"django-multiselectfield~=0.1.13",
|
||||||
@ -38,11 +37,11 @@ dependencies = [
|
|||||||
"djangorestframework~=3.15",
|
"djangorestframework~=3.15",
|
||||||
"djangorestframework-guardian~=0.3.0",
|
"djangorestframework-guardian~=0.3.0",
|
||||||
"drf-spectacular~=0.28",
|
"drf-spectacular~=0.28",
|
||||||
"drf-spectacular-sidecar~=2025.4.1",
|
"drf-spectacular-sidecar~=2025.3.1",
|
||||||
"drf-writable-nested~=0.7.1",
|
"drf-writable-nested~=0.7.1",
|
||||||
"filelock~=3.18.0",
|
"filelock~=3.17.0",
|
||||||
"flower~=2.0.1",
|
"flower~=2.0.1",
|
||||||
"gotenberg-client~=0.10.0",
|
"gotenberg-client~=0.9.0",
|
||||||
"httpx-oauth~=0.16",
|
"httpx-oauth~=0.16",
|
||||||
"imap-tools~=1.10.0",
|
"imap-tools~=1.10.0",
|
||||||
"inotifyrecursive~=0.3",
|
"inotifyrecursive~=0.3",
|
||||||
@ -53,12 +52,12 @@ dependencies = [
|
|||||||
"pathvalidate~=3.2.3",
|
"pathvalidate~=3.2.3",
|
||||||
"pdf2image~=1.17.0",
|
"pdf2image~=1.17.0",
|
||||||
"python-dateutil~=2.9.0",
|
"python-dateutil~=2.9.0",
|
||||||
"python-dotenv~=1.1.0",
|
"python-dotenv~=1.0.1",
|
||||||
"python-gnupg~=0.5.4",
|
"python-gnupg~=0.5.4",
|
||||||
"python-ipware~=3.0.0",
|
"python-ipware~=3.0.0",
|
||||||
"python-magic~=0.4.27",
|
"python-magic~=0.4.27",
|
||||||
"pyzbar~=0.1.9",
|
"pyzbar~=0.1.9",
|
||||||
"rapidfuzz~=3.13.0",
|
"rapidfuzz~=3.12.1",
|
||||||
"redis[hiredis]~=5.2.1",
|
"redis[hiredis]~=5.2.1",
|
||||||
"scikit-learn~=1.6.1",
|
"scikit-learn~=1.6.1",
|
||||||
"setproctitle~=1.3.4",
|
"setproctitle~=1.3.4",
|
||||||
@ -79,7 +78,7 @@ optional-dependencies.postgres = [
|
|||||||
"psycopg-c==3.2.5",
|
"psycopg-c==3.2.5",
|
||||||
]
|
]
|
||||||
optional-dependencies.webserver = [
|
optional-dependencies.webserver = [
|
||||||
"granian[uvloop]~=2.3.2",
|
"granian[uvloop]~=2.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
@ -222,9 +221,52 @@ lint.per-file-ignores."src/documents/parsers.py" = [
|
|||||||
lint.per-file-ignores."src/documents/signals/handlers.py" = [
|
lint.per-file-ignores."src/documents/signals/handlers.py" = [
|
||||||
"PTH",
|
"PTH",
|
||||||
] # TODO Enable & remove
|
] # 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_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/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/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_tesseract/tests/test_parser.py" = [
|
lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [
|
||||||
|
"PTH",
|
||||||
"RUF001",
|
"RUF001",
|
||||||
]
|
] # TODO PTH Enable & remove
|
||||||
lint.isort.force-single-line = true
|
lint.isort.force-single-line = true
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
|
@ -6,7 +6,6 @@ A document with an id of ${DOCUMENT_ID} was just consumed. I know the
|
|||||||
following additional information about it:
|
following additional information about it:
|
||||||
|
|
||||||
* Generated File Name: ${DOCUMENT_FILE_NAME}
|
* Generated File Name: ${DOCUMENT_FILE_NAME}
|
||||||
* Document type: ${DOCUMENT_TYPE}
|
|
||||||
* Archive Path: ${DOCUMENT_ARCHIVE_PATH}
|
* Archive Path: ${DOCUMENT_ARCHIVE_PATH}
|
||||||
* Source Path: ${DOCUMENT_SOURCE_PATH}
|
* Source Path: ${DOCUMENT_SOURCE_PATH}
|
||||||
* Created: ${DOCUMENT_CREATED}
|
* Created: ${DOCUMENT_CREATED}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
export const getDocument = jest.fn(() => ({
|
|
||||||
promise: Promise.resolve({ numPages: 3 }),
|
|
||||||
}))
|
|
||||||
|
|
||||||
export const GlobalWorkerOptions = { workerSrc: '' }
|
|
||||||
export const VerbosityLevel = { ERRORS: 0 }
|
|
||||||
|
|
||||||
globalThis.pdfjsLib = {
|
|
||||||
getDocument,
|
|
||||||
GlobalWorkerOptions,
|
|
||||||
VerbosityLevel,
|
|
||||||
AbortException: class AbortException extends Error {},
|
|
||||||
}
|
|
@ -27,7 +27,6 @@
|
|||||||
"el-GR": "src/locale/messages.el_GR.xlf",
|
"el-GR": "src/locale/messages.el_GR.xlf",
|
||||||
"en-GB": "src/locale/messages.en_GB.xlf",
|
"en-GB": "src/locale/messages.en_GB.xlf",
|
||||||
"es-ES": "src/locale/messages.es_ES.xlf",
|
"es-ES": "src/locale/messages.es_ES.xlf",
|
||||||
"fa-IR": "src/locale/messages.fa_IR.xlf",
|
|
||||||
"fi-FI": "src/locale/messages.fi_FI.xlf",
|
"fi-FI": "src/locale/messages.fi_FI.xlf",
|
||||||
"fr-FR": "src/locale/messages.fr_FR.xlf",
|
"fr-FR": "src/locale/messages.fr_FR.xlf",
|
||||||
"hu-HU": "src/locale/messages.hu_HU.xlf",
|
"hu-HU": "src/locale/messages.hu_HU.xlf",
|
||||||
@ -60,12 +59,10 @@
|
|||||||
"path": "./extra-webpack.config.ts"
|
"path": "./extra-webpack.config.ts"
|
||||||
},
|
},
|
||||||
"outputPath": "dist/paperless-ui",
|
"outputPath": "dist/paperless-ui",
|
||||||
"main": "src/main.ts",
|
|
||||||
"outputHashing": "none",
|
"outputHashing": "none",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
"polyfills": [
|
"main": "src/main.ts",
|
||||||
"src/polyfills.ts"
|
"polyfills": "src/polyfills.ts",
|
||||||
],
|
|
||||||
"tsConfig": "tsconfig.app.json",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"localize": true,
|
"localize": true,
|
||||||
"assets": [
|
"assets": [
|
||||||
@ -88,15 +85,12 @@
|
|||||||
"file-saver",
|
"file-saver",
|
||||||
"utif"
|
"utif"
|
||||||
],
|
],
|
||||||
|
"vendorChunk": true,
|
||||||
"extractLicenses": false,
|
"extractLicenses": false,
|
||||||
|
"buildOptimizer": false,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"optimization": false,
|
"optimization": false,
|
||||||
"namedChunks": true,
|
"namedChunks": true
|
||||||
"stylePreprocessorOptions": {
|
|
||||||
"includePaths": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@ -112,6 +106,8 @@
|
|||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"namedChunks": false,
|
"namedChunks": false,
|
||||||
"extractLicenses": true,
|
"extractLicenses": true,
|
||||||
|
"vendorChunk": false,
|
||||||
|
"buildOptimizer": true,
|
||||||
"budgets": [
|
"budgets": [
|
||||||
{
|
{
|
||||||
"type": "initial",
|
"type": "initial",
|
||||||
@ -191,30 +187,6 @@
|
|||||||
},
|
},
|
||||||
"@angular-eslint/schematics:library": {
|
"@angular-eslint/schematics:library": {
|
||||||
"setParserOptionsProject": true
|
"setParserOptionsProject": true
|
||||||
},
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"type": "component"
|
|
||||||
},
|
|
||||||
"@schematics/angular:directive": {
|
|
||||||
"type": "directive"
|
|
||||||
},
|
|
||||||
"@schematics/angular:service": {
|
|
||||||
"type": "service"
|
|
||||||
},
|
|
||||||
"@schematics/angular:guard": {
|
|
||||||
"typeSeparator": "."
|
|
||||||
},
|
|
||||||
"@schematics/angular:interceptor": {
|
|
||||||
"typeSeparator": "."
|
|
||||||
},
|
|
||||||
"@schematics/angular:module": {
|
|
||||||
"typeSeparator": "."
|
|
||||||
},
|
|
||||||
"@schematics/angular:pipe": {
|
|
||||||
"typeSeparator": "."
|
|
||||||
},
|
|
||||||
"@schematics/angular:resolver": {
|
|
||||||
"typeSeparator": "."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ module.exports = {
|
|||||||
'abstract-paperless-service',
|
'abstract-paperless-service',
|
||||||
],
|
],
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
`<rootDir>/node_modules/.pnpm/(?!.*\\.mjs$|lodash-es|@angular\\+common.*locales)`,
|
`<rootDir>/node_modules/.pnpm/(?!.*\\.mjs$|lodash-es)`,
|
||||||
],
|
],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'^src/(.*)': '<rootDir>/src/$1',
|
'^src/(.*)': '<rootDir>/src/$1',
|
||||||
|
1279
src-ui/messages.xlf
1279
src-ui/messages.xlf
File diff suppressed because it is too large
Load Diff
@ -1,74 +1,76 @@
|
|||||||
{
|
{
|
||||||
"name": "paperless-ngx-ui",
|
"name": "paperless-ngx-ui",
|
||||||
"version": "2.17.1",
|
"version": "2.15.3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"preinstall": "npx only-allow pnpm",
|
"preinstall": "npx only-allow pnpm",
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"test": "ng test --no-watch --coverage",
|
"test": "ng test --no-watch --coverage",
|
||||||
"lint": "ng lint"
|
"lint": "ng lint",
|
||||||
|
"postinstall": "patch-package"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/cdk": "^20.0.4",
|
"@angular/cdk": "^19.2.7",
|
||||||
"@angular/common": "~20.0.6",
|
"@angular/common": "~19.2.4",
|
||||||
"@angular/compiler": "~20.0.6",
|
"@angular/compiler": "~19.2.4",
|
||||||
"@angular/core": "~20.0.6",
|
"@angular/core": "~19.2.4",
|
||||||
"@angular/forms": "~20.0.6",
|
"@angular/forms": "~19.2.4",
|
||||||
"@angular/localize": "~20.0.6",
|
"@angular/localize": "~19.2.4",
|
||||||
"@angular/platform-browser": "~20.0.6",
|
"@angular/platform-browser": "~19.2.4",
|
||||||
"@angular/platform-browser-dynamic": "~20.0.6",
|
"@angular/platform-browser-dynamic": "~19.2.4",
|
||||||
"@angular/router": "~20.0.6",
|
"@angular/router": "~19.2.4",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^19.0.1",
|
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
|
||||||
"@ng-select/ng-select": "^15.1.3",
|
"@ng-select/ng-select": "^14.2.6",
|
||||||
"@ngneat/dirty-check-forms": "^3.0.3",
|
"@ngneat/dirty-check-forms": "^3.0.3",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"bootstrap": "^5.3.7",
|
"bootstrap": "^5.3.3",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"mime-names": "^1.0.0",
|
"mime-names": "^1.0.0",
|
||||||
"ng2-pdf-viewer": "^10.4.0",
|
"ng2-pdf-viewer": "^10.4.0",
|
||||||
"ngx-bootstrap-icons": "^1.9.3",
|
"ngx-bootstrap-icons": "^1.9.3",
|
||||||
"ngx-color": "^10.0.0",
|
"ngx-color": "^10.0.0",
|
||||||
"ngx-cookie-service": "^20.0.1",
|
"ngx-cookie-service": "^19.1.2",
|
||||||
"ngx-device-detector": "^10.0.2",
|
"ngx-device-detector": "^9.0.0",
|
||||||
"ngx-ui-tour-ng-bootstrap": "^17.0.0",
|
"ngx-file-drop": "^16.0.0",
|
||||||
|
"ngx-ui-tour-ng-bootstrap": "^16.0.0",
|
||||||
"rxjs": "^7.8.2",
|
"rxjs": "^7.8.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"utif": "^3.1.0",
|
"utif": "^3.1.0",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"zone.js": "^0.15.1"
|
"zone.js": "^0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-builders/custom-webpack": "^20.0.0",
|
"@angular-builders/custom-webpack": "^19.0.0",
|
||||||
"@angular-builders/jest": "^20.0.0",
|
"@angular-builders/jest": "^19.0.0",
|
||||||
"@angular-devkit/core": "^20.0.4",
|
"@angular-devkit/build-angular": "^19.2.5",
|
||||||
"@angular-devkit/schematics": "^20.0.4",
|
"@angular-devkit/core": "^19.2.5",
|
||||||
"@angular-eslint/builder": "20.1.1",
|
"@angular-devkit/schematics": "^19.2.5",
|
||||||
"@angular-eslint/eslint-plugin": "20.1.1",
|
"@angular-eslint/builder": "19.3.0",
|
||||||
"@angular-eslint/eslint-plugin-template": "20.1.1",
|
"@angular-eslint/eslint-plugin": "19.3.0",
|
||||||
"@angular-eslint/schematics": "20.1.1",
|
"@angular-eslint/eslint-plugin-template": "19.3.0",
|
||||||
"@angular-eslint/template-parser": "20.1.1",
|
"@angular-eslint/schematics": "19.3.0",
|
||||||
"@angular/build": "^20.0.4",
|
"@angular-eslint/template-parser": "19.3.0",
|
||||||
"@angular/cli": "~20.0.4",
|
"@angular/cli": "~19.2.5",
|
||||||
"@angular/compiler-cli": "~20.0.6",
|
"@angular/compiler-cli": "~19.2.4",
|
||||||
"@codecov/webpack-plugin": "^1.9.1",
|
"@codecov/webpack-plugin": "^1.9.0",
|
||||||
"@playwright/test": "^1.53.2",
|
"@playwright/test": "^1.51.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^24.0.10",
|
"@types/node": "^22.13.17",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.35.1",
|
"@typescript-eslint/eslint-plugin": "^8.29.0",
|
||||||
"@typescript-eslint/parser": "^8.35.1",
|
"@typescript-eslint/parser": "^8.29.0",
|
||||||
"@typescript-eslint/utils": "^8.35.1",
|
"@typescript-eslint/utils": "^8.29.0",
|
||||||
"eslint": "^9.30.1",
|
"eslint": "^9.23.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-junit": "^16.0.0",
|
||||||
"jest-preset-angular": "^14.5.5",
|
"jest-preset-angular": "^14.5.4",
|
||||||
"jest-websocket-mock": "^2.5.0",
|
"jest-websocket-mock": "^2.5.0",
|
||||||
|
"patch-package": "^8.0.0",
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
"prettier-plugin-organize-imports": "^4.1.0",
|
||||||
"ts-node": "~10.9.1",
|
"ts-node": "~10.9.1",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.5.4"
|
||||||
"webpack": "^5.99.9"
|
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
@ -78,5 +80,6 @@
|
|||||||
"lmdb",
|
"lmdb",
|
||||||
"msgpackr-extract"
|
"msgpackr-extract"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"typings": "./src/typings.d.ts"
|
||||||
}
|
}
|
||||||
|
206
src-ui/patches/ngx-file-drop+16.0.0.patch
Normal file
206
src-ui/patches/ngx-file-drop+16.0.0.patch
Normal file
File diff suppressed because one or more lines are too long
5706
src-ui/pnpm-lock.yaml
generated
5706
src-ui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -20,7 +20,6 @@ import localeDe from '@angular/common/locales/de'
|
|||||||
import localeEl from '@angular/common/locales/el'
|
import localeEl from '@angular/common/locales/el'
|
||||||
import localeEnGb from '@angular/common/locales/en-GB'
|
import localeEnGb from '@angular/common/locales/en-GB'
|
||||||
import localeEs from '@angular/common/locales/es'
|
import localeEs from '@angular/common/locales/es'
|
||||||
import localeFa from '@angular/common/locales/fa'
|
|
||||||
import localeFi from '@angular/common/locales/fi'
|
import localeFi from '@angular/common/locales/fi'
|
||||||
import localeFr from '@angular/common/locales/fr'
|
import localeFr from '@angular/common/locales/fr'
|
||||||
import localeHu from '@angular/common/locales/hu'
|
import localeHu from '@angular/common/locales/hu'
|
||||||
@ -54,7 +53,6 @@ registerLocaleData(localeDe)
|
|||||||
registerLocaleData(localeEl)
|
registerLocaleData(localeEl)
|
||||||
registerLocaleData(localeEnGb)
|
registerLocaleData(localeEnGb)
|
||||||
registerLocaleData(localeEs)
|
registerLocaleData(localeEs)
|
||||||
registerLocaleData(localeFa)
|
|
||||||
registerLocaleData(localeFi)
|
registerLocaleData(localeFi)
|
||||||
registerLocaleData(localeFr)
|
registerLocaleData(localeFr)
|
||||||
registerLocaleData(localeHu)
|
registerLocaleData(localeHu)
|
||||||
@ -123,4 +121,19 @@ HTMLCanvasElement.prototype.getContext = <
|
|||||||
typeof HTMLCanvasElement.prototype.getContext
|
typeof HTMLCanvasElement.prototype.getContext
|
||||||
>jest.fn()
|
>jest.fn()
|
||||||
|
|
||||||
jest.mock('pdfjs-dist')
|
// pdfjs
|
||||||
|
jest.mock('pdfjs-dist', () => ({
|
||||||
|
getDocument: jest.fn(() => ({
|
||||||
|
promise: Promise.resolve({ numPages: 3 }),
|
||||||
|
})),
|
||||||
|
GlobalWorkerOptions: { workerSrc: '' },
|
||||||
|
VerbosityLevel: { ERRORS: 0 },
|
||||||
|
globalThis: {
|
||||||
|
pdfjsLib: {
|
||||||
|
GlobalWorkerOptions: {
|
||||||
|
workerSrc: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
jest.mock('pdfjs-dist/web/pdf_viewer', () => ({}))
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
import { Router, RouterModule } from '@angular/router'
|
import { Router, RouterModule } from '@angular/router'
|
||||||
import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { allIcons, NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { allIcons, NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
|
import { NgxFileDropModule } from 'ngx-file-drop'
|
||||||
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
|
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { routes } from './app-routing.module'
|
import { routes } from './app-routing.module'
|
||||||
@ -42,6 +43,7 @@ describe('AppComponent', () => {
|
|||||||
imports: [
|
imports: [
|
||||||
TourNgBootstrapModule,
|
TourNgBootstrapModule,
|
||||||
RouterModule.forRoot(routes),
|
RouterModule.forRoot(routes),
|
||||||
|
NgxFileDropModule,
|
||||||
NgbModalModule,
|
NgbModalModule,
|
||||||
AppComponent,
|
AppComponent,
|
||||||
ToastsComponent,
|
ToastsComponent,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, inject, OnDestroy, OnInit, Renderer2 } from '@angular/core'
|
import { Component, OnDestroy, OnInit, Renderer2 } from '@angular/core'
|
||||||
import { Router, RouterOutlet } from '@angular/router'
|
import { Router, RouterOutlet } from '@angular/router'
|
||||||
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
|
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
|
||||||
import { first, Subscription } from 'rxjs'
|
import { first, Subscription } from 'rxjs'
|
||||||
@ -29,22 +29,22 @@ import { WebsocketStatusService } from './services/websocket-status.service'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, OnDestroy {
|
export class AppComponent implements OnInit, OnDestroy {
|
||||||
private settings = inject(SettingsService)
|
|
||||||
private websocketStatusService = inject(WebsocketStatusService)
|
|
||||||
private toastService = inject(ToastService)
|
|
||||||
private router = inject(Router)
|
|
||||||
private tasksService = inject(TasksService)
|
|
||||||
tourService = inject(TourService)
|
|
||||||
private renderer = inject(Renderer2)
|
|
||||||
private permissionsService = inject(PermissionsService)
|
|
||||||
private hotKeyService = inject(HotKeyService)
|
|
||||||
private componentRouterService = inject(ComponentRouterService)
|
|
||||||
|
|
||||||
newDocumentSubscription: Subscription
|
newDocumentSubscription: Subscription
|
||||||
successSubscription: Subscription
|
successSubscription: Subscription
|
||||||
failedSubscription: Subscription
|
failedSubscription: Subscription
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private settings: SettingsService,
|
||||||
|
private websocketStatusService: WebsocketStatusService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private router: Router,
|
||||||
|
private tasksService: TasksService,
|
||||||
|
public tourService: TourService,
|
||||||
|
private renderer: Renderer2,
|
||||||
|
private permissionsService: PermissionsService,
|
||||||
|
private hotKeyService: HotKeyService,
|
||||||
|
private componentRouterService: ComponentRouterService
|
||||||
|
) {
|
||||||
let anyWindow = window as any
|
let anyWindow = window as any
|
||||||
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.mjs'
|
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.mjs'
|
||||||
this.settings.updateAppearanceSettings()
|
this.settings.updateAppearanceSettings()
|
||||||
|
@ -105,9 +105,9 @@ describe('ConfigComponent', () => {
|
|||||||
|
|
||||||
it('should support JSON validation for e.g. user_args', () => {
|
it('should support JSON validation for e.g. user_args', () => {
|
||||||
component.configForm.patchValue({ user_args: '{ foo bar }' })
|
component.configForm.patchValue({ user_args: '{ foo bar }' })
|
||||||
expect(component.errors['user_args']).toEqual('Invalid JSON')
|
expect(component.errors).toEqual({ user_args: 'Invalid JSON' })
|
||||||
component.configForm.patchValue({ user_args: '{ "foo": "bar" }' })
|
component.configForm.patchValue({ user_args: '{ "foo": "bar" }' })
|
||||||
expect(component.errors['user_args']).toBeNull()
|
expect(component.errors).toEqual({ user_args: null })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should upload file, show error if necessary', () => {
|
it('should upload file, show error if necessary', () => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { AsyncPipe } from '@angular/common'
|
import { AsyncPipe } from '@angular/common'
|
||||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
|
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
AbstractControl,
|
AbstractControl,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -57,10 +57,6 @@ export class ConfigComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit, OnDestroy, DirtyComponent
|
implements OnInit, OnDestroy, DirtyComponent
|
||||||
{
|
{
|
||||||
private configService = inject(ConfigService)
|
|
||||||
private toastService = inject(ToastService)
|
|
||||||
private settingsService = inject(SettingsService)
|
|
||||||
|
|
||||||
public readonly ConfigOptionType = ConfigOptionType
|
public readonly ConfigOptionType = ConfigOptionType
|
||||||
|
|
||||||
// generated dynamically
|
// generated dynamically
|
||||||
@ -81,7 +77,11 @@ export class ConfigComponent
|
|||||||
storeSub: Subscription
|
storeSub: Subscription
|
||||||
isDirty$: Observable<boolean>
|
isDirty$: Observable<boolean>
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private configService: ConfigService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private settingsService: SettingsService
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
this.configForm.addControl('id', new FormControl())
|
this.configForm.addControl('id', new FormControl())
|
||||||
PaperlessConfigOptions.forEach((option) => {
|
PaperlessConfigOptions.forEach((option) => {
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
inject,
|
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
@ -29,8 +28,12 @@ export class LogsComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
private logService = inject(LogService)
|
constructor(
|
||||||
private changedetectorRef = inject(ChangeDetectorRef)
|
private logService: LogService,
|
||||||
|
private changedetectorRef: ChangeDetectorRef
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
public logs: string[] = []
|
public logs: string[] = []
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ import { AsyncPipe, ViewportScroller } from '@angular/common'
|
|||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
Component,
|
Component,
|
||||||
|
Inject,
|
||||||
LOCALE_ID,
|
LOCALE_ID,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
inject,
|
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -104,20 +104,6 @@ export class SettingsComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, AfterViewInit, OnDestroy, DirtyComponent
|
implements OnInit, AfterViewInit, OnDestroy, DirtyComponent
|
||||||
{
|
{
|
||||||
private documentListViewService = inject(DocumentListViewService)
|
|
||||||
private toastService = inject(ToastService)
|
|
||||||
private settings = inject(SettingsService)
|
|
||||||
currentLocale = inject(LOCALE_ID)
|
|
||||||
private viewportScroller = inject(ViewportScroller)
|
|
||||||
private activatedRoute = inject(ActivatedRoute)
|
|
||||||
readonly tourService = inject(TourService)
|
|
||||||
private usersService = inject(UserService)
|
|
||||||
private groupsService = inject(GroupService)
|
|
||||||
private router = inject(Router)
|
|
||||||
permissionsService = inject(PermissionsService)
|
|
||||||
private modalService = inject(NgbModal)
|
|
||||||
private systemStatusService = inject(SystemStatusService)
|
|
||||||
|
|
||||||
activeNavID: number
|
activeNavID: number
|
||||||
|
|
||||||
settingsForm = new FormGroup({
|
settingsForm = new FormGroup({
|
||||||
@ -193,7 +179,21 @@ export class SettingsComponent
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private documentListViewService: DocumentListViewService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private settings: SettingsService,
|
||||||
|
@Inject(LOCALE_ID) public currentLocale: string,
|
||||||
|
private viewportScroller: ViewportScroller,
|
||||||
|
private activatedRoute: ActivatedRoute,
|
||||||
|
public readonly tourService: TourService,
|
||||||
|
private usersService: UserService,
|
||||||
|
private groupsService: GroupService,
|
||||||
|
private router: Router,
|
||||||
|
public permissionsService: PermissionsService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
private systemStatusService: SystemStatusService
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
this.settings.settingsSaved.subscribe(() => {
|
this.settings.settingsSaved.subscribe(() => {
|
||||||
if (!this.savePending) this.initialize()
|
if (!this.savePending) this.initialize()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { NgTemplateOutlet, SlicePipe } from '@angular/common'
|
import { NgTemplateOutlet, SlicePipe } from '@angular/common'
|
||||||
import { Component, inject, OnDestroy, OnInit } from '@angular/core'
|
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import {
|
import {
|
||||||
@ -69,10 +69,6 @@ export class TasksComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
tasksService = inject(TasksService)
|
|
||||||
private modalService = inject(NgbModal)
|
|
||||||
private readonly router = inject(Router)
|
|
||||||
|
|
||||||
public activeTab: TaskTab
|
public activeTab: TaskTab
|
||||||
public selectedTasks: Set<number> = new Set()
|
public selectedTasks: Set<number> = new Set()
|
||||||
public togggleAll: boolean = false
|
public togggleAll: boolean = false
|
||||||
@ -109,6 +105,14 @@ export class TasksComponent
|
|||||||
: $localize`Dismiss all`
|
: $localize`Dismiss all`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public tasksService: TasksService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
private readonly router: Router
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.tasksService.reload()
|
this.tasksService.reload()
|
||||||
timer(5000, 5000)
|
timer(5000, 5000)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnDestroy, inject } from '@angular/core'
|
import { Component, OnDestroy } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
import {
|
import {
|
||||||
@ -36,19 +36,19 @@ export class TrashComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnDestroy
|
implements OnDestroy
|
||||||
{
|
{
|
||||||
private trashService = inject(TrashService)
|
|
||||||
private toastService = inject(ToastService)
|
|
||||||
private modalService = inject(NgbModal)
|
|
||||||
private settingsService = inject(SettingsService)
|
|
||||||
private router = inject(Router)
|
|
||||||
|
|
||||||
public documentsInTrash: Document[] = []
|
public documentsInTrash: Document[] = []
|
||||||
public selectedDocuments: Set<number> = new Set()
|
public selectedDocuments: Set<number> = new Set()
|
||||||
public allToggled: boolean = false
|
public allToggled: boolean = false
|
||||||
public page: number = 1
|
public page: number = 1
|
||||||
public totalDocuments: number
|
public totalDocuments: number
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private trashService: TrashService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
private settingsService: SettingsService,
|
||||||
|
private router: Router
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
this.reload()
|
this.reload()
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
|
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { Subject, first, takeUntil } from 'rxjs'
|
import { Subject, first, takeUntil } from 'rxjs'
|
||||||
@ -31,18 +31,22 @@ export class UsersAndGroupsComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
private usersService = inject(UserService)
|
|
||||||
private groupsService = inject(GroupService)
|
|
||||||
private toastService = inject(ToastService)
|
|
||||||
private modalService = inject(NgbModal)
|
|
||||||
permissionsService = inject(PermissionsService)
|
|
||||||
private settings = inject(SettingsService)
|
|
||||||
|
|
||||||
users: User[]
|
users: User[]
|
||||||
groups: Group[]
|
groups: Group[]
|
||||||
|
|
||||||
unsubscribeNotifier: Subject<any> = new Subject()
|
unsubscribeNotifier: Subject<any> = new Subject()
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private usersService: UserService,
|
||||||
|
private groupsService: GroupService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
public permissionsService: PermissionsService,
|
||||||
|
private settings: SettingsService
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.usersService
|
this.usersService
|
||||||
.listAll(null, null, { full_perms: true })
|
.listAll(null, null, { full_perms: true })
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
moveItemInArray,
|
moveItemInArray,
|
||||||
} from '@angular/cdk/drag-drop'
|
} from '@angular/cdk/drag-drop'
|
||||||
import { NgClass } from '@angular/common'
|
import { NgClass } from '@angular/common'
|
||||||
import { Component, HostListener, inject, OnInit } from '@angular/core'
|
import { Component, HostListener, OnInit } from '@angular/core'
|
||||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router'
|
import { ActivatedRoute, Router, RouterModule } from '@angular/router'
|
||||||
import {
|
import {
|
||||||
NgbCollapseModule,
|
NgbCollapseModule,
|
||||||
@ -74,27 +74,27 @@ export class AppFrameComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, ComponentCanDeactivate
|
implements OnInit, ComponentCanDeactivate
|
||||||
{
|
{
|
||||||
router = inject(Router)
|
versionString = `${environment.appTitle} ${environment.version}`
|
||||||
private activatedRoute = inject(ActivatedRoute)
|
|
||||||
private openDocumentsService = inject(OpenDocumentsService)
|
|
||||||
savedViewService = inject(SavedViewService)
|
|
||||||
private remoteVersionService = inject(RemoteVersionService)
|
|
||||||
settingsService = inject(SettingsService)
|
|
||||||
tasksService = inject(TasksService)
|
|
||||||
private readonly toastService = inject(ToastService)
|
|
||||||
private modalService = inject(NgbModal)
|
|
||||||
permissionsService = inject(PermissionsService)
|
|
||||||
private djangoMessagesService = inject(DjangoMessagesService)
|
|
||||||
|
|
||||||
appRemoteVersion: AppRemoteVersion
|
appRemoteVersion: AppRemoteVersion
|
||||||
|
|
||||||
isMenuCollapsed: boolean = true
|
isMenuCollapsed: boolean = true
|
||||||
|
|
||||||
slimSidebarAnimating: boolean = false
|
slimSidebarAnimating: boolean = false
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
public router: Router,
|
||||||
|
private activatedRoute: ActivatedRoute,
|
||||||
|
private openDocumentsService: OpenDocumentsService,
|
||||||
|
public savedViewService: SavedViewService,
|
||||||
|
private remoteVersionService: RemoteVersionService,
|
||||||
|
public settingsService: SettingsService,
|
||||||
|
public tasksService: TasksService,
|
||||||
|
private readonly toastService: ToastService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
public permissionsService: PermissionsService,
|
||||||
|
private djangoMessagesService: DjangoMessagesService
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
const permissionsService = this.permissionsService
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
permissionsService.currentUserCan(
|
permissionsService.currentUserCan(
|
||||||
@ -142,10 +142,6 @@ export class AppFrameComponent
|
|||||||
}, 200) // slightly longer than css animation for slim sidebar
|
}, 200) // slightly longer than css animation for slim sidebar
|
||||||
}
|
}
|
||||||
|
|
||||||
get versionString(): string {
|
|
||||||
return `${environment.appTitle} v${this.settingsService.get(SETTINGS_KEYS.VERSION)}${environment.production ? '' : ` #${environment.tag}`}`
|
|
||||||
}
|
|
||||||
|
|
||||||
get customAppTitle(): string {
|
get customAppTitle(): string {
|
||||||
return this.settingsService.get(SETTINGS_KEYS.APP_TITLE)
|
return this.settingsService.get(SETTINGS_KEYS.APP_TITLE)
|
||||||
}
|
}
|
||||||
|
@ -405,7 +405,7 @@ describe('GlobalSearchComponent', () => {
|
|||||||
expect(toastErrorSpy).toHaveBeenCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
|
|
||||||
// succeed
|
// succeed
|
||||||
editDialog.succeeded.emit(object as any)
|
editDialog.succeeded.emit(true)
|
||||||
expect(toastInfoSpy).toHaveBeenCalled()
|
expect(toastInfoSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -456,7 +456,7 @@ describe('GlobalSearchComponent', () => {
|
|||||||
expect(toastErrorSpy).toHaveBeenCalled()
|
expect(toastErrorSpy).toHaveBeenCalled()
|
||||||
|
|
||||||
// succeed
|
// succeed
|
||||||
editDialog.succeeded.emit(searchResults.tags[0] as any)
|
editDialog.succeeded.emit(true)
|
||||||
expect(toastInfoSpy).toHaveBeenCalled()
|
expect(toastInfoSpy).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -529,17 +529,6 @@ describe('GlobalSearchComponent', () => {
|
|||||||
expect(dispatchSpy).toHaveBeenCalledTimes(2) // once for keydown, second for click
|
expect(dispatchSpy).toHaveBeenCalledTimes(2) // once for keydown, second for click
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should support using base href in navigateOrOpenInNewWindow', () => {
|
|
||||||
jest
|
|
||||||
.spyOn(component['locationStrategy'], 'getBaseHref')
|
|
||||||
.mockReturnValue('/base/')
|
|
||||||
const openSpy = jest.spyOn(window, 'open')
|
|
||||||
const event = new Event('click')
|
|
||||||
event['ctrlKey'] = true
|
|
||||||
component.primaryAction(DataType.Document, { id: 1 }, event as any)
|
|
||||||
expect(openSpy).toHaveBeenCalledWith('/base/documents/1', '_blank')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should support title content search and advanced search', () => {
|
it('should support title content search and advanced search', () => {
|
||||||
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
|
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
|
||||||
component.query = 'test'
|
component.query = 'test'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { LocationStrategy, NgTemplateOutlet } from '@angular/common'
|
import { NgTemplateOutlet } from '@angular/common'
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
@ -6,7 +6,6 @@ import {
|
|||||||
QueryList,
|
QueryList,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
inject,
|
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { Router } from '@angular/router'
|
import { Router } from '@angular/router'
|
||||||
@ -70,17 +69,6 @@ import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-e
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GlobalSearchComponent implements OnInit {
|
export class GlobalSearchComponent implements OnInit {
|
||||||
searchService = inject(SearchService)
|
|
||||||
private router = inject(Router)
|
|
||||||
private modalService = inject(NgbModal)
|
|
||||||
private documentService = inject(DocumentService)
|
|
||||||
private documentListViewService = inject(DocumentListViewService)
|
|
||||||
private permissionsService = inject(PermissionsService)
|
|
||||||
private toastService = inject(ToastService)
|
|
||||||
private hotkeyService = inject(HotKeyService)
|
|
||||||
private settingsService = inject(SettingsService)
|
|
||||||
private locationStrategy = inject(LocationStrategy)
|
|
||||||
|
|
||||||
public DataType = DataType
|
public DataType = DataType
|
||||||
public query: string
|
public query: string
|
||||||
public queryDebounce: Subject<string>
|
public queryDebounce: Subject<string>
|
||||||
@ -102,7 +90,17 @@ export class GlobalSearchComponent implements OnInit {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
public searchService: SearchService,
|
||||||
|
private router: Router,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
private documentService: DocumentService,
|
||||||
|
private documentListViewService: DocumentListViewService,
|
||||||
|
private permissionsService: PermissionsService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private hotkeyService: HotKeyService,
|
||||||
|
private settingsService: SettingsService
|
||||||
|
) {
|
||||||
this.queryDebounce = new Subject<string>()
|
this.queryDebounce = new Subject<string>()
|
||||||
|
|
||||||
this.queryDebounce
|
this.queryDebounce
|
||||||
@ -423,13 +421,10 @@ export class GlobalSearchComponent implements OnInit {
|
|||||||
extras: Object = {}
|
extras: Object = {}
|
||||||
) {
|
) {
|
||||||
if (newWindow) {
|
if (newWindow) {
|
||||||
const serializedUrl = this.router.serializeUrl(
|
const url = this.router.serializeUrl(
|
||||||
this.router.createUrlTree(commands, extras)
|
this.router.createUrlTree(commands, extras)
|
||||||
)
|
)
|
||||||
const baseHref = this.locationStrategy.getBaseHref()
|
window.open(url, '_blank')
|
||||||
const fullUrl =
|
|
||||||
baseHref.replace(/\/+$/, '') + '/' + serializedUrl.replace(/^\/+/, '')
|
|
||||||
window.open(fullUrl, '_blank')
|
|
||||||
} else {
|
} else {
|
||||||
this.router.navigate(commands, extras)
|
this.router.navigate(commands, extras)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
|
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
NgbDropdownModule,
|
NgbDropdownModule,
|
||||||
NgbProgressbarModule,
|
NgbProgressbarModule,
|
||||||
@ -20,7 +20,7 @@ import { ToastComponent } from '../../common/toast/toast.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ToastsDropdownComponent implements OnInit, OnDestroy {
|
export class ToastsDropdownComponent implements OnInit, OnDestroy {
|
||||||
toastService = inject(ToastService)
|
constructor(public toastService: ToastService) {}
|
||||||
|
|
||||||
private subscription: Subscription
|
private subscription: Subscription
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { DecimalPipe } from '@angular/common'
|
import { DecimalPipe } from '@angular/common'
|
||||||
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
|
import { Component, EventEmitter, Input, Output } from '@angular/core'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||||
@ -12,7 +12,9 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
|
|||||||
imports: [DecimalPipe, SafeHtmlPipe],
|
imports: [DecimalPipe, SafeHtmlPipe],
|
||||||
})
|
})
|
||||||
export class ConfirmDialogComponent extends LoadingComponentWithPermissions {
|
export class ConfirmDialogComponent extends LoadingComponentWithPermissions {
|
||||||
activeModal = inject(NgbActiveModal)
|
constructor(public activeModal: NgbActiveModal) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
public confirmClicked = new EventEmitter()
|
public confirmClicked = new EventEmitter()
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Component, TemplateRef, ViewChild, inject } from '@angular/core'
|
import { Component, TemplateRef, ViewChild } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import {
|
import {
|
||||||
PDFDocumentProxy,
|
PDFDocumentProxy,
|
||||||
PdfViewerComponent,
|
PdfViewerComponent,
|
||||||
@ -16,8 +17,6 @@ import { ConfirmDialogComponent } from '../confirm-dialog.component'
|
|||||||
imports: [PdfViewerModule, FormsModule, ReactiveFormsModule, SafeHtmlPipe],
|
imports: [PdfViewerModule, FormsModule, ReactiveFormsModule, SafeHtmlPipe],
|
||||||
})
|
})
|
||||||
export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
|
export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
|
||||||
private documentService = inject(DocumentService)
|
|
||||||
|
|
||||||
public documentID: number
|
public documentID: number
|
||||||
public pages: number[] = []
|
public pages: number[] = []
|
||||||
public currentPage: number = 1
|
public currentPage: number = 1
|
||||||
@ -35,8 +34,11 @@ export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
|
|||||||
return this.documentService.getPreviewUrl(this.documentID)
|
return this.documentService.getPreviewUrl(this.documentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
activeModal: NgbActiveModal,
|
||||||
|
private documentService: DocumentService
|
||||||
|
) {
|
||||||
|
super(activeModal)
|
||||||
}
|
}
|
||||||
|
|
||||||
public pdfPreviewLoaded(pdf: PDFDocumentProxy) {
|
public pdfPreviewLoaded(pdf: PDFDocumentProxy) {
|
||||||
|
@ -3,8 +3,9 @@ import {
|
|||||||
DragDropModule,
|
DragDropModule,
|
||||||
moveItemInArray,
|
moveItemInArray,
|
||||||
} from '@angular/cdk/drag-drop'
|
} from '@angular/cdk/drag-drop'
|
||||||
import { Component, OnInit, inject } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { takeUntil } from 'rxjs'
|
import { takeUntil } from 'rxjs'
|
||||||
import { Document } from 'src/app/data/document'
|
import { Document } from 'src/app/data/document'
|
||||||
@ -27,9 +28,6 @@ export class MergeConfirmDialogComponent
|
|||||||
extends ConfirmDialogComponent
|
extends ConfirmDialogComponent
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
private documentService = inject(DocumentService)
|
|
||||||
private permissionService = inject(PermissionsService)
|
|
||||||
|
|
||||||
public documentIDs: number[] = []
|
public documentIDs: number[] = []
|
||||||
public archiveFallback: boolean = false
|
public archiveFallback: boolean = false
|
||||||
public deleteOriginals: boolean = false
|
public deleteOriginals: boolean = false
|
||||||
@ -40,8 +38,12 @@ export class MergeConfirmDialogComponent
|
|||||||
|
|
||||||
public metadataDocumentID: number = -1
|
public metadataDocumentID: number = -1
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
activeModal: NgbActiveModal,
|
||||||
|
private documentService: DocumentService,
|
||||||
|
private permissionService: PermissionsService
|
||||||
|
) {
|
||||||
|
super(activeModal)
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { NgStyle } from '@angular/common'
|
import { NgStyle } from '@angular/common'
|
||||||
import { Component, inject } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||||
import { DocumentService } from 'src/app/services/rest/document.service'
|
import { DocumentService } from 'src/app/services/rest/document.service'
|
||||||
@ -12,8 +13,6 @@ import { ConfirmDialogComponent } from '../confirm-dialog.component'
|
|||||||
imports: [NgStyle, NgxBootstrapIconsModule, SafeHtmlPipe],
|
imports: [NgStyle, NgxBootstrapIconsModule, SafeHtmlPipe],
|
||||||
})
|
})
|
||||||
export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
|
export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
|
||||||
documentService = inject(DocumentService)
|
|
||||||
|
|
||||||
public documentID: number
|
public documentID: number
|
||||||
public showPDFNote: boolean = true
|
public showPDFNote: boolean = true
|
||||||
|
|
||||||
@ -26,8 +25,11 @@ export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
|
|||||||
return degrees
|
return degrees
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
activeModal: NgbActiveModal,
|
||||||
|
public documentService: DocumentService
|
||||||
|
) {
|
||||||
|
super(activeModal)
|
||||||
}
|
}
|
||||||
|
|
||||||
rotate(clockwise: boolean = true) {
|
rotate(clockwise: boolean = true) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Component, OnInit, inject } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer'
|
import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { Document } from 'src/app/data/document'
|
import { Document } from 'src/app/data/document'
|
||||||
@ -22,9 +23,6 @@ export class SplitConfirmDialogComponent
|
|||||||
extends ConfirmDialogComponent
|
extends ConfirmDialogComponent
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
private documentService = inject(DocumentService)
|
|
||||||
private permissionService = inject(PermissionsService)
|
|
||||||
|
|
||||||
public get pagesString(): string {
|
public get pagesString(): string {
|
||||||
let pagesStr = ''
|
let pagesStr = ''
|
||||||
|
|
||||||
@ -64,8 +62,12 @@ export class SplitConfirmDialogComponent
|
|||||||
return this.documentService.getPreviewUrl(this.documentID)
|
return this.documentService.getPreviewUrl(this.documentID)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
activeModal: NgbActiveModal,
|
||||||
|
private documentService: DocumentService,
|
||||||
|
private permissionService: PermissionsService
|
||||||
|
) {
|
||||||
|
super(activeModal)
|
||||||
this.confirmButtonEnabled = this.pages.size > 0
|
this.confirmButtonEnabled = this.pages.size > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
|
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
|
||||||
import { Component, Input, LOCALE_ID, OnInit, inject } from '@angular/core'
|
import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'
|
||||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { takeUntil } from 'rxjs'
|
import { takeUntil } from 'rxjs'
|
||||||
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
|
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
|
||||||
@ -20,9 +20,6 @@ export class CustomFieldDisplayComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
private customFieldService = inject(CustomFieldsService)
|
|
||||||
private documentService = inject(DocumentService)
|
|
||||||
|
|
||||||
CustomFieldDataType = CustomFieldDataType
|
CustomFieldDataType = CustomFieldDataType
|
||||||
|
|
||||||
private _document: Document
|
private _document: Document
|
||||||
@ -66,9 +63,11 @@ export class CustomFieldDisplayComponent
|
|||||||
|
|
||||||
private defaultCurrencyCode: any
|
private defaultCurrencyCode: any
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
const currentLocale = inject(LOCALE_ID)
|
private customFieldService: CustomFieldsService,
|
||||||
|
private documentService: DocumentService,
|
||||||
|
@Inject(LOCALE_ID) currentLocale: string
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
this.defaultCurrencyCode = getLocaleCurrencyCode(currentLocale)
|
this.defaultCurrencyCode = getLocaleCurrencyCode(currentLocale)
|
||||||
this.customFieldService.listAll().subscribe((r) => {
|
this.customFieldService.listAll().subscribe((r) => {
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
QueryList,
|
QueryList,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
inject,
|
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
@ -38,11 +37,6 @@ import { CustomFieldEditDialogComponent } from '../edit-dialog/custom-field-edit
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissions {
|
export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissions {
|
||||||
private customFieldsService = inject(CustomFieldsService)
|
|
||||||
private modalService = inject(NgbModal)
|
|
||||||
private toastService = inject(ToastService)
|
|
||||||
private permissionsService = inject(PermissionsService)
|
|
||||||
|
|
||||||
public popperOptions = pngxPopperOptions
|
public popperOptions = pngxPopperOptions
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
@ -84,7 +78,12 @@ export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissio
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private customFieldsService: CustomFieldsService,
|
||||||
|
private modalService: NgbModal,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private permissionsService: PermissionsService
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
this.getFields()
|
this.getFields()
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import { NgTemplateOutlet } from '@angular/common'
|
|||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
inject,
|
|
||||||
Input,
|
Input,
|
||||||
Output,
|
Output,
|
||||||
QueryList,
|
QueryList,
|
||||||
@ -179,8 +178,6 @@ export class CustomFieldQueriesModel {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPermissions {
|
export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPermissions {
|
||||||
protected customFieldsService = inject(CustomFieldsService)
|
|
||||||
|
|
||||||
public CustomFieldQueryComponentType = CustomFieldQueryElementType
|
public CustomFieldQueryComponentType = CustomFieldQueryElementType
|
||||||
public CustomFieldQueryOperator = CustomFieldQueryOperator
|
public CustomFieldQueryOperator = CustomFieldQueryOperator
|
||||||
public CustomFieldDataType = CustomFieldDataType
|
public CustomFieldDataType = CustomFieldDataType
|
||||||
@ -248,7 +245,7 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm
|
|||||||
|
|
||||||
public readonly today: string = new Date().toISOString().split('T')[0]
|
public readonly today: string = new Date().toISOString().split('T')[0]
|
||||||
|
|
||||||
constructor() {
|
constructor(protected customFieldsService: CustomFieldsService) {
|
||||||
super()
|
super()
|
||||||
this.selectionModel = new CustomFieldQueriesModel()
|
this.selectionModel = new CustomFieldQueriesModel()
|
||||||
this.getFields()
|
this.getFields()
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
inject,
|
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import {
|
import {
|
||||||
@ -64,9 +63,7 @@ export enum RelativeDate {
|
|||||||
export class DatesDropdownComponent implements OnInit, OnDestroy {
|
export class DatesDropdownComponent implements OnInit, OnDestroy {
|
||||||
public popperOptions = pngxPopperOptions
|
public popperOptions = pngxPopperOptions
|
||||||
|
|
||||||
constructor() {
|
constructor(settings: SettingsService) {
|
||||||
const settings = inject(SettingsService)
|
|
||||||
|
|
||||||
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
|
this.datePlaceHolder = settings.getLocalizedDateInputFormat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
||||||
@if (patternRequired) {
|
@if (patternRequired) {
|
||||||
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
||||||
|
}
|
||||||
|
@if (patternRequired) {
|
||||||
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></pngx-input-check>
|
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive" novalidate></pngx-input-check>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Component, inject } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { Correspondent } from 'src/app/data/correspondent'
|
import { Correspondent } from 'src/app/data/correspondent'
|
||||||
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
||||||
@ -12,7 +13,6 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
|
|||||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
|
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { CheckComponent } from '../../input/check/check.component'
|
|
||||||
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
|
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
|
||||||
import { SelectComponent } from '../../input/select/select.component'
|
import { SelectComponent } from '../../input/select/select.component'
|
||||||
import { TextComponent } from '../../input/text/text.component'
|
import { TextComponent } from '../../input/text/text.component'
|
||||||
@ -22,7 +22,6 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
templateUrl: './correspondent-edit-dialog.component.html',
|
templateUrl: './correspondent-edit-dialog.component.html',
|
||||||
styleUrls: ['./correspondent-edit-dialog.component.scss'],
|
styleUrls: ['./correspondent-edit-dialog.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
CheckComponent,
|
|
||||||
SelectComponent,
|
SelectComponent,
|
||||||
PermissionsFormComponent,
|
PermissionsFormComponent,
|
||||||
TextComponent,
|
TextComponent,
|
||||||
@ -32,11 +31,13 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CorrespondentEditDialogComponent extends EditDialogComponent<Correspondent> {
|
export class CorrespondentEditDialogComponent extends EditDialogComponent<Correspondent> {
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: CorrespondentService,
|
||||||
this.service = inject(CorrespondentService)
|
activeModal: NgbActiveModal,
|
||||||
this.userService = inject(UserService)
|
userService: UserService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
QueryList,
|
QueryList,
|
||||||
ViewChildren,
|
ViewChildren,
|
||||||
inject,
|
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormArray,
|
FormArray,
|
||||||
@ -14,6 +13,7 @@ import {
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { takeUntil } from 'rxjs'
|
import { takeUntil } from 'rxjs'
|
||||||
import {
|
import {
|
||||||
@ -54,11 +54,13 @@ export class CustomFieldEditDialogComponent
|
|||||||
.select_options as FormArray
|
.select_options as FormArray
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: CustomFieldsService,
|
||||||
this.service = inject(CustomFieldsService)
|
activeModal: NgbActiveModal,
|
||||||
this.userService = inject(UserService)
|
userService: UserService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
||||||
@if (patternRequired) {
|
@if (patternRequired) {
|
||||||
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
||||||
|
}
|
||||||
|
@if (patternRequired) {
|
||||||
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
|
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Component, inject } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { DocumentType } from 'src/app/data/document-type'
|
import { DocumentType } from 'src/app/data/document-type'
|
||||||
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
||||||
@ -12,7 +13,6 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
|
|||||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { CheckComponent } from '../../input/check/check.component'
|
|
||||||
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
|
import { PermissionsFormComponent } from '../../input/permissions/permissions-form/permissions-form.component'
|
||||||
import { SelectComponent } from '../../input/select/select.component'
|
import { SelectComponent } from '../../input/select/select.component'
|
||||||
import { TextComponent } from '../../input/text/text.component'
|
import { TextComponent } from '../../input/text/text.component'
|
||||||
@ -22,7 +22,6 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
templateUrl: './document-type-edit-dialog.component.html',
|
templateUrl: './document-type-edit-dialog.component.html',
|
||||||
styleUrls: ['./document-type-edit-dialog.component.scss'],
|
styleUrls: ['./document-type-edit-dialog.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
CheckComponent,
|
|
||||||
SelectComponent,
|
SelectComponent,
|
||||||
PermissionsFormComponent,
|
PermissionsFormComponent,
|
||||||
TextComponent,
|
TextComponent,
|
||||||
@ -32,11 +31,13 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class DocumentTypeEditDialogComponent extends EditDialogComponent<DocumentType> {
|
export class DocumentTypeEditDialogComponent extends EditDialogComponent<DocumentType> {
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: DocumentTypeService,
|
||||||
this.service = inject(DocumentTypeService)
|
activeModal: NgbActiveModal,
|
||||||
this.userService = inject(UserService)
|
userService: UserService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
|
@ -41,9 +41,13 @@ import { EditDialogComponent, EditDialogMode } from './edit-dialog.component'
|
|||||||
imports: [FormsModule, ReactiveFormsModule],
|
imports: [FormsModule, ReactiveFormsModule],
|
||||||
})
|
})
|
||||||
class TestComponent extends EditDialogComponent<Tag> {
|
class TestComponent extends EditDialogComponent<Tag> {
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: TagService,
|
||||||
this.service = TestBed.inject(TagService)
|
activeModal: NgbActiveModal,
|
||||||
|
userService: UserService,
|
||||||
|
settingsService: SettingsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
}
|
}
|
||||||
|
|
||||||
getForm(): FormGroup<any> {
|
getForm(): FormGroup<any> {
|
||||||
|
@ -1,11 +1,4 @@
|
|||||||
import {
|
import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'
|
||||||
Directive,
|
|
||||||
EventEmitter,
|
|
||||||
Input,
|
|
||||||
OnInit,
|
|
||||||
Output,
|
|
||||||
inject,
|
|
||||||
} from '@angular/core'
|
|
||||||
import { FormGroup } from '@angular/forms'
|
import { FormGroup } from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { Observable } from 'rxjs'
|
import { Observable } from 'rxjs'
|
||||||
@ -36,12 +29,14 @@ export abstract class EditDialogComponent<
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
protected service = inject<AbstractPaperlessService<T>>(
|
constructor(
|
||||||
AbstractPaperlessService
|
protected service: AbstractPaperlessService<T>,
|
||||||
)
|
private activeModal: NgbActiveModal,
|
||||||
protected activeModal = inject(NgbActiveModal)
|
private userService: UserService,
|
||||||
protected userService = inject(UserService)
|
protected settingsService: SettingsService
|
||||||
protected settingsService = inject(SettingsService)
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
users: User[]
|
users: User[]
|
||||||
|
|
||||||
@ -52,7 +47,7 @@ export abstract class EditDialogComponent<
|
|||||||
object: T
|
object: T
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
succeeded = new EventEmitter<T>()
|
succeeded = new EventEmitter()
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
failed = new EventEmitter()
|
failed = new EventEmitter()
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Component, inject } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { Group } from 'src/app/data/group'
|
import { Group } from 'src/app/data/group'
|
||||||
import { GroupService } from 'src/app/services/rest/group.service'
|
import { GroupService } from 'src/app/services/rest/group.service'
|
||||||
@ -25,11 +26,13 @@ import { PermissionsSelectComponent } from '../../permissions-select/permissions
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GroupEditDialogComponent extends EditDialogComponent<Group> {
|
export class GroupEditDialogComponent extends EditDialogComponent<Group> {
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: GroupService,
|
||||||
this.service = inject(GroupService)
|
activeModal: NgbActiveModal,
|
||||||
this.userService = inject(UserService)
|
userService: UserService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import { Component, ViewChild, inject } from '@angular/core'
|
import { Component, ViewChild } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbAlert, NgbAlertModule } from '@ng-bootstrap/ng-bootstrap'
|
import {
|
||||||
|
NgbActiveModal,
|
||||||
|
NgbAlert,
|
||||||
|
NgbAlertModule,
|
||||||
|
} from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { IMAPSecurity, MailAccount } from 'src/app/data/mail-account'
|
import { IMAPSecurity, MailAccount } from 'src/app/data/mail-account'
|
||||||
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
|
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
|
||||||
@ -43,11 +47,13 @@ export class MailAccountEditDialogComponent extends EditDialogComponent<MailAcco
|
|||||||
|
|
||||||
@ViewChild('testResultAlert', { static: false }) testResultAlert: NgbAlert
|
@ViewChild('testResultAlert', { static: false }) testResultAlert: NgbAlert
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: MailAccountService,
|
||||||
this.service = inject(MailAccountService)
|
activeModal: NgbActiveModal,
|
||||||
this.userService = inject(UserService)
|
userService: UserService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Component, inject } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { first } from 'rxjs'
|
import { first } from 'rxjs'
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { Correspondent } from 'src/app/data/correspondent'
|
import { Correspondent } from 'src/app/data/correspondent'
|
||||||
@ -154,34 +155,32 @@ const METADATA_CORRESPONDENT_OPTIONS = [
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class MailRuleEditDialogComponent extends EditDialogComponent<MailRule> {
|
export class MailRuleEditDialogComponent extends EditDialogComponent<MailRule> {
|
||||||
private accountService: MailAccountService
|
|
||||||
private correspondentService: CorrespondentService
|
|
||||||
private documentTypeService: DocumentTypeService
|
|
||||||
|
|
||||||
accounts: MailAccount[]
|
accounts: MailAccount[]
|
||||||
correspondents: Correspondent[]
|
correspondents: Correspondent[]
|
||||||
documentTypes: DocumentType[]
|
documentTypes: DocumentType[]
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: MailRuleService,
|
||||||
this.service = inject(MailRuleService)
|
activeModal: NgbActiveModal,
|
||||||
this.accountService = inject(MailAccountService)
|
accountService: MailAccountService,
|
||||||
this.correspondentService = inject(CorrespondentService)
|
correspondentService: CorrespondentService,
|
||||||
this.documentTypeService = inject(DocumentTypeService)
|
documentTypeService: DocumentTypeService,
|
||||||
this.userService = inject(UserService)
|
userService: UserService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
|
|
||||||
this.accountService
|
accountService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.accounts = result.results))
|
.subscribe((result) => (this.accounts = result.results))
|
||||||
|
|
||||||
this.correspondentService
|
correspondentService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.correspondents = result.results))
|
.subscribe((result) => (this.correspondents = result.results))
|
||||||
|
|
||||||
this.documentTypeService
|
documentTypeService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.documentTypes = result.results))
|
.subscribe((result) => (this.documentTypes = result.results))
|
||||||
|
@ -64,6 +64,8 @@
|
|||||||
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
||||||
@if (patternRequired) {
|
@if (patternRequired) {
|
||||||
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
||||||
|
}
|
||||||
|
@if (patternRequired) {
|
||||||
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
|
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
|
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
|
||||||
import { Component, OnDestroy, inject } from '@angular/core'
|
import { Component, OnDestroy } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbAccordionModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgSelectComponent } from '@ng-select/ng-select'
|
import { NgSelectComponent } from '@ng-select/ng-select'
|
||||||
import {
|
import {
|
||||||
Observable,
|
Observable,
|
||||||
@ -60,8 +60,6 @@ export class StoragePathEditDialogComponent
|
|||||||
extends EditDialogComponent<StoragePath>
|
extends EditDialogComponent<StoragePath>
|
||||||
implements OnDestroy
|
implements OnDestroy
|
||||||
{
|
{
|
||||||
private documentsService = inject(DocumentService)
|
|
||||||
|
|
||||||
public documentsInput$ = new Subject<string>()
|
public documentsInput$ = new Subject<string>()
|
||||||
public foundDocuments$: Observable<Document[]>
|
public foundDocuments$: Observable<Document[]>
|
||||||
private testDocument: Document
|
private testDocument: Document
|
||||||
@ -70,11 +68,14 @@ export class StoragePathEditDialogComponent
|
|||||||
public loading = false
|
public loading = false
|
||||||
public testLoading = false
|
public testLoading = false
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: StoragePathService,
|
||||||
this.service = inject(StoragePathService)
|
activeModal: NgbActiveModal,
|
||||||
this.userService = inject(UserService)
|
userService: UserService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService,
|
||||||
|
private documentsService: DocumentService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
this.initPathObservables()
|
this.initPathObservables()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
<pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
|
||||||
@if (patternRequired) {
|
@if (patternRequired) {
|
||||||
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
<pngx-input-text i18n-title title="Matching pattern" formControlName="match" [error]="error?.match"></pngx-input-text>
|
||||||
|
}
|
||||||
|
@if (patternRequired) {
|
||||||
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
|
<pngx-input-check i18n-title title="Case insensitive" formControlName="is_insensitive"></pngx-input-check>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Component, inject } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
|
||||||
import { Tag } from 'src/app/data/tag'
|
import { Tag } from 'src/app/data/tag'
|
||||||
@ -35,11 +36,13 @@ import { TextComponent } from '../../input/text/text.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class TagEditDialogComponent extends EditDialogComponent<Tag> {
|
export class TagEditDialogComponent extends EditDialogComponent<Tag> {
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: TagService,
|
||||||
this.service = inject(TagService)
|
activeModal: NgbActiveModal,
|
||||||
this.userService = inject(UserService)
|
userService: UserService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateTitle() {
|
getCreateTitle() {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { Component, OnInit, inject } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { first } from 'rxjs'
|
import { first } from 'rxjs'
|
||||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
|
||||||
import { Group } from 'src/app/data/group'
|
import { Group } from 'src/app/data/group'
|
||||||
@ -36,21 +37,21 @@ export class UserEditDialogComponent
|
|||||||
extends EditDialogComponent<User>
|
extends EditDialogComponent<User>
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
private toastService = inject(ToastService)
|
|
||||||
private permissionsService = inject(PermissionsService)
|
|
||||||
private groupsService: GroupService
|
|
||||||
|
|
||||||
groups: Group[]
|
groups: Group[]
|
||||||
passwordIsSet: boolean = false
|
passwordIsSet: boolean = false
|
||||||
public totpLoading: boolean = false
|
public totpLoading: boolean = false
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: UserService,
|
||||||
this.service = inject(UserService)
|
activeModal: NgbActiveModal,
|
||||||
this.groupsService = inject(GroupService)
|
groupsService: GroupService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService,
|
||||||
|
private toastService: ToastService,
|
||||||
|
private permissionsService: PermissionsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, service, settingsService)
|
||||||
|
|
||||||
this.groupsService
|
groupsService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.groups = result.results))
|
.subscribe((result) => (this.groups = result.results))
|
||||||
|
@ -123,15 +123,7 @@
|
|||||||
<p class="small" i18n>Set scheduled trigger offset and which date field to use.</p>
|
<p class="small" i18n>Set scheduled trigger offset and which date field to use.</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<pngx-input-number
|
<pngx-input-number i18n-title title="Offset days" formControlName="schedule_offset_days" [showAdd]="false" [error]="error?.schedule_offset_days"></pngx-input-number>
|
||||||
i18n-title
|
|
||||||
title="Offset days"
|
|
||||||
formControlName="schedule_offset_days"
|
|
||||||
[showAdd]="false"
|
|
||||||
[error]="error?.schedule_offset_days"
|
|
||||||
hint="Positive values will trigger after the date, negative values before."
|
|
||||||
i18n-hint
|
|
||||||
></pngx-input-number>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<pngx-input-select i18n-title title="Relative to" formControlName="schedule_date_field" [items]="scheduleDateFieldOptions" [error]="error?.schedule_date_field"></pngx-input-select>
|
<pngx-input-select i18n-title title="Relative to" formControlName="schedule_date_field" [items]="scheduleDateFieldOptions" [error]="error?.schedule_date_field"></pngx-input-select>
|
||||||
|
@ -4,7 +4,7 @@ import {
|
|||||||
moveItemInArray,
|
moveItemInArray,
|
||||||
} from '@angular/cdk/drag-drop'
|
} from '@angular/cdk/drag-drop'
|
||||||
import { NgTemplateOutlet } from '@angular/common'
|
import { NgTemplateOutlet } from '@angular/common'
|
||||||
import { Component, OnInit, inject } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormArray,
|
FormArray,
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
} from '@angular/forms'
|
} from '@angular/forms'
|
||||||
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbAccordionModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { first } from 'rxjs'
|
import { first } from 'rxjs'
|
||||||
import { Correspondent } from 'src/app/data/correspondent'
|
import { Correspondent } from 'src/app/data/correspondent'
|
||||||
@ -171,12 +171,6 @@ export class WorkflowEditDialogComponent
|
|||||||
public WorkflowTriggerType = WorkflowTriggerType
|
public WorkflowTriggerType = WorkflowTriggerType
|
||||||
public WorkflowActionType = WorkflowActionType
|
public WorkflowActionType = WorkflowActionType
|
||||||
|
|
||||||
private correspondentService: CorrespondentService
|
|
||||||
private documentTypeService: DocumentTypeService
|
|
||||||
private storagePathService: StoragePathService
|
|
||||||
private mailRuleService: MailRuleService
|
|
||||||
private customFieldsService: CustomFieldsService
|
|
||||||
|
|
||||||
templates: Workflow[]
|
templates: Workflow[]
|
||||||
correspondents: Correspondent[]
|
correspondents: Correspondent[]
|
||||||
documentTypes: DocumentType[]
|
documentTypes: DocumentType[]
|
||||||
@ -189,38 +183,40 @@ export class WorkflowEditDialogComponent
|
|||||||
|
|
||||||
private allowedActionTypes = []
|
private allowedActionTypes = []
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
super()
|
service: WorkflowService,
|
||||||
this.service = inject(WorkflowService)
|
activeModal: NgbActiveModal,
|
||||||
this.correspondentService = inject(CorrespondentService)
|
correspondentService: CorrespondentService,
|
||||||
this.documentTypeService = inject(DocumentTypeService)
|
documentTypeService: DocumentTypeService,
|
||||||
this.storagePathService = inject(StoragePathService)
|
storagePathService: StoragePathService,
|
||||||
this.mailRuleService = inject(MailRuleService)
|
mailRuleService: MailRuleService,
|
||||||
this.userService = inject(UserService)
|
userService: UserService,
|
||||||
this.settingsService = inject(SettingsService)
|
settingsService: SettingsService,
|
||||||
this.customFieldsService = inject(CustomFieldsService)
|
customFieldsService: CustomFieldsService
|
||||||
|
) {
|
||||||
|
super(service, activeModal, userService, settingsService)
|
||||||
|
|
||||||
this.correspondentService
|
correspondentService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.correspondents = result.results))
|
.subscribe((result) => (this.correspondents = result.results))
|
||||||
|
|
||||||
this.documentTypeService
|
documentTypeService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.documentTypes = result.results))
|
.subscribe((result) => (this.documentTypes = result.results))
|
||||||
|
|
||||||
this.storagePathService
|
storagePathService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.storagePaths = result.results))
|
.subscribe((result) => (this.storagePaths = result.results))
|
||||||
|
|
||||||
this.mailRuleService
|
mailRuleService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => (this.mailRules = result.results))
|
.subscribe((result) => (this.mailRules = result.results))
|
||||||
|
|
||||||
this.customFieldsService
|
customFieldsService
|
||||||
.listAll()
|
.listAll()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((result) => {
|
.subscribe((result) => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, Input, inject } from '@angular/core'
|
import { Component, Input } from '@angular/core'
|
||||||
import { FormsModule } from '@angular/forms'
|
import { FormsModule } from '@angular/forms'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
@ -13,10 +13,6 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
|
|||||||
imports: [FormsModule, NgxBootstrapIconsModule],
|
imports: [FormsModule, NgxBootstrapIconsModule],
|
||||||
})
|
})
|
||||||
export class EmailDocumentDialogComponent extends LoadingComponentWithPermissions {
|
export class EmailDocumentDialogComponent extends LoadingComponentWithPermissions {
|
||||||
private activeModal = inject(NgbActiveModal)
|
|
||||||
private documentService = inject(DocumentService)
|
|
||||||
private toastService = inject(ToastService)
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
title = $localize`Email Document`
|
title = $localize`Email Document`
|
||||||
|
|
||||||
@ -41,7 +37,11 @@ export class EmailDocumentDialogComponent extends LoadingComponentWithPermission
|
|||||||
public emailSubject: string = ''
|
public emailSubject: string = ''
|
||||||
public emailMessage: string = ''
|
public emailMessage: string = ''
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private activeModal: NgbActiveModal,
|
||||||
|
private documentService: DocumentService,
|
||||||
|
private toastService: ToastService
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
inject,
|
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
@ -435,9 +434,6 @@ export class FilterableDropdownComponent
|
|||||||
extends LoadingComponentWithPermissions
|
extends LoadingComponentWithPermissions
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
private filterPipe = inject(FilterPipe)
|
|
||||||
private hotkeyService = inject(HotKeyService)
|
|
||||||
|
|
||||||
@ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
|
@ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
|
||||||
@ViewChild('dropdown') dropdown: NgbDropdown
|
@ViewChild('dropdown') dropdown: NgbDropdown
|
||||||
@ViewChild('buttonItems') buttonItems: ElementRef
|
@ViewChild('buttonItems') buttonItems: ElementRef
|
||||||
@ -540,7 +536,10 @@ export class FilterableDropdownComponent
|
|||||||
|
|
||||||
private keyboardIndex: number
|
private keyboardIndex: number
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private filterPipe: FilterPipe,
|
||||||
|
private hotkeyService: HotKeyService
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
this.selectionModelChange.subscribe((updatedModel) => {
|
this.selectionModelChange.subscribe((updatedModel) => {
|
||||||
this.modelIsDirty = updatedModel.isDirty()
|
this.modelIsDirty = updatedModel.isDirty()
|
||||||
@ -587,8 +586,6 @@ export class FilterableDropdownComponent
|
|||||||
this.selectionModel.reset()
|
this.selectionModel.reset()
|
||||||
this.modelIsDirty = false
|
this.modelIsDirty = false
|
||||||
}
|
}
|
||||||
this.selectionModel.singleSelect =
|
|
||||||
this.editing && !this.selectionModel.manyToOne
|
|
||||||
this.opened.next(this)
|
this.opened.next(this)
|
||||||
} else {
|
} else {
|
||||||
if (this.creating) {
|
if (this.creating) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, inject } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
|
||||||
const SYMBOLS = {
|
const SYMBOLS = {
|
||||||
@ -19,11 +19,11 @@ const SYMBOLS = {
|
|||||||
styleUrl: './hotkey-dialog.component.scss',
|
styleUrl: './hotkey-dialog.component.scss',
|
||||||
})
|
})
|
||||||
export class HotkeyDialogComponent {
|
export class HotkeyDialogComponent {
|
||||||
activeModal = inject(NgbActiveModal)
|
|
||||||
|
|
||||||
public title: string = $localize`Keyboard shortcuts`
|
public title: string = $localize`Keyboard shortcuts`
|
||||||
public hotkeys: Map<string, string> = new Map()
|
public hotkeys: Map<string, string> = new Map()
|
||||||
|
|
||||||
|
constructor(public activeModal: NgbActiveModal) {}
|
||||||
|
|
||||||
public close(): void {
|
public close(): void {
|
||||||
this.activeModal.close()
|
this.activeModal.close()
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
inject,
|
|
||||||
Input,
|
Input,
|
||||||
Output,
|
Output,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
@ -56,9 +55,7 @@ import { UrlComponent } from '../url/url.component'
|
|||||||
export class CustomFieldsValuesComponent extends AbstractInputComponent<Object> {
|
export class CustomFieldsValuesComponent extends AbstractInputComponent<Object> {
|
||||||
public CustomFieldDataType = CustomFieldDataType
|
public CustomFieldDataType = CustomFieldDataType
|
||||||
|
|
||||||
constructor() {
|
constructor(customFieldsService: CustomFieldsService) {
|
||||||
const customFieldsService = inject(CustomFieldsService)
|
|
||||||
|
|
||||||
super()
|
super()
|
||||||
customFieldsService.listAll().subscribe((items) => {
|
customFieldsService.listAll().subscribe((items) => {
|
||||||
this.fields = items.results
|
this.fields = items.results
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
inject,
|
|
||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
@ -46,9 +45,13 @@ export class DateComponent
|
|||||||
extends AbstractInputComponent<string>
|
extends AbstractInputComponent<string>
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
private settings = inject(SettingsService)
|
constructor(
|
||||||
private ngbDateParserFormatter = inject(NgbDateParserFormatter)
|
private settings: SettingsService,
|
||||||
private isoDateAdapter = inject<NgbDateAdapter<string>>(NgbDateAdapter)
|
private ngbDateParserFormatter: NgbDateParserFormatter,
|
||||||
|
private isoDateAdapter: NgbDateAdapter<string>
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
suggestions: string[]
|
suggestions: string[]
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
|
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
|
||||||
import {
|
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'
|
||||||
Component,
|
|
||||||
forwardRef,
|
|
||||||
inject,
|
|
||||||
Input,
|
|
||||||
OnDestroy,
|
|
||||||
OnInit,
|
|
||||||
} from '@angular/core'
|
|
||||||
import {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@ -59,8 +52,6 @@ export class DocumentLinkComponent
|
|||||||
extends AbstractInputComponent<any[]>
|
extends AbstractInputComponent<any[]>
|
||||||
implements OnInit, OnDestroy
|
implements OnInit, OnDestroy
|
||||||
{
|
{
|
||||||
private documentsService = inject(DocumentService)
|
|
||||||
|
|
||||||
documentsInput$ = new Subject<string>()
|
documentsInput$ = new Subject<string>()
|
||||||
foundDocuments$: Observable<Document[]>
|
foundDocuments$: Observable<Document[]>
|
||||||
loading = false
|
loading = false
|
||||||
@ -84,6 +75,10 @@ export class DocumentLinkComponent
|
|||||||
return this.selectedDocuments.map((d) => d.id)
|
return this.selectedDocuments.map((d) => d.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor(private documentsService: DocumentService) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.loadDocs()
|
this.loadDocs()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
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 { LOCALE_ID } from '@angular/core'
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
import { NG_VALUE_ACCESSOR } from '@angular/forms'
|
||||||
import { MonetaryComponent } from './monetary.component'
|
import { MonetaryComponent } from './monetary.component'
|
||||||
@ -42,6 +41,8 @@ describe('MonetaryComponent', () => {
|
|||||||
|
|
||||||
it('should set the default currency code based on LOCALE_ID', () => {
|
it('should set the default currency code based on LOCALE_ID', () => {
|
||||||
expect(component.defaultCurrencyCode).toEqual('USD') // default
|
expect(component.defaultCurrencyCode).toEqual('USD') // default
|
||||||
|
component = new MonetaryComponent('pt-BR')
|
||||||
|
expect(component.defaultCurrencyCode).toEqual('BRL')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should support setting a default currency code', () => {
|
it('should support setting a default currency code', () => {
|
||||||
@ -86,28 +87,3 @@ describe('MonetaryComponent', () => {
|
|||||||
expect(component.value).toEqual('USD0.00')
|
expect(component.value).toEqual('USD0.00')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('MonetaryComponent (Alternate Locale)', () => {
|
|
||||||
let component: MonetaryComponent
|
|
||||||
let fixture: ComponentFixture<MonetaryComponent>
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [MonetaryComponent],
|
|
||||||
providers: [
|
|
||||||
{ provide: LOCALE_ID, useValue: 'pt-BR' }, // Brazilian Portuguese
|
|
||||||
provideHttpClient(withInterceptorsFromDi()),
|
|
||||||
provideHttpClientTesting(),
|
|
||||||
],
|
|
||||||
}).compileComponents()
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(MonetaryComponent)
|
|
||||||
fixture.debugElement.injector.get(NG_VALUE_ACCESSOR)
|
|
||||||
component = fixture.componentInstance
|
|
||||||
fixture.detectChanges()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should set the default currency code based on LOCALE_ID', () => {
|
|
||||||
expect(component.defaultCurrencyCode).toEqual('BRL')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
|
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
|
||||||
import { Component, forwardRef, inject, Input, LOCALE_ID } from '@angular/core'
|
import { Component, forwardRef, Inject, Input, LOCALE_ID } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@ -27,8 +27,6 @@ import { AbstractInputComponent } from '../abstract-input'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class MonetaryComponent extends AbstractInputComponent<string> {
|
export class MonetaryComponent extends AbstractInputComponent<string> {
|
||||||
currentLocale = inject(LOCALE_ID)
|
|
||||||
|
|
||||||
public currency: string = ''
|
public currency: string = ''
|
||||||
|
|
||||||
public _monetaryValue: string = ''
|
public _monetaryValue: string = ''
|
||||||
@ -47,10 +45,11 @@ export class MonetaryComponent extends AbstractInputComponent<string> {
|
|||||||
if (currency) this.defaultCurrencyCode = currency
|
if (currency) this.defaultCurrencyCode = currency
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor(@Inject(LOCALE_ID) currentLocale: string) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.currency = this.defaultCurrencyCode =
|
this.currency = this.defaultCurrencyCode =
|
||||||
this.defaultCurrency ?? getLocaleCurrencyCode(this.currentLocale)
|
this.defaultCurrency ?? getLocaleCurrencyCode(currentLocale)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeValue(newValue: any): void {
|
writeValue(newValue: any): void {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, forwardRef, inject, Input } from '@angular/core'
|
import { Component, forwardRef, Input } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@ -22,14 +22,16 @@ import { AbstractInputComponent } from '../abstract-input'
|
|||||||
imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
|
imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
|
||||||
})
|
})
|
||||||
export class NumberComponent extends AbstractInputComponent<number> {
|
export class NumberComponent extends AbstractInputComponent<number> {
|
||||||
private documentService = inject(DocumentService)
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
showAdd: boolean = true
|
showAdd: boolean = true
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
step: number = 1
|
step: number = 1
|
||||||
|
|
||||||
|
constructor(private documentService: DocumentService) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
nextAsn() {
|
nextAsn() {
|
||||||
if (this.value) {
|
if (this.value) {
|
||||||
return
|
return
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, forwardRef, inject } from '@angular/core'
|
import { Component, forwardRef } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@ -26,9 +26,7 @@ import { AbstractInputComponent } from '../../abstract-input'
|
|||||||
export class PermissionsGroupComponent extends AbstractInputComponent<Group> {
|
export class PermissionsGroupComponent extends AbstractInputComponent<Group> {
|
||||||
groups: Group[]
|
groups: Group[]
|
||||||
|
|
||||||
constructor() {
|
constructor(groupService: GroupService) {
|
||||||
const groupService = inject(GroupService)
|
|
||||||
|
|
||||||
super()
|
super()
|
||||||
groupService
|
groupService
|
||||||
.listAll()
|
.listAll()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, forwardRef, inject } from '@angular/core'
|
import { Component, forwardRef } from '@angular/core'
|
||||||
import {
|
import {
|
||||||
FormsModule,
|
FormsModule,
|
||||||
NG_VALUE_ACCESSOR,
|
NG_VALUE_ACCESSOR,
|
||||||
@ -8,6 +8,7 @@ import { NgSelectComponent } from '@ng-select/ng-select'
|
|||||||
import { first } from 'rxjs/operators'
|
import { first } from 'rxjs/operators'
|
||||||
import { User } from 'src/app/data/user'
|
import { User } from 'src/app/data/user'
|
||||||
import { UserService } from 'src/app/services/rest/user.service'
|
import { UserService } from 'src/app/services/rest/user.service'
|
||||||
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { AbstractInputComponent } from '../../abstract-input'
|
import { AbstractInputComponent } from '../../abstract-input'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -26,9 +27,7 @@ import { AbstractInputComponent } from '../../abstract-input'
|
|||||||
export class PermissionsUserComponent extends AbstractInputComponent<User[]> {
|
export class PermissionsUserComponent extends AbstractInputComponent<User[]> {
|
||||||
users: User[]
|
users: User[]
|
||||||
|
|
||||||
constructor() {
|
constructor(userService: UserService, settings: SettingsService) {
|
||||||
const userService = inject(UserService)
|
|
||||||
|
|
||||||
super()
|
super()
|
||||||
userService
|
userService
|
||||||
.listAll()
|
.listAll()
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-select>
|
</ng-select>
|
||||||
@if (allowCreate && !hideAddButton) {
|
@if (allowCreate && !hideAddButton) {
|
||||||
<button class="btn btn-outline-secondary" type="button" (click)="createTag(null, true)" [disabled]="disabled">
|
<button class="btn btn-outline-secondary" type="button" (click)="createTag()" [disabled]="disabled">
|
||||||
<i-bs width="1.2em" height="1.2em" name="plus"></i-bs>
|
<i-bs width="1.2em" height="1.2em" name="plus"></i-bs>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
inject,
|
|
||||||
Input,
|
Input,
|
||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
@ -46,10 +45,10 @@ import { TagComponent } from '../../tag/tag.component'
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class TagsComponent implements OnInit, ControlValueAccessor {
|
export class TagsComponent implements OnInit, ControlValueAccessor {
|
||||||
private tagService = inject(TagService)
|
constructor(
|
||||||
private modalService = inject(NgbModal)
|
private tagService: TagService,
|
||||||
|
private modalService: NgbModal
|
||||||
constructor() {
|
) {
|
||||||
this.createTagRef = this.createTag.bind(this)
|
this.createTagRef = this.createTag.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +130,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createTag(name: string = null, add: boolean = false) {
|
createTag(name: string = null) {
|
||||||
var modal = this.modalService.open(TagEditDialogComponent, {
|
var modal = this.modalService.open(TagEditDialogComponent, {
|
||||||
backdrop: 'static',
|
backdrop: 'static',
|
||||||
})
|
})
|
||||||
@ -144,10 +143,9 @@ export class TagsComponent implements OnInit, ControlValueAccessor {
|
|||||||
return firstValueFrom(
|
return firstValueFrom(
|
||||||
(modal.componentInstance as TagEditDialogComponent).succeeded.pipe(
|
(modal.componentInstance as TagEditDialogComponent).succeeded.pipe(
|
||||||
first(),
|
first(),
|
||||||
tap((newTag) => {
|
tap(() => {
|
||||||
this.tagService.listAll().subscribe((tags) => {
|
this.tagService.listAll().subscribe((tags) => {
|
||||||
this.tags = tags.results
|
this.tags = tags.results
|
||||||
add && this.addTag(newTag.id)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, Input, inject } from '@angular/core'
|
import { Component, Input } from '@angular/core'
|
||||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
|
||||||
import { SettingsService } from 'src/app/services/settings.service'
|
import { SettingsService } from 'src/app/services/settings.service'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
@ -9,8 +9,6 @@ import { environment } from 'src/environments/environment'
|
|||||||
styleUrls: ['./logo.component.scss'],
|
styleUrls: ['./logo.component.scss'],
|
||||||
})
|
})
|
||||||
export class LogoComponent {
|
export class LogoComponent {
|
||||||
private settingsService = inject(SettingsService)
|
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
extra_classes: string
|
extra_classes: string
|
||||||
|
|
||||||
@ -26,6 +24,8 @@ export class LogoComponent {
|
|||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor(private settingsService: SettingsService) {}
|
||||||
|
|
||||||
getClasses() {
|
getClasses() {
|
||||||
return ['logo'].concat(this.extra_classes).join(' ')
|
return ['logo'].concat(this.extra_classes).join(' ')
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user