mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Merge branch 'dev' into feature-permissions
This commit is contained in:
		
							
								
								
									
										1
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -195,6 +195,7 @@ jobs: | ||||
|         with: | ||||
|           node-version: ${{ matrix.node-version }} | ||||
|       - run: cd src-ui && npm ci | ||||
|       - run: cd src-ui && npm run lint | ||||
|       - run: cd src-ui && npm run test | ||||
|       - run: cd src-ui && npm run e2e:ci | ||||
|  | ||||
|   | ||||
							
								
								
									
										1
									
								
								.github/workflows/installer-library.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/installer-library.yml
									
									
									
									
										vendored
									
									
								
							| @@ -127,6 +127,7 @@ jobs: | ||||
|     uses: ./.github/workflows/reusable-workflow-builder.yml | ||||
|     with: | ||||
|       dockerfile: ./docker-builders/Dockerfile.qpdf | ||||
|       build-platforms: linux/amd64 | ||||
|       build-json: ${{ needs.prepare-docker-build.outputs.qpdf-json }} | ||||
|       build-args: | | ||||
|         QPDF_VERSION=${{ fromJSON(needs.prepare-docker-build.outputs.qpdf-json).version }} | ||||
|   | ||||
| @@ -13,6 +13,10 @@ on: | ||||
|         required: false | ||||
|         default: "" | ||||
|         type: string | ||||
|       build-platforms: | ||||
|         required: false | ||||
|         default: linux/amd64,linux/arm64,linux/arm/v7 | ||||
|         type: string | ||||
|  | ||||
| concurrency: | ||||
|   group: ${{ github.workflow }}-${{ fromJSON(inputs.build-json).name }}-${{ fromJSON(inputs.build-json).version }} | ||||
| @@ -46,7 +50,7 @@ jobs: | ||||
|           context: . | ||||
|           file: ${{ inputs.dockerfile }} | ||||
|           tags: ${{ fromJSON(inputs.build-json).image_tag }} | ||||
|           platforms: linux/amd64,linux/arm64,linux/arm/v7 | ||||
|           platforms: ${{ inputs.build-platforms }} | ||||
|           build-args: ${{ inputs.build-args }} | ||||
|           push: true | ||||
|           cache-from: type=registry,ref=${{ fromJSON(inputs.build-json).cache_tag }} | ||||
|   | ||||
							
								
								
									
										10
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -58,6 +58,12 @@ LABEL org.opencontainers.image.url="https://github.com/paperless-ngx/paperless-n | ||||
| LABEL org.opencontainers.image.licenses="GPL-3.0-only" | ||||
|  | ||||
| ARG DEBIAN_FRONTEND=noninteractive | ||||
| # Buildx provided | ||||
| ARG TARGETARCH | ||||
| ARG TARGETVARIANT | ||||
|  | ||||
| # Workflow provided | ||||
| ARG QPDF_VERSION | ||||
|  | ||||
| # | ||||
| # Begin installation and configuration | ||||
| @@ -194,8 +200,8 @@ RUN --mount=type=bind,from=qpdf-builder,target=/qpdf \ | ||||
|     --mount=type=bind,from=pikepdf-builder,target=/pikepdf \ | ||||
|   set -eux \ | ||||
|   && echo "Installing qpdf" \ | ||||
|     && apt-get install --yes --no-install-recommends /qpdf/usr/src/qpdf/libqpdf29_*.deb \ | ||||
|     && apt-get install --yes --no-install-recommends /qpdf/usr/src/qpdf/qpdf_*.deb \ | ||||
|     && apt-get install --yes --no-install-recommends /qpdf/usr/src/qpdf/${QPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/libqpdf29_*.deb \ | ||||
|     && apt-get install --yes --no-install-recommends /qpdf/usr/src/qpdf/${QPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/qpdf_*.deb \ | ||||
|   && echo "Installing pikepdf and dependencies" \ | ||||
|     && python3 -m pip install --no-cache-dir /pikepdf/usr/src/wheels/*.whl \ | ||||
|     && python3 -m pip list \ | ||||
|   | ||||
| @@ -10,9 +10,9 @@ | ||||
| # Example Usage: | ||||
| #	./build-docker-image.sh Dockerfile -t paperless-ngx:my-awesome-feature | ||||
|  | ||||
| set -eux | ||||
| set -eu | ||||
|  | ||||
| if ! command -v jq;  then | ||||
| if ! command -v jq &> /dev/null ;  then | ||||
| 	echo "jq required" | ||||
| 	exit 1 | ||||
| elif [ ! -f "$1" ]; then | ||||
| @@ -20,28 +20,62 @@ elif [ ! -f "$1" ]; then | ||||
| 	exit 1 | ||||
| fi | ||||
|  | ||||
| # Parse what we can from Pipfile.lock | ||||
| pikepdf_version=$(jq ".default.pikepdf.version" Pipfile.lock  | sed 's/=//g' | sed 's/"//g') | ||||
| psycopg2_version=$(jq ".default.psycopg2.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g') | ||||
| pillow_version=$(jq ".default.pillow.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g') | ||||
| lxml_version=$(jq ".default.lxml.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g') | ||||
| # Read this from the other config file | ||||
| qpdf_version=$(jq ".qpdf.version" .build-config.json | sed 's/"//g') | ||||
| jbig2enc_version=$(jq ".jbig2enc.version" .build-config.json | sed 's/"//g') | ||||
| # Get the branch name (used for caching) | ||||
| branch_name=$(git rev-parse --abbrev-ref HEAD) | ||||
|  | ||||
| # https://docs.docker.com/develop/develop-images/build_enhancements/ | ||||
| # Required to use cache-from | ||||
| export DOCKER_BUILDKIT=1 | ||||
| # Parse eithe Pipfile.lock or the .build-config.json | ||||
| jbig2enc_version=$(jq ".jbig2enc.version" .build-config.json | sed 's/"//g') | ||||
| qpdf_version=$(jq ".qpdf.version" .build-config.json | sed 's/"//g') | ||||
| psycopg2_version=$(jq ".default.psycopg2.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g') | ||||
| pikepdf_version=$(jq ".default.pikepdf.version" Pipfile.lock  | sed 's/=//g' | sed 's/"//g') | ||||
| pillow_version=$(jq ".default.pillow.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g') | ||||
| lxml_version=$(jq ".default.lxml.version" Pipfile.lock | sed 's/=//g' | sed 's/"//g') | ||||
|  | ||||
| docker build --file "$1" \ | ||||
| base_filename="$(basename -- "${1}")" | ||||
| build_args_str="" | ||||
| cache_from_str="" | ||||
|  | ||||
| case "${base_filename}" in | ||||
|  | ||||
| 	*.jbig2enc) | ||||
| 		build_args_str="--build-arg JBIG2ENC_VERSION=${jbig2enc_version}" | ||||
| 		cache_from_str="--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/jbig2enc:${jbig2enc_version}" | ||||
| 		;; | ||||
|  | ||||
| 	*.psycopg2) | ||||
| 		build_args_str="--build-arg PSYCOPG2_VERSION=${psycopg2_version}" | ||||
| 		cache_from_str="--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/psycopg2:${psycopg2_version}" | ||||
| 		;; | ||||
|  | ||||
| 	*.qpdf) | ||||
| 		build_args_str="--build-arg QPDF_VERSION=${qpdf_version}" | ||||
| 		cache_from_str="--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/qpdf:${qpdf_version}" | ||||
| 		;; | ||||
|  | ||||
| 	*.pikepdf) | ||||
| 		build_args_str="--build-arg QPDF_VERSION=${qpdf_version} --build-arg PIKEPDF_VERSION=${pikepdf_version} --build-arg PILLOW_VERSION=${pillow_version} --build-arg LXML_VERSION=${lxml_version}" | ||||
| 		cache_from_str="--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/pikepdf:${pikepdf_version}" | ||||
| 		;; | ||||
|  | ||||
| 	Dockerfile) | ||||
| 		build_args_str="--build-arg QPDF_VERSION=${qpdf_version} --build-arg PIKEPDF_VERSION=${pikepdf_version} --build-arg PSYCOPG2_VERSION=${psycopg2_version} --build-arg JBIG2ENC_VERSION=${jbig2enc_version}" | ||||
| 		cache_from_str="--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/app:${branch_name} --cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/app:dev" | ||||
| 		;; | ||||
|  | ||||
| 	*) | ||||
| 		echo "Unable to match ${base_filename}" | ||||
| 		exit 1 | ||||
| 		;; | ||||
| esac | ||||
|  | ||||
| read -r -a build_args_arr <<< "${build_args_str}" | ||||
| read -r -a cache_from_arr <<< "${cache_from_str}" | ||||
|  | ||||
| set -eux | ||||
|  | ||||
| docker buildx build --file "${1}" \ | ||||
| 	--progress=plain \ | ||||
| 	--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/app:"${branch_name}" \ | ||||
| 	--cache-from ghcr.io/paperless-ngx/paperless-ngx/builder/cache/app:dev \ | ||||
| 	--build-arg JBIG2ENC_VERSION="${jbig2enc_version}" \ | ||||
| 	--build-arg QPDF_VERSION="${qpdf_version}" \ | ||||
| 	--build-arg PIKEPDF_VERSION="${pikepdf_version}" \ | ||||
| 	--build-arg PILLOW_VERSION="${pillow_version}" \ | ||||
| 	--build-arg LXML_VERSION="${lxml_version}" \ | ||||
| 	--build-arg PSYCOPG2_VERSION="${psycopg2_version}" "${@:2}" . | ||||
| 	--output=type=docker \ | ||||
| 	"${cache_from_arr[@]}" \ | ||||
| 	"${build_args_arr[@]}" \ | ||||
| 	"${@:2}" . | ||||
|   | ||||
| @@ -16,7 +16,13 @@ FROM python:3.9-slim-bullseye as main | ||||
|  | ||||
| LABEL org.opencontainers.image.description="A intermediate image with pikepdf wheel built" | ||||
|  | ||||
| # Buildx provided | ||||
| ARG TARGETARCH | ||||
| ARG TARGETVARIANT | ||||
|  | ||||
| ARG DEBIAN_FRONTEND=noninteractive | ||||
| # Workflow provided | ||||
| ARG QPDF_VERSION | ||||
| ARG PIKEPDF_VERSION | ||||
| # These are not used, but will still bust the cache if one changes | ||||
| # Otherwise, the main image will try to build thing (and fail) | ||||
| @@ -54,7 +60,7 @@ ARG BUILD_PACKAGES="\ | ||||
|  | ||||
| WORKDIR /usr/src | ||||
|  | ||||
| COPY --from=qpdf-builder /usr/src/qpdf/*.deb ./ | ||||
| COPY --from=qpdf-builder /usr/src/qpdf/${QPDF_VERSION}/${TARGETARCH}${TARGETVARIANT}/*.deb ./ | ||||
|  | ||||
| # As this is an base image for a multi-stage final image | ||||
| # the added size of the install is basically irrelevant | ||||
| @@ -77,6 +83,8 @@ RUN set -eux \ | ||||
|     && python3 -m pip wheel \ | ||||
|       # Build the package at the required version | ||||
|       pikepdf==${PIKEPDF_VERSION} \ | ||||
|       # Look to piwheels for additional pre-built wheels | ||||
|       --extra-index-url https://www.piwheels.org/simple \ | ||||
|       # Output the *.whl into this directory | ||||
|       --wheel-dir wheels \ | ||||
|       # Do not use a binary packge for the package being built | ||||
| @@ -86,6 +94,8 @@ RUN set -eux \ | ||||
|       # Don't cache build files | ||||
|       --no-cache-dir \ | ||||
|     && ls -ahl wheels \ | ||||
|   && echo "Gathering package data" \ | ||||
|     && dpkg-query -f '${Package;-40}${Version}\n' -W > ./wheels/pkg-list.txt \ | ||||
|   && echo "Cleaning up image" \ | ||||
|     && apt-get -y purge ${BUILD_PACKAGES} \ | ||||
|     && apt-get -y autoremove --purge \ | ||||
|   | ||||
| @@ -42,6 +42,8 @@ RUN set -eux \ | ||||
|       # Don't cache build files | ||||
|       --no-cache-dir \ | ||||
|     && ls -ahl wheels/ \ | ||||
|   && echo "Gathering package data" \ | ||||
|     && dpkg-query -f '${Package;-40}${Version}\n' -W > ./wheels/pkg-list.txt \ | ||||
|   && echo "Cleaning up image" \ | ||||
|     && apt-get -y purge ${BUILD_PACKAGES} \ | ||||
|     && apt-get -y autoremove --purge \ | ||||
|   | ||||
| @@ -1,48 +1,156 @@ | ||||
| # This Dockerfile compiles the jbig2enc library | ||||
| # Inputs: | ||||
| #    - QPDF_VERSION - the version of qpdf to build a .deb. | ||||
| #                     Must be present as a deb-src in bookworm | ||||
| # | ||||
| # Stage: pre-build | ||||
| # Purpose: | ||||
| #  - Installs common packages | ||||
| #  - Sets common environment variables related to dpkg | ||||
| #  - Aquires the qpdf source from bookwork | ||||
| # Useful Links: | ||||
| #  - https://qpdf.readthedocs.io/en/stable/installation.html#system-requirements | ||||
| #  - https://wiki.debian.org/Multiarch/HOWTO | ||||
| #  - https://wiki.debian.org/CrossCompiling | ||||
| # | ||||
|  | ||||
| FROM debian:bullseye-slim as main | ||||
| FROM debian:bullseye-slim as pre-build | ||||
|  | ||||
| LABEL org.opencontainers.image.description="A intermediate image with qpdf built" | ||||
|  | ||||
| ARG DEBIAN_FRONTEND=noninteractive | ||||
| # This must match to pikepdf's minimum at least | ||||
| ARG QPDF_VERSION | ||||
|  | ||||
| ARG BUILD_PACKAGES="\ | ||||
|   build-essential \ | ||||
|   debhelper \ | ||||
| ARG COMMON_BUILD_PACKAGES="\ | ||||
|   cmake \ | ||||
|   debhelper\ | ||||
|   debian-keyring \ | ||||
|   devscripts \ | ||||
|   equivs  \ | ||||
|   libtool \ | ||||
|   # https://qpdf.readthedocs.io/en/stable/installation.html#system-requirements | ||||
|   libjpeg62-turbo-dev \ | ||||
|   libgnutls28-dev \ | ||||
|   dpkg-dev \ | ||||
|   equivs \ | ||||
|   packaging-dev \ | ||||
|   cmake \ | ||||
|   zlib1g-dev" | ||||
|   libtool" | ||||
|  | ||||
| ENV DEB_BUILD_OPTIONS="terse nocheck nodoc parallel=2" | ||||
|  | ||||
| WORKDIR /usr/src | ||||
|  | ||||
| RUN set -eux \ | ||||
|   && echo "Installing build tools" \ | ||||
|   && echo "Installing common packages" \ | ||||
|     && apt-get update --quiet \ | ||||
|     && apt-get install --yes --quiet --no-install-recommends $BUILD_PACKAGES \ | ||||
|   && echo "Getting qpdf src" \ | ||||
|     && apt-get install --yes --quiet --no-install-recommends ${COMMON_BUILD_PACKAGES} \ | ||||
|   && echo "Getting qpdf source" \ | ||||
|     && echo "deb-src http://deb.debian.org/debian/ bookworm main" > /etc/apt/sources.list.d/bookworm-src.list \ | ||||
|     && apt-get update \ | ||||
|     && mkdir qpdf \ | ||||
|     && cd qpdf \ | ||||
|     && apt-get source --yes --quiet qpdf=${QPDF_VERSION}-1/bookworm \ | ||||
|   && echo "Building qpdf" \ | ||||
|     && cd qpdf-$QPDF_VERSION \ | ||||
|     && export DEB_BUILD_OPTIONS="terse nocheck nodoc parallel=2" \ | ||||
|     && dpkg-buildpackage --build=binary --unsigned-source --unsigned-changes --post-clean \ | ||||
|     && ls -ahl ../*.deb \ | ||||
|   && echo "Cleaning up image" \ | ||||
|     && apt-get -y purge ${BUILD_PACKAGES} \ | ||||
|     && apt-get -y autoremove --purge \ | ||||
|     && rm -rf /var/lib/apt/lists/* | ||||
|     && apt-get update --quiet \ | ||||
|     && apt-get source --yes --quiet qpdf=${QPDF_VERSION}-1/bookworm | ||||
|  | ||||
| # | ||||
| # Stage: amd64-builder | ||||
| # Purpose: Builds qpdf for x86_64 (native build) | ||||
| # | ||||
| FROM pre-build as amd64-builder | ||||
|  | ||||
| ARG AMD64_BUILD_PACKAGES="\ | ||||
|   build-essential \ | ||||
|   libjpeg62-turbo-dev:amd64 \ | ||||
|   libgnutls28-dev:amd64 \ | ||||
|   zlib1g-dev:amd64" | ||||
|  | ||||
| WORKDIR /usr/src/qpdf-${QPDF_VERSION} | ||||
|  | ||||
| RUN set -eux \ | ||||
|   && echo "Beginning amd64" \ | ||||
|     && echo "Install amd64 packages" \ | ||||
|       && apt-get update --quiet \ | ||||
|       && apt-get install --yes --quiet --no-install-recommends ${AMD64_BUILD_PACKAGES} \ | ||||
|     && echo "Building amd64" \ | ||||
|       && dpkg-buildpackage --build=binary --unsigned-source --unsigned-changes --post-clean \ | ||||
|     && echo "Removing debug files" \ | ||||
|       && rm -f ../libqpdf29-dbgsym* \ | ||||
|       && rm -f ../qpdf-dbgsym* \ | ||||
|     && echo "Gathering package data" \ | ||||
|       && dpkg-query -f '${Package;-40}${Version}\n' -W > ../pkg-list.txt | ||||
| # | ||||
| # Stage: armhf-builder | ||||
| # Purpose: | ||||
| #  - Sets armhf specific environment | ||||
| #  - Builds qpdf for armhf (cross compile) | ||||
| # | ||||
| FROM pre-build as armhf-builder | ||||
|  | ||||
| ARG ARMHF_PACKAGES="\ | ||||
|   crossbuild-essential-armhf \ | ||||
|   libjpeg62-turbo-dev:armhf \ | ||||
|   libgnutls28-dev:armhf \ | ||||
|   zlib1g-dev:armhf" | ||||
|  | ||||
| WORKDIR /usr/src/qpdf-${QPDF_VERSION} | ||||
|  | ||||
| ENV CXX="/usr/bin/arm-linux-gnueabihf-g++" \ | ||||
|     CC="/usr/bin/arm-linux-gnueabihf-gcc" | ||||
|  | ||||
| RUN set -eux \ | ||||
|   && echo "Beginning armhf" \ | ||||
|     && echo "Install armhf packages" \ | ||||
|       && dpkg --add-architecture armhf \ | ||||
|       && apt-get update --quiet \ | ||||
|       && apt-get install --yes --quiet --no-install-recommends ${ARMHF_PACKAGES} \ | ||||
|     && echo "Building armhf" \ | ||||
|       && dpkg-buildpackage --build=binary --unsigned-source --unsigned-changes --post-clean --host-arch armhf \ | ||||
|     && echo "Removing debug files" \ | ||||
|       && rm -f ../libqpdf29-dbgsym* \ | ||||
|       && rm -f ../qpdf-dbgsym* \ | ||||
|     && echo "Gathering package data" \ | ||||
|       && dpkg-query -f '${Package;-40}${Version}\n' -W > ../pkg-list.txt | ||||
|  | ||||
| # | ||||
| # Stage: aarch64-builder | ||||
| # Purpose: | ||||
| #  - Sets aarch64 specific environment | ||||
| #  - Builds qpdf for aarch64 (cross compile) | ||||
| # | ||||
| FROM pre-build as aarch64-builder | ||||
|  | ||||
| ARG ARM64_PACKAGES="\ | ||||
|   crossbuild-essential-arm64 \ | ||||
|   libjpeg62-turbo-dev:arm64 \ | ||||
|   libgnutls28-dev:arm64 \ | ||||
|   zlib1g-dev:arm64" | ||||
|  | ||||
| ENV CXX="/usr/bin/aarch64-linux-gnu-g++" \ | ||||
|     CC="/usr/bin/aarch64-linux-gnu-gcc" | ||||
|  | ||||
| WORKDIR /usr/src/qpdf-${QPDF_VERSION} | ||||
|  | ||||
| RUN set -eux \ | ||||
|   && echo "Beginning arm64" \ | ||||
|     && echo "Install arm64 packages" \ | ||||
|       && dpkg --add-architecture arm64 \ | ||||
|       && apt-get update --quiet \ | ||||
|       && apt-get install --yes --quiet --no-install-recommends ${ARM64_PACKAGES} \ | ||||
|     && echo "Building arm64" \ | ||||
|       && dpkg-buildpackage --build=binary --unsigned-source --unsigned-changes --post-clean --host-arch arm64 \ | ||||
|     && echo "Removing debug files" \ | ||||
|       && rm -f ../libqpdf29-dbgsym* \ | ||||
|       && rm -f ../qpdf-dbgsym* \ | ||||
|     && echo "Gathering package data" \ | ||||
|       && dpkg-query -f '${Package;-40}${Version}\n' -W > ../pkg-list.txt | ||||
|  | ||||
| # | ||||
| # Stage: package | ||||
| # Purpose: Holds the compiled .deb files in arch/variant specific folders | ||||
| # | ||||
| FROM alpine:3.17 as package | ||||
|  | ||||
| LABEL org.opencontainers.image.description="A image with qpdf installers stored in architecture & version specific folders" | ||||
|  | ||||
| ARG QPDF_VERSION | ||||
|  | ||||
| WORKDIR /usr/src/qpdf/${QPDF_VERSION}/amd64 | ||||
|  | ||||
| COPY --from=amd64-builder /usr/src/*.deb ./ | ||||
| COPY --from=amd64-builder /usr/src/pkg-list.txt ./ | ||||
|  | ||||
| # Note this is ${TARGETARCH}${TARGETVARIANT} for armv7 | ||||
| WORKDIR /usr/src/qpdf/${QPDF_VERSION}/armv7 | ||||
|  | ||||
| COPY --from=armhf-builder /usr/src/*.deb ./ | ||||
| COPY --from=armhf-builder /usr/src/pkg-list.txt ./ | ||||
|  | ||||
| WORKDIR /usr/src/qpdf/${QPDF_VERSION}/arm64 | ||||
|  | ||||
| COPY --from=aarch64-builder /usr/src/*.deb ./ | ||||
| COPY --from=aarch64-builder /usr/src/pkg-list.txt ./ | ||||
|   | ||||
| @@ -20,7 +20,6 @@ wait_for_postgres() { | ||||
| 			exit 1 | ||||
| 		else | ||||
| 			echo "Attempt $attempt_num failed! Trying again in 5 seconds..." | ||||
|  | ||||
| 		fi | ||||
|  | ||||
| 		attempt_num=$(("$attempt_num" + 1)) | ||||
| @@ -67,10 +66,16 @@ migrations() { | ||||
| 		# of the current container starts. | ||||
| 		flock 200 | ||||
| 		echo "Apply database migrations..." | ||||
| 		python3 manage.py migrate | ||||
| 		python3 manage.py migrate --skip-checks --no-input | ||||
| 	) 200>"${DATA_DIR}/migration_lock" | ||||
| } | ||||
|  | ||||
| django_checks() { | ||||
| 	# Explicitly run the Django system checks | ||||
| 	echo "Running Django checks" | ||||
| 	python3 manage.py check | ||||
| } | ||||
|  | ||||
| search_index() { | ||||
|  | ||||
| 	local -r index_version=1 | ||||
| @@ -100,6 +105,8 @@ do_work() { | ||||
|  | ||||
| 	migrations | ||||
|  | ||||
| 	django_checks | ||||
|  | ||||
| 	search_index | ||||
|  | ||||
| 	superuser | ||||
|   | ||||
| @@ -233,6 +233,7 @@ optional arguments: | ||||
| -c, --compare-checksums | ||||
| -f, --use-filename-format | ||||
| -d, --delete | ||||
| -z  --zip | ||||
| ``` | ||||
|  | ||||
| `target` is a folder to which the data gets written. This includes | ||||
| @@ -258,6 +259,9 @@ current export such as files from deleted documents, specify `--delete`. | ||||
| Be careful when pointing paperless to a directory that already contains | ||||
| other files. | ||||
|  | ||||
| If `-z` or `--zip` is provided, the export will be a zipfile | ||||
| in the target directory, named according to the current date. | ||||
|  | ||||
| The filenames generated by this command follow the format | ||||
| `[date created] [correspondent] [title].[extension]`. If you want | ||||
| paperless to use `PAPERLESS_FILENAME_FORMAT` for exported filenames | ||||
|   | ||||
							
								
								
									
										51
									
								
								src-ui/.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src-ui/.eslintrc.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "ignorePatterns": [ | ||||
|     "projects/**/*" | ||||
|   ], | ||||
|   "overrides": [ | ||||
|     { | ||||
|       "files": [ | ||||
|         "*.ts" | ||||
|       ], | ||||
|       "parserOptions": { | ||||
|         "project": [ | ||||
|           "tsconfig.json", | ||||
|           "e2e/tsconfig.json" | ||||
|         ], | ||||
|         "createDefaultProgram": true | ||||
|       }, | ||||
|       "extends": [ | ||||
|         "plugin:@angular-eslint/recommended", | ||||
|         "plugin:@angular-eslint/template/process-inline-templates" | ||||
|       ], | ||||
|       "rules": { | ||||
|         "@angular-eslint/directive-selector": [ | ||||
|           "error", | ||||
|           { | ||||
|             "type": "attribute", | ||||
|             "prefix": "app", | ||||
|             "style": "camelCase" | ||||
|           } | ||||
|         ], | ||||
|         "@angular-eslint/component-selector": [ | ||||
|           "error", | ||||
|           { | ||||
|             "type": "element", | ||||
|             "prefix": "app", | ||||
|             "style": "kebab-case" | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "files": [ | ||||
|         "*.html" | ||||
|       ], | ||||
|       "extends": [ | ||||
|         "plugin:@angular-eslint/template/recommended" | ||||
|       ], | ||||
|       "rules": {} | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -1,179 +1,196 @@ | ||||
| { | ||||
| 	"$schema": "./node_modules/@angular/cli/lib/config/schema.json", | ||||
| 	"version": 1, | ||||
| 	"newProjectRoot": "projects", | ||||
| 	"projects": { | ||||
| 		"paperless-ui": { | ||||
| 			"projectType": "application", | ||||
| 			"schematics": { | ||||
| 				"@schematics/angular:component": { | ||||
| 					"style": "scss" | ||||
| 				} | ||||
| 			}, | ||||
| 			"root": "", | ||||
| 			"sourceRoot": "src", | ||||
| 			"prefix": "app", | ||||
| 			"i18n": { | ||||
| 				"sourceLocale": "en-US", | ||||
| 				"locales": { | ||||
| 					"be-BY": "src/locale/messages.be_BY.xlf", | ||||
| 					"cs-CZ": "src/locale/messages.cs_CZ.xlf", | ||||
| 					"da-DK": "src/locale/messages.da_DK.xlf", | ||||
| 					"de-DE": "src/locale/messages.de_DE.xlf", | ||||
| 					"en-GB": "src/locale/messages.en_GB.xlf", | ||||
| 					"es-ES": "src/locale/messages.es_ES.xlf", | ||||
| 					"fr-FR": "src/locale/messages.fr_FR.xlf", | ||||
| 					"it-IT": "src/locale/messages.it_IT.xlf", | ||||
| 					"lb-LU": "src/locale/messages.lb_LU.xlf", | ||||
| 					"nl-NL": "src/locale/messages.nl_NL.xlf", | ||||
| 					"pl-PL": "src/locale/messages.pl_PL.xlf", | ||||
| 					"pt-BR": "src/locale/messages.pt_BR.xlf", | ||||
| 					"pt-PT": "src/locale/messages.pt_PT.xlf", | ||||
| 					"ro-RO": "src/locale/messages.ro_RO.xlf", | ||||
| 					"ru-RU": "src/locale/messages.ru_RU.xlf", | ||||
| 					"sl-SI": "src/locale/messages.sl_SI.xlf", | ||||
| 					"sr-CS": "src/locale/messages.sr_CS.xlf", | ||||
| 					"sv-SE": "src/locale/messages.sv_SE.xlf", | ||||
| 					"tr-TR": "src/locale/messages.tr_TR.xlf", | ||||
| 					"zh-CN": "src/locale/messages.zh_CN.xlf" | ||||
| 				} | ||||
| 			}, | ||||
| 			"architect": { | ||||
| 				"build": { | ||||
| 					"builder": "@angular-devkit/build-angular:browser", | ||||
| 					"options": { | ||||
| 						"outputPath": "dist/paperless-ui", | ||||
| 						"outputHashing": "none", | ||||
| 						"index": "src/index.html", | ||||
| 						"main": "src/main.ts", | ||||
| 						"polyfills": "src/polyfills.ts", | ||||
| 						"tsConfig": "tsconfig.app.json", | ||||
| 						"localize": true, | ||||
| 						"assets": [ | ||||
| 							"src/favicon.ico", | ||||
| 							"src/apple-touch-icon.png", | ||||
| 							"src/assets", | ||||
| 							"src/manifest.webmanifest", { | ||||
| 								"glob": "pdf.worker.min.js", | ||||
| 								"input": "node_modules/pdfjs-dist/build/", | ||||
| 								"output": "/assets/js/" | ||||
| 							} | ||||
| 						], | ||||
| 						"styles": [ | ||||
| 							"src/styles.scss" | ||||
| 						], | ||||
| 						"scripts": [], | ||||
| 						"allowedCommonJsDependencies": [ | ||||
| 							"ng2-pdf-viewer" | ||||
| 						], | ||||
| 						"vendorChunk": true, | ||||
| 						"extractLicenses": false, | ||||
| 						"buildOptimizer": false, | ||||
| 						"sourceMap": true, | ||||
| 						"optimization": false, | ||||
| 						"namedChunks": true | ||||
| 					}, | ||||
| 					"configurations": { | ||||
| 						"production": { | ||||
| 							"fileReplacements": [ | ||||
| 								{ | ||||
| 									"replace": "src/environments/environment.ts", | ||||
| 									"with": "src/environments/environment.prod.ts" | ||||
| 								} | ||||
| 							], | ||||
| 							"outputPath": "../src/documents/static/frontend/", | ||||
| 							"optimization": true, | ||||
| 							"outputHashing": "none", | ||||
| 							"sourceMap": false, | ||||
| 							"namedChunks": false, | ||||
| 							"extractLicenses": true, | ||||
| 							"vendorChunk": false, | ||||
| 							"buildOptimizer": true, | ||||
| 							"budgets": [ | ||||
| 								{ | ||||
| 									"type": "initial", | ||||
| 									"maximumWarning": "2mb", | ||||
| 									"maximumError": "5mb" | ||||
| 								}, | ||||
| 								{ | ||||
| 									"type": "anyComponentStyle", | ||||
| 									"maximumWarning": "6kb", | ||||
| 									"maximumError": "10kb" | ||||
| 								} | ||||
| 							] | ||||
| 						}, | ||||
| 						"en-US": { | ||||
| 							"localize": ["en-US"] | ||||
| 						} | ||||
| 					}, | ||||
| 					"defaultConfiguration": "" | ||||
| 				}, | ||||
| 				"serve": { | ||||
| 					"builder": "@angular-devkit/build-angular:dev-server", | ||||
| 					"options": { | ||||
| 						"browserTarget": "paperless-ui:build:en-US" | ||||
| 					}, | ||||
| 					"configurations": { | ||||
| 						"production": { | ||||
| 							"browserTarget": "paperless-ui:build:production" | ||||
| 						} | ||||
| 					} | ||||
| 				}, | ||||
| 				"extract-i18n": { | ||||
| 					"builder": "@angular-devkit/build-angular:extract-i18n", | ||||
| 					"options": { | ||||
| 						"browserTarget": "paperless-ui:build" | ||||
| 					} | ||||
| 				}, | ||||
| 				"test": { | ||||
| 					"builder": "@angular-builders/jest:run", | ||||
| 					"options": { | ||||
| 						"tsConfig": "tsconfig.spec.json", | ||||
| 						"assets": [ | ||||
| 							"src/favicon.ico", | ||||
| 							"src/apple-touch-icon.png", | ||||
| 							"src/assets", | ||||
| 							"src/manifest.webmanifest" | ||||
| 						], | ||||
| 						"styles": [ | ||||
| 							"src/styles.scss" | ||||
| 						], | ||||
| 						"scripts": [] | ||||
| 					} | ||||
| 				}, | ||||
| 				"e2e": { | ||||
| 					"builder": "@cypress/schematic:cypress", | ||||
| 					"options": { | ||||
| 						"devServerTarget": "paperless-ui:serve", | ||||
| 						"watch": true, | ||||
| 						"headless": false | ||||
| 					}, | ||||
| 					"configurations": { | ||||
| 						"production": { | ||||
| 							"devServerTarget": "paperless-ui:serve:production" | ||||
| 						} | ||||
| 					} | ||||
| 				}, | ||||
| 				"cypress-run": { | ||||
| 					"builder": "@cypress/schematic:cypress", | ||||
| 					"options": { | ||||
| 						"devServerTarget": "paperless-ui:serve" | ||||
| 					}, | ||||
| 					"configurations": { | ||||
| 						"production": { | ||||
| 							"devServerTarget": "paperless-ui:serve:production" | ||||
| 						} | ||||
| 					} | ||||
| 				}, | ||||
| 				"cypress-open": { | ||||
| 					"builder": "@cypress/schematic:cypress", | ||||
| 					"options": { | ||||
| 						"watch": true, | ||||
| 						"headless": false | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| 	"defaultProject": "paperless-ui" | ||||
|   "$schema": "./node_modules/@angular/cli/lib/config/schema.json", | ||||
|   "version": 1, | ||||
|   "newProjectRoot": "projects", | ||||
|   "projects": { | ||||
|     "paperless-ui": { | ||||
|       "projectType": "application", | ||||
|       "schematics": { | ||||
|         "@schematics/angular:component": { | ||||
|           "style": "scss" | ||||
|         } | ||||
|       }, | ||||
|       "root": "", | ||||
|       "sourceRoot": "src", | ||||
|       "prefix": "app", | ||||
|       "i18n": { | ||||
|         "sourceLocale": "en-US", | ||||
|         "locales": { | ||||
|           "be-BY": "src/locale/messages.be_BY.xlf", | ||||
|           "cs-CZ": "src/locale/messages.cs_CZ.xlf", | ||||
|           "da-DK": "src/locale/messages.da_DK.xlf", | ||||
|           "de-DE": "src/locale/messages.de_DE.xlf", | ||||
|           "en-GB": "src/locale/messages.en_GB.xlf", | ||||
|           "es-ES": "src/locale/messages.es_ES.xlf", | ||||
|           "fr-FR": "src/locale/messages.fr_FR.xlf", | ||||
|           "it-IT": "src/locale/messages.it_IT.xlf", | ||||
|           "lb-LU": "src/locale/messages.lb_LU.xlf", | ||||
|           "nl-NL": "src/locale/messages.nl_NL.xlf", | ||||
|           "pl-PL": "src/locale/messages.pl_PL.xlf", | ||||
|           "pt-BR": "src/locale/messages.pt_BR.xlf", | ||||
|           "pt-PT": "src/locale/messages.pt_PT.xlf", | ||||
|           "ro-RO": "src/locale/messages.ro_RO.xlf", | ||||
|           "ru-RU": "src/locale/messages.ru_RU.xlf", | ||||
|           "sl-SI": "src/locale/messages.sl_SI.xlf", | ||||
|           "sr-CS": "src/locale/messages.sr_CS.xlf", | ||||
|           "sv-SE": "src/locale/messages.sv_SE.xlf", | ||||
|           "tr-TR": "src/locale/messages.tr_TR.xlf", | ||||
|           "zh-CN": "src/locale/messages.zh_CN.xlf" | ||||
|         } | ||||
|       }, | ||||
|       "architect": { | ||||
|         "build": { | ||||
|           "builder": "@angular-devkit/build-angular:browser", | ||||
|           "options": { | ||||
|             "outputPath": "dist/paperless-ui", | ||||
|             "outputHashing": "none", | ||||
|             "index": "src/index.html", | ||||
|             "main": "src/main.ts", | ||||
|             "polyfills": "src/polyfills.ts", | ||||
|             "tsConfig": "tsconfig.app.json", | ||||
|             "localize": true, | ||||
|             "assets": [ | ||||
|               "src/favicon.ico", | ||||
|               "src/apple-touch-icon.png", | ||||
|               "src/assets", | ||||
|               "src/manifest.webmanifest", | ||||
|               { | ||||
|                 "glob": "pdf.worker.min.js", | ||||
|                 "input": "node_modules/pdfjs-dist/build/", | ||||
|                 "output": "/assets/js/" | ||||
|               } | ||||
|             ], | ||||
|             "styles": [ | ||||
|               "src/styles.scss" | ||||
|             ], | ||||
|             "scripts": [], | ||||
|             "allowedCommonJsDependencies": [ | ||||
|               "ng2-pdf-viewer" | ||||
|             ], | ||||
|             "vendorChunk": true, | ||||
|             "extractLicenses": false, | ||||
|             "buildOptimizer": false, | ||||
|             "sourceMap": true, | ||||
|             "optimization": false, | ||||
|             "namedChunks": true | ||||
|           }, | ||||
|           "configurations": { | ||||
|             "production": { | ||||
|               "fileReplacements": [ | ||||
|                 { | ||||
|                   "replace": "src/environments/environment.ts", | ||||
|                   "with": "src/environments/environment.prod.ts" | ||||
|                 } | ||||
|               ], | ||||
|               "outputPath": "../src/documents/static/frontend/", | ||||
|               "optimization": true, | ||||
|               "outputHashing": "none", | ||||
|               "sourceMap": false, | ||||
|               "namedChunks": false, | ||||
|               "extractLicenses": true, | ||||
|               "vendorChunk": false, | ||||
|               "buildOptimizer": true, | ||||
|               "budgets": [ | ||||
|                 { | ||||
|                   "type": "initial", | ||||
|                   "maximumWarning": "2mb", | ||||
|                   "maximumError": "5mb" | ||||
|                 }, | ||||
|                 { | ||||
|                   "type": "anyComponentStyle", | ||||
|                   "maximumWarning": "6kb", | ||||
|                   "maximumError": "10kb" | ||||
|                 } | ||||
|               ] | ||||
|             }, | ||||
|             "en-US": { | ||||
|               "localize": [ | ||||
|                 "en-US" | ||||
|               ] | ||||
|             } | ||||
|           }, | ||||
|           "defaultConfiguration": "" | ||||
|         }, | ||||
|         "serve": { | ||||
|           "builder": "@angular-devkit/build-angular:dev-server", | ||||
|           "options": { | ||||
|             "browserTarget": "paperless-ui:build:en-US" | ||||
|           }, | ||||
|           "configurations": { | ||||
|             "production": { | ||||
|               "browserTarget": "paperless-ui:build:production" | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "extract-i18n": { | ||||
|           "builder": "@angular-devkit/build-angular:extract-i18n", | ||||
|           "options": { | ||||
|             "browserTarget": "paperless-ui:build" | ||||
|           } | ||||
|         }, | ||||
|         "test": { | ||||
|           "builder": "@angular-builders/jest:run", | ||||
|           "options": { | ||||
|             "tsConfig": "tsconfig.spec.json", | ||||
|             "assets": [ | ||||
|               "src/favicon.ico", | ||||
|               "src/apple-touch-icon.png", | ||||
|               "src/assets", | ||||
|               "src/manifest.webmanifest" | ||||
|             ], | ||||
|             "styles": [ | ||||
|               "src/styles.scss" | ||||
|             ], | ||||
|             "scripts": [] | ||||
|           } | ||||
|         }, | ||||
|         "e2e": { | ||||
|           "builder": "@cypress/schematic:cypress", | ||||
|           "options": { | ||||
|             "devServerTarget": "paperless-ui:serve", | ||||
|             "watch": true, | ||||
|             "headless": false | ||||
|           }, | ||||
|           "configurations": { | ||||
|             "production": { | ||||
|               "devServerTarget": "paperless-ui:serve:production" | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "cypress-run": { | ||||
|           "builder": "@cypress/schematic:cypress", | ||||
|           "options": { | ||||
|             "devServerTarget": "paperless-ui:serve" | ||||
|           }, | ||||
|           "configurations": { | ||||
|             "production": { | ||||
|               "devServerTarget": "paperless-ui:serve:production" | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "cypress-open": { | ||||
|           "builder": "@cypress/schematic:cypress", | ||||
|           "options": { | ||||
|             "watch": true, | ||||
|             "headless": false | ||||
|           } | ||||
|         }, | ||||
|         "lint": { | ||||
|           "builder": "@angular-eslint/builder:lint", | ||||
|           "options": { | ||||
|             "lintFilePatterns": [ | ||||
|               "src/**/*.ts", | ||||
|               "src/**/*.html" | ||||
|             ] | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "defaultProject": "paperless-ui", | ||||
|   "cli": { | ||||
|     "schematicCollections": [ | ||||
|       "@angular-eslint/schematics" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -711,7 +711,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">476</context> | ||||
|           <context context-type="linenumber">492</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2526035785704676448" datatype="html"> | ||||
| @@ -758,19 +758,19 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">234</context> | ||||
|           <context context-type="linenumber">279</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">272</context> | ||||
|           <context context-type="linenumber">319</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">308</context> | ||||
|           <context context-type="linenumber">357</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">344</context> | ||||
|           <context context-type="linenumber">395</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6371576811194810854" datatype="html"> | ||||
| @@ -967,7 +967,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html</context> | ||||
|           <context context-type="linenumber">35</context> | ||||
|           <context context-type="linenumber">36</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context> | ||||
| @@ -1006,7 +1006,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html</context> | ||||
|           <context context-type="linenumber">36</context> | ||||
|           <context context-type="linenumber">37</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.html</context> | ||||
| @@ -1120,39 +1120,39 @@ | ||||
|           <context context-type="linenumber">18</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8758081884575368561" datatype="html"> | ||||
|         <source>Create new mail account</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">22</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5559445021532852612" datatype="html"> | ||||
|         <source>Edit mail account</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">26</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="451418349275958054" datatype="html"> | ||||
|         <source>No encryption</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">43</context> | ||||
|           <context context-type="linenumber">12</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3719080555538542367" datatype="html"> | ||||
|         <source>SSL</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">44</context> | ||||
|           <context context-type="linenumber">13</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2620794666957669114" datatype="html"> | ||||
|         <source>STARTTLS</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">45</context> | ||||
|           <context context-type="linenumber">14</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8758081884575368561" datatype="html"> | ||||
|         <source>Create new mail account</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">28</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5559445021532852612" datatype="html"> | ||||
|         <source>Edit mail account</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">32</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4086606389696938932" datatype="html"> | ||||
| @@ -1285,39 +1285,36 @@ | ||||
|           <context context-type="linenumber">30</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3147349817770432927" datatype="html"> | ||||
|         <source>Create new mail rule</source> | ||||
|       <trans-unit id="1519954996184640001" datatype="html"> | ||||
|         <source>Error</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">57</context> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.html</context> | ||||
|           <context context-type="linenumber">35</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3374331029704382439" datatype="html"> | ||||
|         <source>Edit mail rule</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">61</context> | ||||
|           <context context-type="sourcefile">src/app/services/toast.service.ts</context> | ||||
|           <context context-type="linenumber">32</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6233529027580744166" datatype="html"> | ||||
|         <source>Only process attachments.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">98</context> | ||||
|           <context context-type="linenumber">24</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3860563069570088911" datatype="html"> | ||||
|         <source>Process all files, including 'inline' attachments.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">102</context> | ||||
|           <context context-type="linenumber">28</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7022070615528435141" datatype="html"> | ||||
|         <source>Delete</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">111</context> | ||||
|           <context context-type="linenumber">35</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context> | ||||
| @@ -1325,7 +1322,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> | ||||
|           <context context-type="linenumber">97</context> | ||||
|           <context context-type="linenumber">126</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context> | ||||
| @@ -1380,70 +1377,84 @@ | ||||
|         <source>Move to specified folder</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">115</context> | ||||
|           <context context-type="linenumber">39</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4593278936733161020" datatype="html"> | ||||
|         <source>Mark as read, don't process read mails</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">119</context> | ||||
|           <context context-type="linenumber">43</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2378921144019636516" datatype="html"> | ||||
|         <source>Flag the mail, don't process flagged mails</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">123</context> | ||||
|           <context context-type="linenumber">47</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6457024618858980302" datatype="html"> | ||||
|         <source>Tag the mail with specified tag, don't process tagged mails</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">127</context> | ||||
|           <context context-type="linenumber">51</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4673329664686432878" datatype="html"> | ||||
|         <source>Use subject as title</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">136</context> | ||||
|           <context context-type="linenumber">58</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8645471396972938185" datatype="html"> | ||||
|         <source>Use attachment filename as title</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">140</context> | ||||
|           <context context-type="linenumber">62</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1568902914205618549" datatype="html"> | ||||
|         <source>Do not assign a correspondent</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">149</context> | ||||
|           <context context-type="linenumber">69</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3567746385454588269" datatype="html"> | ||||
|         <source>Use mail address</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">153</context> | ||||
|           <context context-type="linenumber">73</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="445154175758965852" datatype="html"> | ||||
|         <source>Use name (or mail address if not available)</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">157</context> | ||||
|           <context context-type="linenumber">77</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1258862217749148424" datatype="html"> | ||||
|         <source>Use correspondent selected below</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">161</context> | ||||
|           <context context-type="linenumber">81</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3147349817770432927" datatype="html"> | ||||
|         <source>Create new mail rule</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">121</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3374331029704382439" datatype="html"> | ||||
|         <source>Edit mail rule</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">125</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6036319582202941456" datatype="html"> | ||||
| @@ -1801,32 +1812,32 @@ | ||||
|         <source>Processing: <x id="PH" equiv-text="countUploadingAndProcessing"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context> | ||||
|           <context context-type="linenumber">37</context> | ||||
|           <context context-type="linenumber">36</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="9182918211699394982" datatype="html"> | ||||
|         <source>Failed: <x id="PH" equiv-text="countFailed"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context> | ||||
|           <context context-type="linenumber">40</context> | ||||
|           <context context-type="linenumber">39</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="534116346205124059" datatype="html"> | ||||
|         <source>Added: <x id="PH" equiv-text="countSuccess"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context> | ||||
|           <context context-type="linenumber">43</context> | ||||
|           <context context-type="linenumber">42</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="760986369763309193" datatype="html"> | ||||
|         <source>, </source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts</context> | ||||
|           <context context-type="linenumber">46</context> | ||||
|           <context context-type="linenumber">45</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">179</context> | ||||
|           <context context-type="linenumber">224</context> | ||||
|         </context-group> | ||||
|         <note priority="1" from="description">this string is used to separate processing, failed and added on the file upload widget</note> | ||||
|       </trans-unit> | ||||
| @@ -1931,6 +1942,10 @@ | ||||
|           <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context> | ||||
|           <context context-type="linenumber">19</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> | ||||
|           <context context-type="linenumber">90</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context> | ||||
|           <context context-type="linenumber">58</context> | ||||
| @@ -1955,7 +1970,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> | ||||
|           <context context-type="linenumber">90</context> | ||||
|           <context context-type="linenumber">77</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1418444397960583910" datatype="html"> | ||||
| @@ -2258,7 +2273,7 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">387</context> | ||||
|           <context context-type="linenumber">454</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="9197453786953646058" datatype="html"> | ||||
| @@ -2276,19 +2291,19 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">364</context> | ||||
|           <context context-type="linenumber">417</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">389</context> | ||||
|           <context context-type="linenumber">456</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">560</context> | ||||
|           <context context-type="linenumber">576</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">619</context> | ||||
|           <context context-type="linenumber">635</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1181910457994920507" datatype="html"> | ||||
| @@ -2299,15 +2314,15 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">391</context> | ||||
|           <context context-type="linenumber">458</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">562</context> | ||||
|           <context context-type="linenumber">578</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">621</context> | ||||
|           <context context-type="linenumber">637</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5729001209753056399" datatype="html"> | ||||
| @@ -2388,7 +2403,7 @@ | ||||
|         <source>Actions</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> | ||||
|           <context context-type="linenumber">75</context> | ||||
|           <context context-type="linenumber">74</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/management-list/management-list.component.html</context> | ||||
| @@ -2423,18 +2438,32 @@ | ||||
|           <context context-type="linenumber">44</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1559181160090274453" datatype="html"> | ||||
|         <source> Download <x id="START_TAG_DIV" ctype="x-div" equiv-text="<div *ngIf="awaitingDownload" class="spinner-border spinner-border-sm" role="status">"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="visually-hidden">"/>Preparing download...<x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="</div>"/></source> | ||||
|       <trans-unit id="1015374532025907183" datatype="html"> | ||||
|         <source>Include:</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> | ||||
|           <context context-type="linenumber">78,82</context> | ||||
|           <context context-type="linenumber">96</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="9202029122138685465" datatype="html"> | ||||
|         <source> Download originals <x id="START_TAG_DIV" ctype="x-div" equiv-text="<div *ngIf="awaitingDownload" class="spinner-border spinner-border-sm" role="status">"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="<span class="visually-hidden">"/>Preparing download...<x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="</span>"/><x id="CLOSE_TAG_DIV" ctype="x-div" equiv-text="</div>"/></source> | ||||
|       <trans-unit id="1208547554603365604" datatype="html"> | ||||
|         <source> Archived files </source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> | ||||
|           <context context-type="linenumber">84,88</context> | ||||
|           <context context-type="linenumber">100,102</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6791570188945688785" datatype="html"> | ||||
|         <source> Original files </source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> | ||||
|           <context context-type="linenumber">106,108</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3608345051493493574" datatype="html"> | ||||
|         <source> Use formatted filename </source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> | ||||
|           <context context-type="linenumber">113,115</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7985804062689412812" datatype="html"> | ||||
| @@ -2443,25 +2472,25 @@ | ||||
|             )"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">103,105</context> | ||||
|           <context context-type="linenumber">144,146</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7894972847287473517" datatype="html"> | ||||
|         <source>"<x id="PH" equiv-text="items[0].name"/>"</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">171</context> | ||||
|           <context context-type="linenumber">216</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">177</context> | ||||
|           <context context-type="linenumber">222</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8639884465898458690" datatype="html"> | ||||
|         <source>"<x id="PH" equiv-text="items[0].name"/>" and "<x id="PH_1" equiv-text="items[1].name"/>"</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">173</context> | ||||
|           <context context-type="linenumber">218</context> | ||||
|         </context-group> | ||||
|         <note priority="1" from="description">This is for messages like 'modify "tag1" and "tag2"'</note> | ||||
|       </trans-unit> | ||||
| @@ -2469,7 +2498,7 @@ | ||||
|         <source><x id="PH" equiv-text="list"/> and "<x id="PH_1" equiv-text="items[items.length - 1].name"/>"</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">181,183</context> | ||||
|           <context context-type="linenumber">226,228</context> | ||||
|         </context-group> | ||||
|         <note priority="1" from="description">this is for messages like 'modify "tag1", "tag2" and "tag3"'</note> | ||||
|       </trans-unit> | ||||
| @@ -2477,14 +2506,14 @@ | ||||
|         <source>Confirm tags assignment</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">198</context> | ||||
|           <context context-type="linenumber">243</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6619516195038467207" datatype="html"> | ||||
|         <source>This operation will add the tag "<x id="PH" equiv-text="tag.name"/>" to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">204</context> | ||||
|           <context context-type="linenumber">249</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1894412783609570695" datatype="html"> | ||||
| @@ -2493,14 +2522,14 @@ | ||||
|         )"/> to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">209,211</context> | ||||
|           <context context-type="linenumber">254,256</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7181166515756808573" datatype="html"> | ||||
|         <source>This operation will remove the tag "<x id="PH" equiv-text="tag.name"/>" from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">217</context> | ||||
|           <context context-type="linenumber">262</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3819792277998068944" datatype="html"> | ||||
| @@ -2509,7 +2538,7 @@ | ||||
|         )"/> from <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">222,224</context> | ||||
|           <context context-type="linenumber">267,269</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2739066218579571288" datatype="html"> | ||||
| @@ -2520,98 +2549,98 @@ | ||||
|         )"/> on <x id="PH_2" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">226,230</context> | ||||
|           <context context-type="linenumber">271,275</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2996713129519325161" datatype="html"> | ||||
|         <source>Confirm correspondent assignment</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">265</context> | ||||
|           <context context-type="linenumber">312</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6900893559485781849" datatype="html"> | ||||
|         <source>This operation will assign the correspondent "<x id="PH" equiv-text="correspondent.name"/>" to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">267</context> | ||||
|           <context context-type="linenumber">314</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1257522660364398440" datatype="html"> | ||||
|         <source>This operation will remove the correspondent from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">269</context> | ||||
|           <context context-type="linenumber">316</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5393409374423140648" datatype="html"> | ||||
|         <source>Confirm document type assignment</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">301</context> | ||||
|           <context context-type="linenumber">350</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="332180123895325027" datatype="html"> | ||||
|         <source>This operation will assign the document type "<x id="PH" equiv-text="documentType.name"/>" to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">303</context> | ||||
|           <context context-type="linenumber">352</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2236642492594872779" datatype="html"> | ||||
|         <source>This operation will remove the document type from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">305</context> | ||||
|           <context context-type="linenumber">354</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6386555513013840736" datatype="html"> | ||||
|         <source>Confirm storage path assignment</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">337</context> | ||||
|           <context context-type="linenumber">388</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8750527458618415924" datatype="html"> | ||||
|         <source>This operation will assign the storage path "<x id="PH" equiv-text="storagePath.name"/>" to <x id="PH_1" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">339</context> | ||||
|           <context context-type="linenumber">390</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="60728365335056946" datatype="html"> | ||||
|         <source>This operation will remove the storage path from <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">341</context> | ||||
|           <context context-type="linenumber">392</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="749430623564850405" datatype="html"> | ||||
|         <source>Delete confirm</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">362</context> | ||||
|           <context context-type="linenumber">415</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4303174930844518780" datatype="html"> | ||||
|         <source>This operation will permanently delete <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">363</context> | ||||
|           <context context-type="linenumber">416</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6734339521247847366" datatype="html"> | ||||
|         <source>Delete document(s)</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">366</context> | ||||
|           <context context-type="linenumber">419</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8968869182645922415" datatype="html"> | ||||
|         <source>This operation will permanently redo OCR for <x id="PH" equiv-text="this.list.selected.size"/> selected document(s).</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context> | ||||
|           <context context-type="linenumber">388</context> | ||||
|           <context context-type="linenumber">455</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8076495233090006322" datatype="html"> | ||||
| @@ -3653,49 +3682,49 @@ | ||||
|         <source>Saved view "<x id="PH" equiv-text="savedView.name"/>" deleted.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">367</context> | ||||
|           <context context-type="linenumber">383</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3891152409365583719" datatype="html"> | ||||
|         <source>Settings saved</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">460</context> | ||||
|           <context context-type="linenumber">476</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7217000812750597833" datatype="html"> | ||||
|         <source>Settings were saved successfully.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">461</context> | ||||
|           <context context-type="linenumber">477</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="525012668859298131" datatype="html"> | ||||
|         <source>Settings were saved successfully. Reload is required to apply some changes.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">465</context> | ||||
|           <context context-type="linenumber">481</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8491974984518503778" datatype="html"> | ||||
|         <source>Reload now</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">466</context> | ||||
|           <context context-type="linenumber">482</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6839066544204061364" datatype="html"> | ||||
|         <source>Use system language</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">484</context> | ||||
|           <context context-type="linenumber">500</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7729897675462249787" datatype="html"> | ||||
|         <source>Use date format of display language</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">491</context> | ||||
|           <context context-type="linenumber">507</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8488620293789898901" datatype="html"> | ||||
| @@ -3704,91 +3733,91 @@ | ||||
|             )"/></source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">511,513</context> | ||||
|           <context context-type="linenumber">527,529</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6327501535846658797" datatype="html"> | ||||
|         <source>Saved account "<x id="PH" equiv-text="newMailAccount.name"/>".</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">538</context> | ||||
|           <context context-type="linenumber">554</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6428427497555765743" datatype="html"> | ||||
|         <source>Error saving account: <x id="PH" equiv-text="e.toString()"/>.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">548</context> | ||||
|           <context context-type="linenumber">564</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5641934153807844674" datatype="html"> | ||||
|         <source>Confirm delete mail account</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">558</context> | ||||
|           <context context-type="linenumber">574</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2105352414355663637" datatype="html"> | ||||
|         <source>This operation will permanently this mail account.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">559</context> | ||||
|           <context context-type="linenumber">575</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4233826387148482123" datatype="html"> | ||||
|         <source>Deleted mail account</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">568</context> | ||||
|           <context context-type="linenumber">584</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="7443801450153832973" datatype="html"> | ||||
|         <source>Error deleting mail account: <x id="PH" equiv-text="e.toString()"/>.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">577</context> | ||||
|           <context context-type="linenumber">593</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="123368655395433699" datatype="html"> | ||||
|         <source>Saved rule "<x id="PH" equiv-text="newMailRule.name"/>".</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">596</context> | ||||
|           <context context-type="linenumber">612</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4741216051394823471" datatype="html"> | ||||
|         <source>Error saving rule: <x id="PH" equiv-text="e.toString()"/>.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">607</context> | ||||
|           <context context-type="linenumber">623</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3896080636020672118" datatype="html"> | ||||
|         <source>Confirm delete mail rule</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">617</context> | ||||
|           <context context-type="linenumber">633</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6183247517597275616" datatype="html"> | ||||
|         <source>This operation will permanently this mail rule.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">618</context> | ||||
|           <context context-type="linenumber">634</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="9077981247971516916" datatype="html"> | ||||
|         <source>Deleted mail rule</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">627</context> | ||||
|           <context context-type="linenumber">643</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4740074357089345173" datatype="html"> | ||||
|         <source>Error deleting mail rule: <x id="PH" equiv-text="e.toString()"/>.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context> | ||||
|           <context context-type="linenumber">636</context> | ||||
|           <context context-type="linenumber">652</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5101757640976222639" datatype="html"> | ||||
| @@ -4041,7 +4070,7 @@ | ||||
|         <source>Unsaved Changes</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/guards/dirty-form.guard.ts</context> | ||||
|           <context context-type="linenumber">18</context> | ||||
|           <context context-type="linenumber">17</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/guards/dirty-saved-view.guard.ts</context> | ||||
| @@ -4049,36 +4078,36 @@ | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context> | ||||
|           <context context-type="linenumber">116</context> | ||||
|           <context context-type="linenumber">103</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context> | ||||
|           <context context-type="linenumber">143</context> | ||||
|           <context context-type="linenumber">130</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2573823578527613511" datatype="html"> | ||||
|         <source>You have unsaved changes.</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/guards/dirty-form.guard.ts</context> | ||||
|           <context context-type="linenumber">19</context> | ||||
|           <context context-type="linenumber">18</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context> | ||||
|           <context context-type="linenumber">144</context> | ||||
|           <context context-type="linenumber">131</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3305084982600522070" datatype="html"> | ||||
|         <source>Are you sure you want to leave?</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/guards/dirty-form.guard.ts</context> | ||||
|           <context context-type="linenumber">20</context> | ||||
|           <context context-type="linenumber">19</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="729881853265307704" datatype="html"> | ||||
|         <source>Leave page</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/guards/dirty-form.guard.ts</context> | ||||
|           <context context-type="linenumber">22</context> | ||||
|           <context context-type="linenumber">21</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1649285023712919370" datatype="html"> | ||||
| @@ -4222,35 +4251,35 @@ | ||||
|         <source>You have unsaved changes to the document</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context> | ||||
|           <context context-type="linenumber">118</context> | ||||
|           <context context-type="linenumber">105</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2089045849587358256" datatype="html"> | ||||
|         <source>Are you sure you want to close this document?</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context> | ||||
|           <context context-type="linenumber">122</context> | ||||
|           <context context-type="linenumber">109</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2885986061416655600" datatype="html"> | ||||
|         <source>Close document</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context> | ||||
|           <context context-type="linenumber">124</context> | ||||
|           <context context-type="linenumber">111</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6755718693176327396" datatype="html"> | ||||
|         <source>Are you sure you want to close all documents?</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context> | ||||
|           <context context-type="linenumber">145</context> | ||||
|           <context context-type="linenumber">132</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="4215561719980781894" datatype="html"> | ||||
|         <source>Close documents</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/open-documents.service.ts</context> | ||||
|           <context context-type="linenumber">147</context> | ||||
|           <context context-type="linenumber">134</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="3553216189604488439" datatype="html"> | ||||
| @@ -4436,13 +4465,6 @@ | ||||
|           <context context-type="linenumber">394</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="1519954996184640001" datatype="html"> | ||||
|         <source>Error</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/services/toast.service.ts</context> | ||||
|           <context context-type="linenumber">32</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5037437391296624618" datatype="html"> | ||||
|         <source>Information</source> | ||||
|         <context-group purpose="location"> | ||||
|   | ||||
							
								
								
									
										3239
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3239
									
								
								src-ui/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -40,17 +40,23 @@ | ||||
|   "devDependencies": { | ||||
|     "@angular-builders/jest": "14.1.0", | ||||
|     "@angular-devkit/build-angular": "~14.2.7", | ||||
|     "@angular-eslint/builder": "14.4.0", | ||||
|     "@angular-eslint/eslint-plugin": "14.4.0", | ||||
|     "@angular-eslint/eslint-plugin-template": "14.4.0", | ||||
|     "@angular-eslint/schematics": "14.4.0", | ||||
|     "@angular-eslint/template-parser": "14.4.0", | ||||
|     "@angular/cli": "~14.2.7", | ||||
|     "@angular/compiler-cli": "~14.2.8", | ||||
|     "@types/jest": "28.1.6", | ||||
|     "@types/node": "^18.7.23", | ||||
|     "codelyzer": "^6.0.2", | ||||
|     "@typescript-eslint/eslint-plugin": "5.43.0", | ||||
|     "@typescript-eslint/parser": "5.43.0", | ||||
|     "concurrently": "7.4.0", | ||||
|     "eslint": "^8.28.0", | ||||
|     "jest": "28.1.3", | ||||
|     "jest-environment-jsdom": "^29.2.2", | ||||
|     "jest-preset-angular": "^12.2.3", | ||||
|     "ts-node": "~10.9.1", | ||||
|     "tslint": "~6.1.3", | ||||
|     "typescript": "~4.8.4", | ||||
|     "wait-on": "~6.0.1" | ||||
|   }, | ||||
|   | ||||
| @@ -24,7 +24,7 @@ import { CorrespondentEditDialogComponent } from './components/common/edit-dialo | ||||
| import { TagEditDialogComponent } from './components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component' | ||||
| import { DocumentTypeEditDialogComponent } from './components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component' | ||||
| import { TagComponent } from './components/common/tag/tag.component' | ||||
| import { ClearableBadge } from './components/common/clearable-badge/clearable-badge.component' | ||||
| import { ClearableBadgeComponent } from './components/common/clearable-badge/clearable-badge.component' | ||||
| import { PageHeaderComponent } from './components/common/page-header/page-header.component' | ||||
| import { AppFrameComponent } from './components/app-frame/app-frame.component' | ||||
| import { ToastsComponent } from './components/common/toasts/toasts.component' | ||||
| @@ -157,7 +157,7 @@ function initializeApp(settings: SettingsService) { | ||||
|     DocumentTypeEditDialogComponent, | ||||
|     StoragePathEditDialogComponent, | ||||
|     TagComponent, | ||||
|     ClearableBadge, | ||||
|     ClearableBadgeComponent, | ||||
|     PageHeaderComponent, | ||||
|     AppFrameComponent, | ||||
|     ToastsComponent, | ||||
|   | ||||
| @@ -220,6 +220,12 @@ main { | ||||
|   font-size: 1rem; | ||||
| } | ||||
|  | ||||
| @media screen and (min-width: 768px) { | ||||
|   .navbar-brand.slim { | ||||
|     max-width: 50px; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .dropdown.show .dropdown-toggle, | ||||
| .dropdown-toggle:hover { | ||||
|   opacity: 0.7; | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import { Component, Input, Output, EventEmitter } from '@angular/core' | ||||
|   templateUrl: './clearable-badge.component.html', | ||||
|   styleUrls: ['./clearable-badge.component.scss'], | ||||
| }) | ||||
| export class ClearableBadge { | ||||
| export class ClearableBadgeComponent { | ||||
|   constructor() {} | ||||
|  | ||||
|   @Input() | ||||
|   | ||||
| @@ -30,7 +30,7 @@ export abstract class EditDialogComponent< | ||||
|   object: T | ||||
|  | ||||
|   @Output() | ||||
|   success = new EventEmitter() | ||||
|   succeeded = new EventEmitter() | ||||
|  | ||||
|   networkActive = false | ||||
|  | ||||
| @@ -119,7 +119,7 @@ export abstract class EditDialogComponent< | ||||
|     serverResponse.subscribe({ | ||||
|       next: (result) => { | ||||
|         this.activeModal.close() | ||||
|         this.success.emit(result) | ||||
|         this.succeeded.emit(result) | ||||
|       }, | ||||
|       error: (error) => { | ||||
|         this.error = error.error | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|   </div> | ||||
|   <div class="modal-body"> | ||||
|  | ||||
|     <p *ngIf="this.dialogMode == 'edit'" i18n> | ||||
|     <p *ngIf="this.dialogMode === 'edit'" i18n> | ||||
|       <em>Note that editing a path does not apply changes to stored files until you have run the 'document_renamer' utility. See the <a target="_blank" href="https://docs.paperless-ngx.com/administration/#renamer">documentation</a>.</em> | ||||
|     </p> | ||||
|  | ||||
|   | ||||
| @@ -324,7 +324,7 @@ export class FilterableDropdownComponent { | ||||
|   apply = new EventEmitter<ChangedItems>() | ||||
|  | ||||
|   @Output() | ||||
|   open = new EventEmitter() | ||||
|   opened = new EventEmitter() | ||||
|  | ||||
|   get operatorToggleEnabled(): boolean { | ||||
|     return ( | ||||
| @@ -359,7 +359,7 @@ export class FilterableDropdownComponent { | ||||
|       if (this.editing) { | ||||
|         this.selectionModel.reset() | ||||
|       } | ||||
|       this.open.next(this) | ||||
|       this.opened.next(this) | ||||
|     } else { | ||||
|       this.filterText = '' | ||||
|       if (this.applyOnClose && this.selectionModel.isDirty()) { | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | ||||
| import { Component, EventEmitter, Input, Output } from '@angular/core' | ||||
| import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' | ||||
| import { ObjectWithId } from 'src/app/data/object-with-id' | ||||
|  | ||||
| @@ -7,7 +7,7 @@ import { ObjectWithId } from 'src/app/data/object-with-id' | ||||
|   templateUrl: './select-dialog.component.html', | ||||
|   styleUrls: ['./select-dialog.component.scss'], | ||||
| }) | ||||
| export class SelectDialogComponent implements OnInit { | ||||
| export class SelectDialogComponent { | ||||
|   constructor(public activeModal: NgbActiveModal) {} | ||||
|  | ||||
|   @Output() | ||||
| @@ -24,8 +24,6 @@ export class SelectDialogComponent implements OnInit { | ||||
|  | ||||
|   selected: number | ||||
|  | ||||
|   ngOnInit(): void {} | ||||
|  | ||||
|   cancelClicked() { | ||||
|     this.activeModal.close() | ||||
|   } | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { Component, Input, OnInit } from '@angular/core' | ||||
| import { Component, Input } from '@angular/core' | ||||
| import { PaperlessTag } from 'src/app/data/paperless-tag' | ||||
|  | ||||
| @Component({ | ||||
| @@ -6,7 +6,7 @@ import { PaperlessTag } from 'src/app/data/paperless-tag' | ||||
|   templateUrl: './tag.component.html', | ||||
|   styleUrls: ['./tag.component.scss'], | ||||
| }) | ||||
| export class TagComponent implements OnInit { | ||||
| export class TagComponent { | ||||
|   constructor() {} | ||||
|  | ||||
|   @Input() | ||||
| @@ -17,6 +17,4 @@ export class TagComponent implements OnInit { | ||||
|  | ||||
|   @Input() | ||||
|   clickable: boolean = false | ||||
|  | ||||
|   ngOnInit(): void {} | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <app-widget-frame title="Statistics" [loading]="loading" i18n-title> | ||||
|   <ng-container content> | ||||
|     <p class="card-text" i18n *ngIf="statistics?.documents_inbox != null">Documents in inbox: {{statistics?.documents_inbox}}</p> | ||||
|     <p class="card-text" i18n *ngIf="statistics?.documents_inbox !== null">Documents in inbox: {{statistics?.documents_inbox}}</p> | ||||
|     <p class="card-text" i18n>Total documents: {{statistics?.documents_total}}</p> | ||||
|   </ng-container> | ||||
| </app-widget-frame> | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import { HttpEventType } from '@angular/common/http' | ||||
| import { Component, OnInit } from '@angular/core' | ||||
| import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop' | ||||
| import { Component } from '@angular/core' | ||||
| import { NgxFileDropEntry } from 'ngx-file-drop' | ||||
| import { ComponentWithPermissions } from 'src/app/components/with-permissions/with-permissions.component' | ||||
| import { | ||||
|   ConsumerStatusService, | ||||
| @@ -16,10 +15,7 @@ const MAX_ALERTS = 5 | ||||
|   templateUrl: './upload-file-widget.component.html', | ||||
|   styleUrls: ['./upload-file-widget.component.scss'], | ||||
| }) | ||||
| export class UploadFileWidgetComponent | ||||
|   extends ComponentWithPermissions | ||||
|   implements OnInit | ||||
| { | ||||
| export class UploadFileWidgetComponent extends ComponentWithPermissions { | ||||
|   alertsExpanded = false | ||||
|  | ||||
|   constructor( | ||||
| @@ -115,8 +111,6 @@ export class UploadFileWidgetComponent | ||||
|     this.consumerStatusService.dismissCompleted() | ||||
|   } | ||||
|  | ||||
|   ngOnInit(): void {} | ||||
|  | ||||
|   public fileOver(event) {} | ||||
|  | ||||
|   public fileLeave(event) {} | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { Component, OnInit } from '@angular/core' | ||||
| import { Component } from '@angular/core' | ||||
| import { TourService } from 'ngx-ui-tour-ng-bootstrap' | ||||
|  | ||||
| @Component({ | ||||
| @@ -6,8 +6,6 @@ import { TourService } from 'ngx-ui-tour-ng-bootstrap' | ||||
|   templateUrl: './welcome-widget.component.html', | ||||
|   styleUrls: ['./welcome-widget.component.scss'], | ||||
| }) | ||||
| export class WelcomeWidgetComponent implements OnInit { | ||||
| export class WelcomeWidgetComponent { | ||||
|   constructor(public readonly tourService: TourService) {} | ||||
|  | ||||
|   ngOnInit(): void {} | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| import { Component, Input, OnInit } from '@angular/core' | ||||
| import { Component, Input } from '@angular/core' | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-widget-frame', | ||||
|   templateUrl: './widget-frame.component.html', | ||||
|   styleUrls: ['./widget-frame.component.scss'], | ||||
| }) | ||||
| export class WidgetFrameComponent implements OnInit { | ||||
| export class WidgetFrameComponent { | ||||
|   constructor() {} | ||||
|  | ||||
|   @Input() | ||||
| @@ -13,6 +13,4 @@ export class WidgetFrameComponent implements OnInit { | ||||
|  | ||||
|   @Input() | ||||
|   loading: boolean = false | ||||
|  | ||||
|   ngOnInit(): void {} | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <app-page-header [(title)]="title"> | ||||
|     <div class="input-group input-group-sm me-5 d-none d-md-flex" *ngIf="getContentType() == 'application/pdf' && !useNativePdfViewer"> | ||||
|     <div class="input-group input-group-sm me-5 d-none d-md-flex" *ngIf="getContentType() === 'application/pdf' && !useNativePdfViewer"> | ||||
|       <div class="input-group-text" i18n>Page</div> | ||||
|       <input class="form-control flex-grow-0 w-auto" type="number" min="1" [max]="previewNumPages" [(ngModel)]="previewCurrentPage" /> | ||||
|       <div class="input-group-text" i18n>of {{previewNumPages}}</div> | ||||
| @@ -149,9 +149,9 @@ | ||||
|  | ||||
|                 <li [ngbNavItem]="4" class="d-md-none"> | ||||
|                     <a ngbNavLink>Preview</a> | ||||
|                     <ng-template ngbNavContent *ngIf="pdfPreview.offsetParent == undefined"> | ||||
|                     <ng-template ngbNavContent *ngIf="pdfPreview.offsetParent === undefined"> | ||||
|                         <div class="position-relative"> | ||||
|                             <ng-container *ngIf="getContentType() == 'application/pdf'"> | ||||
|                             <ng-container *ngIf="getContentType() === 'application/pdf'"> | ||||
|                                 <div class="preview-sticky pdf-viewer-container" *ngIf="!useNativePdfViewer ; else nativePdfViewer"> | ||||
|                                     <pdf-viewer [src]="{ url: previewUrl, password: password }" [original-size]="false" [show-borders]="true" [show-all]="true" [(page)]="previewCurrentPage" [render-text-mode]="2" (error)="onError($event)" (after-load-complete)="pdfPreviewLoaded($event)"></pdf-viewer> | ||||
|                                 </div> | ||||
| @@ -159,7 +159,7 @@ | ||||
|                                     <object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object> | ||||
|                                 </ng-template> | ||||
|                             </ng-container> | ||||
|                             <ng-container *ngIf="getContentType() == 'text/plain'"> | ||||
|                             <ng-container *ngIf="getContentType() === 'text/plain'"> | ||||
|                                 <object [data]="previewUrl | safeUrl" type="text/plain" class="preview-sticky bg-white" width="100%"></object> | ||||
|                             </ng-container> | ||||
|                             <div *ngIf="requiresPassword" class="password-prompt"> | ||||
| @@ -191,15 +191,15 @@ | ||||
|             <div [ngbNavOutlet]="nav" class="mt-2"></div> | ||||
|  | ||||
|             <ng-container> | ||||
|                 <button type="button" class="btn btn-outline-secondary" (click)="discard()" i18n [disabled]="!userCanEdit || networkActive || !(isDirty$ | async)">Discard</button>  | ||||
|                 <button type="button" class="btn btn-outline-primary" (click)="saveEditNext()" *ngIf="hasNext()" i18n [disabled]="!userCanEdit || networkActive || !(isDirty$ | async) || error">Save & next</button>  | ||||
|                 <button type="submit" class="btn btn-primary" *ifPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }" i18n [disabled]="!userCanEdit || networkActive || !(isDirty$ | async) || error">Save</button>  | ||||
|                 <button type="button" class="btn btn-outline-secondary" (click)="discard()" i18n [disabled]="!userCanEdit || networkActive || (isDirty$ | async) === false">Discard</button>  | ||||
|                 <button type="button" class="btn btn-outline-primary" (click)="saveEditNext()" *ngIf="hasNext()" i18n [disabled]="!userCanEdit || networkActive || (isDirty$ | async) === false || error">Save & next</button>  | ||||
|                 <button type="submit" class="btn btn-primary" *ifPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }" i18n [disabled]="!userCanEdit || networkActive || (isDirty$ | async) === false || error">Save</button>  | ||||
|             </ng-container> | ||||
|         </form> | ||||
|     </div> | ||||
|  | ||||
|     <div class="col-md-6 col-xl-8 mb-3 d-none d-md-block position-relative" #pdfPreview> | ||||
|         <ng-container *ngIf="getContentType() == 'application/pdf'"> | ||||
|         <ng-container *ngIf="getContentType() === 'application/pdf'"> | ||||
|             <div class="preview-sticky pdf-viewer-container" *ngIf="!useNativePdfViewer ; else nativePdfViewer"> | ||||
|                 <pdf-viewer [src]="{ url: previewUrl, password: password }" [original-size]="false" [show-borders]="true" [show-all]="true" [(page)]="previewCurrentPage" [render-text-mode]="2" (error)="onError($event)" (after-load-complete)="pdfPreviewLoaded($event)"></pdf-viewer> | ||||
|             </div> | ||||
| @@ -207,7 +207,7 @@ | ||||
|                 <object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object> | ||||
|             </ng-template> | ||||
|         </ng-container> | ||||
|         <ng-container *ngIf="getContentType() == 'text/plain'"> | ||||
|         <ng-container *ngIf="getContentType() === 'text/plain'"> | ||||
|             <object [data]="previewUrl | safeUrl" type="text/plain" class="preview-sticky bg-white" width="100%"></object> | ||||
|         </ng-container> | ||||
|         <div *ngIf="requiresPassword" class="password-prompt"> | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| import { Component, Input, OnInit } from '@angular/core' | ||||
| import { Component, Input } from '@angular/core' | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-metadata-collapse', | ||||
|   templateUrl: './metadata-collapse.component.html', | ||||
|   styleUrls: ['./metadata-collapse.component.scss'], | ||||
| }) | ||||
| export class MetadataCollapseComponent implements OnInit { | ||||
| export class MetadataCollapseComponent { | ||||
|   constructor() {} | ||||
|  | ||||
|   expand = false | ||||
| @@ -15,6 +15,4 @@ export class MetadataCollapseComponent implements OnInit { | ||||
|  | ||||
|   @Input() | ||||
|   title = $localize`Metadata` | ||||
|  | ||||
|   ngOnInit(): void {} | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,6 @@ import { | ||||
|   Component, | ||||
|   EventEmitter, | ||||
|   Input, | ||||
|   OnInit, | ||||
|   Output, | ||||
|   ViewChild, | ||||
| } from '@angular/core' | ||||
| @@ -21,10 +20,7 @@ import { ComponentWithPermissions } from '../../with-permissions/with-permission | ||||
|     '../popover-preview/popover-preview.scss', | ||||
|   ], | ||||
| }) | ||||
| export class DocumentCardLargeComponent | ||||
|   extends ComponentWithPermissions | ||||
|   implements OnInit | ||||
| { | ||||
| export class DocumentCardLargeComponent extends ComponentWithPermissions { | ||||
|   constructor( | ||||
|     private documentService: DocumentService, | ||||
|     private settingsService: SettingsService | ||||
| @@ -77,8 +73,6 @@ export class DocumentCardLargeComponent | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ngOnInit(): void {} | ||||
|  | ||||
|   getIsThumbInverted() { | ||||
|     return this.settingsService.get(SETTINGS_KEYS.DARK_MODE_THUMB_INVERTED) | ||||
|   } | ||||
|   | ||||
| @@ -53,7 +53,7 @@ | ||||
|       </div> | ||||
|       <div> | ||||
|         <button *ngFor="let f of getSortFields()" ngbDropdownItem (click)="setSortField(f.field)" | ||||
|           [class.active]="list.sortField == f.field">{{f.name}} | ||||
|           [class.active]="list.sortField === f.field">{{f.name}} | ||||
|         </button> | ||||
|       </div> | ||||
|     </div> | ||||
| @@ -96,7 +96,7 @@ | ||||
|       </ng-container> | ||||
|       <span i18n *ngIf="list.selected.size > 0">{list.collectionSize, plural, =1 {Selected {{list.selected.size}} of one document} other {Selected {{list.selected.size}} of {{list.collectionSize || 0}} documents}}</span> | ||||
|       <ng-container *ngIf="!list.isReloading"> | ||||
|         <span i18n *ngIf="list.selected.size == 0">{list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}}</span> <span i18n *ngIf="isFiltered">(filtered)</span> | ||||
|         <span i18n *ngIf="list.selected.size === 0">{list.collectionSize, plural, =1 {One document} other {{{list.collectionSize || 0}} documents}}</span> <span i18n *ngIf="isFiltered">(filtered)</span> | ||||
|       </ng-container> | ||||
|     </p> | ||||
|     <ngb-pagination *ngIf="list.collectionSize" [pageSize]="list.currentPageSize" [collectionSize]="list.collectionSize" [(page)]="list.currentPage" [maxSize]="5" | ||||
| @@ -113,52 +113,52 @@ | ||||
| </ng-container> | ||||
|  | ||||
| <ng-template #documentListNoError> | ||||
|   <div *ngIf="displayMode == 'largeCards'"> | ||||
|   <div *ngIf="displayMode === 'largeCards'"> | ||||
|     <app-document-card-large [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" *ngFor="let d of list.documents; trackBy: trackByDocumentId" [document]="d" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickDocumentType)="clickDocumentType($event)" (clickStoragePath)="clickStoragePath($event)" (clickMoreLike)="clickMoreLike(d.id)"> | ||||
|     </app-document-card-large> | ||||
|   </div> | ||||
|  | ||||
|   <table class="table table-sm align-middle border shadow-sm" *ngIf="displayMode == 'details'"> | ||||
|   <table class="table table-sm align-middle border shadow-sm" *ngIf="displayMode === 'details'"> | ||||
|     <thead> | ||||
|       <th></th> | ||||
|       <th class="d-none d-lg-table-cell" | ||||
|         sortable="archive_serial_number" | ||||
|         appSortable="archive_serial_number" | ||||
|         [currentSortField]="list.sortField" | ||||
|         [currentSortReverse]="list.sortReverse" | ||||
|         (sort)="onSort($event)" | ||||
|         i18n>ASN</th> | ||||
|       <th class="d-none d-md-table-cell" | ||||
|         sortable="correspondent__name" | ||||
|         appSortable="correspondent__name" | ||||
|         [currentSortField]="list.sortField" | ||||
|         [currentSortReverse]="list.sortReverse" | ||||
|         (sort)="onSort($event)" | ||||
|         i18n>Correspondent</th> | ||||
|       <th | ||||
|         sortable="title" | ||||
|         appSortable="title" | ||||
|         [currentSortField]="list.sortField" | ||||
|         [currentSortReverse]="list.sortReverse" | ||||
|         (sort)="onSort($event)" | ||||
|         i18n>Title</th> | ||||
|       <th class="d-none d-xl-table-cell" | ||||
|         sortable="document_type__name" | ||||
|         appSortable="document_type__name" | ||||
|         [currentSortField]="list.sortField" | ||||
|         [currentSortReverse]="list.sortReverse" | ||||
|         (sort)="onSort($event)" | ||||
|         i18n>Document type</th> | ||||
|       <th class="d-none d-xl-table-cell" | ||||
|         sortable="storage_path__name" | ||||
|         appSortable="storage_path__name" | ||||
|         [currentSortField]="list.sortField" | ||||
|         [currentSortReverse]="list.sortReverse" | ||||
|         (sort)="onSort($event)" | ||||
|         i18n>Storage path</th> | ||||
|       <th | ||||
|         sortable="created" | ||||
|         appSortable="created" | ||||
|         [currentSortField]="list.sortField" | ||||
|         [currentSortReverse]="list.sortReverse" | ||||
|         (sort)="onSort($event)" | ||||
|         i18n>Created</th> | ||||
|       <th class="d-none d-xl-table-cell" | ||||
|         sortable="added" | ||||
|         appSortable="added" | ||||
|         [currentSortField]="list.sortField" | ||||
|         [currentSortReverse]="list.sortReverse" | ||||
|         (sort)="onSort($event)" | ||||
| @@ -204,7 +204,7 @@ | ||||
|     </tbody> | ||||
|   </table> | ||||
|  | ||||
|   <div class="row row-cols-paperless-cards" *ngIf="displayMode == 'smallCards'"> | ||||
|   <div class="row row-cols-paperless-cards" *ngIf="displayMode === 'smallCards'"> | ||||
|     <app-document-card-small class="p-0" [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" [document]="d" *ngFor="let d of list.documents; trackBy: trackByDocumentId" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)" (clickStoragePath)="clickStoragePath($event)" (clickDocumentType)="clickDocumentType($event)"></app-document-card-small> | ||||
|   </div> | ||||
|   <div *ngIf="list.documents?.length > 15" class="mt-3"> | ||||
|   | ||||
| @@ -5,10 +5,10 @@ | ||||
|            <div ngbDropdown> | ||||
|             <button class="btn btn-sm btn-outline-primary" ngbDropdownToggle>{{textFilterTargetName}}</button> | ||||
|             <div class="dropdown-menu shadow" ngbDropdownMenu> | ||||
|               <button *ngFor="let t of textFilterTargets" ngbDropdownItem [class.active]="textFilterTarget == t.id" (click)="changeTextFilterTarget(t.id)">{{t.name}}</button> | ||||
|               <button *ngFor="let t of textFilterTargets" ngbDropdownItem [class.active]="textFilterTarget === t.id" (click)="changeTextFilterTarget(t.id)">{{t.name}}</button> | ||||
|             </div> | ||||
|           </div> | ||||
|           <select *ngIf="textFilterTarget == 'asn'" class="form-select flex-grow-0 w-auto" [(ngModel)]="textFilterModifier" (change)="textFilterModifierChange()"> | ||||
|           <select *ngIf="textFilterTarget === 'asn'" class="form-select flex-grow-0 w-auto" [(ngModel)]="textFilterModifier" (change)="textFilterModifierChange()"> | ||||
|             <option *ngFor="let m of textFilterModifiers" ngbDropdownItem [value]="m.id">{{m.label}}</option> | ||||
|           </select> | ||||
|           <button *ngIf="_textFilter" class="btn btn-link btn-sm px-0 position-absolute top-0 end-0 z-10" (click)="resetTextField()"> | ||||
| @@ -16,7 +16,7 @@ | ||||
|               <path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/> | ||||
|             </svg> | ||||
|           </button> | ||||
|           <input #textFilterInput class="form-control form-control-sm" type="text" [disabled]="textFilterModifierIsNull" [(ngModel)]="textFilter" (keyup)="textFilterKeyup($event)" [readonly]="textFilterTarget == 'fulltext-morelike'"> | ||||
|           <input #textFilterInput class="form-control form-control-sm" type="text" [disabled]="textFilterModifierIsNull" [(ngModel)]="textFilter" (keyup)="textFilterKeyup($event)" [readonly]="textFilterTarget === 'fulltext-morelike'"> | ||||
|          </div> | ||||
|      </div> | ||||
|   </div> | ||||
|   | ||||
| @@ -16,10 +16,10 @@ | ||||
| <table class="table table-striped align-middle border shadow-sm"> | ||||
|   <thead> | ||||
|     <tr> | ||||
|       <th scope="col" sortable="name" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Name</th> | ||||
|       <th scope="col" class="d-none d-sm-table-cell" sortable="matching_algorithm" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Matching</th> | ||||
|       <th scope="col" sortable="document_count" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Document count</th> | ||||
|       <th scope="col" *ngFor="let column of extraColumns" sortable="{{column.key}}" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)">{{column.name}}</th> | ||||
|       <th scope="col" appSortable="name" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Name</th> | ||||
|       <th scope="col" class="d-none d-sm-table-cell" appSortable="matching_algorithm" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Matching</th> | ||||
|       <th scope="col" appSortable="document_count" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)" i18n>Document count</th> | ||||
|       <th scope="col" *ngFor="let column of extraColumns" appSortable="{{column.key}}" [currentSortField]="sortField" [currentSortReverse]="sortReverse" (sort)="onSort($event)">{{column.name}}</th> | ||||
|       <th scope="col" i18n>Actions</th> | ||||
|     </tr> | ||||
|   </thead> | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
|           <div class="col"> | ||||
|  | ||||
|             <select class="form-select" formControlName="displayLanguage"> | ||||
|               <option *ngFor="let lang of displayLanguageOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code && currentLocale != 'en-US'"> - {{lang.englishName}}</span></option> | ||||
|               <option *ngFor="let lang of displayLanguageOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code && currentLocale !== 'en-US'"> - {{lang.englishName}}</span></option> | ||||
|             </select> | ||||
|  | ||||
|             <small *ngIf="displayLanguageIsDirty" class="form-text text-primary" i18n>You need to reload the page after applying a new language.</small> | ||||
| @@ -215,7 +215,7 @@ | ||||
|               </div> | ||||
|             </div> | ||||
|  | ||||
|             <div *ngIf="savedViews && savedViews.length == 0" i18n>No saved views defined.</div> | ||||
|             <div *ngIf="savedViews && savedViews.length === 0" i18n>No saved views defined.</div> | ||||
|  | ||||
|             <div *ngIf="!savedViews"> | ||||
|               <div class="spinner-border spinner-border-sm fw-normal ms-2 me-auto" role="status"></div> | ||||
| @@ -265,7 +265,7 @@ | ||||
|                   </div> | ||||
|                 </li> | ||||
|  | ||||
|                 <div *ngIf="mailAccounts.length == 0" i18n>No mail accounts defined.</div> | ||||
|                 <div *ngIf="mailAccounts.length === 0" i18n>No mail accounts defined.</div> | ||||
|             </ul> | ||||
|           </ng-container> | ||||
|  | ||||
| @@ -302,7 +302,7 @@ | ||||
|                   </div> | ||||
|                 </li> | ||||
|  | ||||
|                 <div *ngIf="mailRules.length == 0" i18n>No mail rules defined.</div> | ||||
|                 <div *ngIf="mailRules.length === 0" i18n>No mail rules defined.</div> | ||||
|             </ul> | ||||
|           </ng-container> | ||||
|         </ng-container> | ||||
| @@ -404,5 +404,5 @@ | ||||
|  | ||||
|   <div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div> | ||||
|  | ||||
|   <button type="submit" class="btn btn-primary mb-2" *ifPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }" [disabled]="!(isDirty$ | async)" i18n>Save</button> | ||||
|   <button type="submit" class="btn btn-primary mb-2" *ifPermissions="{ action: PermissionAction.Change, type: PermissionType.UISettings }" [disabled]="(isDirty$ | async) === false" i18n>Save</button> | ||||
| </form> | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| <app-page-header title="File Tasks" i18n-title> | ||||
|   <div class="btn-toolbar col col-md-auto"> | ||||
|     <button class="btn btn-sm btn-outline-secondary me-2" (click)="clearSelection()" [hidden]="selectedTasks.size == 0"> | ||||
|     <button class="btn btn-sm btn-outline-secondary me-2" (click)="clearSelection()" [hidden]="selectedTasks.size === 0"> | ||||
|       <svg class="sidebaricon" fill="currentColor"> | ||||
|         <use xlink:href="assets/bootstrap-icons.svg#x"/> | ||||
|       </svg> <ng-container i18n>Clear selection</ng-container> | ||||
|     </button> | ||||
|     <button class="btn btn-sm btn-outline-primary me-4" (click)="dismissTasks()" *ifPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }" [disabled]="tasksService.total == 0"> | ||||
|     <button class="btn btn-sm btn-outline-primary me-4" (click)="dismissTasks()" *ifPermissions="{ action: PermissionAction.Change, type: PermissionType.PaperlessTask }" [disabled]="tasksService.total === 0"> | ||||
|       <svg class="sidebaricon" fill="currentColor"> | ||||
|         <use xlink:href="assets/bootstrap-icons.svg#check2-all"/> | ||||
|       </svg> <ng-container i18n>{{dismissButtonText}}</ng-container> | ||||
| @@ -33,13 +33,13 @@ | ||||
|       <tr> | ||||
|         <th scope="col"> | ||||
|           <div class="form-check"> | ||||
|             <input type="checkbox" class="form-check-input" id="all-tasks" [disabled]="currentTasks.length == 0" (click)="toggleAll($event); $event.stopPropagation();"> | ||||
|             <input type="checkbox" class="form-check-input" id="all-tasks" [disabled]="currentTasks.length === 0" (click)="toggleAll($event); $event.stopPropagation();"> | ||||
|             <label class="form-check-label" for="all-tasks"></label> | ||||
|           </div> | ||||
|         </th> | ||||
|         <th scope="col" i18n>Name</th> | ||||
|         <th scope="col" class="d-none d-lg-table-cell" i18n>Created</th> | ||||
|         <th scope="col" class="d-none d-lg-table-cell" *ngIf="activeTab != 'started' && activeTab != 'queued'" i18n>Results</th> | ||||
|         <th scope="col" class="d-none d-lg-table-cell" *ngIf="activeTab !== 'started' && activeTab !== 'queued'" i18n>Results</th> | ||||
|         <th scope="col" class="d-table-cell d-lg-none" i18n>Info</th> | ||||
|         <th scope="col" i18n>Actions</th> | ||||
|       </tr> | ||||
| @@ -55,7 +55,7 @@ | ||||
|         </th> | ||||
|         <td class="overflow-auto">{{ task.task_file_name }}</td> | ||||
|         <td class="d-none d-lg-table-cell">{{ task.date_created | customDate:'short' }}</td> | ||||
|         <td class="d-none d-lg-table-cell" *ngIf="activeTab != 'started' && activeTab != 'queued'"> | ||||
|         <td class="d-none d-lg-table-cell" *ngIf="activeTab !== 'started' && activeTab !== 'queued'"> | ||||
|           <div *ngIf="task.result?.length > 50" class="result" (click)="expandTask(task); $event.stopPropagation();" | ||||
|             [ngbPopover]="resultPopover" popoverClass="shadow small mobile" triggers="mouseenter:mouseleave" container="body"> | ||||
|             <span class="small d-none d-md-inline-block font-monospace text-muted">{{ task.result | slice:0:50 }}…</span> | ||||
| @@ -91,7 +91,7 @@ | ||||
|         </td> | ||||
|       </tr> | ||||
|       <tr> | ||||
|         <td class="p-0" [class.border-0]="expandedTask != task.id" colspan="5"> | ||||
|         <td class="p-0" [class.border-0]="expandedTask !== task.id" colspan="5"> | ||||
|           <pre #collapse="ngbCollapse" [ngbCollapse]="expandedTask !== task.id" class="small mb-0"><div class="small p-1 p-lg-3 ms-lg-3">{{ task.result }}</div></pre> | ||||
|         </td> | ||||
|       </tr> | ||||
|   | ||||
| @@ -1,12 +1,10 @@ | ||||
| import { Component, OnInit } from '@angular/core' | ||||
| import { Component } from '@angular/core' | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'app-not-found', | ||||
|   templateUrl: './not-found.component.html', | ||||
|   styleUrls: ['./not-found.component.scss'], | ||||
| }) | ||||
| export class NotFoundComponent implements OnInit { | ||||
| export class NotFoundComponent { | ||||
|   constructor() {} | ||||
|  | ||||
|   ngOnInit(): void {} | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,11 @@ | ||||
| import { Directive, EventEmitter, Input, Output } from '@angular/core' | ||||
| import { | ||||
|   Directive, | ||||
|   EventEmitter, | ||||
|   HostBinding, | ||||
|   HostListener, | ||||
|   Input, | ||||
|   Output, | ||||
| } from '@angular/core' | ||||
|  | ||||
| export interface SortEvent { | ||||
|   column: string | ||||
| @@ -6,18 +13,13 @@ export interface SortEvent { | ||||
| } | ||||
|  | ||||
| @Directive({ | ||||
|   selector: 'th[sortable]', | ||||
|   host: { | ||||
|     '[class.asc]': 'currentSortField == sortable && !currentSortReverse', | ||||
|     '[class.des]': 'currentSortField == sortable && currentSortReverse', | ||||
|     '(click)': 'rotate()', | ||||
|   }, | ||||
|   selector: 'th[appSortable]', | ||||
| }) | ||||
| export class SortableDirective { | ||||
|   constructor() {} | ||||
|  | ||||
|   @Input() | ||||
|   sortable: string = '' | ||||
|   appSortable: string = '' | ||||
|  | ||||
|   @Input() | ||||
|   currentSortReverse: boolean = false | ||||
| @@ -27,11 +29,20 @@ export class SortableDirective { | ||||
|  | ||||
|   @Output() sort = new EventEmitter<SortEvent>() | ||||
|  | ||||
|   rotate() { | ||||
|     if (this.currentSortField != this.sortable) { | ||||
|       this.sort.emit({ column: this.sortable, reverse: false }) | ||||
|   @HostBinding('class.asc') get asc() { | ||||
|     return ( | ||||
|       this.currentSortField === this.appSortable && !this.currentSortReverse | ||||
|     ) | ||||
|   } | ||||
|   @HostBinding('class.des') get des() { | ||||
|     return this.currentSortField === this.appSortable && this.currentSortReverse | ||||
|   } | ||||
|  | ||||
|   @HostListener('click') rotate() { | ||||
|     if (this.currentSortField != this.appSortable) { | ||||
|       this.sort.emit({ column: this.appSortable, reverse: false }) | ||||
|     } else if ( | ||||
|       this.currentSortField == this.sortable && | ||||
|       this.currentSortField == this.appSortable && | ||||
|       !this.currentSortReverse | ||||
|     ) { | ||||
|       this.sort.emit({ column: this.currentSortField, reverse: true }) | ||||
|   | ||||
| @@ -1,152 +0,0 @@ | ||||
| { | ||||
|   "extends": "tslint:recommended", | ||||
|   "rulesDirectory": [ | ||||
|     "codelyzer" | ||||
|   ], | ||||
|   "rules": { | ||||
|     "align": { | ||||
|       "options": [ | ||||
|         "parameters", | ||||
|         "statements" | ||||
|       ] | ||||
|     }, | ||||
|     "array-type": false, | ||||
|     "arrow-return-shorthand": true, | ||||
|     "curly": true, | ||||
|     "deprecation": { | ||||
|       "severity": "warning" | ||||
|     }, | ||||
|     "eofline": true, | ||||
|     "import-blacklist": [ | ||||
|       true, | ||||
|       "rxjs/Rx" | ||||
|     ], | ||||
|     "import-spacing": true, | ||||
|     "indent": { | ||||
|       "options": [ | ||||
|         "spaces" | ||||
|       ] | ||||
|     }, | ||||
|     "max-classes-per-file": false, | ||||
|     "max-line-length": [ | ||||
|       true, | ||||
|       140 | ||||
|     ], | ||||
|     "member-ordering": [ | ||||
|       true, | ||||
|       { | ||||
|         "order": [ | ||||
|           "static-field", | ||||
|           "instance-field", | ||||
|           "static-method", | ||||
|           "instance-method" | ||||
|         ] | ||||
|       } | ||||
|     ], | ||||
|     "no-console": [ | ||||
|       true, | ||||
|       "debug", | ||||
|       "info", | ||||
|       "time", | ||||
|       "timeEnd", | ||||
|       "trace" | ||||
|     ], | ||||
|     "no-empty": false, | ||||
|     "no-inferrable-types": [ | ||||
|       true, | ||||
|       "ignore-params" | ||||
|     ], | ||||
|     "no-non-null-assertion": true, | ||||
|     "no-redundant-jsdoc": true, | ||||
|     "no-switch-case-fall-through": true, | ||||
|     "no-var-requires": false, | ||||
|     "object-literal-key-quotes": [ | ||||
|       true, | ||||
|       "as-needed" | ||||
|     ], | ||||
|     "quotemark": [ | ||||
|       true, | ||||
|       "single" | ||||
|     ], | ||||
|     "semicolon": { | ||||
|       "options": [ | ||||
|         "always" | ||||
|       ] | ||||
|     }, | ||||
|     "space-before-function-paren": { | ||||
|       "options": { | ||||
|         "anonymous": "never", | ||||
|         "asyncArrow": "always", | ||||
|         "constructor": "never", | ||||
|         "method": "never", | ||||
|         "named": "never" | ||||
|       } | ||||
|     }, | ||||
|     "typedef": [ | ||||
|       true, | ||||
|       "call-signature" | ||||
|     ], | ||||
|     "typedef-whitespace": { | ||||
|       "options": [ | ||||
|         { | ||||
|           "call-signature": "nospace", | ||||
|           "index-signature": "nospace", | ||||
|           "parameter": "nospace", | ||||
|           "property-declaration": "nospace", | ||||
|           "variable-declaration": "nospace" | ||||
|         }, | ||||
|         { | ||||
|           "call-signature": "onespace", | ||||
|           "index-signature": "onespace", | ||||
|           "parameter": "onespace", | ||||
|           "property-declaration": "onespace", | ||||
|           "variable-declaration": "onespace" | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     "variable-name": { | ||||
|       "options": [ | ||||
|         "ban-keywords", | ||||
|         "check-format", | ||||
|         "allow-pascal-case" | ||||
|       ] | ||||
|     }, | ||||
|     "whitespace": { | ||||
|       "options": [ | ||||
|         "check-branch", | ||||
|         "check-decl", | ||||
|         "check-operator", | ||||
|         "check-separator", | ||||
|         "check-type", | ||||
|         "check-typecast" | ||||
|       ] | ||||
|     }, | ||||
|     "component-class-suffix": true, | ||||
|     "contextual-lifecycle": true, | ||||
|     "directive-class-suffix": true, | ||||
|     "no-conflicting-lifecycle": true, | ||||
|     "no-host-metadata-property": true, | ||||
|     "no-input-rename": true, | ||||
|     "no-inputs-metadata-property": true, | ||||
|     "no-output-native": true, | ||||
|     "no-output-on-prefix": true, | ||||
|     "no-output-rename": true, | ||||
|     "no-outputs-metadata-property": true, | ||||
|     "template-banana-in-box": true, | ||||
|     "template-no-negated-async": true, | ||||
|     "use-lifecycle-interface": true, | ||||
|     "use-pipe-transform-interface": true, | ||||
|     "directive-selector": [ | ||||
|       true, | ||||
|       "attribute", | ||||
|       "app", | ||||
|       "camelCase" | ||||
|     ], | ||||
|     "component-selector": [ | ||||
|       true, | ||||
|       "element", | ||||
|       "app", | ||||
|       "kebab-case" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
| @@ -4,6 +4,7 @@ from guardian.admin import GuardedModelAdmin | ||||
| from .models import Correspondent | ||||
| from .models import Document | ||||
| from .models import DocumentType | ||||
| from .models import PaperlessTask | ||||
| from .models import SavedView | ||||
| from .models import SavedViewFilterRule | ||||
| from .models import StoragePath | ||||
| @@ -113,9 +114,27 @@ class StoragePathAdmin(GuardedModelAdmin): | ||||
|     list_editable = ("path", "match", "matching_algorithm") | ||||
|  | ||||
|  | ||||
| class TaskAdmin(admin.ModelAdmin): | ||||
|  | ||||
|     list_display = ("task_id", "task_file_name", "task_name", "date_done", "status") | ||||
|     list_filter = ("status", "date_done", "task_file_name", "task_name") | ||||
|     search_fields = ("task_name", "task_id", "status") | ||||
|     readonly_fields = ( | ||||
|         "task_id", | ||||
|         "task_file_name", | ||||
|         "task_name", | ||||
|         "status", | ||||
|         "date_created", | ||||
|         "date_started", | ||||
|         "date_done", | ||||
|         "result", | ||||
|     ) | ||||
|  | ||||
|  | ||||
| admin.site.register(Correspondent, CorrespondentAdmin) | ||||
| admin.site.register(Tag, TagAdmin) | ||||
| admin.site.register(DocumentType, DocumentTypeAdmin) | ||||
| admin.site.register(Document, DocumentAdmin) | ||||
| admin.site.register(SavedView, SavedViewAdmin) | ||||
| admin.site.register(StoragePath, StoragePathAdmin) | ||||
| admin.site.register(PaperlessTask, TaskAdmin) | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import hashlib | ||||
| import json | ||||
| import os | ||||
| import shutil | ||||
| import tempfile | ||||
| import time | ||||
|  | ||||
| import tqdm | ||||
| @@ -12,6 +13,7 @@ from django.core import serializers | ||||
| from django.core.management.base import BaseCommand | ||||
| from django.core.management.base import CommandError | ||||
| from django.db import transaction | ||||
| from django.utils import timezone | ||||
| from documents.models import Comment | ||||
| from documents.models import Correspondent | ||||
| from documents.models import Document | ||||
| @@ -76,6 +78,7 @@ class Command(BaseCommand): | ||||
|             "do not belong to the current export, such as files from " | ||||
|             "deleted documents.", | ||||
|         ) | ||||
|  | ||||
|         parser.add_argument( | ||||
|             "--no-progress-bar", | ||||
|             default=False, | ||||
| @@ -83,6 +86,14 @@ class Command(BaseCommand): | ||||
|             help="If set, the progress bar will not be shown", | ||||
|         ) | ||||
|  | ||||
|         parser.add_argument( | ||||
|             "-z", | ||||
|             "--zip", | ||||
|             default=False, | ||||
|             action="store_true", | ||||
|             help="Export the documents to a zip file in the given directory", | ||||
|         ) | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         BaseCommand.__init__(self, *args, **kwargs) | ||||
|         self.target = None | ||||
| @@ -98,6 +109,19 @@ class Command(BaseCommand): | ||||
|         self.compare_checksums = options["compare_checksums"] | ||||
|         self.use_filename_format = options["use_filename_format"] | ||||
|         self.delete = options["delete"] | ||||
|         zip_export: bool = options["zip"] | ||||
|  | ||||
|         # If zipping, save the original target for later and | ||||
|         # get a temporary directory for the target | ||||
|         temp_dir = None | ||||
|         original_target = None | ||||
|         if zip_export: | ||||
|             original_target = self.target | ||||
|             temp_dir = tempfile.TemporaryDirectory( | ||||
|                 dir=settings.SCRATCH_DIR, | ||||
|                 prefix="paperless-export", | ||||
|             ) | ||||
|             self.target = temp_dir.name | ||||
|  | ||||
|         if not os.path.exists(self.target): | ||||
|             raise CommandError("That path doesn't exist") | ||||
| @@ -105,8 +129,26 @@ class Command(BaseCommand): | ||||
|         if not os.access(self.target, os.W_OK): | ||||
|             raise CommandError("That path doesn't appear to be writable") | ||||
|  | ||||
|         with FileLock(settings.MEDIA_LOCK): | ||||
|             self.dump(options["no_progress_bar"]) | ||||
|         try: | ||||
|             with FileLock(settings.MEDIA_LOCK): | ||||
|                 self.dump(options["no_progress_bar"]) | ||||
|  | ||||
|                 # We've written everything to the temporary directory in this case, | ||||
|                 # now make an archive in the original target, with all files stored | ||||
|                 if zip_export: | ||||
|                     shutil.make_archive( | ||||
|                         os.path.join( | ||||
|                             original_target, | ||||
|                             f"export-{timezone.localdate().isoformat()}", | ||||
|                         ), | ||||
|                         format="zip", | ||||
|                         root_dir=temp_dir.name, | ||||
|                     ) | ||||
|  | ||||
|         finally: | ||||
|             # Always cleanup the temporary directory, if one was created | ||||
|             if zip_export and temp_dir is not None: | ||||
|                 temp_dir.cleanup() | ||||
|  | ||||
|     def dump(self, progress_bar_disable=False): | ||||
|         # 1. Take a snapshot of what files exist in the current export folder | ||||
|   | ||||
| @@ -5,10 +5,12 @@ import shutil | ||||
| import tempfile | ||||
| from pathlib import Path | ||||
| from unittest import mock | ||||
| from zipfile import ZipFile | ||||
|  | ||||
| from django.core.management import call_command | ||||
| from django.test import override_settings | ||||
| from django.test import TestCase | ||||
| from django.utils import timezone | ||||
| from documents.management.commands import document_exporter | ||||
| from documents.models import Comment | ||||
| from documents.models import Correspondent | ||||
| @@ -365,3 +367,74 @@ class TestExportImport(DirectoriesMixin, TestCase): | ||||
|             mime_type="application/pdf", | ||||
|         ) | ||||
|         self.assertRaises(FileNotFoundError, call_command, "document_exporter", target) | ||||
|  | ||||
|     @override_settings(PASSPHRASE="test") | ||||
|     def test_export_zipped(self): | ||||
|         """ | ||||
|         GIVEN: | ||||
|             - Request to export documents to zipfile | ||||
|         WHEN: | ||||
|             - Documents are exported | ||||
|         THEN: | ||||
|             - Zipfile is created | ||||
|             - Zipfile contains exported files | ||||
|         """ | ||||
|         shutil.rmtree(os.path.join(self.dirs.media_dir, "documents")) | ||||
|         shutil.copytree( | ||||
|             os.path.join(os.path.dirname(__file__), "samples", "documents"), | ||||
|             os.path.join(self.dirs.media_dir, "documents"), | ||||
|         ) | ||||
|  | ||||
|         args = ["document_exporter", self.target, "--zip"] | ||||
|  | ||||
|         call_command(*args) | ||||
|  | ||||
|         expected_file = os.path.join( | ||||
|             self.target, | ||||
|             f"export-{timezone.localdate().isoformat()}.zip", | ||||
|         ) | ||||
|  | ||||
|         self.assertTrue(os.path.isfile(expected_file)) | ||||
|  | ||||
|         with ZipFile(expected_file) as zip: | ||||
|             self.assertEqual(len(zip.namelist()), 11) | ||||
|             self.assertIn("manifest.json", zip.namelist()) | ||||
|             self.assertIn("version.json", zip.namelist()) | ||||
|  | ||||
|     @override_settings(PASSPHRASE="test") | ||||
|     def test_export_zipped_format(self): | ||||
|         """ | ||||
|         GIVEN: | ||||
|             - Request to export documents to zipfile | ||||
|             - Export is following filename formatting | ||||
|         WHEN: | ||||
|             - Documents are exported | ||||
|         THEN: | ||||
|             - Zipfile is created | ||||
|             - Zipfile contains exported files | ||||
|         """ | ||||
|         shutil.rmtree(os.path.join(self.dirs.media_dir, "documents")) | ||||
|         shutil.copytree( | ||||
|             os.path.join(os.path.dirname(__file__), "samples", "documents"), | ||||
|             os.path.join(self.dirs.media_dir, "documents"), | ||||
|         ) | ||||
|  | ||||
|         args = ["document_exporter", self.target, "--zip", "--use-filename-format"] | ||||
|  | ||||
|         with override_settings( | ||||
|             FILENAME_FORMAT="{created_year}/{correspondent}/{title}", | ||||
|         ): | ||||
|             call_command(*args) | ||||
|  | ||||
|         expected_file = os.path.join( | ||||
|             self.target, | ||||
|             f"export-{timezone.localdate().isoformat()}.zip", | ||||
|         ) | ||||
|  | ||||
|         self.assertTrue(os.path.isfile(expected_file)) | ||||
|  | ||||
|         with ZipFile(expected_file) as zip: | ||||
|             # Extras are from the directories, which also appear in the listing | ||||
|             self.assertEqual(len(zip.namelist()), 14) | ||||
|             self.assertIn("manifest.json", zip.namelist()) | ||||
|             self.assertIn("version.json", zip.namelist()) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Michael Shamoon
					Michael Shamoon