mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-09-08 21:23:44 -05:00
Compare commits
138 Commits
v2.16.0
...
fded55dc70
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fded55dc70 | ||
![]() |
20da51278e | ||
![]() |
293c84d871 | ||
![]() |
1fe8599266 | ||
![]() |
5410074062 | ||
![]() |
4b8f6ed643 | ||
![]() |
f8689c4819 | ||
![]() |
cebc227701 | ||
![]() |
814df94e8d | ||
![]() |
fa496dfc8d | ||
![]() |
924471b59c | ||
![]() |
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 | ||
![]() |
e5673e4817 | ||
![]() |
ed48392450 | ||
![]() |
48d6bedf1c | ||
![]() |
ec2dc6cd80 | ||
![]() |
51e6eed72a | ||
![]() |
422bffe1a6 | ||
![]() |
31351c5f5c | ||
![]() |
c30cf2e0cd | ||
![]() |
e97cfb9b5e | ||
![]() |
42100588d5 | ||
![]() |
bc2facc87f | ||
![]() |
b4c6c4b61e | ||
![]() |
4e082f997c | ||
![]() |
1512599f4f | ||
![]() |
6c8f0b54ad | ||
![]() |
419ee9d6e7 | ||
![]() |
34b649aa01 | ||
![]() |
a9982abde8 | ||
![]() |
a94cc62207 | ||
![]() |
fc1b3e674b | ||
![]() |
603ad6c817 | ||
![]() |
9c9a0e4496 | ||
![]() |
7c33785c07 | ||
![]() |
9c32d931bc | ||
![]() |
d7b2e002ce | ||
![]() |
51e70f0a20 | ||
![]() |
bd257925bd | ||
![]() |
15b1b83c66 | ||
![]() |
b06c0a0eba | ||
![]() |
e9746aa0e3 | ||
![]() |
bfaab21589 | ||
![]() |
3849569bd1 | ||
![]() |
c40a7751b9 | ||
![]() |
f39463ff4e | ||
![]() |
2ada8ec681 | ||
![]() |
bdbf1b57ce | ||
![]() |
4c6fdbb21f | ||
![]() |
889c4378a9 | ||
![]() |
06dd039083 | ||
![]() |
00acbd4f1d | ||
![]() |
28a1b9d1ac | ||
![]() |
4f8a931a61 | ||
![]() |
716ebfe08a | ||
![]() |
e1760db85c | ||
![]() |
3cbb5239fb | ||
![]() |
aed629269d | ||
![]() |
59bf25edb1 | ||
![]() |
eb07876657 | ||
![]() |
ae3ac2b719 | ||
![]() |
5b4b316bbc | ||
![]() |
1583783a0b | ||
![]() |
bd5f05ff2b | ||
![]() |
9be6b28141 | ||
![]() |
3de8c9073d | ||
![]() |
e90a2aa4eb | ||
![]() |
edef851c89 | ||
![]() |
7b37e037e4 | ||
![]() |
e092627da6 | ||
![]() |
0a03ca94c7 | ||
![]() |
86dec8f344 |
@@ -83,7 +83,8 @@ RUN set -eux \
|
||||
&& apt-get update \
|
||||
&& apt-get install --yes --quiet ${PYTHON_PACKAGES}
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.7.8 /uv /bin/uv
|
||||
|
||||
|
||||
RUN set -eux \
|
||||
&& echo "Installing pre-built updates" \
|
||||
@@ -128,7 +129,6 @@ RUN set -eux \
|
||||
&& echo "Configuring ImageMagick" \
|
||||
&& mv paperless-policy.xml /etc/ImageMagick-6/policy.xml
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
|
||||
|
||||
# Packages needed only for building a few quick Python
|
||||
# dependencies
|
||||
|
@@ -47,39 +47,19 @@ To start the DevContainer:
|
||||
|
||||
1. Open VSCode.
|
||||
2. Open the project folder.
|
||||
3. Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
4. Type and select `Dev Containers: Rebuild and Reopen in Container`.
|
||||
3. Open the command palette and choose `Dev Containers: Rebuild and Reopen in Container`.
|
||||
|
||||
VSCode will build and start the DevContainer environment.
|
||||
|
||||
### Step 2: Initial Setup
|
||||
|
||||
Once the DevContainer is up and running, perform the following steps:
|
||||
Once the DevContainer is up and running, run the `Project Setup: Run all Init Tasks` task to initialize the project.
|
||||
|
||||
1. **Compile Frontend Assets**:
|
||||
Alternatively, the Project Setup can be done with individual tasks:
|
||||
|
||||
- Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
- Select `Tasks: Run Task`.
|
||||
- Choose `Frontend Compile`.
|
||||
|
||||
2. **Run Database Migrations**:
|
||||
|
||||
- Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
- Select `Tasks: Run Task`.
|
||||
- Choose `Migrate Database`.
|
||||
|
||||
3. **Create Superuser**:
|
||||
- Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
- Select `Tasks: Run Task`.
|
||||
- Choose `Create Superuser`.
|
||||
1. **Compile Frontend Assets**: `Maintenance: Compile frontend for production`.
|
||||
2. **Run Database Migrations**: `Maintenance: manage.py migrate`.
|
||||
3. **Create Superuser**: `Maintenance: manage.py createsuperuser`.
|
||||
|
||||
### Debugging and Running Services
|
||||
|
||||
@@ -95,11 +75,8 @@ You can start and debug backend services either as debugging sessions via `launc
|
||||
|
||||
#### Using Tasks
|
||||
|
||||
1. Open the command palette:
|
||||
- **Windows/Linux**: `Ctrl+Shift+P`
|
||||
- **Mac**: `Cmd+Shift+P`
|
||||
2. Select `Tasks: Run Task`.
|
||||
3. Choose the desired task:
|
||||
1. Open the command palette and select `Tasks: Run Task`.
|
||||
2. Choose the desired task:
|
||||
- `Runserver`
|
||||
- `Document Consumer`
|
||||
- `Celery`
|
||||
|
@@ -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
|
||||
|
@@ -156,7 +156,7 @@
|
||||
"label": "Maintenance: recreate .venv",
|
||||
"description": "Recreate the python virtual environment and install python dependencies",
|
||||
"type": "shell",
|
||||
"command": "rm -R -v .venv/* || uv install --dev",
|
||||
"command": "rm -rf .venv && uv venv && uv sync --dev",
|
||||
"group": "none",
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
|
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
|
21
.github/dependabot.yml
vendored
21
.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
|
||||
@@ -16,9 +15,6 @@ updates:
|
||||
labels:
|
||||
- "frontend"
|
||||
- "dependencies"
|
||||
# Add reviewers
|
||||
reviewers:
|
||||
- "paperless-ngx/frontend"
|
||||
groups:
|
||||
frontend-angular-dependencies:
|
||||
patterns:
|
||||
@@ -44,9 +40,6 @@ updates:
|
||||
labels:
|
||||
- "backend"
|
||||
- "dependencies"
|
||||
# Add reviewers
|
||||
reviewers:
|
||||
- "paperless-ngx/backend"
|
||||
groups:
|
||||
development:
|
||||
patterns:
|
||||
@@ -65,6 +58,9 @@ updates:
|
||||
update-types:
|
||||
- "minor"
|
||||
- "patch"
|
||||
exclude-patterns:
|
||||
- "*django*"
|
||||
- "drf-*"
|
||||
pre-built:
|
||||
patterns:
|
||||
- psycopg*
|
||||
@@ -79,9 +75,6 @@ updates:
|
||||
labels:
|
||||
- "ci-cd"
|
||||
- "dependencies"
|
||||
# Add reviewers
|
||||
reviewers:
|
||||
- "paperless-ngx/ci-cd"
|
||||
groups:
|
||||
actions:
|
||||
update-types:
|
||||
@@ -90,12 +83,12 @@ updates:
|
||||
- "patch"
|
||||
# Update Dockerfile in root directory
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
directories:
|
||||
- "/"
|
||||
- "/.devcontainer/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- "paperless-ngx/ci-cd"
|
||||
labels:
|
||||
- "dependencies"
|
||||
commit-message:
|
||||
@@ -107,8 +100,6 @@ updates:
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 5
|
||||
reviewers:
|
||||
- "paperless-ngx/ci-cd"
|
||||
labels:
|
||||
- "dependencies"
|
||||
commit-message:
|
||||
|
7
.github/labeler.yml
vendored
7
.github/labeler.yml
vendored
@@ -17,3 +17,10 @@ ci-cd:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- '.github/**'
|
||||
# pr types
|
||||
bug:
|
||||
- head-branch:
|
||||
- ['^fix']
|
||||
enhancement:
|
||||
- head-branch:
|
||||
- ['^feature']
|
||||
|
14
.github/release-drafter.yml
vendored
14
.github/release-drafter.yml
vendored
@@ -1,15 +1,3 @@
|
||||
autolabeler:
|
||||
- label: "bug"
|
||||
branch:
|
||||
- '/^fix/'
|
||||
title:
|
||||
- "/^fix/i"
|
||||
- "/^Bugfix/i"
|
||||
- label: "enhancement"
|
||||
branch:
|
||||
- '/^feature/'
|
||||
title:
|
||||
- "/^feature/i"
|
||||
categories:
|
||||
- title: 'Breaking Changes'
|
||||
labels:
|
||||
@@ -17,7 +5,7 @@ categories:
|
||||
- title: 'Notable Changes'
|
||||
labels:
|
||||
- 'notable'
|
||||
- title: 'Features'
|
||||
- title: 'Features / Enhancements'
|
||||
labels:
|
||||
- 'enhancement'
|
||||
- title: 'Bug Fixes'
|
||||
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -12,7 +12,7 @@ on:
|
||||
branches-ignore:
|
||||
- 'translations**'
|
||||
env:
|
||||
DEFAULT_UV_VERSION: "0.6.x"
|
||||
DEFAULT_UV_VERSION: "0.7.x"
|
||||
# This is the default version of Python to use in most steps which aren't specific
|
||||
DEFAULT_PYTHON_VERSION: "3.11"
|
||||
jobs:
|
||||
|
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:
|
||||
|
28
.github/workflows/pr-bot.yml
vendored
28
.github/workflows/pr-bot.yml
vendored
@@ -10,7 +10,8 @@ jobs:
|
||||
name: Automated PR Bot
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Label by file path
|
||||
- name: Label PR by file path or branch name
|
||||
# see .github/labeler.yml for the labeler config
|
||||
uses: actions/labeler@v5
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -24,6 +25,31 @@ jobs:
|
||||
s_diff: '99999'
|
||||
fail_if_xl: 'false'
|
||||
excluded_files: /\.lock$/ /\.txt$/ ^src-ui/pnpm-lock\.yaml$ ^src-ui/messages\.xlf$ ^src/locale/en_US/LC_MESSAGES/django\.po$
|
||||
- name: Label by PR title
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
const title = pr.title.toLowerCase();
|
||||
const labels = [];
|
||||
|
||||
if (/^(fix|bugfix)/i.test(title)) {
|
||||
labels.push('bug');
|
||||
} else if (/^feature/i.test(title)) {
|
||||
labels.push('enhancement');
|
||||
} else if (!/^(dependabot)/i.test(title)) {
|
||||
labels.push('enhancement'); // Default fallback
|
||||
}
|
||||
|
||||
if (labels.length) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
labels,
|
||||
});
|
||||
core.info(`Added labels based on title: ${labels.join(', ')}`);
|
||||
}
|
||||
- name: Label bot-generated PRs
|
||||
if: ${{ contains(github.actor, 'dependabot') || contains(github.actor, 'crowdin-bot') }}
|
||||
uses: actions/github-script@v7
|
||||
|
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"
|
||||
|
@@ -141,7 +141,7 @@ The admins occasionally invite contributors directly if we believe having them o
|
||||
# Automatic Repository Maintenance
|
||||
|
||||
The Paperless-ngx team appreciates all effort and interest from the community in filing bug reports, creating feature requests, sharing ideas and helping other
|
||||
community members. That said, in an effort to keep the repository organized and managebale the project uses automatic handling of certain areas:
|
||||
community members. That said, in an effort to keep the repository organized and manageable the project uses automatic handling of certain areas:
|
||||
|
||||
- Issues that cannot be reproduced will be marked 'stale' after 7 days of inactivity and closed after 14 further days of inactivity.
|
||||
- Issues, pull requests and discussions that are closed will be locked after 30 days of inactivity.
|
||||
|
@@ -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.6.16-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
|
||||
|
||||
@@ -265,4 +265,4 @@ ENTRYPOINT ["/init"]
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000" ]
|
||||
HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "-L", "--max-time", "2", "http://localhost:8000" ]
|
||||
|
@@ -1,8 +1,7 @@
|
||||
# Docker Compose file for running paperless testing with actual gotenberg
|
||||
# Docker Compose file for running paperless testing with actual Gotenberg
|
||||
# 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
|
||||
|
@@ -32,6 +32,6 @@
|
||||
# Note that this is different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines
|
||||
# the language used for OCR.
|
||||
# The container installs English, German, Italian, Spanish and French by default.
|
||||
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster
|
||||
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names
|
||||
# for available languages.
|
||||
#PAPERLESS_OCR_LANGUAGES=tur ces
|
||||
|
@@ -16,8 +16,8 @@
|
||||
# - Instead of SQLite (default), MariaDB is used as the database server.
|
||||
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
||||
# is configured to use these services. These provide support for consuming
|
||||
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
|
||||
# parts.
|
||||
# Office documents (Word, Excel, PowerPoint and their LibreOffice counter-
|
||||
# parts).
|
||||
#
|
||||
# To install and update paperless with this file, do the following:
|
||||
#
|
||||
@@ -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
|
||||
|
@@ -16,8 +16,8 @@
|
||||
# - Instead of SQLite (default), PostgreSQL is used as the database server.
|
||||
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
||||
# is configured to use these services. These provide support for consuming
|
||||
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
|
||||
# parts.
|
||||
# Office documents (Word, Excel, PowerPoint and their LibreOffice counter-
|
||||
# parts).
|
||||
#
|
||||
# To install and update paperless with this file, do the following:
|
||||
#
|
||||
@@ -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
|
||||
|
@@ -16,8 +16,8 @@
|
||||
#
|
||||
# - Apache Tika and Gotenberg servers are started with paperless and paperless
|
||||
# is configured to use these services. These provide support for consuming
|
||||
# Office documents (Word, Excel, Power Point and their LibreOffice counter-
|
||||
# parts.
|
||||
# Office documents (Word, Excel, PowerPoint and their LibreOffice counter-
|
||||
# parts).
|
||||
#
|
||||
# To install and update paperless with this file, do the following:
|
||||
#
|
||||
@@ -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
|
||||
|
@@ -306,7 +306,7 @@ in dedicated folders according to their nature: `archive`, `originals`,
|
||||
If `-sm` or `--split-manifest` is provided, information about document
|
||||
will be placed in individual json files, instead of a single JSON file. The main
|
||||
manifest.json will still contain application wide information (e.g. tags, correspondent,
|
||||
documenttype, etc)
|
||||
document type, etc)
|
||||
|
||||
If `-z` or `--zip` is provided, the export will be a zip file
|
||||
in the target directory, named according to the current local date or the
|
||||
@@ -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
|
||||
|
@@ -179,6 +179,7 @@ variables:
|
||||
| ---------------------------- | ---------------------------------------------- |
|
||||
| `DOCUMENT_ID` | Database primary key of the document |
|
||||
| `DOCUMENT_FILE_NAME` | Formatted filename, not including paths |
|
||||
| `DOCUMENT_TYPE` | The document type (if any) |
|
||||
| `DOCUMENT_CREATED` | Date & time when document created |
|
||||
| `DOCUMENT_MODIFIED` | Date & time when document was last modified |
|
||||
| `DOCUMENT_ADDED` | Date & time when document was added |
|
||||
|
@@ -1,5 +1,250 @@
|
||||
# Changelog
|
||||
|
||||
## paperless-ngx 2.17.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: correct PAPERLESS_EMPTY_TRASH_DIR to Path [@shamoon](https://github.com/shamoon) ([#10227](https://github.com/paperless-ngx/paperless-ngx/pull/10227))
|
||||
|
||||
### All App Changes
|
||||
|
||||
- Fix: correct PAPERLESS_EMPTY_TRASH_DIR to Path [@shamoon](https://github.com/shamoon) ([#10227](https://github.com/paperless-ngx/paperless-ngx/pull/10227))
|
||||
|
||||
## paperless-ngx 2.17.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Fix: restore expected pre-2.16 scheduled workflow offset behavior [@shamoon](https://github.com/shamoon) ([#10218](https://github.com/paperless-ngx/paperless-ngx/pull/10218))
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- QoL: log version at startup, show backend vs frontend mismatch in system status [@shamoon](https://github.com/shamoon) ([#10214](https://github.com/paperless-ngx/paperless-ngx/pull/10214))
|
||||
- Feature: add Persian translation [@shamoon](https://github.com/shamoon) ([#10183](https://github.com/paperless-ngx/paperless-ngx/pull/10183))
|
||||
- Enhancement: support import of zipped export [@kaerbr](https://github.com/kaerbr) ([#10073](https://github.com/paperless-ngx/paperless-ngx/pull/10073))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: more api fixes [@shamoon](https://github.com/shamoon) ([#10204](https://github.com/paperless-ngx/paperless-ngx/pull/10204))
|
||||
- Fix: restore expected pre-2.16 scheduled workflow offset behavior [@shamoon](https://github.com/shamoon) ([#10218](https://github.com/paperless-ngx/paperless-ngx/pull/10218))
|
||||
- Fix: fix some API crashes [@shamoon](https://github.com/shamoon) ([#10196](https://github.com/paperless-ngx/paperless-ngx/pull/10196))
|
||||
- Fix: remove duplicate base path in websocket urls [@robertmx](https://github.com/robertmx) ([#10194](https://github.com/paperless-ngx/paperless-ngx/pull/10194))
|
||||
- Fix: use hard delete for custom fields with workflow removal [@shamoon](https://github.com/shamoon) ([#10191](https://github.com/paperless-ngx/paperless-ngx/pull/10191))
|
||||
- Fix: fix mail account test api schema [@shamoon](https://github.com/shamoon) ([#10164](https://github.com/paperless-ngx/paperless-ngx/pull/10164))
|
||||
- Fix: correct api schema for mail_account process [@shamoon](https://github.com/shamoon) ([#10157](https://github.com/paperless-ngx/paperless-ngx/pull/10157))
|
||||
- Fix: correct api schema for next_asn [@shamoon](https://github.com/shamoon) ([#10151](https://github.com/paperless-ngx/paperless-ngx/pull/10151))
|
||||
- Fix: fix email and notes endpoints api spec [@shamoon](https://github.com/shamoon) ([#10148](https://github.com/paperless-ngx/paperless-ngx/pull/10148))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Chore: bump angular/common to 19.12.14 [@shamoon](https://github.com/shamoon) ([#10212](https://github.com/paperless-ngx/paperless-ngx/pull/10212))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>14 changes</summary>
|
||||
|
||||
- QoL: log version at startup, show backend vs frontend mismatch in system status [@shamoon](https://github.com/shamoon) ([#10214](https://github.com/paperless-ngx/paperless-ngx/pull/10214))
|
||||
- Fix: more api fixes [@shamoon](https://github.com/shamoon) ([#10204](https://github.com/paperless-ngx/paperless-ngx/pull/10204))
|
||||
- Fix: restore expected pre-2.16 scheduled workflow offset behavior [@shamoon](https://github.com/shamoon) ([#10218](https://github.com/paperless-ngx/paperless-ngx/pull/10218))
|
||||
- Chore: switch from os.path to pathlib.Path [@gothicVI](https://github.com/gothicVI) ([#9933](https://github.com/paperless-ngx/paperless-ngx/pull/9933))
|
||||
- Chore: bump angular/common to 19.12.14 [@shamoon](https://github.com/shamoon) ([#10212](https://github.com/paperless-ngx/paperless-ngx/pull/10212))
|
||||
- Fix: fix some API crashes [@shamoon](https://github.com/shamoon) ([#10196](https://github.com/paperless-ngx/paperless-ngx/pull/10196))
|
||||
- Fix: remove duplicate base path in websocket urls [@robertmx](https://github.com/robertmx) ([#10194](https://github.com/paperless-ngx/paperless-ngx/pull/10194))
|
||||
- Fix: use hard delete for custom fields with workflow removal [@shamoon](https://github.com/shamoon) ([#10191](https://github.com/paperless-ngx/paperless-ngx/pull/10191))
|
||||
- Feature: add Persian translation [@shamoon](https://github.com/shamoon) ([#10183](https://github.com/paperless-ngx/paperless-ngx/pull/10183))
|
||||
- Enhancement: support import of zipped export [@kaerbr](https://github.com/kaerbr) ([#10073](https://github.com/paperless-ngx/paperless-ngx/pull/10073))
|
||||
- Fix: fix mail account test api schema [@shamoon](https://github.com/shamoon) ([#10164](https://github.com/paperless-ngx/paperless-ngx/pull/10164))
|
||||
- Fix: correct api schema for mail_account process [@shamoon](https://github.com/shamoon) ([#10157](https://github.com/paperless-ngx/paperless-ngx/pull/10157))
|
||||
- Fix: correct api schema for next_asn [@shamoon](https://github.com/shamoon) ([#10151](https://github.com/paperless-ngx/paperless-ngx/pull/10151))
|
||||
- Fix: fix email and notes endpoints api spec [@shamoon](https://github.com/shamoon) ([#10148](https://github.com/paperless-ngx/paperless-ngx/pull/10148))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.16.3
|
||||
|
||||
### Features / Enhancements
|
||||
|
||||
- Performance: pre-filter document list in scheduled workflow checks [@shamoon](https://github.com/shamoon) ([#10031](https://github.com/paperless-ngx/paperless-ngx/pull/10031))
|
||||
- Chore: refactor consumer plugin checks to a pre-flight plugin [@shamoon](https://github.com/shamoon) ([#9994](https://github.com/paperless-ngx/paperless-ngx/pull/9994))
|
||||
- Enhancement: include DOCUMENT_TYPE to post consume scripts [@matthesrieke](https://github.com/matthesrieke) ([#9977](https://github.com/paperless-ngx/paperless-ngx/pull/9977))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: handle whoosh query correction errors [@shamoon](https://github.com/shamoon) ([#10121](https://github.com/paperless-ngx/paperless-ngx/pull/10121))
|
||||
- Fix: handle favicon with non-default static dir [@shamoon](https://github.com/shamoon) ([#10107](https://github.com/paperless-ngx/paperless-ngx/pull/10107))
|
||||
- Fixhancement: cleanup user or group references in settings upon deletion [@shamoon](https://github.com/shamoon) ([#10049](https://github.com/paperless-ngx/paperless-ngx/pull/10049))
|
||||
- Fix: Add exception to in [@Freilichtbuehne](https://github.com/Freilichtbuehne) ([#10070](https://github.com/paperless-ngx/paperless-ngx/pull/10070))
|
||||
- Fix: include base href when opening global search result in new window [@shamoon](https://github.com/shamoon) ([#10066](https://github.com/paperless-ngx/paperless-ngx/pull/10066))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>10 changes</summary>
|
||||
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10085](https://github.com/paperless-ngx/paperless-ngx/pull/10085))
|
||||
- Chore(deps): Update granian[uvloop] requirement from ~=2.2.0 to ~=2.3.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10055](https://github.com/paperless-ngx/paperless-ngx/pull/10055))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 18 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10099](https://github.com/paperless-ngx/paperless-ngx/pull/10099))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10100](https://github.com/paperless-ngx/paperless-ngx/pull/10100))
|
||||
- Chore(deps): Bump bootstrap from 5.3.3 to 5.3.6 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10091](https://github.com/paperless-ngx/paperless-ngx/pull/10091))
|
||||
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.9.0 to 1.9.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10090](https://github.com/paperless-ngx/paperless-ngx/pull/10090))
|
||||
- Chore(deps-dev): Bump @types/node from 22.15.3 to 22.15.29 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10089](https://github.com/paperless-ngx/paperless-ngx/pull/10089))
|
||||
- Chore(deps): Bump zone.js from 0.15.0 to 0.15.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10088](https://github.com/paperless-ngx/paperless-ngx/pull/10088))
|
||||
- docker(deps): bump astral-sh/uv from 0.7.8-python3.12-bookworm-slim to 0.7.9-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10084](https://github.com/paperless-ngx/paperless-ngx/pull/10084))
|
||||
- docker(deps): Bump astral-sh/uv from 0.6.16-python3.12-bookworm-slim to 0.7.8-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10056](https://github.com/paperless-ngx/paperless-ngx/pull/10056))
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>16 changes</summary>
|
||||
|
||||
- Fix: handle whoosh query correction errors [@shamoon](https://github.com/shamoon) ([#10121](https://github.com/paperless-ngx/paperless-ngx/pull/10121))
|
||||
- Performance: pre-filter document list in scheduled workflow checks [@shamoon](https://github.com/shamoon) ([#10031](https://github.com/paperless-ngx/paperless-ngx/pull/10031))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10085](https://github.com/paperless-ngx/paperless-ngx/pull/10085))
|
||||
- Chore: refactor consumer plugin checks to a pre-flight plugin [@shamoon](https://github.com/shamoon) ([#9994](https://github.com/paperless-ngx/paperless-ngx/pull/9994))
|
||||
- Chore(deps): Update granian[uvloop] requirement from ~=2.2.0 to ~=2.3.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10055](https://github.com/paperless-ngx/paperless-ngx/pull/10055))
|
||||
- Fix: handle favicon with non-default static dir [@shamoon](https://github.com/shamoon) ([#10107](https://github.com/paperless-ngx/paperless-ngx/pull/10107))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 18 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10099](https://github.com/paperless-ngx/paperless-ngx/pull/10099))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10100](https://github.com/paperless-ngx/paperless-ngx/pull/10100))
|
||||
- Chore(deps): Bump bootstrap from 5.3.3 to 5.3.6 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10091](https://github.com/paperless-ngx/paperless-ngx/pull/10091))
|
||||
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.9.0 to 1.9.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10090](https://github.com/paperless-ngx/paperless-ngx/pull/10090))
|
||||
- Chore(deps-dev): Bump @types/node from 22.15.3 to 22.15.29 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10089](https://github.com/paperless-ngx/paperless-ngx/pull/10089))
|
||||
- Chore(deps): Bump zone.js from 0.15.0 to 0.15.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10088](https://github.com/paperless-ngx/paperless-ngx/pull/10088))
|
||||
- Fixhancement: cleanup user or group references in settings upon deletion [@shamoon](https://github.com/shamoon) ([#10049](https://github.com/paperless-ngx/paperless-ngx/pull/10049))
|
||||
- Enhancement: include DOCUMENT_TYPE to post consume scripts [@matthesrieke](https://github.com/matthesrieke) ([#9977](https://github.com/paperless-ngx/paperless-ngx/pull/9977))
|
||||
- Fix: Add exception to in [@Freilichtbuehne](https://github.com/Freilichtbuehne) ([#10070](https://github.com/paperless-ngx/paperless-ngx/pull/10070))
|
||||
- Fix: include base href when opening global search result in new window [@shamoon](https://github.com/shamoon) ([#10066](https://github.com/paperless-ngx/paperless-ngx/pull/10066))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.16.2
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: accept datetime for created [@shamoon](https://github.com/shamoon) ([#10021](https://github.com/paperless-ngx/paperless-ngx/pull/10021))
|
||||
- Fix: created date fixes in v2.16 [@shamoon](https://github.com/shamoon) ([#10026](https://github.com/paperless-ngx/paperless-ngx/pull/10026))
|
||||
- Fix: mark fields for created objects as dirty [@shamoon](https://github.com/shamoon) ([#10022](https://github.com/paperless-ngx/paperless-ngx/pull/10022))
|
||||
- Fix: add fallback to copyfile on PermissionError @samuel-kosmann ([#10023](https://github.com/paperless-ngx/paperless-ngx/pull/10023))
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Chore: warn users about removal of postgres v13 support [@shamoon](https://github.com/shamoon) ([#9980](https://github.com/paperless-ngx/paperless-ngx/pull/9980))
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>5 changes</summary>
|
||||
|
||||
- Fix: accept datetime for created [@shamoon](https://github.com/shamoon) ([#10021](https://github.com/paperless-ngx/paperless-ngx/pull/10021))
|
||||
- Fix: add fallback to copyfile on PermissionError @samuel-kosmann ([#10023](https://github.com/paperless-ngx/paperless-ngx/pull/10023))
|
||||
- Fix: created date fixes in v2.16 [@shamoon](https://github.com/shamoon) ([#10026](https://github.com/paperless-ngx/paperless-ngx/pull/10026))
|
||||
- Fix: mark fields for created objects as dirty [@shamoon](https://github.com/shamoon) ([#10022](https://github.com/paperless-ngx/paperless-ngx/pull/10022))
|
||||
- Chore: warn users about removal of postgres v13 support [@shamoon](https://github.com/shamoon) ([#9980](https://github.com/paperless-ngx/paperless-ngx/pull/9980))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.16.1
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: fix created date filtering broken in 2.16.0 [@shamoon](https://github.com/shamoon) ([#9976](https://github.com/paperless-ngx/paperless-ngx/pull/9976))
|
||||
|
||||
### All App Changes
|
||||
|
||||
- Fix: fix created date filtering broken in 2.16.0 [@shamoon](https://github.com/shamoon) ([#9976](https://github.com/paperless-ngx/paperless-ngx/pull/9976))
|
||||
|
||||
## paperless-ngx 2.16.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- [BREAKING] Change: treat created as date not datetime [@shamoon](https://github.com/shamoon) ([#9793](https://github.com/paperless-ngx/paperless-ngx/pull/9793))
|
||||
|
||||
### Features
|
||||
|
||||
- Enhancement: support negative offset in scheduled workflows [@shamoon](https://github.com/shamoon) ([#9746](https://github.com/paperless-ngx/paperless-ngx/pull/9746))
|
||||
- Enhancement: support heic images [@shamoon](https://github.com/shamoon) ([#9771](https://github.com/paperless-ngx/paperless-ngx/pull/9771))
|
||||
- Enhancement: use patch instead of put for frontend document changes [@shamoon](https://github.com/shamoon) ([#9744](https://github.com/paperless-ngx/paperless-ngx/pull/9744))
|
||||
- Fixhancement: automatically disable email verification if no smtp setup [@shamoon](https://github.com/shamoon) ([#9949](https://github.com/paperless-ngx/paperless-ngx/pull/9949))
|
||||
- Fixhancement: better handle removed social apps in profile [@shamoon](https://github.com/shamoon) ([#9876](https://github.com/paperless-ngx/paperless-ngx/pull/9876))
|
||||
- Enhancement: add barcode frontend config [@shamoon](https://github.com/shamoon) ([#9742](https://github.com/paperless-ngx/paperless-ngx/pull/9742))
|
||||
- Enhancement: support allauth disable unknown account emails [@shamoon](https://github.com/shamoon) ([#9743](https://github.com/paperless-ngx/paperless-ngx/pull/9743))
|
||||
- Fixhancement: tag plus button should add tag to doc [@shamoon](https://github.com/shamoon) ([#9762](https://github.com/paperless-ngx/paperless-ngx/pull/9762))
|
||||
- Fixhancement: check more permissions for status consumer messages [@shamoon](https://github.com/shamoon) ([#9804](https://github.com/paperless-ngx/paperless-ngx/pull/9804))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix: include subpath in drf-spectacular settings if set [@shamoon](https://github.com/shamoon) ([#9738](https://github.com/paperless-ngx/paperless-ngx/pull/9738))
|
||||
- Fix: handle created change with api version increment, use created only on frontend, deprecate created_date [@shamoon](https://github.com/shamoon) ([#9962](https://github.com/paperless-ngx/paperless-ngx/pull/9962))
|
||||
- Fix: ignore logo file from sanity checker [@shamoon](https://github.com/shamoon) ([#9946](https://github.com/paperless-ngx/paperless-ngx/pull/9946))
|
||||
- Fix: correctly handle empty user for old notes api format, fix frontend API version [@shamoon](https://github.com/shamoon) ([#9846](https://github.com/paperless-ngx/paperless-ngx/pull/9846))
|
||||
- Fix: fix single select in filterable dropdowns when editing [@shamoon](https://github.com/shamoon) ([#9834](https://github.com/paperless-ngx/paperless-ngx/pull/9834))
|
||||
- Fix: always update classifier task result [@shamoon](https://github.com/shamoon) ([#9817](https://github.com/paperless-ngx/paperless-ngx/pull/9817))
|
||||
- Fix: fix zoom increase/decrease buttons in FF [@shamoon](https://github.com/shamoon) ([#9761](https://github.com/paperless-ngx/paperless-ngx/pull/9761))
|
||||
|
||||
### Maintenance
|
||||
|
||||
- Chore(deps): Bump astral-sh/setup-uv from 5 to 6 in the actions group @[dependabot[bot]](https://github.com/apps/dependabot) ([#9842](https://github.com/paperless-ngx/paperless-ngx/pull/9842))
|
||||
- Chore: split ci frontend e2e vs unit tests [@shamoon](https://github.com/shamoon) ([#9851](https://github.com/paperless-ngx/paperless-ngx/pull/9851))
|
||||
- Chore: auto-generate translation strings [@shamoon](https://github.com/shamoon) ([#9462](https://github.com/paperless-ngx/paperless-ngx/pull/9462))
|
||||
- Chore: add ymlfmt [@shamoon](https://github.com/shamoon) ([#9745](https://github.com/paperless-ngx/paperless-ngx/pull/9745))
|
||||
- Chore: replace secretary with GHA [@shamoon](https://github.com/shamoon) ([#9723](https://github.com/paperless-ngx/paperless-ngx/pull/9723))
|
||||
- Chore: resolve dynamic import warnings from pdfjs, again [@shamoon](https://github.com/shamoon) ([#9924](https://github.com/paperless-ngx/paperless-ngx/pull/9924))
|
||||
- Fix/Chore: replace file drop package [@shamoon](https://github.com/shamoon) ([#9926](https://github.com/paperless-ngx/paperless-ngx/pull/9926))
|
||||
|
||||
### Dependencies
|
||||
|
||||
<details>
|
||||
<summary>14 changes</summary>
|
||||
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9921](https://github.com/paperless-ngx/paperless-ngx/pull/9921))
|
||||
- docker-compose(deps): Bump library/redis from 7 to 8 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#9879](https://github.com/paperless-ngx/paperless-ngx/pull/9879))
|
||||
- Chore(deps): Bump astral-sh/setup-uv from 5 to 6 in the actions group @[dependabot[bot]](https://github.com/apps/dependabot) ([#9842](https://github.com/paperless-ngx/paperless-ngx/pull/9842))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 14 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9848](https://github.com/paperless-ngx/paperless-ngx/pull/9848))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9849](https://github.com/paperless-ngx/paperless-ngx/pull/9849))
|
||||
- Chore(deps-dev): Bump @types/node from 22.13.17 to 22.15.3 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#9850](https://github.com/paperless-ngx/paperless-ngx/pull/9850))
|
||||
- docker(deps): Bump astral-sh/uv from 0.6.14-python3.12-bookworm-slim to 0.6.16-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#9767](https://github.com/paperless-ngx/paperless-ngx/pull/9767))
|
||||
- docker-compose(deps): bump gotenberg/gotenberg from 8.19 to 8.20 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#9661](https://github.com/paperless-ngx/paperless-ngx/pull/9661))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 17 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9768](https://github.com/paperless-ngx/paperless-ngx/pull/9768))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9770](https://github.com/paperless-ngx/paperless-ngx/pull/9770))
|
||||
- Chore(deps-dev): Bump jest-preset-angular from 14.5.4 to 14.5.5 in /src-ui in the frontend-jest-dependencies group @[dependabot[bot]](https://github.com/apps/dependabot) ([#9769](https://github.com/paperless-ngx/paperless-ngx/pull/9769))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9764](https://github.com/paperless-ngx/paperless-ngx/pull/9764))
|
||||
- Chore(deps): Bump the django group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9753](https://github.com/paperless-ngx/paperless-ngx/pull/9753))
|
||||
- docker(deps): bump astral-sh/uv from 0.6.13-python3.12-bookworm-slim to 0.6.14-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#9656](https://github.com/paperless-ngx/paperless-ngx/pull/9656))
|
||||
</details>
|
||||
|
||||
### All App Changes
|
||||
|
||||
<details>
|
||||
<summary>29 changes</summary>
|
||||
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9921](https://github.com/paperless-ngx/paperless-ngx/pull/9921))
|
||||
- Fix: handle created change with api version increment, use created only on frontend, deprecate created_date [@shamoon](https://github.com/shamoon) ([#9962](https://github.com/paperless-ngx/paperless-ngx/pull/9962))
|
||||
- Fixhancement: automatically disable email verification if no smtp setup [@shamoon](https://github.com/shamoon) ([#9949](https://github.com/paperless-ngx/paperless-ngx/pull/9949))
|
||||
- Fix: ignore logo file from sanity checker [@shamoon](https://github.com/shamoon) ([#9946](https://github.com/paperless-ngx/paperless-ngx/pull/9946))
|
||||
- [BREAKING] Change: treat created as date not datetime [@shamoon](https://github.com/shamoon) ([#9793](https://github.com/paperless-ngx/paperless-ngx/pull/9793))
|
||||
- Fix/Chore: replace file drop package [@shamoon](https://github.com/shamoon) ([#9926](https://github.com/paperless-ngx/paperless-ngx/pull/9926))
|
||||
- Chore: resolve dynamic import warnings from pdfjs, again [@shamoon](https://github.com/shamoon) ([#9924](https://github.com/paperless-ngx/paperless-ngx/pull/9924))
|
||||
- Enhancement: support negative offset in scheduled workflows [@shamoon](https://github.com/shamoon) ([#9746](https://github.com/paperless-ngx/paperless-ngx/pull/9746))
|
||||
- Fixhancement: better handle removed social apps in profile [@shamoon](https://github.com/shamoon) ([#9876](https://github.com/paperless-ngx/paperless-ngx/pull/9876))
|
||||
- Enhancement: add barcode frontend config [@shamoon](https://github.com/shamoon) ([#9742](https://github.com/paperless-ngx/paperless-ngx/pull/9742))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 14 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9848](https://github.com/paperless-ngx/paperless-ngx/pull/9848))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9849](https://github.com/paperless-ngx/paperless-ngx/pull/9849))
|
||||
- Chore(deps-dev): Bump @types/node from 22.13.17 to 22.15.3 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#9850](https://github.com/paperless-ngx/paperless-ngx/pull/9850))
|
||||
- Fix: correctly handle empty user for old notes api format, fix frontend API version [@shamoon](https://github.com/shamoon) ([#9846](https://github.com/paperless-ngx/paperless-ngx/pull/9846))
|
||||
- Fix: fix single select in filterable dropdowns when editing [@shamoon](https://github.com/shamoon) ([#9834](https://github.com/paperless-ngx/paperless-ngx/pull/9834))
|
||||
- Fix: always update classifier task result [@shamoon](https://github.com/shamoon) ([#9817](https://github.com/paperless-ngx/paperless-ngx/pull/9817))
|
||||
- Fixhancement: check more permissions for status consumer messages [@shamoon](https://github.com/shamoon) ([#9804](https://github.com/paperless-ngx/paperless-ngx/pull/9804))
|
||||
- Enhancement: support heic images [@shamoon](https://github.com/shamoon) ([#9771](https://github.com/paperless-ngx/paperless-ngx/pull/9771))
|
||||
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 17 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9768](https://github.com/paperless-ngx/paperless-ngx/pull/9768))
|
||||
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9770](https://github.com/paperless-ngx/paperless-ngx/pull/9770))
|
||||
- Chore(deps-dev): Bump jest-preset-angular from 14.5.4 to 14.5.5 in /src-ui in the frontend-jest-dependencies group @[dependabot[bot]](https://github.com/apps/dependabot) ([#9769](https://github.com/paperless-ngx/paperless-ngx/pull/9769))
|
||||
- Enhancement: support allauth disable unknown account emails [@shamoon](https://github.com/shamoon) ([#9743](https://github.com/paperless-ngx/paperless-ngx/pull/9743))
|
||||
- Enhancement: use patch instead of put for frontend document changes [@shamoon](https://github.com/shamoon) ([#9744](https://github.com/paperless-ngx/paperless-ngx/pull/9744))
|
||||
- Chore(deps): Bump the small-changes group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9764](https://github.com/paperless-ngx/paperless-ngx/pull/9764))
|
||||
- Chore(deps): Bump the django group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#9753](https://github.com/paperless-ngx/paperless-ngx/pull/9753))
|
||||
- Fixhancement: tag plus button should add tag to doc [@shamoon](https://github.com/shamoon) ([#9762](https://github.com/paperless-ngx/paperless-ngx/pull/9762))
|
||||
- Fix: fix zoom increase/decrease buttons in FF [@shamoon](https://github.com/shamoon) ([#9761](https://github.com/paperless-ngx/paperless-ngx/pull/9761))
|
||||
- Chore: switch from os.path to pathlib.Path [@gothicVI](https://github.com/gothicVI) ([#9339](https://github.com/paperless-ngx/paperless-ngx/pull/9339))
|
||||
- Fix: include subpath in drf-spectacular settings if set [@shamoon](https://github.com/shamoon) ([#9738](https://github.com/paperless-ngx/paperless-ngx/pull/9738))
|
||||
</details>
|
||||
|
||||
## paperless-ngx 2.15.3
|
||||
|
||||
### Bug Fixes
|
||||
@@ -5762,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
|
||||
|
@@ -50,47 +50,48 @@ matcher.
|
||||
|
||||
### Database
|
||||
|
||||
By default, Paperless uses **SQLite** with a database stored at `data/db.sqlite3`.
|
||||
To switch to **PostgreSQL** or **MariaDB**, set [`PAPERLESS_DBHOST`](#PAPERLESS_DBHOST) and optionally configure other
|
||||
database-related environment variables.
|
||||
|
||||
#### [`PAPERLESS_DBHOST=<hostname>`](#PAPERLESS_DBHOST) {#PAPERLESS_DBHOST}
|
||||
|
||||
: If unset, Paperless uses **SQLite** by default.
|
||||
|
||||
Set `PAPERLESS_DBHOST` to switch to PostgreSQL or MariaDB instead.
|
||||
|
||||
#### [`PAPERLESS_DBENGINE=<engine_name>`](#PAPERLESS_DBENGINE) {#PAPERLESS_DBENGINE}
|
||||
|
||||
: Optional, gives the ability to choose Postgres or MariaDB for
|
||||
database engine. Available options are `postgresql` and
|
||||
`mariadb`.
|
||||
: Optional. Specifies the database engine to use when connecting to a remote database.
|
||||
Available options are `postgresql` and `mariadb`.
|
||||
|
||||
Default is `postgresql`.
|
||||
Defaults to `postgresql` if `PAPERLESS_DBHOST` is set.
|
||||
|
||||
!!! warning
|
||||
|
||||
Using MariaDB comes with some caveats. See [MySQL Caveats](advanced_usage.md#mysql-caveats).
|
||||
|
||||
#### [`PAPERLESS_DBHOST=<hostname>`](#PAPERLESS_DBHOST) {#PAPERLESS_DBHOST}
|
||||
|
||||
: By default, sqlite is used as the database backend. This can be
|
||||
changed here.
|
||||
|
||||
Set PAPERLESS_DBHOST and another database will be used instead of
|
||||
sqlite.
|
||||
|
||||
#### [`PAPERLESS_DBPORT=<port>`](#PAPERLESS_DBPORT) {#PAPERLESS_DBPORT}
|
||||
|
||||
: Adjust port if necessary.
|
||||
: Port to use when connecting to PostgreSQL or MariaDB.
|
||||
|
||||
Default is 5432.
|
||||
Default is `5432` for PostgreSQL and `3306` for MariaDB.
|
||||
|
||||
#### [`PAPERLESS_DBNAME=<name>`](#PAPERLESS_DBNAME) {#PAPERLESS_DBNAME}
|
||||
|
||||
: Database name in PostgreSQL or MariaDB.
|
||||
: Name of the database to connect to when using PostgreSQL or MariaDB.
|
||||
|
||||
Defaults to "paperless".
|
||||
|
||||
#### [`PAPERLESS_DBUSER=<name>`](#PAPERLESS_DBUSER) {#PAPERLESS_DBUSER}
|
||||
|
||||
: Database user in PostgreSQL or MariaDB.
|
||||
: Username for authenticating with the PostgreSQL or MariaDB database.
|
||||
|
||||
Defaults to "paperless".
|
||||
|
||||
#### [`PAPERLESS_DBPASS=<password>`](#PAPERLESS_DBPASS) {#PAPERLESS_DBPASS}
|
||||
|
||||
: Database password for PostgreSQL or MariaDB.
|
||||
: Password for the PostgreSQL or MariaDB database user.
|
||||
|
||||
Defaults to "paperless".
|
||||
|
||||
@@ -110,20 +111,20 @@ changed here.
|
||||
|
||||
#### [`PAPERLESS_DBSSLROOTCERT=<ca-path>`](#PAPERLESS_DBSSLROOTCERT) {#PAPERLESS_DBSSLROOTCERT}
|
||||
|
||||
: SSL root certificate path
|
||||
: Path to the SSL root certificate used to verify the database server.
|
||||
|
||||
See [the official documentation about
|
||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
Changes path of `root.crt`.
|
||||
Changes the location of `root.crt`.
|
||||
|
||||
See [the official documentation about
|
||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-ca).
|
||||
|
||||
Defaults to unset, using the documented path in the home directory.
|
||||
Defaults to unset, using the standard location in the home directory.
|
||||
|
||||
#### [`PAPERLESS_DBSSLCERT=<client-cert-path>`](#PAPERLESS_DBSSLCERT) {#PAPERLESS_DBSSLCERT}
|
||||
|
||||
: SSL client certificate path
|
||||
: Path to the client SSL certificate used when connecting securely.
|
||||
|
||||
See [the official documentation about
|
||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
@@ -131,13 +132,13 @@ changed here.
|
||||
See [the official documentation about
|
||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-cert).
|
||||
|
||||
Changes path of `postgresql.crt`.
|
||||
Changes the location of `postgresql.crt`.
|
||||
|
||||
Defaults to unset, using the documented path in the home directory.
|
||||
Defaults to unset, using the standard location in the home directory.
|
||||
|
||||
#### [`PAPERLESS_DBSSLKEY=<client-cert-key>`](#PAPERLESS_DBSSLKEY) {#PAPERLESS_DBSSLKEY}
|
||||
|
||||
: SSL client key path
|
||||
: Path to the client SSL private key used when connecting securely.
|
||||
|
||||
See [the official documentation about
|
||||
sslmode for PostgreSQL](https://www.postgresql.org/docs/current/libpq-ssl.html).
|
||||
@@ -145,17 +146,53 @@ changed here.
|
||||
See [the official documentation about
|
||||
sslmode for MySQL and MariaDB](https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_ssl-key).
|
||||
|
||||
Changes path of `postgresql.key`.
|
||||
Changes the location of `postgresql.key`.
|
||||
|
||||
Defaults to unset, using the documented path in the home directory.
|
||||
Defaults to unset, using the standard location in the home directory.
|
||||
|
||||
#### [`PAPERLESS_DB_TIMEOUT=<int>`](#PAPERLESS_DB_TIMEOUT) {#PAPERLESS_DB_TIMEOUT}
|
||||
|
||||
: Amount of time for a database connection to wait for the database to
|
||||
unlock. Mostly applicable for sqlite based installation. Consider changing
|
||||
to postgresql if you are having concurrency problems with sqlite.
|
||||
: Sets how long a database connection should wait before timing out.
|
||||
|
||||
Defaults to unset, keeping the Django defaults.
|
||||
For SQLite, this sets how long to wait if the database is locked.
|
||||
For PostgreSQL or MariaDB, this sets the connection timeout.
|
||||
|
||||
Defaults to unset, which uses Django’s built-in defaults.
|
||||
|
||||
#### [`PAPERLESS_DB_READ_CACHE_ENABLED=<bool>`](#PAPERLESS_DB_READ_CACHE_ENABLED) {#PAPERLESS_DB_READ_CACHE_ENABLED}
|
||||
|
||||
: Caches the database read query results into Redis. This can significantly improve application response times by caching database queries, at the cost of slightly increased memory usage.
|
||||
|
||||
Defaults to `false`.
|
||||
|
||||
!!! danger
|
||||
|
||||
**Do not modify the database outside the application while it is running.**
|
||||
This includes actions such as restoring a backup, upgrading the database, or performing manual inserts. All external modifications must be done **only when the application is stopped**.
|
||||
After making any such changes, you **must invalidate the DB read cache** using the `invalidate_cachalot` management command.
|
||||
|
||||
#### [`PAPERLESS_READ_CACHE_TTL=<int>`](#PAPERLESS_READ_CACHE_TTL) {#PAPERLESS_READ_CACHE_TTL}
|
||||
|
||||
: Specifies how long (in seconds) read data should be cached.
|
||||
|
||||
Allowed values are between `1` (one second) and `31536000` (one year). Defaults to `3600` (one hour).
|
||||
|
||||
!!! warning
|
||||
|
||||
A high TTL increases memory usage over time. Memory may be used until end of TTL, even if the cache is invalidated with the `invalidate_cachalot` command.
|
||||
|
||||
In case of an out-of-memory (OOM) situation, Redis may stop accepting new data — including cache entries, scheduled tasks, and documents to consume.
|
||||
If your system has limited RAM, consider configuring a dedicated Redis instance for the read cache, with a memory limit and the eviction policy set to `allkeys-lru`.
|
||||
For more details, refer to the [Redis eviction policy documentation](https://redis.io/docs/latest/develop/reference/eviction/), and see the `PAPERLESS_READ_CACHE_REDIS_URL` setting to specify a separate Redis broker.
|
||||
|
||||
#### [`PAPERLESS_READ_CACHE_REDIS_URL=<url>`](#PAPERLESS_READ_CACHE_REDIS_URL) {#PAPERLESS_READ_CACHE_REDIS_URL}
|
||||
|
||||
: Defines the Redis instance used for the read cache.
|
||||
|
||||
Defaults to `None`.
|
||||
|
||||
!!! Note
|
||||
If this value is not set, the same Redis instance used for scheduled tasks will be used for caching as well.
|
||||
|
||||
## Optional Services
|
||||
|
||||
@@ -200,7 +237,7 @@ and watch out for indentation if editing the YAML file.
|
||||
|
||||
### Email Parsing
|
||||
|
||||
#### [`PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT=<int>`(#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT) {#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT}
|
||||
#### [`PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT=<int>`](#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT) {#PAPERLESS_EMAIL_PARSE_DEFAULT_LAYOUT}
|
||||
|
||||
: The default layout to use for emails that are consumed as documents. Must be one of the integer choices below. Note that mail
|
||||
rules can specify this setting, thus this fallback is used for the default selection and for .eml files consumed by other means.
|
||||
@@ -966,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
|
||||
|
@@ -95,13 +95,13 @@ first-time setup.
|
||||
|
||||
7. You can now either ...
|
||||
|
||||
- install redis or
|
||||
- install Redis or
|
||||
|
||||
- use the included `scripts/start_services.sh` to use docker to fire
|
||||
up a redis instance (and some other services such as tika,
|
||||
gotenberg and a database server) or
|
||||
- use the included `scripts/start_services.sh` to use Docker to fire
|
||||
up a Redis instance (and some other services such as Tika,
|
||||
Gotenberg and a database server) or
|
||||
|
||||
- spin up a bare redis container
|
||||
- spin up a bare Redis container
|
||||
|
||||
```
|
||||
docker run -d -p 6379:6379 --restart unless-stopped redis:latest
|
||||
@@ -147,7 +147,7 @@ $ ng build --configuration production
|
||||
### Testing
|
||||
|
||||
- Run `pytest` in the `src/` directory to execute all tests. This also
|
||||
generates a HTML coverage report. When runnings test, `paperless.conf`
|
||||
generates a HTML coverage report. When running tests, `paperless.conf`
|
||||
is loaded as well. However, the tests rely on the default
|
||||
configuration. This is not ideal. But for now, make sure no settings
|
||||
except for DEBUG are overridden when testing.
|
||||
|
@@ -30,7 +30,7 @@ physical documents into a searchable online archive so you can keep, well, _less
|
||||
- Utilizes the open-source Tesseract engine to recognize more than 100 languages.
|
||||
- Documents are saved as PDF/A format which is designed for long term storage, alongside the unaltered originals.
|
||||
- Uses machine-learning to automatically add tags, correspondents and document types to your documents.
|
||||
- Supports PDF documents, images, plain text files, Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents)[^1] and more.
|
||||
- Supports PDF documents, images, plain text files, Office documents (Word, Excel, PowerPoint, and LibreOffice equivalents)[^1] and more.
|
||||
- Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and their format can be configured freely with different configurations assigned to different documents.
|
||||
- **Beautiful, modern web application** that features:
|
||||
- Customizable dashboard with statistics.
|
||||
|
@@ -445,7 +445,7 @@ are released, dependency support is confirmed, etc.
|
||||
13. Configure ImageMagick to allow processing of PDF documents. Most
|
||||
distributions have this disabled by default, since PDF documents can
|
||||
contain malware. If you don't do this, paperless will fall back to
|
||||
ghostscript for certain steps such as thumbnail generation.
|
||||
Ghostscript for certain steps such as thumbnail generation.
|
||||
|
||||
Edit `/etc/ImageMagick-6/policy.xml` and adjust
|
||||
|
||||
|
@@ -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
|
||||
@@ -335,7 +335,7 @@ You may see errors when deleting documents like:
|
||||
Data too long for column 'transaction_id' at row 1
|
||||
```
|
||||
|
||||
This error can occur in installations which have upgraded from a version of Paperless-ngx that used Django 4 (Paperless-ngx versions prior to v2.13.0) with a MariaDB/MySQL database. Due to the backawards-incompatible change in Django 5, the column "documents_document.transaction_id" will need to be re-created, which can be done with a one-time run of the following management command:
|
||||
This error can occur in installations which have upgraded from a version of Paperless-ngx that used Django 4 (Paperless-ngx versions prior to v2.13.0) with a MariaDB/MySQL database. Due to the backwards-incompatible change in Django 5, the column "documents_document.transaction_id" will need to be re-created, which can be done with a one-time run of the following management command:
|
||||
|
||||
```shell-session
|
||||
$ python3 manage.py convert_mariadb_uuid
|
||||
|
@@ -338,25 +338,25 @@ Global permissions define what areas of the app and API endpoints users can acce
|
||||
determine if a user can create, edit, delete or view _any_ documents, but individual documents themselves
|
||||
still have "object-level" permissions.
|
||||
|
||||
| Type | Details |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| AppConfig | _Change_ or higher permissions grants access to the "Application Configuration" area. |
|
||||
| Correspondent | Add, edit, delete or view Correspondents. |
|
||||
| CustomField | Add, edit, delete or view Custom Fields. |
|
||||
| Document | Add, edit, delete or view Documents. |
|
||||
| DocumentType | Add, edit, delete or view Document Types. |
|
||||
| Group | Add, edit, delete or view Groups. |
|
||||
| MailAccount | Add, edit, delete or view Mail Accounts. |
|
||||
| MailRule | Add, edit, delete or view Mail Rules. |
|
||||
| Note | Add, edit, delete or view Notes. |
|
||||
| PaperlessTask | View or dismiss (_Change_) File Tasks. |
|
||||
| SavedView | Add, edit, delete or view Saved Views. |
|
||||
| ShareLink | Add, delete or view Share Links. |
|
||||
| StoragePath | Add, edit, delete or view Storage Paths. |
|
||||
| Tag | Add, edit, delete or view Tags. |
|
||||
| UISettings | Add, edit, delete or view the UI settings that are used by the web app.<br/>:warning: **Users that will access the web UI must be granted at least _View_ permissions.** |
|
||||
| User | Add, edit, delete or view Users. |
|
||||
| Workflow | Add, edit, delete or view Workflows.<br/>Note that Workflows are global, in other words all users who can access workflows have access to the same set of them. |
|
||||
| Type | Details |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| AppConfig | _Change_ or higher permissions grants access to the "Application Configuration" area. |
|
||||
| Correspondent | Add, edit, delete or view Correspondents. |
|
||||
| CustomField | Add, edit, delete or view Custom Fields. |
|
||||
| Document | Add, edit, delete or view Documents. |
|
||||
| DocumentType | Add, edit, delete or view Document Types. |
|
||||
| Group | Add, edit, delete or view Groups. |
|
||||
| MailAccount | Add, edit, delete or view Mail Accounts. |
|
||||
| MailRule | Add, edit, delete or view Mail Rules. |
|
||||
| Note | Add, edit, delete or view Notes. |
|
||||
| PaperlessTask | View or dismiss (_Change_) File Tasks. |
|
||||
| SavedView | Add, edit, delete or view Saved Views. |
|
||||
| ShareLink | Add, delete or view Share Links. |
|
||||
| StoragePath | Add, edit, delete or view Storage Paths. |
|
||||
| Tag | Add, edit, delete or view Tags. |
|
||||
| UISettings | Add, edit, delete or view the UI settings that are used by the web app.<br/>:warning: **Users that will access the web UI must be granted at least _View_ permissions.** |
|
||||
| User | Add, edit, delete or view Users. |
|
||||
| Workflow | Add, edit, delete or view Workflows.<br/>Note that Workflows are global; all users who can access workflows see the same set. Workflows have other permission implications — see [Workflow permissions](#workflow-permissions). |
|
||||
|
||||
#### Detailed Explanation of Object Permissions {#object-permissions}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -533,7 +533,7 @@ The following placeholders are only available for "added" or "updated" triggers
|
||||
All users who have application permissions for editing workflows can see the same set
|
||||
of workflows. In other words, workflows themselves intentionally do not have an owner or permissions.
|
||||
|
||||
Given their potentially far-reaching capabilities, you may want to restrict access to workflows.
|
||||
Given their potentially far-reaching capabilities, including changing the permissions of existing documents, you may want to restrict access to workflows.
|
||||
|
||||
Upon migration, existing installs will grant access to workflows to users who can add
|
||||
documents (and superusers who can always access all parts of the app).
|
||||
|
@@ -52,12 +52,12 @@ if ! command -v wget &> /dev/null ; then
|
||||
fi
|
||||
|
||||
if ! command -v docker &> /dev/null ; then
|
||||
echo "docker executable not found. Is docker installed?"
|
||||
echo "docker executable not found. Is Docker installed?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! docker compose &> /dev/null ; then
|
||||
echo "docker compose plugin not found. Is docker compose installed?"
|
||||
echo "docker compose plugin not found. Is Docker Compose installed?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -66,7 +66,7 @@ fi
|
||||
if ! docker stats --no-stream &> /dev/null ; then
|
||||
echo ""
|
||||
echo "WARN: It look like the current user does not have Docker permissions."
|
||||
echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user (may require restarting shell)."
|
||||
echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user (may require restarting the shell)."
|
||||
echo ""
|
||||
sleep 3
|
||||
fi
|
||||
@@ -135,7 +135,7 @@ DATABASE_BACKEND=$ask_result
|
||||
|
||||
echo ""
|
||||
echo "Paperless is able to use Apache Tika to support Office documents such as"
|
||||
echo "Word, Excel, Powerpoint, and Libreoffice equivalents. This feature"
|
||||
echo "Word, Excel, PowerPoint, and LibreOffice equivalents. This feature"
|
||||
echo "requires more resources due to the required services."
|
||||
echo ""
|
||||
|
||||
@@ -157,7 +157,7 @@ echo ""
|
||||
echo "Specify the user id and group id you wish to run paperless as."
|
||||
echo "Paperless will also change ownership on the data, media and consume"
|
||||
echo "folder to the specified values, so it's a good idea to supply the user id"
|
||||
echo "and group id of your unix user account."
|
||||
echo "and group id of your Unix user account."
|
||||
echo "If unsure, leave default."
|
||||
echo ""
|
||||
|
||||
@@ -212,7 +212,7 @@ if [[ "$DATABASE_BACKEND" == "sqlite" ]] ; then
|
||||
echo -n "SQLite database, the "
|
||||
fi
|
||||
echo "search index and other data."
|
||||
echo "As with the media folder, leave empty to have this managed by docker."
|
||||
echo "As with the media folder, leave empty to have this managed by Docker."
|
||||
echo ""
|
||||
echo "CAUTION: If specified, you must specify an absolute path starting with /"
|
||||
echo "or a relative path starting with ./ here."
|
||||
@@ -224,7 +224,7 @@ DATA_FOLDER=$ask_result
|
||||
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
|
||||
echo ""
|
||||
echo "The database folder, where your database stores its data."
|
||||
echo "Leave empty to have this managed by docker."
|
||||
echo "Leave empty to have this managed by Docker."
|
||||
echo ""
|
||||
echo "CAUTION: If specified, you must specify an absolute path starting with /"
|
||||
echo "or a relative path starting with ./ here."
|
||||
@@ -276,18 +276,18 @@ echo ""
|
||||
echo "Target folder: $TARGET_FOLDER"
|
||||
echo "Consume folder: $CONSUME_FOLDER"
|
||||
if [[ -z $MEDIA_FOLDER ]] ; then
|
||||
echo "Media folder: Managed by docker"
|
||||
echo "Media folder: Managed by Docker"
|
||||
else
|
||||
echo "Media folder: $MEDIA_FOLDER"
|
||||
fi
|
||||
if [[ -z $DATA_FOLDER ]] ; then
|
||||
echo "Data folder: Managed by docker"
|
||||
echo "Data folder: Managed by Docker"
|
||||
else
|
||||
echo "Data folder: $DATA_FOLDER"
|
||||
fi
|
||||
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
|
||||
if [[ -z $DATABASE_FOLDER ]] ; then
|
||||
echo "Database folder: Managed by docker"
|
||||
echo "Database folder: Managed by Docker"
|
||||
else
|
||||
echo "Database folder: $DATABASE_FOLDER"
|
||||
fi
|
||||
|
@@ -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.0"
|
||||
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.2.0",
|
||||
"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,34 +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/tests/test_consumer.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/documents/tests/test_file_handling.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/documents/tests/test_migration_archive_files.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/documents/views.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/paperless/checks.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/paperless/settings.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/paperless/views.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/paperless_mail/mail.py" = [
|
||||
"PTH",
|
||||
] # TODO Enable & remove
|
||||
lint.per-file-ignores."src/paperless_tesseract/tests/test_parser.py" = [
|
||||
"PTH",
|
||||
"RUF001",
|
||||
] # TODO PTH Enable & remove
|
||||
]
|
||||
lint.isort.force-single-line = true
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
@@ -325,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'" },
|
||||
|
@@ -6,6 +6,7 @@ A document with an id of ${DOCUMENT_ID} was just consumed. I know the
|
||||
following additional information about it:
|
||||
|
||||
* Generated File Name: ${DOCUMENT_FILE_NAME}
|
||||
* Document type: ${DOCUMENT_TYPE}
|
||||
* Archive Path: ${DOCUMENT_ARCHIVE_PATH}
|
||||
* Source Path: ${DOCUMENT_SOURCE_PATH}
|
||||
* Created: ${DOCUMENT_CREATED}
|
||||
|
@@ -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',
|
||||
|
1189
src-ui/messages.xlf
1189
src-ui/messages.xlf
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "paperless-ngx-ui",
|
||||
"version": "2.16.0",
|
||||
"version": "2.17.1",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"ng": "ng",
|
||||
@@ -11,55 +11,55 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/cdk": "^19.2.14",
|
||||
"@angular/common": "~19.2.9",
|
||||
"@angular/compiler": "~19.2.9",
|
||||
"@angular/core": "~19.2.9",
|
||||
"@angular/forms": "~19.2.9",
|
||||
"@angular/localize": "~19.2.9",
|
||||
"@angular/platform-browser": "~19.2.9",
|
||||
"@angular/platform-browser-dynamic": "~19.2.9",
|
||||
"@angular/router": "~19.2.9",
|
||||
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
|
||||
"@ng-select/ng-select": "^14.7.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.3",
|
||||
"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",
|
||||
"uuid": "^11.1.0",
|
||||
"zone.js": "^0.15.0"
|
||||
"zone.js": "^0.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "^19.0.1",
|
||||
"@angular-builders/jest": "^19.0.1",
|
||||
"@angular-devkit/build-angular": "^19.2.10",
|
||||
"@angular-devkit/core": "^19.2.10",
|
||||
"@angular-devkit/schematics": "^19.2.10",
|
||||
"@angular-eslint/builder": "19.3.0",
|
||||
"@angular-eslint/eslint-plugin": "19.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "19.3.0",
|
||||
"@angular-eslint/schematics": "19.3.0",
|
||||
"@angular-eslint/template-parser": "19.3.0",
|
||||
"@angular/cli": "~19.2.10",
|
||||
"@angular/compiler-cli": "~19.2.9",
|
||||
"@codecov/webpack-plugin": "^1.9.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@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.53.2",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.15.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.31.1",
|
||||
"@typescript-eslint/parser": "^8.31.1",
|
||||
"@typescript-eslint/utils": "^8.31.1",
|
||||
"eslint": "^9.25.1",
|
||||
"@types/node": "^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,7 +67,8 @@
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"ts-node": "~10.9.1",
|
||||
"typescript": "^5.5.4"
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.99.9"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
@@ -77,6 +78,5 @@
|
||||
"lmdb",
|
||||
"msgpackr-extract"
|
||||
]
|
||||
},
|
||||
"typings": "./src/typings.d.ts"
|
||||
}
|
||||
}
|
||||
|
4952
src-ui/pnpm-lock.yaml
generated
4952
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[] = []
|
||||
|
||||
|
@@ -176,6 +176,7 @@
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<pngx-input-check i18n-title title="Show warning when closing saved views with unsaved changes" formControlName="savedViewsWarnOnUnsavedChange"></pngx-input-check>
|
||||
<pngx-input-check i18n-title title="Show document counts in sidebar saved views" formControlName="sidebarViewsShowCount"></pngx-input-check>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@@ -31,6 +31,7 @@ import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
|
||||
import { SafeHtmlPipe } from 'src/app/pipes/safehtml.pipe'
|
||||
import { PermissionsService } from 'src/app/services/permissions.service'
|
||||
import { GroupService } from 'src/app/services/rest/group.service'
|
||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import { SettingsService } from 'src/app/services/settings.service'
|
||||
import { SystemStatusService } from 'src/app/services/system-status.service'
|
||||
@@ -72,6 +73,7 @@ describe('SettingsComponent', () => {
|
||||
let groupService: GroupService
|
||||
let modalService: NgbModal
|
||||
let systemStatusService: SystemStatusService
|
||||
let savedViewsService: SavedViewService
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -122,6 +124,7 @@ describe('SettingsComponent', () => {
|
||||
permissionsService = TestBed.inject(PermissionsService)
|
||||
modalService = TestBed.inject(NgbModal)
|
||||
systemStatusService = TestBed.inject(SystemStatusService)
|
||||
savedViewsService = TestBed.inject(SavedViewService)
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
jest
|
||||
.spyOn(permissionsService, 'currentUserHasObjectPermissions')
|
||||
@@ -212,7 +215,7 @@ describe('SettingsComponent', () => {
|
||||
expect(toastErrorSpy).toHaveBeenCalled()
|
||||
expect(storeSpy).toHaveBeenCalled()
|
||||
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
|
||||
expect(setSpy).toHaveBeenCalledTimes(29)
|
||||
expect(setSpy).toHaveBeenCalledTimes(30)
|
||||
|
||||
// succeed
|
||||
storeSpy.mockReturnValueOnce(of(true))
|
||||
@@ -345,4 +348,14 @@ describe('SettingsComponent', () => {
|
||||
component.reset()
|
||||
expect(component.settingsForm.get('themeColor').value).toEqual('')
|
||||
})
|
||||
|
||||
it('should trigger maybeRefreshDocumentCounts on settings save', () => {
|
||||
completeSetup()
|
||||
const maybeRefreshSpy = jest.spyOn(
|
||||
savedViewsService,
|
||||
'maybeRefreshDocumentCounts'
|
||||
)
|
||||
settingsService.settingsSaved.emit(true)
|
||||
expect(maybeRefreshSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
@@ -2,10 +2,10 @@ import { AsyncPipe, ViewportScroller } from '@angular/common'
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
Inject,
|
||||
LOCALE_ID,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
inject,
|
||||
} from '@angular/core'
|
||||
import {
|
||||
FormControl,
|
||||
@@ -49,6 +49,7 @@ import {
|
||||
PermissionsService,
|
||||
} from 'src/app/services/permissions.service'
|
||||
import { GroupService } from 'src/app/services/rest/group.service'
|
||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service'
|
||||
import { UserService } from 'src/app/services/rest/user.service'
|
||||
import {
|
||||
LanguageOption,
|
||||
@@ -104,6 +105,21 @@ 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)
|
||||
private savedViewsService = inject(SavedViewService)
|
||||
|
||||
activeNavID: number
|
||||
|
||||
settingsForm = new FormGroup({
|
||||
@@ -138,6 +154,7 @@ export class SettingsComponent
|
||||
notificationsConsumerSuppressOnDashboard: new FormControl(null),
|
||||
|
||||
savedViewsWarnOnUnsavedChange: new FormControl(null),
|
||||
sidebarViewsShowCount: new FormControl(null),
|
||||
})
|
||||
|
||||
SettingsNavIDs = SettingsNavIDs
|
||||
@@ -179,24 +196,11 @@ 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()
|
||||
this.savedViewsService.maybeRefreshDocumentCounts()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -308,6 +312,9 @@ export class SettingsComponent
|
||||
savedViewsWarnOnUnsavedChange: this.settings.get(
|
||||
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE
|
||||
),
|
||||
sidebarViewsShowCount: this.settings.get(
|
||||
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT
|
||||
),
|
||||
defaultPermsOwner: this.settings.get(SETTINGS_KEYS.DEFAULT_PERMS_OWNER),
|
||||
defaultPermsViewUsers: this.settings.get(
|
||||
SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS
|
||||
@@ -485,6 +492,10 @@ export class SettingsComponent
|
||||
SETTINGS_KEYS.SAVED_VIEWS_WARN_ON_UNSAVED_CHANGE,
|
||||
this.settingsForm.value.savedViewsWarnOnUnsavedChange
|
||||
)
|
||||
this.settings.set(
|
||||
SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT,
|
||||
this.settingsForm.value.sidebarViewsShowCount
|
||||
)
|
||||
this.settings.set(
|
||||
SETTINGS_KEYS.DEFAULT_PERMS_OWNER,
|
||||
this.settingsForm.value.defaultPermsOwner
|
||||
|
@@ -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 })
|
||||
|
@@ -112,7 +112,14 @@
|
||||
routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name"
|
||||
[disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave"
|
||||
popoverClass="popover-slim">
|
||||
<i-bs class="me-1" name="funnel"></i-bs><span> {{view.name}}</span>
|
||||
<i-bs class="me-1" name="funnel"></i-bs><span> {{view.name}}
|
||||
@if (showSidebarCounts && !slimSidebarEnabled) {
|
||||
<span><span class="badge bg-info text-dark ms-2 d-inline">{{ savedViewService.getDocumentCount(view) }}</span></span>
|
||||
}
|
||||
</span>
|
||||
@if (showSidebarCounts && slimSidebarEnabled) {
|
||||
<span class="badge bg-info text-dark position-absolute top-0 end-0 d-none d-md-block">{{ savedViewService.getDocumentCount(view) }}</span>
|
||||
}
|
||||
</a>
|
||||
@if (settingsService.organizingSidebarSavedViews) {
|
||||
<div class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
|
||||
|
@@ -92,6 +92,7 @@ describe('AppFrameComponent', () => {
|
||||
let router: Router
|
||||
let savedViewSpy
|
||||
let modalService: NgbModal
|
||||
let maybeRefreshSpy
|
||||
|
||||
beforeEach(async () => {
|
||||
TestBed.configureTestingModule({
|
||||
@@ -113,7 +114,11 @@ describe('AppFrameComponent', () => {
|
||||
{
|
||||
provide: SavedViewService,
|
||||
useValue: {
|
||||
reload: () => {},
|
||||
reload: (fn: any) => {
|
||||
if (fn) {
|
||||
fn()
|
||||
}
|
||||
},
|
||||
listAll: () =>
|
||||
of({
|
||||
all: [saved_views.map((v) => v.id)],
|
||||
@@ -121,6 +126,8 @@ describe('AppFrameComponent', () => {
|
||||
results: saved_views,
|
||||
}),
|
||||
sidebarViews: saved_views.filter((v) => v.show_in_sidebar),
|
||||
getDocumentCount: (view: SavedView) => 5,
|
||||
maybeRefreshDocumentCounts: () => {},
|
||||
},
|
||||
},
|
||||
PermissionsService,
|
||||
@@ -169,6 +176,7 @@ describe('AppFrameComponent', () => {
|
||||
jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true)
|
||||
|
||||
savedViewSpy = jest.spyOn(savedViewService, 'reload')
|
||||
maybeRefreshSpy = jest.spyOn(savedViewService, 'maybeRefreshDocumentCounts')
|
||||
|
||||
fixture = TestBed.createComponent(AppFrameComponent)
|
||||
component = fixture.componentInstance
|
||||
@@ -359,4 +367,8 @@ describe('AppFrameComponent', () => {
|
||||
expect(toastErrorSpy).toHaveBeenCalledTimes(2)
|
||||
expect(toastInfoSpy).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
it('should call maybeRefreshDocumentCounts after saved views reload', () => {
|
||||
expect(maybeRefreshSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
@@ -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(
|
||||
@@ -102,7 +102,9 @@ export class AppFrameComponent
|
||||
PermissionType.SavedView
|
||||
)
|
||||
) {
|
||||
this.savedViewService.reload()
|
||||
this.savedViewService.reload(() => {
|
||||
this.savedViewService.maybeRefreshDocumentCounts()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +144,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)
|
||||
}
|
||||
@@ -279,4 +285,8 @@ export class AppFrameComponent
|
||||
onLogout() {
|
||||
this.openDocumentsService.closeAll()
|
||||
}
|
||||
|
||||
get showSidebarCounts(): boolean {
|
||||
return this.settingsService.get(SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT)
|
||||
}
|
||||
}
|
||||
|
@@ -529,6 +529,17 @@ describe('GlobalSearchComponent', () => {
|
||||
expect(dispatchSpy).toHaveBeenCalledTimes(2) // once for keydown, second for click
|
||||
})
|
||||
|
||||
it('should support using base href in navigateOrOpenInNewWindow', () => {
|
||||
jest
|
||||
.spyOn(component['locationStrategy'], 'getBaseHref')
|
||||
.mockReturnValue('/base/')
|
||||
const openSpy = jest.spyOn(window, 'open')
|
||||
const event = new Event('click')
|
||||
event['ctrlKey'] = true
|
||||
component.primaryAction(DataType.Document, { id: 1 }, event as any)
|
||||
expect(openSpy).toHaveBeenCalledWith('/base/documents/1', '_blank')
|
||||
})
|
||||
|
||||
it('should support title content search and advanced search', () => {
|
||||
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
|
||||
component.query = 'test'
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { NgTemplateOutlet } from '@angular/common'
|
||||
import { LocationStrategy, NgTemplateOutlet } from '@angular/common'
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
@@ -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,17 +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
|
||||
) {
|
||||
constructor() {
|
||||
this.queryDebounce = new Subject<string>()
|
||||
|
||||
this.queryDebounce
|
||||
@@ -421,10 +423,13 @@ export class GlobalSearchComponent implements OnInit {
|
||||
extras: Object = {}
|
||||
) {
|
||||
if (newWindow) {
|
||||
const url = this.router.serializeUrl(
|
||||
const serializedUrl = this.router.serializeUrl(
|
||||
this.router.createUrlTree(commands, extras)
|
||||
)
|
||||
window.open(url, '_blank')
|
||||
const baseHref = this.locationStrategy.getBaseHref()
|
||||
const fullUrl =
|
||||
baseHref.replace(/\/+$/, '') + '/' + serializedUrl.replace(/^\/+/, '')
|
||||
window.open(fullUrl, '_blank')
|
||||
} else {
|
||||
this.router.navigate(commands, extras)
|
||||
}
|
||||
|
@@ -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
|
||||
@@ -243,9 +246,9 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm
|
||||
|
||||
customFields: CustomField[] = []
|
||||
|
||||
public readonly today: string = new Date().toISOString().split('T')[0]
|
||||
public readonly today: string = new Date().toLocaleDateString('en-CA')
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -162,7 +165,7 @@ export class DatesDropdownComponent implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
placement: string = 'bottom-start'
|
||||
|
||||
public readonly today: string = new Date().toISOString().split('T')[0]
|
||||
public readonly today: string = new Date().toLocaleDateString('en-CA')
|
||||
|
||||
get isActive(): boolean {
|
||||
return (
|
||||
|
@@ -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[]
|
||||
@@ -62,7 +59,7 @@ export class DateComponent
|
||||
@Output()
|
||||
filterDocuments = new EventEmitter<NgbDateStruct[]>()
|
||||
|
||||
public readonly today: string = new Date().toISOString().split('T')[0]
|
||||
public readonly today: string = new Date().toLocaleDateString('en-CA')
|
||||
|
||||
getSuggestions() {
|
||||
return this.suggestions == null
|
||||
|
@@ -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 = ''
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user