diff --git a/.github/actions/apt-install/action.yml b/.github/actions/apt-install/action.yml index 3f20230e65..2a3c651816 100644 --- a/.github/actions/apt-install/action.yml +++ b/.github/actions/apt-install/action.yml @@ -1,53 +1,53 @@ name: Install apt packages description: > - Installs apt packages with a per-attempt timeout and retries. The hosted runner's Azure apt mirror intermittently serves archives at a crawl, which apt's own timeout does not catch. A stuck attempt is killed and retried, which usually lands on a healthy mirror node. + Installs apt packages with bounded download retries and Ubuntu mirror fallback. Caches downloaded packages as an optimization; apt resolves dependencies on every run. inputs: packages: - description: Space separated list of packages to install + description: Space separated list of package names to install required: true attempts: - description: Maximum number of attempts + description: Maximum number of download attempts default: "3" attempt-timeout: - description: Seconds before a single attempt is killed + description: Seconds before a single download attempt is killed default: "180" runs: using: composite steps: + - name: Identify apt cache + id: cache-key + shell: bash + env: + PACKAGES: ${{ inputs.packages }} + run: | + . /etc/os-release + packages_hash=$(printf '%s' "$PACKAGES" | sha256sum | cut -d ' ' -f 1) + # Cached downloads are optional: apt fetches any missing/newer packages. + # Refresh weekly, including inside job containers without ImageVersion. + echo "key=apt-v2-${ID}-${VERSION_ID}-$(dpkg --print-architecture)-$(date -u +%G-%V)-${packages_hash}" >> "$GITHUB_OUTPUT" + - name: Restore apt packages + id: cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: | + ${{ runner.temp }}/apt-packages/*.deb + ${{ runner.temp }}/apt-packages/.keep + key: ${{ steps.cache-key.outputs.key }} - name: Install ${{ inputs.packages }} shell: bash env: PACKAGES: ${{ inputs.packages }} ATTEMPTS: ${{ inputs.attempts }} ATTEMPT_TIMEOUT: ${{ inputs.attempt-timeout }} - run: | - SUDO="" - if [ "$(id -u)" -ne 0 ]; then - SUDO="sudo" - fi - - attempt_install() { - $SUDO apt-get update -q - # Intentionally unquoted, this is a list of package names - $SUDO apt-get install -q --yes --no-install-recommends $PACKAGES - } - export -f attempt_install - export SUDO PACKAGES - - for attempt in $(seq 1 "${ATTEMPTS}"); do - echo "::group::apt install attempt ${attempt}/${ATTEMPTS}" - start=$(date +%s) - if timeout --kill-after=10 "${ATTEMPT_TIMEOUT}" bash -c attempt_install; then - echo "::endgroup::" - echo "Installed in $(( $(date +%s) - start ))s" - exit 0 - fi - echo "::endgroup::" - echo "::warning::apt install attempt ${attempt}/${ATTEMPTS} failed or exceeded ${ATTEMPT_TIMEOUT}s" - # A killed attempt can leave dpkg half configured - $SUDO dpkg --configure -a || true - done - - echo "::error::apt install failed after ${ATTEMPTS} attempts" - exit 1 + CACHE_DIR: ${{ runner.temp }}/apt-packages + run: bash "$GITHUB_ACTION_PATH/install.sh" + # Save immediately: a later test failure must not discard downloaded packages. + - name: Save apt packages + if: steps.cache.outputs.cache-hit != 'true' + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: | + ${{ runner.temp }}/apt-packages/*.deb + ${{ runner.temp }}/apt-packages/.keep + key: ${{ steps.cache-key.outputs.key }} diff --git a/.github/actions/apt-install/install.sh b/.github/actions/apt-install/install.sh new file mode 100644 index 0000000000..11447785f3 --- /dev/null +++ b/.github/actions/apt-install/install.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Run as root in the timed child process. Only downloads may be interrupted. +if [ "${1:-}" = download ]; then + download_archive_dir=$2 + shift 2 + apt-get update -q + apt-get install -q --yes --no-install-recommends --download-only \ + -o "Dir::Cache::archives=$download_archive_dir" "$@" + exit 0 +fi + +SUDO=() +if [ "$(id -u)" -ne 0 ]; then + SUDO=(sudo) +fi +read -r -a requested_packages <<< "$PACKAGES" +mkdir -p "$CACHE_DIR" +touch "$CACHE_DIR/.keep" + +use_ubuntu_mirrors() { + # Hosted runners use a mirror list; job containers can point directly at Azure. + # Handle both layouts, leaving other repositories and signature checks intact. + if [ -f /etc/apt/apt-mirrors.txt ]; then + "${SUDO[@]}" sed -i '\|azure\.archive\.ubuntu\.com|d' /etc/apt/apt-mirrors.txt + fi + for source in /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; do + if [ -f "$source" ]; then + "${SUDO[@]}" sed -i 's|azure\.archive\.ubuntu\.com|archive.ubuntu.com|g' "$source" + fi + done +} + +for attempt in $(seq 1 "$ATTEMPTS"); do + echo "::group::apt download attempt ${attempt}/${ATTEMPTS}" + if timeout --kill-after=10 "$ATTEMPT_TIMEOUT" "${SUDO[@]}" bash "$0" download "$CACHE_DIR" "${requested_packages[@]}"; then + echo "::endgroup::" + # Apt handles aliases (e.g. libmagic1 -> libmagic1t64) and dependencies. + "${SUDO[@]}" apt-get install -q --yes --no-install-recommends --no-download \ + -o "Dir::Cache::archives=$CACHE_DIR" "${requested_packages[@]}" + # Allow the unprivileged cache action to read archives downloaded as root. + find "$CACHE_DIR" -maxdepth 1 -name '*.deb' -exec "${SUDO[@]}" chmod a+r {} + + exit 0 + fi + echo "::endgroup::" + echo "::warning::apt download attempt ${attempt}/${ATTEMPTS} failed or exceeded ${ATTEMPT_TIMEOUT}s" + if [ "$attempt" -eq 1 ] && [ "$ATTEMPTS" -gt 1 ]; then + echo "Switching from Azure to Ubuntu archive mirrors for remaining attempts" + use_ubuntu_mirrors + fi +done + +echo "::error::apt download failed after ${ATTEMPTS} attempts" +exit 1 diff --git a/.github/workflows/ci-backend.yml b/.github/workflows/ci-backend.yml index 0c30f2b1c7..80e13cc688 100644 --- a/.github/workflows/ci-backend.yml +++ b/.github/workflows/ci-backend.yml @@ -77,6 +77,7 @@ jobs: - 'uv.lock' - 'docker/compose/docker-compose.ci-test.yml' - '.github/workflows/ci-backend.yml' + - '.github/actions/apt-install/**' test: needs: changes if: needs.changes.outputs.backend_changed == 'true' @@ -99,11 +100,11 @@ jobs: docker compose --file docker/compose/docker-compose.ci-test.yml up --detach - name: Set up Python id: setup-python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 with: python-version: "${{ matrix.python-version }}" - name: Install uv - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: true @@ -169,11 +170,11 @@ jobs: persist-credentials: false - name: Set up Python id: setup-python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 with: python-version: "${{ env.DEFAULT_PYTHON }}" - name: Install uv - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: true diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 2a6c5a7612..7ff540c8f6 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -109,7 +109,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 - name: Login to GitHub Container Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -155,7 +155,7 @@ jobs: echo "${digest}" > "/tmp/digests/digest-${{ matrix.arch }}.txt" - name: Upload digest if: steps.check-push.outputs.should-push == 'true' - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: digests-${{ matrix.arch }} path: /tmp/digests/digest-${{ matrix.arch }}.txt @@ -173,7 +173,7 @@ jobs: packages: write steps: - name: Download digests - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: path: /tmp/digests pattern: digest-*.txt @@ -185,20 +185,20 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 - name: Login to GitHub Container Registry - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Docker Hub if: needs.build-arch.outputs.push-external == 'true' - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to Quay.io if: needs.build-arch.outputs.push-external == 'true' - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0 with: registry: quay.io username: ${{ secrets.QUAY_USERNAME }} diff --git a/.github/workflows/ci-docs.yml b/.github/workflows/ci-docs.yml index b471dd6d53..24a6057bcb 100644 --- a/.github/workflows/ci-docs.yml +++ b/.github/workflows/ci-docs.yml @@ -75,11 +75,11 @@ jobs: persist-credentials: false - name: Set up Python id: setup-python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} - name: Install uv - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: true diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml index bcf40223ab..83c1230f13 100644 --- a/.github/workflows/ci-frontend.yml +++ b/.github/workflows/ci-frontend.yml @@ -69,6 +69,7 @@ jobs: frontend: - 'src-ui/**' - '.github/workflows/ci-frontend.yml' + - '.github/actions/apt-install/**' install-dependencies: needs: changes if: needs.changes.outputs.frontend_changed == 'true' @@ -86,7 +87,7 @@ jobs: with: package_json_file: src-ui/package.json - name: Use Node.js 24 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24.x cache: 'pnpm' @@ -118,7 +119,7 @@ jobs: with: package_json_file: src-ui/package.json - name: Use Node.js 24 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24.x cache: 'pnpm' @@ -157,7 +158,7 @@ jobs: with: package_json_file: src-ui/package.json - name: Use Node.js 24 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24.x cache: 'pnpm' @@ -206,18 +207,18 @@ jobs: with: package_json_file: src-ui/package.json - name: Use Node.js 24 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24.x cache: 'pnpm' cache-dependency-path: 'src-ui/pnpm-lock.yaml' - name: Set up Python id: setup-python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 with: python-version: '3.12' - name: Install uv - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 with: version: '0.13.x' enable-cache: false @@ -261,7 +262,7 @@ jobs: with: package_json_file: src-ui/package.json - name: Use Node.js 24 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24.x cache: 'pnpm' diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml index 5abeab41a0..b3d885a01d 100644 --- a/.github/workflows/ci-lint.yml +++ b/.github/workflows/ci-lint.yml @@ -22,8 +22,8 @@ jobs: with: persist-credentials: false - name: Install Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 with: python-version: "3.14" - name: Run prek - uses: j178/prek-action@4e14d07f9231acabce116ccfca13b13dd9755ece # v3.0.0 + uses: j178/prek-action@44924fe9ef4a6f88253c4b6b1bf11c689d3ce53d # v3.1.0 diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 6717b74542..33a09a1458 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -44,7 +44,7 @@ jobs: with: package_json_file: src-ui/package.json - name: Use Node.js 24 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24.x package-manager-cache: false @@ -55,11 +55,11 @@ jobs: # ---- Backend Setup ---- - name: Set up Python id: setup-python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} - name: Install uv - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: false @@ -137,7 +137,7 @@ jobs: sudo chown -R 1000:1000 paperless-ngx/ tar -cJf paperless-ngx.tar.xz paperless-ngx/ - name: Upload release artifact - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: release path: dist/paperless-ngx.tar.xz @@ -155,7 +155,7 @@ jobs: version: ${{ steps.get-version.outputs.version }} steps: - name: Download release artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: release path: ./ @@ -172,7 +172,7 @@ jobs: fi - name: Create release and changelog id: create-release - uses: release-drafter/release-drafter@34d80673e067bdc0c24568d3af899c216adcfaa9 # v7.7.0 + uses: release-drafter/release-drafter@978bb0b30c5ee8cdd332ced253758157150d43f0 # v7.10.0 with: name: Paperless-ngx ${{ steps.get-version.outputs.version }} tag: ${{ steps.get-version.outputs.version }} @@ -183,7 +183,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload release archive - uses: shogo82148/actions-upload-release-asset@aaba0f56bdbc1071f4af234d5cb16055e8a400de # v1.10.4 + uses: shogo82148/actions-upload-release-asset@73f83b114970a3485e1423ecc1fb93d887c9b096 # v1.10.7 with: github_token: ${{ secrets.GITHUB_TOKEN }} upload_url: ${{ steps.create-release.outputs.upload_url }} @@ -209,11 +209,11 @@ jobs: persist-credentials: true # for pushing changelog branch - name: Set up Python id: setup-python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} - name: Install uv - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: false diff --git a/.github/workflows/ci-static-analysis.yml b/.github/workflows/ci-static-analysis.yml index c534e75c05..1370ae494f 100644 --- a/.github/workflows/ci-static-analysis.yml +++ b/.github/workflows/ci-static-analysis.yml @@ -27,7 +27,7 @@ jobs: with: persist-credentials: false - name: Run zizmor - uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4 + uses: zizmorcore/zizmor-action@53290badfa369cb0bf7ad16342cb62c2fa47ac7b # v0.8.0 semgrep: name: Semgrep CE runs-on: ubuntu-26.04 @@ -45,7 +45,7 @@ jobs: - name: Run Semgrep run: semgrep scan --config auto --sarif-output results.sarif - name: Upload results to GitHub code scanning - uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 + uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 if: always() with: sarif_file: results.sarif diff --git a/.github/workflows/cleanup-tags.yml b/.github/workflows/cleanup-tags.yml index f09691504f..ec7cd8014a 100644 --- a/.github/workflows/cleanup-tags.yml +++ b/.github/workflows/cleanup-tags.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Clean temporary images if: "${{ env.TOKEN != '' }}" - uses: stumpylog/image-cleaner-action/ephemeral@21f875bab2376314e0525c614e923c669434f0e4 # v0.13.0 + uses: stumpylog/image-cleaner-action/ephemeral@9796867b2a7994f0a3b6b1f2e38a6579f502c977 # v0.14.0 with: token: "${{ env.TOKEN }}" owner: "${{ github.repository_owner }}" @@ -56,7 +56,7 @@ jobs: steps: - name: Clean untagged images if: "${{ env.TOKEN != '' }}" - uses: stumpylog/image-cleaner-action/untagged@21f875bab2376314e0525c614e923c669434f0e4 # v0.13.0 + uses: stumpylog/image-cleaner-action/untagged@9796867b2a7994f0a3b6b1f2e38a6579f502c977 # v0.14.0 with: token: "${{ env.TOKEN }}" owner: "${{ github.repository_owner }}" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 424e52164c..33763f630f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,7 +39,7 @@ jobs: persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 + uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -47,4 +47,4 @@ jobs: # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 + uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index 4f7b8a395b..67c203ee8d 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -22,7 +22,7 @@ jobs: token: ${{ secrets.PNGX_BOT_PAT }} persist-credentials: false - name: crowdin action - uses: crowdin/github-action@9c23991700c0ec5256fd41089b9d9d7d540e424e # v3.3.0 + uses: crowdin/github-action@959dc74ac104447731cba99a016f85d0ae719788 # v3.5.0 with: upload_translations: false download_translations: true diff --git a/.github/workflows/project-actions.yml b/.github/workflows/project-actions.yml index e2941292b7..90114d2086 100644 --- a/.github/workflows/project-actions.yml +++ b/.github/workflows/project-actions.yml @@ -19,6 +19,6 @@ jobs: if: github.event_name == 'pull_request_target' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request.user.login != 'dependabot' steps: - name: Label PR with release-drafter - uses: release-drafter/release-drafter@34d80673e067bdc0c24568d3af899c216adcfaa9 # v7.7.0 + uses: release-drafter/release-drafter@978bb0b30c5ee8cdd332ced253758157150d43f0 # v7.10.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/translate-strings.yml b/.github/workflows/translate-strings.yml index ba8269bc50..0beb20d6e4 100644 --- a/.github/workflows/translate-strings.yml +++ b/.github/workflows/translate-strings.yml @@ -23,14 +23,14 @@ jobs: persist-credentials: true # for pushing translation branch - name: Set up Python id: setup-python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 - name: Install system dependencies timeout-minutes: 12 uses: $/.github/actions/apt-install with: packages: gettext - name: Install uv - uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2 + uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: true @@ -48,7 +48,7 @@ jobs: with: package_json_file: src-ui/package.json - name: Use Node.js 24 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 24.x cache: 'pnpm' @@ -68,7 +68,7 @@ jobs: cd src-ui pnpm run ng extract-i18n - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0 + uses: stefanzweifel/git-auto-commit-action@d0b01107b5db882071f96de5dd4b54961ea93f4c # v7.2.1 with: file_pattern: 'src-ui/messages.xlf src/locale/en_US/LC_MESSAGES/django.po' commit_message: "Auto translate strings" diff --git a/Dockerfile b/Dockerfile index 8397b0d12b..1c98137794 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,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.13.5-python3.14-trixie-slim@sha256:d8e732839dfc4225a1ff698803b478b2223c05636c055d3add41ab9dbec0f315 AS s6-overlay-base +FROM ghcr.io/astral-sh/uv:0.13.7-python3.14-trixie-slim@sha256:e80c05a310400cfc90af00b5b7271ef9ad24816e3265e1162af8a678e1aad099 AS s6-overlay-base WORKDIR /usr/src/s6 diff --git a/SECURITY.md b/SECURITY.md index 5418f67a50..b08aaabae2 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -66,7 +66,7 @@ The following are not generally considered vulnerabilities unless accompanied by - optional webhook, mail, AI, OCR, or integration behavior described without a product-level vulnerability - missing limits or hardening settings presented without concrete impact - generic AI or static-analysis output that is not confirmed against the current codebase and a real deployment scenario -- metadata names visible in a document's custom storage path, even when the user cannot access the underlying metadata object; this is expected behavior +- metadata names or normalized words visible in a document's custom storage path or through search/autocomplete for documents the user can access, even when the user cannot access the underlying metadata object; this is expected behavior - the ability to attach objects that a user cannot access to a document by ID is an intentional design choice, and not considered a vulnerability ## Transparency diff --git a/docker/init-flow.drawio.png b/docker/init-flow.drawio.png deleted file mode 100644 index 2e19ac16a0..0000000000 Binary files a/docker/init-flow.drawio.png and /dev/null differ diff --git a/docker/init-flow.md b/docker/init-flow.md new file mode 100644 index 0000000000..e87706ad00 --- /dev/null +++ b/docker/init-flow.md @@ -0,0 +1,57 @@ +# Container init flow + +Startup order of the s6-rc oneshot services under `docker/rootfs/etc/s6-overlay/s6-rc.d/`. An arrow `A --> B` means B lists A in its `dependencies.d/`, so B waits for A. Redundant (transitive) edges are omitted. + +Each labelled box is a group of services that can run in parallel: every service in it starts as soon as its own dependencies finish, so services in the same box run in parallel. A service does not wait for the rest of its group, only for the arrows pointing at it. Long-running services such as `init-tesseract-langs` (package installs) overlap with several later steps. + +```mermaid +flowchart TD + subgraph w3 ["Run in parallel"] + init-tesseract-langs + init-modify-user + init-wait-for-db + init-wait-for-redis + end + subgraph w4 ["Run in parallel"] + init-folders + init-compile-bytecode + end + subgraph w6 ["Run in parallel"] + init-search-index + init-llmindex-migrate + init-superuser + end + + base --> init-start --> init-env-file + + init-env-file --> init-tesseract-langs + init-env-file --> init-modify-user + init-env-file --> init-wait-for-db + init-env-file --> init-wait-for-redis + init-start --> init-compile-bytecode + + init-modify-user --> init-folders + init-modify-user --> init-compile-bytecode + + init-folders --> init-migrations + init-compile-bytecode --> init-migrations + init-wait-for-db --> init-migrations + + init-migrations --> init-search-index + init-migrations --> init-llmindex-migrate + init-migrations --> init-superuser + + init-tesseract-langs --> init-search-index + init-tesseract-langs --> init-llmindex-migrate + init-tesseract-langs --> init-system-checks + init-superuser --> init-system-checks + + init-search-index --> init-custom-init + init-system-checks --> init-custom-init + init-wait-for-redis --> init-custom-init + + init-custom-init --> init-complete + init-llmindex-migrate --> init-complete +``` + +`init-search-index` and `init-llmindex-migrate` run `manage.py`, which runs the Django system checks, including the check that the configured OCR languages are installed, so they wait for `init-tesseract-langs`. diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-llmindex-migrate/dependencies.d/init-tesseract-langs b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-llmindex-migrate/dependencies.d/init-tesseract-langs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/dependencies.d/init-tesseract-langs b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/dependencies.d/init-tesseract-langs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/changelog.md b/docs/changelog.md index af1ad02098..b2b45bc1e4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,72 @@ # Changelog +## paperless-ngx 3.4.2 + +### Bug Fixes + +- Fix: mobile save button display obsessing [@shamoon](https://github.com/shamoon) ([#14676](https://github.com/paperless-ngx/paperless-ngx/pull/14676)) +- Fix: make index and llmindex init wait for tesseract language install [@stumpylog](https://github.com/stumpylog) ([#14670](https://github.com/paperless-ngx/paperless-ngx/pull/14670)) +- Fix: prevent loss of save view name on save + close [@shamoon](https://github.com/shamoon) ([#14667](https://github.com/paperless-ngx/paperless-ngx/pull/14667)) +- Fixhancement: doc detail mobile ux tweaks [@shamoon](https://github.com/shamoon) ([#14664](https://github.com/paperless-ngx/paperless-ngx/pull/14664)) +- Fix: Bumps filelock to 4.2.6 to pick up ACL related fixes [@stumpylog](https://github.com/stumpylog) ([#14656](https://github.com/paperless-ngx/paperless-ngx/pull/14656)) +- Fix: ensure import bulk\_edit tasks in Celery workers [@shamoon](https://github.com/shamoon) ([#14653](https://github.com/paperless-ngx/paperless-ngx/pull/14653)) +- Fixhancement: add tag names to document history list [@shamoon](https://github.com/shamoon) ([#14625](https://github.com/paperless-ngx/paperless-ngx/pull/14625)) +- Fix: add button type to zoom buttons to prevent form submission [@shamoon](https://github.com/shamoon) ([#14624](https://github.com/paperless-ngx/paperless-ngx/pull/14624)) +- Fix: move document list preview button to own column [@shamoon](https://github.com/shamoon) ([#14610](https://github.com/paperless-ngx/paperless-ngx/pull/14610)) +- Fix: Various small bugs found during typing updates [@stumpylog](https://github.com/stumpylog) ([#14593](https://github.com/paperless-ngx/paperless-ngx/pull/14593)) +- Fix: fix custom fields dropdown text size [@shamoon](https://github.com/shamoon) ([#14606](https://github.com/paperless-ngx/paperless-ngx/pull/14606)) + +### Dependencies + +
+14 changes + +- Chore: ditch the abandoned ngx-bootstrap-icons, roll our own [@shamoon](https://github.com/shamoon) ([#14665](https://github.com/paperless-ngx/paperless-ngx/pull/14665)) +- chore(deps): bump turbohtml to 1.15.3 [@stumpylog](https://github.com/stumpylog) ([#14662](https://github.com/paperless-ngx/paperless-ngx/pull/14662)) +- docker-compose(deps): bump gotenberg/gotenberg from 8.37 to 8.38 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#14634](https://github.com/paperless-ngx/paperless-ngx/pull/14634)) +- chore(deps): bump Django, django-guardian, django-filter, drf-spectacular-sidecar [@stumpylog](https://github.com/stumpylog) ([#14661](https://github.com/paperless-ngx/paperless-ngx/pull/14661)) +- docker-compose(deps): bump greenmail/standalone from 2.1.14 to 2.1.15 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#14631](https://github.com/paperless-ngx/paperless-ngx/pull/14631)) +- docker-compose(deps): Bump nginx from 1.31.6-alpine to 1.31.7-alpine in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#14633](https://github.com/paperless-ngx/paperless-ngx/pull/14633)) +- Chore(deps): Bump the frontend-angular-dependencies group across 1 directory with 13 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14637](https://github.com/paperless-ngx/paperless-ngx/pull/14637)) +- Chore(deps-dev): Bump @playwright/test from 1.62.1 to 1.64.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14641](https://github.com/paperless-ngx/paperless-ngx/pull/14641)) +- Chore(deps): Bump pdfjs-dist from 6.3.289 to 6.4.299 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14644](https://github.com/paperless-ngx/paperless-ngx/pull/14644)) +- Chore(deps): Bump uuid from 14.0.2 to 14.0.3 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14640](https://github.com/paperless-ngx/paperless-ngx/pull/14640)) +- Chore(deps-dev): Bump the frontend-eslint-dependencies group across 1 directory with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14638](https://github.com/paperless-ngx/paperless-ngx/pull/14638)) +- Chore(deps): Bump marked from 18.0.14 to 18.1.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14643](https://github.com/paperless-ngx/paperless-ngx/pull/14643)) +- Chore(deps-dev): Bump prettier from 3.9.9 to 3.9.10 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14645](https://github.com/paperless-ngx/paperless-ngx/pull/14645)) +- Chore(deps-dev): Bump @types/node from 26.6.2 to 26.6.5 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14639](https://github.com/paperless-ngx/paperless-ngx/pull/14639)) + +
+ +### All App Changes + +
+21 changes + +- Fix: mobile save button display obsessing [@shamoon](https://github.com/shamoon) ([#14676](https://github.com/paperless-ngx/paperless-ngx/pull/14676)) +- Fix: prevent loss of save view name on save + close [@shamoon](https://github.com/shamoon) ([#14667](https://github.com/paperless-ngx/paperless-ngx/pull/14667)) +- Chore: ditch the abandoned ngx-bootstrap-icons, roll our own [@shamoon](https://github.com/shamoon) ([#14665](https://github.com/paperless-ngx/paperless-ngx/pull/14665)) +- Fixhancement: doc detail mobile ux tweaks [@shamoon](https://github.com/shamoon) ([#14664](https://github.com/paperless-ngx/paperless-ngx/pull/14664)) +- chore(deps): bump turbohtml to 1.15.3 [@stumpylog](https://github.com/stumpylog) ([#14662](https://github.com/paperless-ngx/paperless-ngx/pull/14662)) +- chore(deps): bump Django, django-guardian, django-filter, drf-spectacular-sidecar [@stumpylog](https://github.com/stumpylog) ([#14661](https://github.com/paperless-ngx/paperless-ngx/pull/14661)) +- Fix: Bumps filelock to 4.2.6 to pick up ACL related fixes [@stumpylog](https://github.com/stumpylog) ([#14656](https://github.com/paperless-ngx/paperless-ngx/pull/14656)) +- Fix: ensure import bulk\_edit tasks in Celery workers [@shamoon](https://github.com/shamoon) ([#14653](https://github.com/paperless-ngx/paperless-ngx/pull/14653)) +- Chore(deps): Bump the frontend-angular-dependencies group across 1 directory with 13 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14637](https://github.com/paperless-ngx/paperless-ngx/pull/14637)) +- Chore(deps-dev): Bump @playwright/test from 1.62.1 to 1.64.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14641](https://github.com/paperless-ngx/paperless-ngx/pull/14641)) +- Chore(deps): Bump pdfjs-dist from 6.3.289 to 6.4.299 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14644](https://github.com/paperless-ngx/paperless-ngx/pull/14644)) +- Chore(deps): Bump uuid from 14.0.2 to 14.0.3 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14640](https://github.com/paperless-ngx/paperless-ngx/pull/14640)) +- Chore(deps-dev): Bump the frontend-eslint-dependencies group across 1 directory with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14638](https://github.com/paperless-ngx/paperless-ngx/pull/14638)) +- Chore(deps): Bump marked from 18.0.14 to 18.1.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14643](https://github.com/paperless-ngx/paperless-ngx/pull/14643)) +- Chore(deps-dev): Bump prettier from 3.9.9 to 3.9.10 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14645](https://github.com/paperless-ngx/paperless-ngx/pull/14645)) +- Chore(deps-dev): Bump @types/node from 26.6.2 to 26.6.5 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14639](https://github.com/paperless-ngx/paperless-ngx/pull/14639)) +- Fixhancement: add tag names to document history list [@shamoon](https://github.com/shamoon) ([#14625](https://github.com/paperless-ngx/paperless-ngx/pull/14625)) +- Fix: add button type to zoom buttons to prevent form submission [@shamoon](https://github.com/shamoon) ([#14624](https://github.com/paperless-ngx/paperless-ngx/pull/14624)) +- Fix: move document list preview button to own column [@shamoon](https://github.com/shamoon) ([#14610](https://github.com/paperless-ngx/paperless-ngx/pull/14610)) +- Fix: Various small bugs found during typing updates [@stumpylog](https://github.com/stumpylog) ([#14593](https://github.com/paperless-ngx/paperless-ngx/pull/14593)) +- Fix: fix custom fields dropdown text size [@shamoon](https://github.com/shamoon) ([#14606](https://github.com/paperless-ngx/paperless-ngx/pull/14606)) + +
+ ## paperless-ngx 3.4.1 ### Bug Fixes diff --git a/pyproject.toml b/pyproject.toml index a113732408..17d9e92a31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "paperless-ngx" -version = "3.4.1" +version = "3.4.2" description = """\ A community-supported supercharged document management system: scan, index and archive all your physical documents\ """ @@ -27,7 +27,7 @@ dependencies = [ # WARNING: django does not use semver. # Only patch versions are guaranteed to not introduce breaking changes. "django~=5.2.18", - "django-allauth[mfa,socialaccount]~=65.20.0", + "django-allauth[mfa,socialaccount]~=65.21.1", "django-auditlog~=3.4.1", "django-cachalot~=2.9.0", "django-compression-middleware~=0.5.0", @@ -112,14 +112,14 @@ lint = [ testing = [ "daphne", "factory-boy~=3.3.1", - "faker>=40.36,<40.40", + "faker>=40.36,<40.44", "imagehash", "pytest~=9.1.1", "pytest-cov~=7.1.0", "pytest-django>=4.12,<4.15", - "pytest-env~=1.7.0", + "pytest-env~=1.8.1", "pytest-httpx", - "pytest-mock~=3.15.1", + "pytest-mock~=3.17.0", "pytest-randomly~=5.0.0", "pytest-rerunfailures~=16.4", "pytest-sugar", diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 073a2a394b..bc7c63a91e 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -687,11 +687,11 @@ src/app/components/document-detail/document-detail.component.html - 441 + 442 src/app/components/document-detail/document-detail.component.html - 442,443 + 443,444 src/app/components/document-list/bulk-editor/custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component.html @@ -839,7 +839,7 @@ src/app/components/common/page-header/page-header.component.html - 30 + 32 src/app/components/common/permissions-dialog/permissions-dialog.component.html @@ -863,7 +863,7 @@ src/app/components/document-detail/document-detail.component.html - 473 + 474 src/app/components/document-list/document-list.component.html @@ -1141,7 +1141,7 @@ src/app/components/common/page-header/page-header.component.html - 18 + 20 src/app/components/common/permissions-select/permissions-select.component.html @@ -1366,7 +1366,7 @@ src/app/components/document-detail/document-detail.component.ts - 1913 + 1918 @@ -3401,11 +3401,11 @@ src/app/components/document-detail/document-detail.component.ts - 1527 + 1532 src/app/components/document-detail/document-detail.component.ts - 1914 + 1919 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -4152,7 +4152,7 @@ src/app/components/document-detail/document-detail.component.ts - 1476 + 1481 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -4296,7 +4296,7 @@ src/app/components/document-detail/document-detail.component.ts - 1967 + 1972 @@ -4307,7 +4307,7 @@ src/app/components/document-detail/document-detail.component.ts - 1968 + 1973 @@ -4318,7 +4318,7 @@ src/app/components/document-detail/document-detail.component.ts - 1969 + 1974 @@ -6575,7 +6575,7 @@ src/app/components/document-detail/document-detail.component.ts - 1480 + 1485 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -7156,14 +7156,14 @@ Copy ID src/app/components/common/page-header/page-header.component.html - 6 + 7 Copied! src/app/components/common/page-header/page-header.component.html - 8 + 9 src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -7198,7 +7198,7 @@ Read more src/app/components/common/page-header/page-header.component.html - 24,25 + 26,27 src/app/components/common/permissions-select/permissions-select.component.html @@ -8473,7 +8473,7 @@ No documents src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 189,192 + 191,194 @@ -8596,7 +8596,7 @@ src/app/components/document-detail/document-detail.component.html - 496 + 497 src/app/components/document-list/document-list.component.html @@ -8871,82 +8871,82 @@ Save & next src/app/components/document-detail/document-detail.component.html - 445 + 446 src/app/components/document-detail/document-detail.component.html - 456 + 457 Save & close src/app/components/document-detail/document-detail.component.html - 447 + 448 src/app/components/document-detail/document-detail.component.html - 458 + 459 Discard src/app/components/document-detail/document-detail.component.html - 449 + 450 src/app/components/document-detail/document-detail.component.html - 450,451 + 451,452 More save options src/app/components/document-detail/document-detail.component.html - 453 + 454 Document loading... src/app/components/document-detail/document-detail.component.html - 468 + 469 of src/app/components/document-detail/document-detail.component.html - 497,498 + 498,499 Zoom out src/app/components/document-detail/document-detail.component.html - 501 + 502 Zoom src/app/components/document-detail/document-detail.component.html - 504 + 505 Zoom in src/app/components/document-detail/document-detail.component.html - 511 + 512 Enter Password src/app/components/document-detail/document-detail.component.html - 545 + 546 @@ -9142,14 +9142,14 @@ Do you really want to move the document "" to the trash? src/app/components/document-detail/document-detail.component.ts - 1477 + 1482 Documents can be restored prior to permanent deletion. src/app/components/document-detail/document-detail.component.ts - 1478 + 1483 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -9160,21 +9160,21 @@ Moved "" to the trash src/app/components/document-detail/document-detail.component.ts - 1496 + 1501 Error deleting document src/app/components/document-detail/document-detail.component.ts - 1503 + 1508 Reprocess confirm src/app/components/document-detail/document-detail.component.ts - 1523 + 1528 src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -9185,102 +9185,102 @@ This operation will permanently recreate the archive file for this document. src/app/components/document-detail/document-detail.component.ts - 1524 + 1529 The archive file will be re-generated with the current settings. src/app/components/document-detail/document-detail.component.ts - 1525 + 1530 Reprocess operation for "" will begin in the background. src/app/components/document-detail/document-detail.component.ts - 1538 + 1543 Error executing operation src/app/components/document-detail/document-detail.component.ts - 1549 + 1554 Error downloading document src/app/components/document-detail/document-detail.component.ts - 1613 + 1618 Page Fit src/app/components/document-detail/document-detail.component.ts - 1691 + 1696 PDF edit operation for "" will begin in the background. src/app/components/document-detail/document-detail.component.ts - 1934 + 1939 Error executing PDF edit operation src/app/components/document-detail/document-detail.component.ts - 1946 + 1951 Please enter the current password before attempting to remove it. src/app/components/document-detail/document-detail.component.ts - 1957 + 1962 Password removal operation for "" will begin in the background. src/app/components/document-detail/document-detail.component.ts - 1991 + 1996 Error executing password removal operation src/app/components/document-detail/document-detail.component.ts - 2005 + 2010 Print failed. src/app/components/document-detail/document-detail.component.ts - 2055 + 2060 Error loading document for printing. src/app/components/document-detail/document-detail.component.ts - 2064 + 2069 An error occurred loading tiff: src/app/components/document-detail/document-detail.component.ts - 2147 + 2152 src/app/components/document-detail/document-detail.component.ts - 2153 + 2158 @@ -10404,31 +10404,31 @@ - View "" saved successfully. + View "" saved successfully. src/app/components/document-list/document-list.component.ts - 422 + 423 - Failed to save view "". + Failed to save view "". src/app/components/document-list/document-list.component.ts - 428 + 429 View "" created successfully. src/app/components/document-list/document-list.component.ts - 497 + 498 View "" created successfully, but could not update visibility settings. src/app/components/document-list/document-list.component.ts - 503 + 504 diff --git a/src-ui/package.json b/src-ui/package.json index 9255ff139e..bd61a91818 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -1,6 +1,6 @@ { "name": "paperless-ngx-ui", - "version": "3.4.1", + "version": "3.4.2", "scripts": { "preinstall": "npx only-allow pnpm", "ng": "ng", diff --git a/src-ui/src/app/app-routing.module.ts b/src-ui/src/app/app-routing.module.ts index ea005f94ec..3a8dd98454 100644 --- a/src-ui/src/app/app-routing.module.ts +++ b/src-ui/src/app/app-routing.module.ts @@ -38,7 +38,7 @@ export const routes: Routes = [ path: 'dashboard', component: DashboardComponent, data: { - componentName: 'AppFrameComponent', + componentName: 'DashboardComponent', }, }, { diff --git a/src-ui/src/app/components/common/page-header/page-header.component.html b/src-ui/src/app/components/common/page-header/page-header.component.html index cd573a6987..d8f66308ba 100644 --- a/src-ui/src/app/components/common/page-header/page-header.component.html +++ b/src-ui/src/app/components/common/page-header/page-header.component.html @@ -1,16 +1,18 @@

- {{title()}} - @if (id()) { - - } + + {{title()}} + @if (id()) { + + } + @if (subTitle()) { {{subTitle()}} } diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html index fb8f8c95ba..823e4f2aff 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html @@ -62,13 +62,13 @@ } @else { @switch (field) { @case (DisplayField.ADDED) { - {{doc.added | customDate}} + {{doc.added | customDate}} } @case (DisplayField.CREATED) { - {{doc.created | customDate}} + {{doc.created | customDate}} } @case (DisplayField.TITLE) { - {{doc.title | documentTitle}} + {{doc.title | documentTitle}} } @case (DisplayField.CORRESPONDENT) { @if (doc.correspondent) { @@ -106,7 +106,7 @@ } @case (DisplayField.NOTES) { @if (doc.notes.length) { - + {{doc.notes.length}} @@ -150,6 +150,7 @@ { it('should navigate to document', () => { const routerSpy = jest.spyOn(router, 'navigate') + const activateSpy = jest.spyOn( + documentListViewService, + 'activateSavedViewWithDocuments' + ) component.openDocumentDetail(documentResults[0]) + expect(activateSpy).toHaveBeenCalled() expect(routerSpy).toHaveBeenCalledWith(['documents', documentResults[0].id]) }) + it('should hand its documents to the list service when opening a document', () => { + jest.spyOn(documentService, 'listFiltered').mockReturnValue( + of({ + all: [2, 3], + count: 25, + results: documentResults, + }) + ) + jest.spyOn(router, 'navigateByUrl').mockResolvedValue(true) + const activateSpy = jest + .spyOn(documentListViewService, 'activateSavedViewWithDocuments') + .mockImplementation(() => {}) + component.ngOnInit() + fixture.detectChanges() + fixture.debugElement + .query(By.css('a[title="Open document"]')) + .nativeElement.click() + expect(activateSpy).toHaveBeenCalledWith(savedView, documentResults, 25, 20) + }) + + it('should fall back to the default dashboard page size', () => { + component.savedView = { ...savedView, page_size: undefined } + expect(component.pageSize).toEqual(DEFAULT_DASHBOARD_VIEW_PAGE_SIZE) + }) + it('should navigate via quickfilter on click tag', () => { const qfSpy = jest.spyOn(documentListViewService, 'quickFilter') component.clickTag(11, new MouseEvent('click')) diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts index 09988c8992..5daf99fb22 100644 --- a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts +++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts @@ -132,7 +132,7 @@ export class SavedViewWidgetComponent ngOnInit(): void { this.placeholderRows = Array.from( { - length: this.savedView?.page_size ?? DEFAULT_DASHBOARD_VIEW_PAGE_SIZE, + length: this.pageSize, }, (_, index) => index ) @@ -187,7 +187,7 @@ export class SavedViewWidgetComponent this.documentService .listFiltered( 1, - this.savedView?.page_size ?? DEFAULT_DASHBOARD_VIEW_PAGE_SIZE, + this.pageSize, this.savedView.sort_field, this.savedView.sort_reverse, this.savedView.filter_rules, @@ -292,7 +292,24 @@ export class SavedViewWidgetComponent ]) } + get pageSize(): number { + return this.savedView?.page_size ?? DEFAULT_DASHBOARD_VIEW_PAGE_SIZE + } + + /** + * Pass the widget's documents to the list service to enable next / previous. + */ + activateViewForNavigation() { + this.documentListViewService.activateSavedViewWithDocuments( + this.savedView, + this.documents(), + this.count(), + this.pageSize + ) + } + openDocumentDetail(document: Document) { + this.activateViewForNavigation() this.router.navigate(['documents', document.id]) } diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html index e1ef6427f7..9dcd7ddd60 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.html +++ b/src-ui/src/app/components/document-detail/document-detail.component.html @@ -452,21 +452,22 @@

+ @let saveDisabled = !userCanEdit || networkActive() || (isDirty$ | async) !== true;
- @if (hasNext()) { - + } @else { - + } -
- +