mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Compare commits
	
		
			67 Commits
		
	
	
		
			v2.16.3
			...
			feb320cae9
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					feb320cae9 | ||
| 
						 | 
					9178af5fb2 | ||
| 
						 | 
					850444c2fc | ||
| 
						 | 
					90baba2cec | ||
| 
						 | 
					9889c59d3d | ||
| 
						 | 
					3d2a3ede71 | ||
| 
						 | 
					bc019fab96 | ||
| 
						 | 
					1cd21d0f38 | ||
| 
						 | 
					f940ed0b7b | ||
| 
						 | 
					3180ccf4cb | ||
| 
						 | 
					43abb0541b | ||
| 
						 | 
					a3a405354f | ||
| 
						 | 
					09e98d600e | ||
| 
						 | 
					01a39b9bb4 | ||
| 
						 | 
					3b0b40f071 | ||
| 
						 | 
					6dce83865f | ||
| 
						 | 
					18252a19d7 | ||
| 
						 | 
					733a9674d6 | ||
| 
						 | 
					f3b6e15321 | ||
| 
						 | 
					6591d5da63 | ||
| 
						 | 
					c974dc9400 | ||
| 
						 | 
					1671d49d44 | ||
| 
						 | 
					6b248ef140 | ||
| 
						 | 
					735681d294 | ||
| 
						 | 
					a9085c65c5 | ||
| 
						 | 
					e312425b1c | ||
| 
						 | 
					13fe064f6e | ||
| 
						 | 
					958f98d7e5 | ||
| 
						 | 
					dfad3c4d8e | ||
| 
						 | 
					37267f3f04 | ||
| 
						 | 
					b34538d991 | ||
| 
						 | 
					fc97bd1315 | ||
| 
						 | 
					dbf3721ec2 | ||
| 
						 | 
					59afbe09b1 | ||
| 
						 | 
					bfeaa1b119 | ||
| 
						 | 
					e1c3124698 | ||
| 
						 | 
					f2e22e103b | ||
| 
						 | 
					dda94f013e | ||
| 
						 | 
					caf00e7ead | ||
| 
						 | 
					f214440d2e | ||
| 
						 | 
					240c9ac511 | ||
| 
						 | 
					a2c9bc346a | ||
| 
						 | 
					b1cbb1c73a | ||
| 
						 | 
					434b1e3245 | ||
| 
						 | 
					497fdcaf4e | ||
| 
						 | 
					52b95f2b62 | ||
| 
						 | 
					83391af866 | ||
| 
						 | 
					0a1786f39b | ||
| 
						 | 
					3b069ac034 | ||
| 
						 | 
					07882b918b | ||
| 
						 | 
					cc5ba71f06 | ||
| 
						 | 
					f16b8fbe2a | ||
| 
						 | 
					fe54d99356 | ||
| 
						 | 
					a49efb07ea | ||
| 
						 | 
					e4fd008441 | ||
| 
						 | 
					de12023311 | ||
| 
						 | 
					60ebdc0ad6 | ||
| 
						 | 
					cbd9823ad6 | ||
| 
						 | 
					ce76303a32 | ||
| 
						 | 
					246f17c6c8 | ||
| 
						 | 
					4313635b01 | ||
| 
						 | 
					7ca2bd0666 | ||
| 
						 | 
					4c6075e962 | ||
| 
						 | 
					8d48e99487 | ||
| 
						 | 
					454a2d9e9e | ||
| 
						 | 
					b8c713d4b9 | ||
| 
						 | 
					2a8cb87232 | 
@@ -20,7 +20,6 @@
 | 
			
		||||
#
 | 
			
		||||
# This file is intended only to be used through VSCOde devcontainers. See README.md
 | 
			
		||||
# in the folder .devcontainer.
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  broker:
 | 
			
		||||
    image: docker.io/library/redis:7
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								.github/DISCUSSION_TEMPLATE/support.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								.github/DISCUSSION_TEMPLATE/support.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										1
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,5 @@
 | 
			
		||||
# Please see the documentation for all configuration options:
 | 
			
		||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
 | 
			
		||||
 | 
			
		||||
version: 2
 | 
			
		||||
# Required for uv support for now
 | 
			
		||||
enable-beta-ecosystems: true
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -162,7 +162,7 @@ jobs:
 | 
			
		||||
      - name: Install pnpm
 | 
			
		||||
        uses: pnpm/action-setup@v4
 | 
			
		||||
        with:
 | 
			
		||||
          package_json_file: 'src-ui/package.json'
 | 
			
		||||
          version: 10
 | 
			
		||||
      - name: Use Node.js 20
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
@@ -195,7 +195,7 @@ jobs:
 | 
			
		||||
      - name: Install pnpm
 | 
			
		||||
        uses: pnpm/action-setup@v4
 | 
			
		||||
        with:
 | 
			
		||||
          package_json_file: 'src-ui/package.json'
 | 
			
		||||
          version: 10
 | 
			
		||||
      - name: Use Node.js 20
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
@@ -245,7 +245,7 @@ jobs:
 | 
			
		||||
      - name: Install pnpm
 | 
			
		||||
        uses: pnpm/action-setup@v4
 | 
			
		||||
        with:
 | 
			
		||||
          package_json_file: 'src-ui/package.json'
 | 
			
		||||
          version: 10
 | 
			
		||||
      - name: Use Node.js 20
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
@@ -288,7 +288,7 @@ jobs:
 | 
			
		||||
      - name: Install pnpm
 | 
			
		||||
        uses: pnpm/action-setup@v4
 | 
			
		||||
        with:
 | 
			
		||||
          package_json_file: 'src-ui/package.json'
 | 
			
		||||
          version: 10
 | 
			
		||||
      - name: Use Node.js 20
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.github/workflows/cleanup-tags.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/cleanup-tags.yml
									
									
									
									
										vendored
									
									
								
							@@ -4,7 +4,6 @@
 | 
			
		||||
# Requires a PAT with the correct scope set in the secrets.
 | 
			
		||||
#
 | 
			
		||||
# This workflow will not trigger runs on forked repos.
 | 
			
		||||
 | 
			
		||||
name: Cleanup Image Tags
 | 
			
		||||
on:
 | 
			
		||||
  delete:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/crowdin.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/crowdin.yml
									
									
									
									
										vendored
									
									
								
							@@ -14,6 +14,8 @@ jobs:
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
        with:
 | 
			
		||||
          token: ${{ secrets.PNGX_BOT_PAT }}
 | 
			
		||||
      - name: crowdin action
 | 
			
		||||
        uses: crowdin/github-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/translate-strings.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/translate-strings.yml
									
									
									
									
										vendored
									
									
								
							@@ -61,7 +61,7 @@ jobs:
 | 
			
		||||
          cd src-ui
 | 
			
		||||
          pnpm run ng extract-i18n
 | 
			
		||||
      - name: Commit changes
 | 
			
		||||
        uses: stefanzweifel/git-auto-commit-action@v5
 | 
			
		||||
        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"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
# This file configures pre-commit hooks.
 | 
			
		||||
# See https://pre-commit.com/ for general information
 | 
			
		||||
# See https://pre-commit.com/hooks.html for a listing of possible hooks
 | 
			
		||||
 | 
			
		||||
repos:
 | 
			
		||||
  # General hooks
 | 
			
		||||
  - repo: https://github.com/pre-commit/pre-commit-hooks
 | 
			
		||||
@@ -29,7 +28,7 @@ repos:
 | 
			
		||||
      - id: check-case-conflict
 | 
			
		||||
      - id: detect-private-key
 | 
			
		||||
  - repo: https://github.com/codespell-project/codespell
 | 
			
		||||
    rev: v2.4.0
 | 
			
		||||
    rev: v2.4.1
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: codespell
 | 
			
		||||
        exclude: "(^src-ui/src/locale/)|(^src-ui/pnpm-lock.yaml)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)"
 | 
			
		||||
@@ -38,7 +37,7 @@ repos:
 | 
			
		||||
          - json
 | 
			
		||||
  # See https://github.com/prettier/prettier/issues/15742 for the fork reason
 | 
			
		||||
  - repo: https://github.com/rbubley/mirrors-prettier
 | 
			
		||||
    rev: 'v3.3.3'
 | 
			
		||||
    rev: 'v3.6.2'
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: prettier
 | 
			
		||||
        types_or:
 | 
			
		||||
@@ -50,17 +49,17 @@ repos:
 | 
			
		||||
          - 'prettier-plugin-organize-imports@4.1.0'
 | 
			
		||||
  # Python hooks
 | 
			
		||||
  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
			
		||||
    rev: v0.9.9
 | 
			
		||||
    rev: v0.12.2
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: ruff
 | 
			
		||||
      - id: ruff-format
 | 
			
		||||
  - repo: https://github.com/tox-dev/pyproject-fmt
 | 
			
		||||
    rev: "v2.5.1"
 | 
			
		||||
    rev: "v2.6.0"
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pyproject-fmt
 | 
			
		||||
  # Dockerfile hooks
 | 
			
		||||
  - repo: https://github.com/AleksaC/hadolint-py
 | 
			
		||||
    rev: v2.12.0.3
 | 
			
		||||
    rev: v2.12.1b3
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: hadolint
 | 
			
		||||
  # Shell script hooks
 | 
			
		||||
@@ -77,7 +76,7 @@ repos:
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: shellcheck
 | 
			
		||||
  - repo: https://github.com/google/yamlfmt
 | 
			
		||||
    rev: v0.14.0
 | 
			
		||||
    rev: v0.17.2
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: yamlfmt
 | 
			
		||||
        exclude: "^src-ui/pnpm-lock.yaml"
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ ARG PNGX_TAG_VERSION=
 | 
			
		||||
RUN set -eux && \
 | 
			
		||||
case "${PNGX_TAG_VERSION}" in \
 | 
			
		||||
  dev|beta|fix*|feature*) \
 | 
			
		||||
    sed -i -E "s/version: '([0-9\.]+)'/version: '\1 #${PNGX_TAG_VERSION}'/g" /src/src-ui/src/environments/environment.prod.ts \
 | 
			
		||||
    sed -i -E "s/tag: '([a-z\.]+)'/tag: '${PNGX_TAG_VERSION}'/g" /src/src-ui/src/environments/environment.prod.ts \
 | 
			
		||||
    ;; \
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
@@ -32,7 +32,7 @@ RUN set -eux \
 | 
			
		||||
# Purpose: Installs s6-overlay and rootfs
 | 
			
		||||
# Comments:
 | 
			
		||||
#  - Don't leave anything extra in here either
 | 
			
		||||
FROM ghcr.io/astral-sh/uv:0.7.9-python3.12-bookworm-slim AS s6-overlay-base
 | 
			
		||||
FROM ghcr.io/astral-sh/uv:0.7.19-python3.12-bookworm-slim AS s6-overlay-base
 | 
			
		||||
 | 
			
		||||
WORKDIR /usr/src/s6
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
# and Tika containers for a more end to end test of the Tika related functionality
 | 
			
		||||
# Can be used locally or by the CI to start the necessary containers with the
 | 
			
		||||
# correct networking for the tests
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  gotenberg:
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.20
 | 
			
		||||
 
 | 
			
		||||
@@ -25,11 +25,9 @@
 | 
			
		||||
#   and '.env' into a folder.
 | 
			
		||||
# - Run 'docker compose pull'.
 | 
			
		||||
# - Run 'docker compose up -d'.
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  broker:
 | 
			
		||||
    image: docker.io/library/redis:8
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,6 @@
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  broker:
 | 
			
		||||
    image: docker.io/library/redis:8
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  broker:
 | 
			
		||||
    image: docker.io/library/redis:8
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,6 @@
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  broker:
 | 
			
		||||
    image: docker.io/library/redis:8
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,6 @@
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  broker:
 | 
			
		||||
    image: docker.io/library/redis:8
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,6 @@
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  broker:
 | 
			
		||||
    image: docker.io/library/redis:8
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  broker:
 | 
			
		||||
    image: docker.io/library/redis:8
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
if [ "$(id --user)" != "0" ]; then
 | 
			
		||||
	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
 | 
			
		||||
	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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
exporter](#exporter) and imports it into paperless.
 | 
			
		||||
 | 
			
		||||
The importer works just like the exporter. You point it at a directory,
 | 
			
		||||
The importer works just like the exporter. You point it at a directory or the generated .zip file,
 | 
			
		||||
and the script does the rest of the work:
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
@@ -351,9 +351,6 @@ When you use the provided docker compose script, put the export inside
 | 
			
		||||
the `export` folder in your paperless source directory. Specify
 | 
			
		||||
`../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
 | 
			
		||||
 | 
			
		||||
    Importing from a previous version of Paperless may work, but for best
 | 
			
		||||
@@ -460,6 +457,22 @@ of the index and usually makes queries faster and also ensures that the
 | 
			
		||||
autocompletion works properly. This command is regularly invoked by the
 | 
			
		||||
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}
 | 
			
		||||
 | 
			
		||||
If you use paperless' feature to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,120 @@
 | 
			
		||||
# 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
 | 
			
		||||
@@ -5892,7 +6007,6 @@ primarily.
 | 
			
		||||
        a very good job at ocr'ing a document with the default
 | 
			
		||||
        language. Certain language specifics such as umlauts may not get
 | 
			
		||||
        picked up properly.
 | 
			
		||||
    -   `PAPERLESS_DEBUG` defaults to `false`.
 | 
			
		||||
    -   The presence of `PAPERLESS_DBHOST` now determines whether to use
 | 
			
		||||
        PostgreSQL or SQLite.
 | 
			
		||||
    -   `PAPERLESS_OCR_THREADS` is gone and replaced with
 | 
			
		||||
 
 | 
			
		||||
@@ -159,6 +159,41 @@ Available options are `postgresql` and `mariadb`.
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
### Tika {#tika}
 | 
			
		||||
@@ -968,6 +1003,22 @@ still perform some basic text pre-processing before matching.
 | 
			
		||||
 | 
			
		||||
    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}
 | 
			
		||||
 | 
			
		||||
: Configures the scheduled email fetching frequency. The value
 | 
			
		||||
 
 | 
			
		||||
@@ -130,7 +130,7 @@ command:
 | 
			
		||||
    - 'gotenberg'
 | 
			
		||||
    - '--chromium-disable-javascript=true'
 | 
			
		||||
    - '--chromium-allow-list=file:///tmp/.*'
 | 
			
		||||
    - '--api-timeout=60'
 | 
			
		||||
    - '--api-timeout=60s'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Permission denied errors in the consumption directory
 | 
			
		||||
 
 | 
			
		||||
@@ -408,7 +408,7 @@ Currently, there are three events that correspond to workflow trigger 'types':
 | 
			
		||||
   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
 | 
			
		||||
   added, created, updated date or you can specify a (date) custom field. You can also specify a day offset from the date (positive
 | 
			
		||||
   offsets will trigger before the date, negative offsets will trigger after).
 | 
			
		||||
   offsets will trigger after the date, negative offsets will trigger before).
 | 
			
		||||
 | 
			
		||||
The following flow diagram illustrates the three document trigger types:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,6 @@
 | 
			
		||||
# Have a look at the docs for documentation.
 | 
			
		||||
# https://docs.paperless-ngx.com/configuration/
 | 
			
		||||
 | 
			
		||||
# Debug. Only enable this for development.
 | 
			
		||||
 | 
			
		||||
#PAPERLESS_DEBUG=false
 | 
			
		||||
 | 
			
		||||
# Required services
 | 
			
		||||
 | 
			
		||||
#PAPERLESS_REDIS=redis://localhost:6379
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
[project]
 | 
			
		||||
name = "paperless-ngx"
 | 
			
		||||
version = "2.16.3"
 | 
			
		||||
version = "2.17.1"
 | 
			
		||||
description = "A community-supported supercharged version of paperless: scan, index and archive all your physical documents"
 | 
			
		||||
readme = "README.md"
 | 
			
		||||
requires-python = ">=3.10"
 | 
			
		||||
@@ -26,6 +26,7 @@ dependencies = [
 | 
			
		||||
  "django~=5.1.7",
 | 
			
		||||
  "django-allauth[socialaccount,mfa]~=65.4.0",
 | 
			
		||||
  "django-auditlog~=3.1.2",
 | 
			
		||||
  "django-cachalot~=2.8.0",
 | 
			
		||||
  "django-celery-results~=2.6.0",
 | 
			
		||||
  "django-compression-middleware~=0.5.0",
 | 
			
		||||
  "django-cors-headers~=4.7.0",
 | 
			
		||||
@@ -43,13 +44,13 @@ dependencies = [
 | 
			
		||||
  "flower~=2.0.1",
 | 
			
		||||
  "gotenberg-client~=0.10.0",
 | 
			
		||||
  "httpx-oauth~=0.16",
 | 
			
		||||
  "imap-tools~=1.10.0",
 | 
			
		||||
  "imap-tools~=1.11.0",
 | 
			
		||||
  "inotifyrecursive~=0.3",
 | 
			
		||||
  "jinja2~=3.1.5",
 | 
			
		||||
  "langdetect~=1.0.9",
 | 
			
		||||
  "nltk~=3.9.1",
 | 
			
		||||
  "ocrmypdf~=16.10.0",
 | 
			
		||||
  "pathvalidate~=3.2.3",
 | 
			
		||||
  "pathvalidate~=3.3.1",
 | 
			
		||||
  "pdf2image~=1.17.0",
 | 
			
		||||
  "python-dateutil~=2.9.0",
 | 
			
		||||
  "python-dotenv~=1.1.0",
 | 
			
		||||
@@ -59,7 +60,7 @@ dependencies = [
 | 
			
		||||
  "pyzbar~=0.1.9",
 | 
			
		||||
  "rapidfuzz~=3.13.0",
 | 
			
		||||
  "redis[hiredis]~=5.2.1",
 | 
			
		||||
  "scikit-learn~=1.6.1",
 | 
			
		||||
  "scikit-learn~=1.7.0",
 | 
			
		||||
  "setproctitle~=1.3.4",
 | 
			
		||||
  "tika-client~=0.9.0",
 | 
			
		||||
  "tqdm~=4.67.1",
 | 
			
		||||
@@ -73,12 +74,12 @@ optional-dependencies.mariadb = [
 | 
			
		||||
  "mysqlclient~=2.2.7",
 | 
			
		||||
]
 | 
			
		||||
optional-dependencies.postgres = [
 | 
			
		||||
  "psycopg[c]==3.2.5",
 | 
			
		||||
  "psycopg[c]==3.2.9",
 | 
			
		||||
  # Direct dependency for proper resolution of the pre-built wheels
 | 
			
		||||
  "psycopg-c==3.2.5",
 | 
			
		||||
  "psycopg-c==3.2.9",
 | 
			
		||||
]
 | 
			
		||||
optional-dependencies.webserver = [
 | 
			
		||||
  "granian[uvloop]~=2.3.2",
 | 
			
		||||
  "granian[uvloop]~=2.4.1",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[dependency-groups]
 | 
			
		||||
@@ -112,7 +113,7 @@ testing = [
 | 
			
		||||
lint = [
 | 
			
		||||
  "pre-commit~=4.1.0",
 | 
			
		||||
  "pre-commit-uv~=4.1.3",
 | 
			
		||||
  "ruff~=0.9.9",
 | 
			
		||||
  "ruff~=0.12.2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
typing = [
 | 
			
		||||
@@ -172,6 +173,7 @@ lint.extend-select = [
 | 
			
		||||
]
 | 
			
		||||
lint.ignore = [
 | 
			
		||||
  "DJ001",
 | 
			
		||||
  "PLC0415",
 | 
			
		||||
  "RUF012",
 | 
			
		||||
  "SIM105",
 | 
			
		||||
]
 | 
			
		||||
@@ -221,22 +223,9 @@ lint.per-file-ignores."src/documents/parsers.py" = [
 | 
			
		||||
lint.per-file-ignores."src/documents/signals/handlers.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_mail/mail.py" = [
 | 
			
		||||
  "PTH",
 | 
			
		||||
] # TODO Enable & remove
 | 
			
		||||
lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [
 | 
			
		||||
  "PTH",
 | 
			
		||||
  "RUF001",
 | 
			
		||||
] # TODO PTH Enable & remove
 | 
			
		||||
]
 | 
			
		||||
lint.isort.force-single-line = true
 | 
			
		||||
 | 
			
		||||
[tool.pytest.ini_options]
 | 
			
		||||
@@ -313,8 +302,8 @@ environments = [
 | 
			
		||||
[tool.uv.sources]
 | 
			
		||||
# Markers are chosen to select these almost exclusively when building the Docker image
 | 
			
		||||
psycopg-c = [
 | 
			
		||||
  { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
 | 
			
		||||
  { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.5/psycopg_c-3.2.5-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
 | 
			
		||||
  { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.9/psycopg_c-3.2.9-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
 | 
			
		||||
  { url = "https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.9/psycopg_c-3.2.9-cp312-cp312-linux_aarch64.whl", marker = "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.12'" },
 | 
			
		||||
]
 | 
			
		||||
zxing-cpp = [
 | 
			
		||||
  { url = "https://github.com/paperless-ngx/builder/releases/download/zxing-2.3.0/zxing_cpp-2.3.0-cp312-cp312-linux_x86_64.whl", marker = "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.12'" },
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@
 | 
			
		||||
          "el-GR": "src/locale/messages.el_GR.xlf",
 | 
			
		||||
          "en-GB": "src/locale/messages.en_GB.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",
 | 
			
		||||
          "fr-FR": "src/locale/messages.fr_FR.xlf",
 | 
			
		||||
          "hu-HU": "src/locale/messages.hu_HU.xlf",
 | 
			
		||||
@@ -47,6 +48,7 @@
 | 
			
		||||
          "sv-SE": "src/locale/messages.sv_SE.xlf",
 | 
			
		||||
          "tr-TR": "src/locale/messages.tr_TR.xlf",
 | 
			
		||||
          "uk-UA": "src/locale/messages.uk_UA.xlf",
 | 
			
		||||
          "vi-VN": "src/locale/messages.vi_VN.xlf",
 | 
			
		||||
          "zh-CN": "src/locale/messages.zh_CN.xlf",
 | 
			
		||||
          "zh-TW": "src/locale/messages.zh_TW.xlf"
 | 
			
		||||
        }
 | 
			
		||||
@@ -59,10 +61,12 @@
 | 
			
		||||
                "path": "./extra-webpack.config.ts"
 | 
			
		||||
            },
 | 
			
		||||
            "outputPath": "dist/paperless-ui",
 | 
			
		||||
            "main": "src/main.ts",
 | 
			
		||||
            "outputHashing": "none",
 | 
			
		||||
            "index": "src/index.html",
 | 
			
		||||
            "main": "src/main.ts",
 | 
			
		||||
            "polyfills": "src/polyfills.ts",
 | 
			
		||||
            "polyfills": [
 | 
			
		||||
              "src/polyfills.ts"
 | 
			
		||||
            ],
 | 
			
		||||
            "tsConfig": "tsconfig.app.json",
 | 
			
		||||
            "localize": true,
 | 
			
		||||
            "assets": [
 | 
			
		||||
@@ -85,12 +89,15 @@
 | 
			
		||||
              "file-saver",
 | 
			
		||||
              "utif"
 | 
			
		||||
            ],
 | 
			
		||||
            "vendorChunk": true,
 | 
			
		||||
            "extractLicenses": false,
 | 
			
		||||
            "buildOptimizer": false,
 | 
			
		||||
            "sourceMap": true,
 | 
			
		||||
            "optimization": false,
 | 
			
		||||
            "namedChunks": true
 | 
			
		||||
            "namedChunks": true,
 | 
			
		||||
            "stylePreprocessorOptions": {
 | 
			
		||||
              "includePaths": [
 | 
			
		||||
                "."
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          "configurations": {
 | 
			
		||||
            "production": {
 | 
			
		||||
@@ -106,8 +113,6 @@
 | 
			
		||||
              "sourceMap": false,
 | 
			
		||||
              "namedChunks": false,
 | 
			
		||||
              "extractLicenses": true,
 | 
			
		||||
              "vendorChunk": false,
 | 
			
		||||
              "buildOptimizer": true,
 | 
			
		||||
              "budgets": [
 | 
			
		||||
                {
 | 
			
		||||
                  "type": "initial",
 | 
			
		||||
@@ -187,6 +192,30 @@
 | 
			
		||||
    },
 | 
			
		||||
    "@angular-eslint/schematics:library": {
 | 
			
		||||
      "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',
 | 
			
		||||
  ],
 | 
			
		||||
  transformIgnorePatterns: [
 | 
			
		||||
    `<rootDir>/node_modules/.pnpm/(?!.*\\.mjs$|lodash-es)`,
 | 
			
		||||
    `<rootDir>/node_modules/.pnpm/(?!.*\\.mjs$|lodash-es|@angular\\+common.*locales)`,
 | 
			
		||||
  ],
 | 
			
		||||
  moduleNameMapper: {
 | 
			
		||||
    '^src/(.*)': '<rootDir>/src/$1',
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "paperless-ngx-ui",
 | 
			
		||||
  "version": "2.16.3",
 | 
			
		||||
  "version": "2.17.1",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "preinstall": "npx only-allow pnpm",
 | 
			
		||||
    "ng": "ng",
 | 
			
		||||
@@ -11,28 +11,28 @@
 | 
			
		||||
  },
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@angular/cdk": "^19.2.14",
 | 
			
		||||
    "@angular/common": "~19.2.13",
 | 
			
		||||
    "@angular/compiler": "~19.2.14",
 | 
			
		||||
    "@angular/core": "~19.2.14",
 | 
			
		||||
    "@angular/forms": "~19.2.14",
 | 
			
		||||
    "@angular/localize": "~19.2.14",
 | 
			
		||||
    "@angular/platform-browser": "~19.2.14",
 | 
			
		||||
    "@angular/platform-browser-dynamic": "~19.2.14",
 | 
			
		||||
    "@angular/router": "~19.2.14",
 | 
			
		||||
    "@ng-bootstrap/ng-bootstrap": "^18.0.0",
 | 
			
		||||
    "@ng-select/ng-select": "^14.9.0",
 | 
			
		||||
    "@angular/cdk": "^20.0.4",
 | 
			
		||||
    "@angular/common": "~20.0.6",
 | 
			
		||||
    "@angular/compiler": "~20.0.6",
 | 
			
		||||
    "@angular/core": "~20.0.6",
 | 
			
		||||
    "@angular/forms": "~20.0.6",
 | 
			
		||||
    "@angular/localize": "~20.0.6",
 | 
			
		||||
    "@angular/platform-browser": "~20.0.6",
 | 
			
		||||
    "@angular/platform-browser-dynamic": "~20.0.6",
 | 
			
		||||
    "@angular/router": "~20.0.6",
 | 
			
		||||
    "@ng-bootstrap/ng-bootstrap": "^19.0.1",
 | 
			
		||||
    "@ng-select/ng-select": "^15.1.3",
 | 
			
		||||
    "@ngneat/dirty-check-forms": "^3.0.3",
 | 
			
		||||
    "@popperjs/core": "^2.11.8",
 | 
			
		||||
    "bootstrap": "^5.3.6",
 | 
			
		||||
    "bootstrap": "^5.3.7",
 | 
			
		||||
    "file-saver": "^2.0.5",
 | 
			
		||||
    "mime-names": "^1.0.0",
 | 
			
		||||
    "ng2-pdf-viewer": "^10.4.0",
 | 
			
		||||
    "ngx-bootstrap-icons": "^1.9.3",
 | 
			
		||||
    "ngx-color": "^10.0.0",
 | 
			
		||||
    "ngx-cookie-service": "^19.1.2",
 | 
			
		||||
    "ngx-device-detector": "^9.0.0",
 | 
			
		||||
    "ngx-ui-tour-ng-bootstrap": "^16.0.0",
 | 
			
		||||
    "ngx-cookie-service": "^20.0.1",
 | 
			
		||||
    "ngx-device-detector": "^10.0.2",
 | 
			
		||||
    "ngx-ui-tour-ng-bootstrap": "^17.0.0",
 | 
			
		||||
    "rxjs": "^7.8.2",
 | 
			
		||||
    "tslib": "^2.8.1",
 | 
			
		||||
    "utif": "^3.1.0",
 | 
			
		||||
@@ -40,26 +40,26 @@
 | 
			
		||||
    "zone.js": "^0.15.1"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@angular-builders/custom-webpack": "^19.0.1",
 | 
			
		||||
    "@angular-builders/jest": "^19.0.1",
 | 
			
		||||
    "@angular-devkit/build-angular": "^19.2.14",
 | 
			
		||||
    "@angular-devkit/core": "^19.2.14",
 | 
			
		||||
    "@angular-devkit/schematics": "^19.2.14",
 | 
			
		||||
    "@angular-eslint/builder": "19.7.0",
 | 
			
		||||
    "@angular-eslint/eslint-plugin": "19.7.0",
 | 
			
		||||
    "@angular-eslint/eslint-plugin-template": "19.7.0",
 | 
			
		||||
    "@angular-eslint/schematics": "19.7.0",
 | 
			
		||||
    "@angular-eslint/template-parser": "19.7.0",
 | 
			
		||||
    "@angular/cli": "~19.2.14",
 | 
			
		||||
    "@angular/compiler-cli": "~19.2.14",
 | 
			
		||||
    "@angular-builders/custom-webpack": "^20.0.0",
 | 
			
		||||
    "@angular-builders/jest": "^20.0.0",
 | 
			
		||||
    "@angular-devkit/core": "^20.0.4",
 | 
			
		||||
    "@angular-devkit/schematics": "^20.0.4",
 | 
			
		||||
    "@angular-eslint/builder": "20.1.1",
 | 
			
		||||
    "@angular-eslint/eslint-plugin": "20.1.1",
 | 
			
		||||
    "@angular-eslint/eslint-plugin-template": "20.1.1",
 | 
			
		||||
    "@angular-eslint/schematics": "20.1.1",
 | 
			
		||||
    "@angular-eslint/template-parser": "20.1.1",
 | 
			
		||||
    "@angular/build": "^20.0.4",
 | 
			
		||||
    "@angular/cli": "~20.0.4",
 | 
			
		||||
    "@angular/compiler-cli": "~20.0.6",
 | 
			
		||||
    "@codecov/webpack-plugin": "^1.9.1",
 | 
			
		||||
    "@playwright/test": "^1.51.1",
 | 
			
		||||
    "@playwright/test": "^1.53.2",
 | 
			
		||||
    "@types/jest": "^29.5.14",
 | 
			
		||||
    "@types/node": "^22.15.29",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^8.33.0",
 | 
			
		||||
    "@typescript-eslint/parser": "^8.33.0",
 | 
			
		||||
    "@typescript-eslint/utils": "^8.33.0",
 | 
			
		||||
    "eslint": "^9.28.0",
 | 
			
		||||
    "@types/node": "^24.0.10",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^8.35.1",
 | 
			
		||||
    "@typescript-eslint/parser": "^8.35.1",
 | 
			
		||||
    "@typescript-eslint/utils": "^8.35.1",
 | 
			
		||||
    "eslint": "^9.30.1",
 | 
			
		||||
    "jest": "29.7.0",
 | 
			
		||||
    "jest-environment-jsdom": "^29.7.0",
 | 
			
		||||
    "jest-junit": "^16.0.0",
 | 
			
		||||
@@ -67,14 +67,8 @@
 | 
			
		||||
    "jest-websocket-mock": "^2.5.0",
 | 
			
		||||
    "prettier-plugin-organize-imports": "^4.1.0",
 | 
			
		||||
    "ts-node": "~10.9.1",
 | 
			
		||||
    "typescript": "^5.5.4"
 | 
			
		||||
  },
 | 
			
		||||
  "packageManager": "pnpm@10.11.1",
 | 
			
		||||
  "devEngines": {
 | 
			
		||||
    "packageManager": {
 | 
			
		||||
      "name": "pnpm",
 | 
			
		||||
      "version": "10.11.1"
 | 
			
		||||
    }
 | 
			
		||||
    "typescript": "^5.8.3",
 | 
			
		||||
    "webpack": "^5.99.9"
 | 
			
		||||
  },
 | 
			
		||||
  "pnpm": {
 | 
			
		||||
    "onlyBuiltDependencies": [
 | 
			
		||||
@@ -84,6 +78,5 @@
 | 
			
		||||
      "lmdb",
 | 
			
		||||
      "msgpackr-extract"
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "typings": "./src/typings.d.ts"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4074
									
								
								src-ui/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4074
									
								
								src-ui/pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -20,6 +20,7 @@ import localeDe from '@angular/common/locales/de'
 | 
			
		||||
import localeEl from '@angular/common/locales/el'
 | 
			
		||||
import localeEnGb from '@angular/common/locales/en-GB'
 | 
			
		||||
import localeEs from '@angular/common/locales/es'
 | 
			
		||||
import localeFa from '@angular/common/locales/fa'
 | 
			
		||||
import localeFi from '@angular/common/locales/fi'
 | 
			
		||||
import localeFr from '@angular/common/locales/fr'
 | 
			
		||||
import localeHu from '@angular/common/locales/hu'
 | 
			
		||||
@@ -39,6 +40,7 @@ import localeSr from '@angular/common/locales/sr'
 | 
			
		||||
import localeSv from '@angular/common/locales/sv'
 | 
			
		||||
import localeTr from '@angular/common/locales/tr'
 | 
			
		||||
import localeUk from '@angular/common/locales/uk'
 | 
			
		||||
import localeVi from '@angular/common/locales/vi'
 | 
			
		||||
import localeZh from '@angular/common/locales/zh'
 | 
			
		||||
import localeZhHant from '@angular/common/locales/zh-Hant'
 | 
			
		||||
 | 
			
		||||
@@ -53,6 +55,7 @@ registerLocaleData(localeDe)
 | 
			
		||||
registerLocaleData(localeEl)
 | 
			
		||||
registerLocaleData(localeEnGb)
 | 
			
		||||
registerLocaleData(localeEs)
 | 
			
		||||
registerLocaleData(localeFa)
 | 
			
		||||
registerLocaleData(localeFi)
 | 
			
		||||
registerLocaleData(localeFr)
 | 
			
		||||
registerLocaleData(localeHu)
 | 
			
		||||
@@ -73,6 +76,7 @@ registerLocaleData(localeSr)
 | 
			
		||||
registerLocaleData(localeSv)
 | 
			
		||||
registerLocaleData(localeTr)
 | 
			
		||||
registerLocaleData(localeUk)
 | 
			
		||||
registerLocaleData(localeVi)
 | 
			
		||||
registerLocaleData(localeZh)
 | 
			
		||||
registerLocaleData(localeZhHant)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, OnDestroy, OnInit, Renderer2 } from '@angular/core'
 | 
			
		||||
import { Component, inject, OnDestroy, OnInit, Renderer2 } from '@angular/core'
 | 
			
		||||
import { Router, RouterOutlet } from '@angular/router'
 | 
			
		||||
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
 | 
			
		||||
import { first, Subscription } from 'rxjs'
 | 
			
		||||
@@ -29,22 +29,22 @@ import { WebsocketStatusService } from './services/websocket-status.service'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
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
 | 
			
		||||
  successSubscription: Subscription
 | 
			
		||||
  failedSubscription: Subscription
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    let anyWindow = window as any
 | 
			
		||||
    anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.mjs'
 | 
			
		||||
    this.settings.updateAppearanceSettings()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { AsyncPipe } from '@angular/common'
 | 
			
		||||
import { Component, OnDestroy, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  AbstractControl,
 | 
			
		||||
  FormControl,
 | 
			
		||||
@@ -57,6 +57,10 @@ export class ConfigComponent
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  implements OnInit, OnDestroy, DirtyComponent
 | 
			
		||||
{
 | 
			
		||||
  private configService = inject(ConfigService)
 | 
			
		||||
  private toastService = inject(ToastService)
 | 
			
		||||
  private settingsService = inject(SettingsService)
 | 
			
		||||
 | 
			
		||||
  public readonly ConfigOptionType = ConfigOptionType
 | 
			
		||||
 | 
			
		||||
  // generated dynamically
 | 
			
		||||
@@ -77,11 +81,7 @@ export class ConfigComponent
 | 
			
		||||
  storeSub: Subscription
 | 
			
		||||
  isDirty$: Observable<boolean>
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private configService: ConfigService,
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.configForm.addControl('id', new FormControl())
 | 
			
		||||
    PaperlessConfigOptions.forEach((option) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import {
 | 
			
		||||
  OnDestroy,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  ViewChild,
 | 
			
		||||
  inject,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
@@ -28,12 +29,8 @@ export class LogsComponent
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  implements OnInit, OnDestroy
 | 
			
		||||
{
 | 
			
		||||
  constructor(
 | 
			
		||||
    private logService: LogService,
 | 
			
		||||
    private changedetectorRef: ChangeDetectorRef
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
  private logService = inject(LogService)
 | 
			
		||||
  private changedetectorRef = inject(ChangeDetectorRef)
 | 
			
		||||
 | 
			
		||||
  public logs: string[] = []
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,10 @@ import { AsyncPipe, ViewportScroller } from '@angular/common'
 | 
			
		||||
import {
 | 
			
		||||
  AfterViewInit,
 | 
			
		||||
  Component,
 | 
			
		||||
  Inject,
 | 
			
		||||
  LOCALE_ID,
 | 
			
		||||
  OnDestroy,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  inject,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
@@ -104,6 +104,20 @@ export class SettingsComponent
 | 
			
		||||
  extends ComponentWithPermissions
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  settingsForm = new FormGroup({
 | 
			
		||||
@@ -179,21 +193,7 @@ export class SettingsComponent
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.settings.settingsSaved.subscribe(() => {
 | 
			
		||||
      if (!this.savePending) this.initialize()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { NgTemplateOutlet, SlicePipe } from '@angular/common'
 | 
			
		||||
import { Component, OnDestroy, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, inject, OnDestroy, OnInit } from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { Router } from '@angular/router'
 | 
			
		||||
import {
 | 
			
		||||
@@ -69,6 +69,10 @@ export class TasksComponent
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  implements OnInit, OnDestroy
 | 
			
		||||
{
 | 
			
		||||
  tasksService = inject(TasksService)
 | 
			
		||||
  private modalService = inject(NgbModal)
 | 
			
		||||
  private readonly router = inject(Router)
 | 
			
		||||
 | 
			
		||||
  public activeTab: TaskTab
 | 
			
		||||
  public selectedTasks: Set<number> = new Set()
 | 
			
		||||
  public togggleAll: boolean = false
 | 
			
		||||
@@ -105,14 +109,6 @@ export class TasksComponent
 | 
			
		||||
      : $localize`Dismiss all`
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    public tasksService: TasksService,
 | 
			
		||||
    private modalService: NgbModal,
 | 
			
		||||
    private readonly router: Router
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this.tasksService.reload()
 | 
			
		||||
    timer(5000, 5000)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, OnDestroy } from '@angular/core'
 | 
			
		||||
import { Component, OnDestroy, inject } from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { Router } from '@angular/router'
 | 
			
		||||
import {
 | 
			
		||||
@@ -36,19 +36,19 @@ export class TrashComponent
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  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 selectedDocuments: Set<number> = new Set()
 | 
			
		||||
  public allToggled: boolean = false
 | 
			
		||||
  public page: number = 1
 | 
			
		||||
  public totalDocuments: number
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private trashService: TrashService,
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private modalService: NgbModal,
 | 
			
		||||
    private settingsService: SettingsService,
 | 
			
		||||
    private router: Router
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.reload()
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, OnDestroy, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
 | 
			
		||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
import { Subject, first, takeUntil } from 'rxjs'
 | 
			
		||||
@@ -31,22 +31,18 @@ export class UsersAndGroupsComponent
 | 
			
		||||
  extends ComponentWithPermissions
 | 
			
		||||
  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[]
 | 
			
		||||
  groups: Group[]
 | 
			
		||||
 | 
			
		||||
  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 {
 | 
			
		||||
    this.usersService
 | 
			
		||||
      .listAll(null, null, { full_perms: true })
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ import {
 | 
			
		||||
  moveItemInArray,
 | 
			
		||||
} from '@angular/cdk/drag-drop'
 | 
			
		||||
import { NgClass } from '@angular/common'
 | 
			
		||||
import { Component, HostListener, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, HostListener, inject, OnInit } from '@angular/core'
 | 
			
		||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router'
 | 
			
		||||
import {
 | 
			
		||||
  NgbCollapseModule,
 | 
			
		||||
@@ -74,27 +74,27 @@ export class AppFrameComponent
 | 
			
		||||
  extends ComponentWithPermissions
 | 
			
		||||
  implements OnInit, ComponentCanDeactivate
 | 
			
		||||
{
 | 
			
		||||
  versionString = `${environment.appTitle} ${environment.version}`
 | 
			
		||||
  router = inject(Router)
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  isMenuCollapsed: boolean = true
 | 
			
		||||
 | 
			
		||||
  slimSidebarAnimating: boolean = false
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    const permissionsService = this.permissionsService
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      permissionsService.currentUserCan(
 | 
			
		||||
@@ -142,6 +142,10 @@ export class AppFrameComponent
 | 
			
		||||
    }, 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 {
 | 
			
		||||
    return this.settingsService.get(SETTINGS_KEYS.APP_TITLE)
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import {
 | 
			
		||||
  QueryList,
 | 
			
		||||
  ViewChild,
 | 
			
		||||
  ViewChildren,
 | 
			
		||||
  inject,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { Router } from '@angular/router'
 | 
			
		||||
@@ -69,6 +70,17 @@ import { WorkflowEditDialogComponent } from '../../common/edit-dialog/workflow-e
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
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 query: string
 | 
			
		||||
  public queryDebounce: Subject<string>
 | 
			
		||||
@@ -90,18 +102,7 @@ export class GlobalSearchComponent implements OnInit {
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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,
 | 
			
		||||
    private locationStrategy: LocationStrategy
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.queryDebounce = new Subject<string>()
 | 
			
		||||
 | 
			
		||||
    this.queryDebounce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, OnDestroy, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  NgbDropdownModule,
 | 
			
		||||
  NgbProgressbarModule,
 | 
			
		||||
@@ -20,7 +20,7 @@ import { ToastComponent } from '../../common/toast/toast.component'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class ToastsDropdownComponent implements OnInit, OnDestroy {
 | 
			
		||||
  constructor(public toastService: ToastService) {}
 | 
			
		||||
  toastService = inject(ToastService)
 | 
			
		||||
 | 
			
		||||
  private subscription: Subscription
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { DecimalPipe } from '@angular/common'
 | 
			
		||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
 | 
			
		||||
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { Subject } from 'rxjs'
 | 
			
		||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
 | 
			
		||||
@@ -12,9 +12,7 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
 | 
			
		||||
  imports: [DecimalPipe, SafeHtmlPipe],
 | 
			
		||||
})
 | 
			
		||||
export class ConfirmDialogComponent extends LoadingComponentWithPermissions {
 | 
			
		||||
  constructor(public activeModal: NgbActiveModal) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
  activeModal = inject(NgbActiveModal)
 | 
			
		||||
 | 
			
		||||
  @Output()
 | 
			
		||||
  public confirmClicked = new EventEmitter()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
import { Component, TemplateRef, ViewChild } from '@angular/core'
 | 
			
		||||
import { Component, TemplateRef, ViewChild, inject } from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import {
 | 
			
		||||
  PDFDocumentProxy,
 | 
			
		||||
  PdfViewerComponent,
 | 
			
		||||
@@ -17,6 +16,8 @@ import { ConfirmDialogComponent } from '../confirm-dialog.component'
 | 
			
		||||
  imports: [PdfViewerModule, FormsModule, ReactiveFormsModule, SafeHtmlPipe],
 | 
			
		||||
})
 | 
			
		||||
export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
 | 
			
		||||
  private documentService = inject(DocumentService)
 | 
			
		||||
 | 
			
		||||
  public documentID: number
 | 
			
		||||
  public pages: number[] = []
 | 
			
		||||
  public currentPage: number = 1
 | 
			
		||||
@@ -34,11 +35,8 @@ export class DeletePagesConfirmDialogComponent extends ConfirmDialogComponent {
 | 
			
		||||
    return this.documentService.getPreviewUrl(this.documentID)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    private documentService: DocumentService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(activeModal)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public pdfPreviewLoaded(pdf: PDFDocumentProxy) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,8 @@ import {
 | 
			
		||||
  DragDropModule,
 | 
			
		||||
  moveItemInArray,
 | 
			
		||||
} from '@angular/cdk/drag-drop'
 | 
			
		||||
import { Component, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnInit, inject } from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
import { takeUntil } from 'rxjs'
 | 
			
		||||
import { Document } from 'src/app/data/document'
 | 
			
		||||
@@ -28,6 +27,9 @@ export class MergeConfirmDialogComponent
 | 
			
		||||
  extends ConfirmDialogComponent
 | 
			
		||||
  implements OnInit
 | 
			
		||||
{
 | 
			
		||||
  private documentService = inject(DocumentService)
 | 
			
		||||
  private permissionService = inject(PermissionsService)
 | 
			
		||||
 | 
			
		||||
  public documentIDs: number[] = []
 | 
			
		||||
  public archiveFallback: boolean = false
 | 
			
		||||
  public deleteOriginals: boolean = false
 | 
			
		||||
@@ -38,12 +40,8 @@ export class MergeConfirmDialogComponent
 | 
			
		||||
 | 
			
		||||
  public metadataDocumentID: number = -1
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    private documentService: DocumentService,
 | 
			
		||||
    private permissionService: PermissionsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(activeModal)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
import { NgStyle } from '@angular/common'
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { Component, inject } from '@angular/core'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
 | 
			
		||||
import { DocumentService } from 'src/app/services/rest/document.service'
 | 
			
		||||
@@ -13,6 +12,8 @@ import { ConfirmDialogComponent } from '../confirm-dialog.component'
 | 
			
		||||
  imports: [NgStyle, NgxBootstrapIconsModule, SafeHtmlPipe],
 | 
			
		||||
})
 | 
			
		||||
export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
 | 
			
		||||
  documentService = inject(DocumentService)
 | 
			
		||||
 | 
			
		||||
  public documentID: number
 | 
			
		||||
  public showPDFNote: boolean = true
 | 
			
		||||
 | 
			
		||||
@@ -25,11 +26,8 @@ export class RotateConfirmDialogComponent extends ConfirmDialogComponent {
 | 
			
		||||
    return degrees
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    public documentService: DocumentService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(activeModal)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  rotate(clockwise: boolean = true) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnInit, inject } from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
import { Document } from 'src/app/data/document'
 | 
			
		||||
@@ -23,6 +22,9 @@ export class SplitConfirmDialogComponent
 | 
			
		||||
  extends ConfirmDialogComponent
 | 
			
		||||
  implements OnInit
 | 
			
		||||
{
 | 
			
		||||
  private documentService = inject(DocumentService)
 | 
			
		||||
  private permissionService = inject(PermissionsService)
 | 
			
		||||
 | 
			
		||||
  public get pagesString(): string {
 | 
			
		||||
    let pagesStr = ''
 | 
			
		||||
 | 
			
		||||
@@ -62,12 +64,8 @@ export class SplitConfirmDialogComponent
 | 
			
		||||
    return this.documentService.getPreviewUrl(this.documentID)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    private documentService: DocumentService,
 | 
			
		||||
    private permissionService: PermissionsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(activeModal)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.confirmButtonEnabled = this.pages.size > 0
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { CurrencyPipe, getLocaleCurrencyCode } from '@angular/common'
 | 
			
		||||
import { Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, Input, LOCALE_ID, OnInit, inject } from '@angular/core'
 | 
			
		||||
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { takeUntil } from 'rxjs'
 | 
			
		||||
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
 | 
			
		||||
@@ -20,6 +20,9 @@ export class CustomFieldDisplayComponent
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  implements OnInit
 | 
			
		||||
{
 | 
			
		||||
  private customFieldService = inject(CustomFieldsService)
 | 
			
		||||
  private documentService = inject(DocumentService)
 | 
			
		||||
 | 
			
		||||
  CustomFieldDataType = CustomFieldDataType
 | 
			
		||||
 | 
			
		||||
  private _document: Document
 | 
			
		||||
@@ -63,11 +66,9 @@ export class CustomFieldDisplayComponent
 | 
			
		||||
 | 
			
		||||
  private defaultCurrencyCode: any
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private customFieldService: CustomFieldsService,
 | 
			
		||||
    private documentService: DocumentService,
 | 
			
		||||
    @Inject(LOCALE_ID) currentLocale: string
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    const currentLocale = inject(LOCALE_ID)
 | 
			
		||||
 | 
			
		||||
    super()
 | 
			
		||||
    this.defaultCurrencyCode = getLocaleCurrencyCode(currentLocale)
 | 
			
		||||
    this.customFieldService.listAll().subscribe((r) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import {
 | 
			
		||||
  QueryList,
 | 
			
		||||
  ViewChild,
 | 
			
		||||
  ViewChildren,
 | 
			
		||||
  inject,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
@@ -37,6 +38,11 @@ import { CustomFieldEditDialogComponent } from '../edit-dialog/custom-field-edit
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissions {
 | 
			
		||||
  private customFieldsService = inject(CustomFieldsService)
 | 
			
		||||
  private modalService = inject(NgbModal)
 | 
			
		||||
  private toastService = inject(ToastService)
 | 
			
		||||
  private permissionsService = inject(PermissionsService)
 | 
			
		||||
 | 
			
		||||
  public popperOptions = pngxPopperOptions
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
@@ -78,12 +84,7 @@ export class CustomFieldsDropdownComponent extends LoadingComponentWithPermissio
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private customFieldsService: CustomFieldsService,
 | 
			
		||||
    private modalService: NgbModal,
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private permissionsService: PermissionsService
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.getFields()
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import { NgTemplateOutlet } from '@angular/common'
 | 
			
		||||
import {
 | 
			
		||||
  Component,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  inject,
 | 
			
		||||
  Input,
 | 
			
		||||
  Output,
 | 
			
		||||
  QueryList,
 | 
			
		||||
@@ -178,6 +179,8 @@ export class CustomFieldQueriesModel {
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPermissions {
 | 
			
		||||
  protected customFieldsService = inject(CustomFieldsService)
 | 
			
		||||
 | 
			
		||||
  public CustomFieldQueryComponentType = CustomFieldQueryElementType
 | 
			
		||||
  public CustomFieldQueryOperator = CustomFieldQueryOperator
 | 
			
		||||
  public CustomFieldDataType = CustomFieldDataType
 | 
			
		||||
@@ -245,7 +248,7 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm
 | 
			
		||||
 | 
			
		||||
  public readonly today: string = new Date().toISOString().split('T')[0]
 | 
			
		||||
 | 
			
		||||
  constructor(protected customFieldsService: CustomFieldsService) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.selectionModel = new CustomFieldQueriesModel()
 | 
			
		||||
    this.getFields()
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import {
 | 
			
		||||
  OnDestroy,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  Output,
 | 
			
		||||
  inject,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import {
 | 
			
		||||
@@ -63,7 +64,9 @@ export enum RelativeDate {
 | 
			
		||||
export class DatesDropdownComponent implements OnInit, OnDestroy {
 | 
			
		||||
  public popperOptions = pngxPopperOptions
 | 
			
		||||
 | 
			
		||||
  constructor(settings: SettingsService) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    const settings = inject(SettingsService)
 | 
			
		||||
 | 
			
		||||
    this.datePlaceHolder = settings.getLocalizedDateInputFormat()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,6 @@
 | 
			
		||||
    <pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
 | 
			
		||||
    @if (patternRequired) {
 | 
			
		||||
      <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>
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { Component, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
			
		||||
import { Correspondent } from 'src/app/data/correspondent'
 | 
			
		||||
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
 | 
			
		||||
@@ -13,6 +12,7 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
 | 
			
		||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service'
 | 
			
		||||
import { UserService } from 'src/app/services/rest/user.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 { SelectComponent } from '../../input/select/select.component'
 | 
			
		||||
import { TextComponent } from '../../input/text/text.component'
 | 
			
		||||
@@ -22,6 +22,7 @@ import { TextComponent } from '../../input/text/text.component'
 | 
			
		||||
  templateUrl: './correspondent-edit-dialog.component.html',
 | 
			
		||||
  styleUrls: ['./correspondent-edit-dialog.component.scss'],
 | 
			
		||||
  imports: [
 | 
			
		||||
    CheckComponent,
 | 
			
		||||
    SelectComponent,
 | 
			
		||||
    PermissionsFormComponent,
 | 
			
		||||
    TextComponent,
 | 
			
		||||
@@ -31,13 +32,11 @@ import { TextComponent } from '../../input/text/text.component'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class CorrespondentEditDialogComponent extends EditDialogComponent<Correspondent> {
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: CorrespondentService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(CorrespondentService)
 | 
			
		||||
    this.userService = inject(UserService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getCreateTitle() {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import {
 | 
			
		||||
  OnInit,
 | 
			
		||||
  QueryList,
 | 
			
		||||
  ViewChildren,
 | 
			
		||||
  inject,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormArray,
 | 
			
		||||
@@ -13,7 +14,6 @@ import {
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
import { takeUntil } from 'rxjs'
 | 
			
		||||
import {
 | 
			
		||||
@@ -54,13 +54,11 @@ export class CustomFieldEditDialogComponent
 | 
			
		||||
      .select_options as FormArray
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: CustomFieldsService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(CustomFieldsService)
 | 
			
		||||
    this.userService = inject(UserService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,8 +14,6 @@
 | 
			
		||||
      <pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
 | 
			
		||||
      @if (patternRequired) {
 | 
			
		||||
        <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>
 | 
			
		||||
      }
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { Component, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
			
		||||
import { DocumentType } from 'src/app/data/document-type'
 | 
			
		||||
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
 | 
			
		||||
@@ -13,6 +12,7 @@ import { IfOwnerDirective } from 'src/app/directives/if-owner.directive'
 | 
			
		||||
import { DocumentTypeService } from 'src/app/services/rest/document-type.service'
 | 
			
		||||
import { UserService } from 'src/app/services/rest/user.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 { SelectComponent } from '../../input/select/select.component'
 | 
			
		||||
import { TextComponent } from '../../input/text/text.component'
 | 
			
		||||
@@ -22,6 +22,7 @@ import { TextComponent } from '../../input/text/text.component'
 | 
			
		||||
  templateUrl: './document-type-edit-dialog.component.html',
 | 
			
		||||
  styleUrls: ['./document-type-edit-dialog.component.scss'],
 | 
			
		||||
  imports: [
 | 
			
		||||
    CheckComponent,
 | 
			
		||||
    SelectComponent,
 | 
			
		||||
    PermissionsFormComponent,
 | 
			
		||||
    TextComponent,
 | 
			
		||||
@@ -31,13 +32,11 @@ import { TextComponent } from '../../input/text/text.component'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class DocumentTypeEditDialogComponent extends EditDialogComponent<DocumentType> {
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: DocumentTypeService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(DocumentTypeService)
 | 
			
		||||
    this.userService = inject(UserService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getCreateTitle() {
 | 
			
		||||
 
 | 
			
		||||
@@ -41,13 +41,9 @@ import { EditDialogComponent, EditDialogMode } from './edit-dialog.component'
 | 
			
		||||
  imports: [FormsModule, ReactiveFormsModule],
 | 
			
		||||
})
 | 
			
		||||
class TestComponent extends EditDialogComponent<Tag> {
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: TagService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = TestBed.inject(TagService)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getForm(): FormGroup<any> {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,11 @@
 | 
			
		||||
import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  Directive,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  Input,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  Output,
 | 
			
		||||
  inject,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
import { FormGroup } from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { Observable } from 'rxjs'
 | 
			
		||||
@@ -29,14 +36,12 @@ export abstract class EditDialogComponent<
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  implements OnInit
 | 
			
		||||
{
 | 
			
		||||
  constructor(
 | 
			
		||||
    protected service: AbstractPaperlessService<T>,
 | 
			
		||||
    private activeModal: NgbActiveModal,
 | 
			
		||||
    private userService: UserService,
 | 
			
		||||
    protected settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
  protected service = inject<AbstractPaperlessService<T>>(
 | 
			
		||||
    AbstractPaperlessService
 | 
			
		||||
  )
 | 
			
		||||
  protected activeModal = inject(NgbActiveModal)
 | 
			
		||||
  protected userService = inject(UserService)
 | 
			
		||||
  protected settingsService = inject(SettingsService)
 | 
			
		||||
 | 
			
		||||
  users: User[]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { Component, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
			
		||||
import { Group } from 'src/app/data/group'
 | 
			
		||||
import { GroupService } from 'src/app/services/rest/group.service'
 | 
			
		||||
@@ -26,13 +25,11 @@ import { PermissionsSelectComponent } from '../../permissions-select/permissions
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class GroupEditDialogComponent extends EditDialogComponent<Group> {
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: GroupService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(GroupService)
 | 
			
		||||
    this.userService = inject(UserService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getCreateTitle() {
 | 
			
		||||
@@ -46,7 +43,7 @@ export class GroupEditDialogComponent extends EditDialogComponent<Group> {
 | 
			
		||||
  getForm(): FormGroup {
 | 
			
		||||
    return new FormGroup({
 | 
			
		||||
      name: new FormControl(''),
 | 
			
		||||
      permissions: new FormControl(null),
 | 
			
		||||
      permissions: new FormControl([]),
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,11 @@
 | 
			
		||||
import { Component, ViewChild } from '@angular/core'
 | 
			
		||||
import { Component, ViewChild, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import {
 | 
			
		||||
  NgbActiveModal,
 | 
			
		||||
  NgbAlert,
 | 
			
		||||
  NgbAlertModule,
 | 
			
		||||
} from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgbAlert, NgbAlertModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
			
		||||
import { IMAPSecurity, MailAccount } from 'src/app/data/mail-account'
 | 
			
		||||
import { MailAccountService } from 'src/app/services/rest/mail-account.service'
 | 
			
		||||
@@ -47,13 +43,11 @@ export class MailAccountEditDialogComponent extends EditDialogComponent<MailAcco
 | 
			
		||||
 | 
			
		||||
  @ViewChild('testResultAlert', { static: false }) testResultAlert: NgbAlert
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: MailAccountService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(MailAccountService)
 | 
			
		||||
    this.userService = inject(UserService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getCreateTitle() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { Component, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { first } from 'rxjs'
 | 
			
		||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
			
		||||
import { Correspondent } from 'src/app/data/correspondent'
 | 
			
		||||
@@ -155,32 +154,34 @@ const METADATA_CORRESPONDENT_OPTIONS = [
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class MailRuleEditDialogComponent extends EditDialogComponent<MailRule> {
 | 
			
		||||
  private accountService: MailAccountService
 | 
			
		||||
  private correspondentService: CorrespondentService
 | 
			
		||||
  private documentTypeService: DocumentTypeService
 | 
			
		||||
 | 
			
		||||
  accounts: MailAccount[]
 | 
			
		||||
  correspondents: Correspondent[]
 | 
			
		||||
  documentTypes: DocumentType[]
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: MailRuleService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    accountService: MailAccountService,
 | 
			
		||||
    correspondentService: CorrespondentService,
 | 
			
		||||
    documentTypeService: DocumentTypeService,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(MailRuleService)
 | 
			
		||||
    this.accountService = inject(MailAccountService)
 | 
			
		||||
    this.correspondentService = inject(CorrespondentService)
 | 
			
		||||
    this.documentTypeService = inject(DocumentTypeService)
 | 
			
		||||
    this.userService = inject(UserService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
 | 
			
		||||
    accountService
 | 
			
		||||
    this.accountService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((result) => (this.accounts = result.results))
 | 
			
		||||
 | 
			
		||||
    correspondentService
 | 
			
		||||
    this.correspondentService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((result) => (this.correspondents = result.results))
 | 
			
		||||
 | 
			
		||||
    documentTypeService
 | 
			
		||||
    this.documentTypeService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((result) => (this.documentTypes = result.results))
 | 
			
		||||
 
 | 
			
		||||
@@ -64,8 +64,6 @@
 | 
			
		||||
    <pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
 | 
			
		||||
    @if (patternRequired) {
 | 
			
		||||
      <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>
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
 | 
			
		||||
import { Component, OnDestroy } from '@angular/core'
 | 
			
		||||
import { Component, OnDestroy, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import { NgbAccordionModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgSelectComponent } from '@ng-select/ng-select'
 | 
			
		||||
import {
 | 
			
		||||
  Observable,
 | 
			
		||||
@@ -60,6 +60,8 @@ export class StoragePathEditDialogComponent
 | 
			
		||||
  extends EditDialogComponent<StoragePath>
 | 
			
		||||
  implements OnDestroy
 | 
			
		||||
{
 | 
			
		||||
  private documentsService = inject(DocumentService)
 | 
			
		||||
 | 
			
		||||
  public documentsInput$ = new Subject<string>()
 | 
			
		||||
  public foundDocuments$: Observable<Document[]>
 | 
			
		||||
  private testDocument: Document
 | 
			
		||||
@@ -68,14 +70,11 @@ export class StoragePathEditDialogComponent
 | 
			
		||||
  public loading = false
 | 
			
		||||
  public testLoading = false
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: StoragePathService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService,
 | 
			
		||||
    private documentsService: DocumentService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(StoragePathService)
 | 
			
		||||
    this.userService = inject(UserService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
    this.initPathObservables()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,6 @@
 | 
			
		||||
    <pngx-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></pngx-input-select>
 | 
			
		||||
    @if (patternRequired) {
 | 
			
		||||
      <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>
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { Component, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
			
		||||
import { DEFAULT_MATCHING_ALGORITHM } from 'src/app/data/matching-model'
 | 
			
		||||
import { Tag } from 'src/app/data/tag'
 | 
			
		||||
@@ -36,13 +35,11 @@ import { TextComponent } from '../../input/text/text.component'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class TagEditDialogComponent extends EditDialogComponent<Tag> {
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: TagService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(TagService)
 | 
			
		||||
    this.userService = inject(UserService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getCreateTitle() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnInit, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { first } from 'rxjs'
 | 
			
		||||
import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component'
 | 
			
		||||
import { Group } from 'src/app/data/group'
 | 
			
		||||
@@ -37,21 +36,21 @@ export class UserEditDialogComponent
 | 
			
		||||
  extends EditDialogComponent<User>
 | 
			
		||||
  implements OnInit
 | 
			
		||||
{
 | 
			
		||||
  private toastService = inject(ToastService)
 | 
			
		||||
  private permissionsService = inject(PermissionsService)
 | 
			
		||||
  private groupsService: GroupService
 | 
			
		||||
 | 
			
		||||
  groups: Group[]
 | 
			
		||||
  passwordIsSet: boolean = false
 | 
			
		||||
  public totpLoading: boolean = false
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: UserService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    groupsService: GroupService,
 | 
			
		||||
    settingsService: SettingsService,
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private permissionsService: PermissionsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, service, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(UserService)
 | 
			
		||||
    this.groupsService = inject(GroupService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
 | 
			
		||||
    groupsService
 | 
			
		||||
    this.groupsService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((result) => (this.groups = result.results))
 | 
			
		||||
 
 | 
			
		||||
@@ -129,7 +129,7 @@
 | 
			
		||||
            formControlName="schedule_offset_days"
 | 
			
		||||
            [showAdd]="false"
 | 
			
		||||
            [error]="error?.schedule_offset_days"
 | 
			
		||||
            hint="Positive values will trigger the workflow before the date, negative values after."
 | 
			
		||||
            hint="Positive values will trigger after the date, negative values before."
 | 
			
		||||
            i18n-hint
 | 
			
		||||
          ></pngx-input-number>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ import {
 | 
			
		||||
  moveItemInArray,
 | 
			
		||||
} from '@angular/cdk/drag-drop'
 | 
			
		||||
import { NgTemplateOutlet } from '@angular/common'
 | 
			
		||||
import { Component, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnInit, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormArray,
 | 
			
		||||
  FormControl,
 | 
			
		||||
@@ -12,7 +12,7 @@ import {
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  ReactiveFormsModule,
 | 
			
		||||
} from '@angular/forms'
 | 
			
		||||
import { NgbAccordionModule, NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
import { first } from 'rxjs'
 | 
			
		||||
import { Correspondent } from 'src/app/data/correspondent'
 | 
			
		||||
@@ -171,6 +171,12 @@ export class WorkflowEditDialogComponent
 | 
			
		||||
  public WorkflowTriggerType = WorkflowTriggerType
 | 
			
		||||
  public WorkflowActionType = WorkflowActionType
 | 
			
		||||
 | 
			
		||||
  private correspondentService: CorrespondentService
 | 
			
		||||
  private documentTypeService: DocumentTypeService
 | 
			
		||||
  private storagePathService: StoragePathService
 | 
			
		||||
  private mailRuleService: MailRuleService
 | 
			
		||||
  private customFieldsService: CustomFieldsService
 | 
			
		||||
 | 
			
		||||
  templates: Workflow[]
 | 
			
		||||
  correspondents: Correspondent[]
 | 
			
		||||
  documentTypes: DocumentType[]
 | 
			
		||||
@@ -183,40 +189,38 @@ export class WorkflowEditDialogComponent
 | 
			
		||||
 | 
			
		||||
  private allowedActionTypes = []
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    service: WorkflowService,
 | 
			
		||||
    activeModal: NgbActiveModal,
 | 
			
		||||
    correspondentService: CorrespondentService,
 | 
			
		||||
    documentTypeService: DocumentTypeService,
 | 
			
		||||
    storagePathService: StoragePathService,
 | 
			
		||||
    mailRuleService: MailRuleService,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    settingsService: SettingsService,
 | 
			
		||||
    customFieldsService: CustomFieldsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super(service, activeModal, userService, settingsService)
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.service = inject(WorkflowService)
 | 
			
		||||
    this.correspondentService = inject(CorrespondentService)
 | 
			
		||||
    this.documentTypeService = inject(DocumentTypeService)
 | 
			
		||||
    this.storagePathService = inject(StoragePathService)
 | 
			
		||||
    this.mailRuleService = inject(MailRuleService)
 | 
			
		||||
    this.userService = inject(UserService)
 | 
			
		||||
    this.settingsService = inject(SettingsService)
 | 
			
		||||
    this.customFieldsService = inject(CustomFieldsService)
 | 
			
		||||
 | 
			
		||||
    correspondentService
 | 
			
		||||
    this.correspondentService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((result) => (this.correspondents = result.results))
 | 
			
		||||
 | 
			
		||||
    documentTypeService
 | 
			
		||||
    this.documentTypeService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((result) => (this.documentTypes = result.results))
 | 
			
		||||
 | 
			
		||||
    storagePathService
 | 
			
		||||
    this.storagePathService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((result) => (this.storagePaths = result.results))
 | 
			
		||||
 | 
			
		||||
    mailRuleService
 | 
			
		||||
    this.mailRuleService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((result) => (this.mailRules = result.results))
 | 
			
		||||
 | 
			
		||||
    customFieldsService
 | 
			
		||||
    this.customFieldsService
 | 
			
		||||
      .listAll()
 | 
			
		||||
      .pipe(first())
 | 
			
		||||
      .subscribe((result) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, Input } from '@angular/core'
 | 
			
		||||
import { Component, Input, inject } from '@angular/core'
 | 
			
		||||
import { FormsModule } from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
@@ -13,6 +13,10 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
 | 
			
		||||
  imports: [FormsModule, NgxBootstrapIconsModule],
 | 
			
		||||
})
 | 
			
		||||
export class EmailDocumentDialogComponent extends LoadingComponentWithPermissions {
 | 
			
		||||
  private activeModal = inject(NgbActiveModal)
 | 
			
		||||
  private documentService = inject(DocumentService)
 | 
			
		||||
  private toastService = inject(ToastService)
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  title = $localize`Email Document`
 | 
			
		||||
 | 
			
		||||
@@ -37,11 +41,7 @@ export class EmailDocumentDialogComponent extends LoadingComponentWithPermission
 | 
			
		||||
  public emailSubject: string = ''
 | 
			
		||||
  public emailMessage: string = ''
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private activeModal: NgbActiveModal,
 | 
			
		||||
    private documentService: DocumentService,
 | 
			
		||||
    private toastService: ToastService
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.loading = false
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import {
 | 
			
		||||
  OnInit,
 | 
			
		||||
  Output,
 | 
			
		||||
  ViewChild,
 | 
			
		||||
  inject,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
@@ -434,6 +435,9 @@ export class FilterableDropdownComponent
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  implements OnInit
 | 
			
		||||
{
 | 
			
		||||
  private filterPipe = inject(FilterPipe)
 | 
			
		||||
  private hotkeyService = inject(HotKeyService)
 | 
			
		||||
 | 
			
		||||
  @ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
 | 
			
		||||
  @ViewChild('dropdown') dropdown: NgbDropdown
 | 
			
		||||
  @ViewChild('buttonItems') buttonItems: ElementRef
 | 
			
		||||
@@ -536,10 +540,7 @@ export class FilterableDropdownComponent
 | 
			
		||||
 | 
			
		||||
  private keyboardIndex: number
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private filterPipe: FilterPipe,
 | 
			
		||||
    private hotkeyService: HotKeyService
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    this.selectionModelChange.subscribe((updatedModel) => {
 | 
			
		||||
      this.modelIsDirty = updatedModel.isDirty()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { Component, inject } from '@angular/core'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
 | 
			
		||||
const SYMBOLS = {
 | 
			
		||||
@@ -19,11 +19,11 @@ const SYMBOLS = {
 | 
			
		||||
  styleUrl: './hotkey-dialog.component.scss',
 | 
			
		||||
})
 | 
			
		||||
export class HotkeyDialogComponent {
 | 
			
		||||
  activeModal = inject(NgbActiveModal)
 | 
			
		||||
 | 
			
		||||
  public title: string = $localize`Keyboard shortcuts`
 | 
			
		||||
  public hotkeys: Map<string, string> = new Map()
 | 
			
		||||
 | 
			
		||||
  constructor(public activeModal: NgbActiveModal) {}
 | 
			
		||||
 | 
			
		||||
  public close(): void {
 | 
			
		||||
    this.activeModal.close()
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import {
 | 
			
		||||
  Component,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  forwardRef,
 | 
			
		||||
  inject,
 | 
			
		||||
  Input,
 | 
			
		||||
  Output,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
@@ -55,7 +56,9 @@ import { UrlComponent } from '../url/url.component'
 | 
			
		||||
export class CustomFieldsValuesComponent extends AbstractInputComponent<Object> {
 | 
			
		||||
  public CustomFieldDataType = CustomFieldDataType
 | 
			
		||||
 | 
			
		||||
  constructor(customFieldsService: CustomFieldsService) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    const customFieldsService = inject(CustomFieldsService)
 | 
			
		||||
 | 
			
		||||
    super()
 | 
			
		||||
    customFieldsService.listAll().subscribe((items) => {
 | 
			
		||||
      this.fields = items.results
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import {
 | 
			
		||||
  Component,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  forwardRef,
 | 
			
		||||
  inject,
 | 
			
		||||
  Input,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  Output,
 | 
			
		||||
@@ -45,13 +46,9 @@ export class DateComponent
 | 
			
		||||
  extends AbstractInputComponent<string>
 | 
			
		||||
  implements OnInit
 | 
			
		||||
{
 | 
			
		||||
  constructor(
 | 
			
		||||
    private settings: SettingsService,
 | 
			
		||||
    private ngbDateParserFormatter: NgbDateParserFormatter,
 | 
			
		||||
    private isoDateAdapter: NgbDateAdapter<string>
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
  private settings = inject(SettingsService)
 | 
			
		||||
  private ngbDateParserFormatter = inject(NgbDateParserFormatter)
 | 
			
		||||
  private isoDateAdapter = inject<NgbDateAdapter<string>>(NgbDateAdapter)
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  suggestions: string[]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,12 @@
 | 
			
		||||
import { AsyncPipe, NgTemplateOutlet } from '@angular/common'
 | 
			
		||||
import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  Component,
 | 
			
		||||
  forwardRef,
 | 
			
		||||
  inject,
 | 
			
		||||
  Input,
 | 
			
		||||
  OnDestroy,
 | 
			
		||||
  OnInit,
 | 
			
		||||
} from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  NG_VALUE_ACCESSOR,
 | 
			
		||||
@@ -52,6 +59,8 @@ export class DocumentLinkComponent
 | 
			
		||||
  extends AbstractInputComponent<any[]>
 | 
			
		||||
  implements OnInit, OnDestroy
 | 
			
		||||
{
 | 
			
		||||
  private documentsService = inject(DocumentService)
 | 
			
		||||
 | 
			
		||||
  documentsInput$ = new Subject<string>()
 | 
			
		||||
  foundDocuments$: Observable<Document[]>
 | 
			
		||||
  loading = false
 | 
			
		||||
@@ -75,10 +84,6 @@ export class DocumentLinkComponent
 | 
			
		||||
    return this.selectedDocuments.map((d) => d.id)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(private documentsService: DocumentService) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this.loadDocs()
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
 | 
			
		||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
 | 
			
		||||
import { LOCALE_ID } from '@angular/core'
 | 
			
		||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
 | 
			
		||||
import { NG_VALUE_ACCESSOR } from '@angular/forms'
 | 
			
		||||
import { MonetaryComponent } from './monetary.component'
 | 
			
		||||
@@ -41,8 +42,6 @@ describe('MonetaryComponent', () => {
 | 
			
		||||
 | 
			
		||||
  it('should set the default currency code based on LOCALE_ID', () => {
 | 
			
		||||
    expect(component.defaultCurrencyCode).toEqual('USD') // default
 | 
			
		||||
    component = new MonetaryComponent('pt-BR')
 | 
			
		||||
    expect(component.defaultCurrencyCode).toEqual('BRL')
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('should support setting a default currency code', () => {
 | 
			
		||||
@@ -87,3 +86,28 @@ describe('MonetaryComponent', () => {
 | 
			
		||||
    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 { Component, forwardRef, Inject, Input, LOCALE_ID } from '@angular/core'
 | 
			
		||||
import { Component, forwardRef, inject, Input, LOCALE_ID } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  NG_VALUE_ACCESSOR,
 | 
			
		||||
@@ -27,6 +27,8 @@ import { AbstractInputComponent } from '../abstract-input'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class MonetaryComponent extends AbstractInputComponent<string> {
 | 
			
		||||
  currentLocale = inject(LOCALE_ID)
 | 
			
		||||
 | 
			
		||||
  public currency: string = ''
 | 
			
		||||
 | 
			
		||||
  public _monetaryValue: string = ''
 | 
			
		||||
@@ -45,11 +47,10 @@ export class MonetaryComponent extends AbstractInputComponent<string> {
 | 
			
		||||
    if (currency) this.defaultCurrencyCode = currency
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(@Inject(LOCALE_ID) currentLocale: string) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
 | 
			
		||||
    this.currency = this.defaultCurrencyCode =
 | 
			
		||||
      this.defaultCurrency ?? getLocaleCurrencyCode(currentLocale)
 | 
			
		||||
      this.defaultCurrency ?? getLocaleCurrencyCode(this.currentLocale)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  writeValue(newValue: any): void {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, forwardRef, Input } from '@angular/core'
 | 
			
		||||
import { Component, forwardRef, inject, Input } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  NG_VALUE_ACCESSOR,
 | 
			
		||||
@@ -22,16 +22,14 @@ import { AbstractInputComponent } from '../abstract-input'
 | 
			
		||||
  imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
 | 
			
		||||
})
 | 
			
		||||
export class NumberComponent extends AbstractInputComponent<number> {
 | 
			
		||||
  private documentService = inject(DocumentService)
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  showAdd: boolean = true
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  step: number = 1
 | 
			
		||||
 | 
			
		||||
  constructor(private documentService: DocumentService) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  nextAsn() {
 | 
			
		||||
    if (this.value) {
 | 
			
		||||
      return
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, forwardRef } from '@angular/core'
 | 
			
		||||
import { Component, forwardRef, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  NG_VALUE_ACCESSOR,
 | 
			
		||||
@@ -26,7 +26,9 @@ import { AbstractInputComponent } from '../../abstract-input'
 | 
			
		||||
export class PermissionsGroupComponent extends AbstractInputComponent<Group> {
 | 
			
		||||
  groups: Group[]
 | 
			
		||||
 | 
			
		||||
  constructor(groupService: GroupService) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    const groupService = inject(GroupService)
 | 
			
		||||
 | 
			
		||||
    super()
 | 
			
		||||
    groupService
 | 
			
		||||
      .listAll()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, forwardRef } from '@angular/core'
 | 
			
		||||
import { Component, forwardRef, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormsModule,
 | 
			
		||||
  NG_VALUE_ACCESSOR,
 | 
			
		||||
@@ -8,7 +8,6 @@ import { NgSelectComponent } from '@ng-select/ng-select'
 | 
			
		||||
import { first } from 'rxjs/operators'
 | 
			
		||||
import { User } from 'src/app/data/user'
 | 
			
		||||
import { UserService } from 'src/app/services/rest/user.service'
 | 
			
		||||
import { SettingsService } from 'src/app/services/settings.service'
 | 
			
		||||
import { AbstractInputComponent } from '../../abstract-input'
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
@@ -27,7 +26,9 @@ import { AbstractInputComponent } from '../../abstract-input'
 | 
			
		||||
export class PermissionsUserComponent extends AbstractInputComponent<User[]> {
 | 
			
		||||
  users: User[]
 | 
			
		||||
 | 
			
		||||
  constructor(userService: UserService, settings: SettingsService) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    const userService = inject(UserService)
 | 
			
		||||
 | 
			
		||||
    super()
 | 
			
		||||
    userService
 | 
			
		||||
      .listAll()
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import {
 | 
			
		||||
  Component,
 | 
			
		||||
  EventEmitter,
 | 
			
		||||
  forwardRef,
 | 
			
		||||
  inject,
 | 
			
		||||
  Input,
 | 
			
		||||
  OnInit,
 | 
			
		||||
  Output,
 | 
			
		||||
@@ -45,10 +46,10 @@ import { TagComponent } from '../../tag/tag.component'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class TagsComponent implements OnInit, ControlValueAccessor {
 | 
			
		||||
  constructor(
 | 
			
		||||
    private tagService: TagService,
 | 
			
		||||
    private modalService: NgbModal
 | 
			
		||||
  ) {
 | 
			
		||||
  private tagService = inject(TagService)
 | 
			
		||||
  private modalService = inject(NgbModal)
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.createTagRef = this.createTag.bind(this)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, Input } from '@angular/core'
 | 
			
		||||
import { Component, Input, inject } from '@angular/core'
 | 
			
		||||
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
 | 
			
		||||
import { SettingsService } from 'src/app/services/settings.service'
 | 
			
		||||
import { environment } from 'src/environments/environment'
 | 
			
		||||
@@ -9,6 +9,8 @@ import { environment } from 'src/environments/environment'
 | 
			
		||||
  styleUrls: ['./logo.component.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class LogoComponent {
 | 
			
		||||
  private settingsService = inject(SettingsService)
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  extra_classes: string
 | 
			
		||||
 | 
			
		||||
@@ -24,8 +26,6 @@ export class LogoComponent {
 | 
			
		||||
      : null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(private settingsService: SettingsService) {}
 | 
			
		||||
 | 
			
		||||
  getClasses() {
 | 
			
		||||
    return ['logo'].concat(this.extra_classes).join(' ')
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, Input } from '@angular/core'
 | 
			
		||||
import { Component, Input, inject } from '@angular/core'
 | 
			
		||||
import { Title } from '@angular/platform-browser'
 | 
			
		||||
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
@@ -12,7 +12,7 @@ import { environment } from 'src/environments/environment'
 | 
			
		||||
  imports: [NgbPopoverModule, NgxBootstrapIconsModule, TourNgBootstrapModule],
 | 
			
		||||
})
 | 
			
		||||
export class PageHeaderComponent {
 | 
			
		||||
  constructor(private titleService: Title) {}
 | 
			
		||||
  private titleService = inject(Title)
 | 
			
		||||
 | 
			
		||||
  _title = ''
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
 | 
			
		||||
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
@@ -24,13 +24,13 @@ import { SwitchComponent } from '../input/switch/switch.component'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class PermissionsDialogComponent {
 | 
			
		||||
  activeModal = inject(NgbActiveModal)
 | 
			
		||||
  private userService = inject(UserService)
 | 
			
		||||
 | 
			
		||||
  users: User[]
 | 
			
		||||
  private o: ObjectWithPermissions = undefined
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    public activeModal: NgbActiveModal,
 | 
			
		||||
    private userService: UserService
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.userService.listAll().subscribe((r) => (this.users = r.results))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { NgClass } from '@angular/common'
 | 
			
		||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
 | 
			
		||||
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgSelectComponent } from '@ng-select/ng-select'
 | 
			
		||||
@@ -58,6 +58,9 @@ export enum OwnerFilterType {
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class PermissionsFilterDropdownComponent extends ComponentWithPermissions {
 | 
			
		||||
  permissionsService = inject(PermissionsService)
 | 
			
		||||
  private settingsService = inject(SettingsService)
 | 
			
		||||
 | 
			
		||||
  public OwnerFilterType = OwnerFilterType
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
@@ -83,12 +86,12 @@ export class PermissionsFilterDropdownComponent extends ComponentWithPermissions
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    public permissionsService: PermissionsService,
 | 
			
		||||
    userService: UserService,
 | 
			
		||||
    private settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    const userService = inject(UserService)
 | 
			
		||||
 | 
			
		||||
    super()
 | 
			
		||||
    const permissionsService = this.permissionsService
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      permissionsService.currentUserCan(
 | 
			
		||||
        PermissionAction.View,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { KeyValuePipe } from '@angular/common'
 | 
			
		||||
import { Component, forwardRef, Input, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, forwardRef, inject, Input, OnInit } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  AbstractControl,
 | 
			
		||||
  ControlValueAccessor,
 | 
			
		||||
@@ -43,6 +43,9 @@ export class PermissionsSelectComponent
 | 
			
		||||
  extends ComponentWithPermissions
 | 
			
		||||
  implements OnInit, ControlValueAccessor
 | 
			
		||||
{
 | 
			
		||||
  private readonly permissionsService = inject(PermissionsService)
 | 
			
		||||
  private readonly settingsService = inject(SettingsService)
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  title: string = 'Permissions'
 | 
			
		||||
 | 
			
		||||
@@ -76,10 +79,7 @@ export class PermissionsSelectComponent
 | 
			
		||||
 | 
			
		||||
  public allowedTypes = Object.keys(PermissionType)
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private readonly permissionsService: PermissionsService,
 | 
			
		||||
    private readonly settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
    if (!this.settingsService.get(SETTINGS_KEYS.AUDITLOG_ENABLED)) {
 | 
			
		||||
      this.allowedTypes.splice(this.allowedTypes.indexOf('History'), 1)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { HttpClient } from '@angular/common/http'
 | 
			
		||||
import { Component, Input, OnDestroy, ViewChild } from '@angular/core'
 | 
			
		||||
import { Component, inject, Input, OnDestroy, ViewChild } from '@angular/core'
 | 
			
		||||
import { NgbPopover, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { PdfViewerComponent, PdfViewerModule } from 'ng2-pdf-viewer'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
@@ -24,6 +24,10 @@ import { SettingsService } from 'src/app/services/settings.service'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class PreviewPopupComponent implements OnDestroy {
 | 
			
		||||
  private settingsService = inject(SettingsService)
 | 
			
		||||
  private documentService = inject(DocumentService)
 | 
			
		||||
  private http = inject(HttpClient)
 | 
			
		||||
 | 
			
		||||
  private _document: Document
 | 
			
		||||
  @Input()
 | 
			
		||||
  set document(document: Document) {
 | 
			
		||||
@@ -82,12 +86,6 @@ export class PreviewPopupComponent implements OnDestroy {
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private settingsService: SettingsService,
 | 
			
		||||
    private documentService: DocumentService,
 | 
			
		||||
    private http: HttpClient
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy(): void {
 | 
			
		||||
    this.unsubscribeNotifier.next(this)
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { Clipboard } from '@angular/cdk/clipboard'
 | 
			
		||||
import { Component, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnInit, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  FormControl,
 | 
			
		||||
  FormGroup,
 | 
			
		||||
@@ -46,6 +46,11 @@ export class ProfileEditDialogComponent
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  implements OnInit
 | 
			
		||||
{
 | 
			
		||||
  private profileService = inject(ProfileService)
 | 
			
		||||
  activeModal = inject(NgbActiveModal)
 | 
			
		||||
  private toastService = inject(ToastService)
 | 
			
		||||
  private clipboard = inject(Clipboard)
 | 
			
		||||
 | 
			
		||||
  public networkActive: boolean = false
 | 
			
		||||
  public error: any
 | 
			
		||||
 | 
			
		||||
@@ -83,15 +88,6 @@ export class ProfileEditDialogComponent
 | 
			
		||||
  public socialAccounts: SocialAccount[] = []
 | 
			
		||||
  public socialAccountProviders: SocialAccountProvider[] = []
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private profileService: ProfileService,
 | 
			
		||||
    public activeModal: NgbActiveModal,
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private clipboard: Clipboard
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.networkActive = true
 | 
			
		||||
    this.profileService
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import {
 | 
			
		||||
  fakeAsync,
 | 
			
		||||
  tick,
 | 
			
		||||
} from '@angular/core/testing'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { By } from '@angular/platform-browser'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
 | 
			
		||||
@@ -33,6 +34,8 @@ describe('ShareLinksDialogComponent', () => {
 | 
			
		||||
      imports: [
 | 
			
		||||
        ShareLinksDialogComponent,
 | 
			
		||||
        NgxBootstrapIconsModule.pick(allIcons),
 | 
			
		||||
        FormsModule,
 | 
			
		||||
        ReactiveFormsModule,
 | 
			
		||||
      ],
 | 
			
		||||
      providers: [
 | 
			
		||||
        provideHttpClient(withInterceptorsFromDi()),
 | 
			
		||||
@@ -223,16 +226,18 @@ describe('ShareLinksDialogComponent', () => {
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('should disable archive switch & option if no archive available', () => {
 | 
			
		||||
  it('should disable archive switch & option if no archive available', (done) => {
 | 
			
		||||
    component.hasArchiveVersion = false
 | 
			
		||||
    component.ngOnInit()
 | 
			
		||||
    fixture.detectChanges()
 | 
			
		||||
    expect(component.useArchiveVersion).toBeFalsy()
 | 
			
		||||
    expect(
 | 
			
		||||
      fixture.debugElement.query(By.css("input[type='checkbox']")).attributes[
 | 
			
		||||
        'ng-reflect-is-disabled'
 | 
			
		||||
      ]
 | 
			
		||||
    ).toBeTruthy()
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      // some stupid change detection issue
 | 
			
		||||
      const inputEl = fixture.debugElement.query(By.css('#versionSwitch'))
 | 
			
		||||
        .nativeElement as HTMLInputElement
 | 
			
		||||
      expect(inputEl.disabled).toBeTruthy()
 | 
			
		||||
      done()
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('should support close', () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { Clipboard } from '@angular/cdk/clipboard'
 | 
			
		||||
import { Component, Input, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, Input, OnInit, inject } from '@angular/core'
 | 
			
		||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
 | 
			
		||||
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
@@ -16,6 +16,11 @@ import { environment } from 'src/environments/environment'
 | 
			
		||||
  imports: [FormsModule, ReactiveFormsModule, NgxBootstrapIconsModule],
 | 
			
		||||
})
 | 
			
		||||
export class ShareLinksDialogComponent implements OnInit {
 | 
			
		||||
  private activeModal = inject(NgbActiveModal)
 | 
			
		||||
  private shareLinkService = inject(ShareLinkService)
 | 
			
		||||
  private toastService = inject(ToastService)
 | 
			
		||||
  private clipboard = inject(Clipboard)
 | 
			
		||||
 | 
			
		||||
  EXPIRATION_OPTIONS = [
 | 
			
		||||
    { label: $localize`1 day`, value: 1 },
 | 
			
		||||
    { label: $localize`7 days`, value: 7 },
 | 
			
		||||
@@ -58,13 +63,6 @@ export class ShareLinksDialogComponent implements OnInit {
 | 
			
		||||
 | 
			
		||||
  useArchiveVersion: boolean = true
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private activeModal: NgbActiveModal,
 | 
			
		||||
    private shareLinkService: ShareLinkService,
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private clipboard: Clipboard
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    if (this._documentId !== undefined) this.refresh()
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,18 @@
 | 
			
		||||
          <div class="card-body">
 | 
			
		||||
            <dl class="card-text">
 | 
			
		||||
              <dt i18n>Paperless-ngx Version</dt>
 | 
			
		||||
              <dd>{{status.pngx_version}}</dd>
 | 
			
		||||
              <dd>
 | 
			
		||||
                {{status.pngx_version}}
 | 
			
		||||
                @if (versionMismatch) {
 | 
			
		||||
                    <button class="btn btn-sm d-inline align-items-center btn-dark text-uppercase small" [ngbPopover]="versionPopover" triggers="click mouseenter:mouseleave">
 | 
			
		||||
                      <i-bs name="exclamation-triangle-fill" class="text-danger lh-1"></i-bs>
 | 
			
		||||
                    </button>
 | 
			
		||||
                }
 | 
			
		||||
                <ng-template #versionPopover>
 | 
			
		||||
                  Frontend version: {{frontendVersion}}<br>
 | 
			
		||||
                  Backend version: {{status.pngx_version}}
 | 
			
		||||
                </ng-template>
 | 
			
		||||
              </dd>
 | 
			
		||||
              <dt i18n>Install Type</dt>
 | 
			
		||||
              <dd>{{status.install_type}}</dd>
 | 
			
		||||
              <dt i18n>Server OS</dt>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,18 @@
 | 
			
		||||
// Mock production environment for testing
 | 
			
		||||
jest.mock('src/environments/environment', () => ({
 | 
			
		||||
  environment: {
 | 
			
		||||
    production: true,
 | 
			
		||||
    apiBaseUrl: 'http://localhost:8000/api/',
 | 
			
		||||
    apiVersion: '9',
 | 
			
		||||
    appTitle: 'Paperless-ngx',
 | 
			
		||||
    tag: 'prod',
 | 
			
		||||
    version: '2.4.3',
 | 
			
		||||
    webSocketHost: 'localhost:8000',
 | 
			
		||||
    webSocketProtocol: 'ws:',
 | 
			
		||||
    webSocketBaseUrl: '/ws/',
 | 
			
		||||
  },
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
import { Clipboard } from '@angular/cdk/clipboard'
 | 
			
		||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
 | 
			
		||||
import { provideHttpClientTesting } from '@angular/common/http/testing'
 | 
			
		||||
@@ -142,4 +157,15 @@ describe('SystemStatusDialogComponent', () => {
 | 
			
		||||
      `Task ${PaperlessTaskName.IndexOptimize} started`
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  it('shoduld handle version mismatch', () => {
 | 
			
		||||
    component.frontendVersion = '2.4.2'
 | 
			
		||||
    component.ngOnInit()
 | 
			
		||||
    expect(component.versionMismatch).toBeTruthy()
 | 
			
		||||
    expect(component.status.pngx_version).toContain('(frontend: 2.4.2)')
 | 
			
		||||
    component.frontendVersion = '2.4.3'
 | 
			
		||||
    component.status.pngx_version = '2.4.3'
 | 
			
		||||
    component.ngOnInit()
 | 
			
		||||
    expect(component.versionMismatch).toBeFalsy()
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { Component, OnInit, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  NgbActiveModal,
 | 
			
		||||
  NgbModalModule,
 | 
			
		||||
@@ -18,6 +18,7 @@ import { PermissionsService } from 'src/app/services/permissions.service'
 | 
			
		||||
import { SystemStatusService } from 'src/app/services/system-status.service'
 | 
			
		||||
import { TasksService } from 'src/app/services/tasks.service'
 | 
			
		||||
import { ToastService } from 'src/app/services/toast.service'
 | 
			
		||||
import { environment } from 'src/environments/environment'
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'pngx-system-status-dialog',
 | 
			
		||||
@@ -33,10 +34,19 @@ import { ToastService } from 'src/app/services/toast.service'
 | 
			
		||||
    NgxBootstrapIconsModule,
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class SystemStatusDialogComponent {
 | 
			
		||||
export class SystemStatusDialogComponent implements OnInit {
 | 
			
		||||
  activeModal = inject(NgbActiveModal)
 | 
			
		||||
  private clipboard = inject(Clipboard)
 | 
			
		||||
  private systemStatusService = inject(SystemStatusService)
 | 
			
		||||
  private tasksService = inject(TasksService)
 | 
			
		||||
  private toastService = inject(ToastService)
 | 
			
		||||
  private permissionsService = inject(PermissionsService)
 | 
			
		||||
 | 
			
		||||
  public SystemStatusItemStatus = SystemStatusItemStatus
 | 
			
		||||
  public PaperlessTaskName = PaperlessTaskName
 | 
			
		||||
  public status: SystemStatus
 | 
			
		||||
  public frontendVersion: string = environment.version
 | 
			
		||||
  public versionMismatch: boolean = false
 | 
			
		||||
 | 
			
		||||
  public copied: boolean = false
 | 
			
		||||
 | 
			
		||||
@@ -46,14 +56,16 @@ export class SystemStatusDialogComponent {
 | 
			
		||||
    return this.permissionsService.isSuperUser()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    public activeModal: NgbActiveModal,
 | 
			
		||||
    private clipboard: Clipboard,
 | 
			
		||||
    private systemStatusService: SystemStatusService,
 | 
			
		||||
    private tasksService: TasksService,
 | 
			
		||||
    private toastService: ToastService,
 | 
			
		||||
    private permissionsService: PermissionsService
 | 
			
		||||
  ) {}
 | 
			
		||||
  public ngOnInit() {
 | 
			
		||||
    this.versionMismatch =
 | 
			
		||||
      environment.production &&
 | 
			
		||||
      this.status.pngx_version &&
 | 
			
		||||
      this.frontendVersion &&
 | 
			
		||||
      this.status.pngx_version !== this.frontendVersion
 | 
			
		||||
    if (this.versionMismatch) {
 | 
			
		||||
      this.status.pngx_version = `${this.status.pngx_version} (frontend: ${this.frontendVersion})`
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public close() {
 | 
			
		||||
    this.activeModal.close()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, Input } from '@angular/core'
 | 
			
		||||
import { Component, inject, Input } from '@angular/core'
 | 
			
		||||
import { Tag } from 'src/app/data/tag'
 | 
			
		||||
import {
 | 
			
		||||
  PermissionAction,
 | 
			
		||||
@@ -13,14 +13,12 @@ import { TagService } from 'src/app/services/rest/tag.service'
 | 
			
		||||
  styleUrls: ['./tag.component.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class TagComponent {
 | 
			
		||||
  private permissionsService = inject(PermissionsService)
 | 
			
		||||
  private tagService = inject(TagService)
 | 
			
		||||
 | 
			
		||||
  private _tag: Tag
 | 
			
		||||
  private _tagID: number
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private permissionsService: PermissionsService,
 | 
			
		||||
    private tagService: TagService
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  public set tag(tag: Tag) {
 | 
			
		||||
    this._tag = tag
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { Clipboard } from '@angular/cdk/clipboard'
 | 
			
		||||
import { DecimalPipe } from '@angular/common'
 | 
			
		||||
import { Component, EventEmitter, Input, Output } from '@angular/core'
 | 
			
		||||
import { Component, EventEmitter, Input, Output, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  NgbProgressbarModule,
 | 
			
		||||
  NgbToastModule,
 | 
			
		||||
@@ -21,6 +21,8 @@ import { Toast } from 'src/app/services/toast.service'
 | 
			
		||||
  styleUrl: './toast.component.scss',
 | 
			
		||||
})
 | 
			
		||||
export class ToastComponent {
 | 
			
		||||
  private clipboard = inject(Clipboard)
 | 
			
		||||
 | 
			
		||||
  @Input() toast: Toast
 | 
			
		||||
 | 
			
		||||
  @Input() autohide: boolean = true
 | 
			
		||||
@@ -31,8 +33,6 @@ export class ToastComponent {
 | 
			
		||||
 | 
			
		||||
  public copied: boolean = false
 | 
			
		||||
 | 
			
		||||
  constructor(private clipboard: Clipboard) {}
 | 
			
		||||
 | 
			
		||||
  onShown(toast: Toast) {
 | 
			
		||||
    if (!this.autohide) return
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, OnDestroy, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
 | 
			
		||||
import {
 | 
			
		||||
  NgbAccordionModule,
 | 
			
		||||
  NgbProgressbarModule,
 | 
			
		||||
@@ -20,7 +20,7 @@ import { ToastComponent } from '../toast/toast.component'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class ToastsComponent implements OnInit, OnDestroy {
 | 
			
		||||
  constructor(public toastService: ToastService) {}
 | 
			
		||||
  toastService = inject(ToastService)
 | 
			
		||||
 | 
			
		||||
  private subscription: Subscription
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import {
 | 
			
		||||
  DragDropModule,
 | 
			
		||||
  moveItemInArray,
 | 
			
		||||
} from '@angular/cdk/drag-drop'
 | 
			
		||||
import { Component } from '@angular/core'
 | 
			
		||||
import { Component, inject } from '@angular/core'
 | 
			
		||||
import { RouterModule } from '@angular/router'
 | 
			
		||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
 | 
			
		||||
import { TourNgBootstrapModule, TourService } from 'ngx-ui-tour-ng-bootstrap'
 | 
			
		||||
@@ -42,13 +42,13 @@ import { WelcomeWidgetComponent } from './widgets/welcome-widget/welcome-widget.
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class DashboardComponent extends ComponentWithPermissions {
 | 
			
		||||
  settingsService = inject(SettingsService)
 | 
			
		||||
  savedViewService = inject(SavedViewService)
 | 
			
		||||
  private tourService = inject(TourService)
 | 
			
		||||
  private toastService = inject(ToastService)
 | 
			
		||||
 | 
			
		||||
  public dashboardViews: SavedView[] = []
 | 
			
		||||
  constructor(
 | 
			
		||||
    public settingsService: SettingsService,
 | 
			
		||||
    public savedViewService: SavedViewService,
 | 
			
		||||
    private tourService: TourService,
 | 
			
		||||
    private toastService: ToastService
 | 
			
		||||
  ) {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    super()
 | 
			
		||||
 | 
			
		||||
    this.savedViewService.listAll().subscribe(() => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import { AsyncPipe, NgClass, NgStyle } from '@angular/common'
 | 
			
		||||
import {
 | 
			
		||||
  Component,
 | 
			
		||||
  inject,
 | 
			
		||||
  Input,
 | 
			
		||||
  OnDestroy,
 | 
			
		||||
  OnInit,
 | 
			
		||||
@@ -84,26 +85,22 @@ export class SavedViewWidgetComponent
 | 
			
		||||
  extends LoadingComponentWithPermissions
 | 
			
		||||
  implements OnInit, OnDestroy
 | 
			
		||||
{
 | 
			
		||||
  private documentService = inject(DocumentService)
 | 
			
		||||
  private router = inject(Router)
 | 
			
		||||
  private list = inject(DocumentListViewService)
 | 
			
		||||
  private websocketStatusService = inject(WebsocketStatusService)
 | 
			
		||||
  openDocumentsService = inject(OpenDocumentsService)
 | 
			
		||||
  documentListViewService = inject(DocumentListViewService)
 | 
			
		||||
  permissionsService = inject(PermissionsService)
 | 
			
		||||
  private settingsService = inject(SettingsService)
 | 
			
		||||
  private customFieldService = inject(CustomFieldsService)
 | 
			
		||||
 | 
			
		||||
  public DisplayMode = DisplayMode
 | 
			
		||||
  public DisplayField = DisplayField
 | 
			
		||||
  public CustomFieldDataType = CustomFieldDataType
 | 
			
		||||
 | 
			
		||||
  private customFields: CustomField[] = []
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private documentService: DocumentService,
 | 
			
		||||
    private router: Router,
 | 
			
		||||
    private list: DocumentListViewService,
 | 
			
		||||
    private websocketStatusService: WebsocketStatusService,
 | 
			
		||||
    public openDocumentsService: OpenDocumentsService,
 | 
			
		||||
    public documentListViewService: DocumentListViewService,
 | 
			
		||||
    public permissionsService: PermissionsService,
 | 
			
		||||
    private settingsService: SettingsService,
 | 
			
		||||
    private customFieldService: CustomFieldsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  savedView: SavedView
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import { DecimalPipe } from '@angular/common'
 | 
			
		||||
import { HttpClient } from '@angular/common/http'
 | 
			
		||||
import { Component, OnDestroy, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, inject, OnDestroy, OnInit } from '@angular/core'
 | 
			
		||||
import { RouterModule } from '@angular/router'
 | 
			
		||||
import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import * as mimeTypeNames from 'mime-names'
 | 
			
		||||
@@ -51,15 +51,11 @@ export class StatisticsWidgetComponent
 | 
			
		||||
  extends ComponentWithPermissions
 | 
			
		||||
  implements OnInit, OnDestroy
 | 
			
		||||
{
 | 
			
		||||
  loading: boolean = false
 | 
			
		||||
  private http = inject(HttpClient)
 | 
			
		||||
  private websocketConnectionService = inject(WebsocketStatusService)
 | 
			
		||||
  private documentListViewService = inject(DocumentListViewService)
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private http: HttpClient,
 | 
			
		||||
    private websocketConnectionService: WebsocketStatusService,
 | 
			
		||||
    private documentListViewService: DocumentListViewService
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
  loading: boolean = false
 | 
			
		||||
 | 
			
		||||
  statistics: Statistics = {}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { NgClass, NgTemplateOutlet } from '@angular/common'
 | 
			
		||||
import { Component, QueryList, ViewChildren } from '@angular/core'
 | 
			
		||||
import { Component, QueryList, ViewChildren, inject } from '@angular/core'
 | 
			
		||||
import { RouterModule } from '@angular/router'
 | 
			
		||||
import {
 | 
			
		||||
  NgbAlert,
 | 
			
		||||
@@ -37,15 +37,11 @@ import { WidgetFrameComponent } from '../widget-frame/widget-frame.component'
 | 
			
		||||
  ],
 | 
			
		||||
})
 | 
			
		||||
export class UploadFileWidgetComponent extends ComponentWithPermissions {
 | 
			
		||||
  @ViewChildren(NgbAlert) alerts: QueryList<NgbAlert>
 | 
			
		||||
  private websocketStatusService = inject(WebsocketStatusService)
 | 
			
		||||
  private uploadDocumentsService = inject(UploadDocumentsService)
 | 
			
		||||
  settingsService = inject(SettingsService)
 | 
			
		||||
 | 
			
		||||
  constructor(
 | 
			
		||||
    private websocketStatusService: WebsocketStatusService,
 | 
			
		||||
    private uploadDocumentsService: UploadDocumentsService,
 | 
			
		||||
    public settingsService: SettingsService
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
  }
 | 
			
		||||
  @ViewChildren(NgbAlert) alerts: QueryList<NgbAlert>
 | 
			
		||||
 | 
			
		||||
  getStatus() {
 | 
			
		||||
    return this.websocketStatusService.getConsumerStatus()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, EventEmitter, Output } from '@angular/core'
 | 
			
		||||
import { Component, EventEmitter, Output, inject } from '@angular/core'
 | 
			
		||||
import { NgbAlertModule } from '@ng-bootstrap/ng-bootstrap'
 | 
			
		||||
import { TourService } from 'ngx-ui-tour-ng-bootstrap'
 | 
			
		||||
 | 
			
		||||
@@ -9,7 +9,7 @@ import { TourService } from 'ngx-ui-tour-ng-bootstrap'
 | 
			
		||||
  imports: [NgbAlertModule],
 | 
			
		||||
})
 | 
			
		||||
export class WelcomeWidgetComponent {
 | 
			
		||||
  constructor(public readonly tourService: TourService) {}
 | 
			
		||||
  readonly tourService = inject(TourService)
 | 
			
		||||
 | 
			
		||||
  @Output()
 | 
			
		||||
  dismiss: EventEmitter<boolean> = new EventEmitter()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { Component, OnInit } from '@angular/core'
 | 
			
		||||
import { Component, OnInit, inject } from '@angular/core'
 | 
			
		||||
import { ActivatedRoute, Router } from '@angular/router'
 | 
			
		||||
import { FILTER_ASN } from '../../data/filter-rule-type'
 | 
			
		||||
import { DocumentService } from '../../services/rest/document.service'
 | 
			
		||||
@@ -9,12 +9,11 @@ import { DocumentService } from '../../services/rest/document.service'
 | 
			
		||||
  styleUrls: ['./document-asn.component.scss'],
 | 
			
		||||
})
 | 
			
		||||
export class DocumentAsnComponent implements OnInit {
 | 
			
		||||
  private documentsService = inject(DocumentService)
 | 
			
		||||
  private route = inject(ActivatedRoute)
 | 
			
		||||
  private router = inject(Router)
 | 
			
		||||
 | 
			
		||||
  asn: string
 | 
			
		||||
  constructor(
 | 
			
		||||
    private documentsService: DocumentService,
 | 
			
		||||
    private route: ActivatedRoute,
 | 
			
		||||
    private router: Router
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  ngOnInit(): void {
 | 
			
		||||
    this.route.paramMap.subscribe((paramMap) => {
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user