Compare commits

..

2 Commits

Author SHA1 Message Date
Trenton Holmes
7d8721c53c (delete me) 2022-04-02 07:14:14 -07:00
Trenton Holmes
aa67c851e3 (delete me) 2022-04-02 07:12:56 -07:00
130 changed files with 1121 additions and 3801 deletions

View File

@@ -1,91 +0,0 @@
name: Bug report
description: Something is not working
title: "[BUG] Concise description of the issue"
labels: ["bug", "unconfirmed"]
body:
- type: markdown
attributes:
value: |
Have a question? 👉 [Start a new discussion](https://github.com/paperless-ngx/paperless-ngx/discussions/new) or [ask in chat](https://matrix.to/#/#paperless:adnidor.de).
Before opening an issue, please check [the documentation](https://paperless-ngx.readthedocs.io/en/latest/troubleshooting.html) and see if it helps you resolve your issue. Please also make sure that you followed the installation instructions.
If you encounter issues while installing or configuring Paperless-ngx, please post in the ["Support" section of the discussions](https://github.com/paperless-ngx/paperless-ngx/discussions/new?category=support). Remember that Paperless successfully runs on a variety of different systems. If Paperless-ngx does not start, it's likely an issue with your system, not an issue of Paperless-ngx.
Finally, please search issues and discussions before opening a new bug report.
- type: textarea
id: description
attributes:
label: Description
description: A clear and concise description of what the bug is.
placeholder: Currently...
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
placeholder: In this situation...
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Steps to reproduce the behavior
placeholder: "1. Go to '...', 2. Click on '....', 3. See error"
validations:
required: true
- type: textarea
id: logs
attributes:
label: Webserver logs
description: If available, post any logs from the web server related to your issue.
render: bash
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your problem.
- type: input
id: version
attributes:
label: Paperless-ngx version
placeholder: e.g. 1.6.0
validations:
required: true
- type: input
id: host-os
attributes:
label: Host OS
description: Host OS of the machine running paperless-ngx
placeholder: e.g. Archlinux / Ubuntu 20.04
validations:
required: true
- type: dropdown
id: install-method
attributes:
label: Installation method
options:
- Docker
- Bare metal
- Other (please describe above)
validations:
required: true
- type: input
id: browser
attributes:
label: Browser
description: Which browser you are using, if relevant
placeholder: e.g. Chrome, Safari
- type: input
id: config-changes
attributes:
label: Configuration changes
description: Any configuration changes you made in `docker-compose.yml`, `docker-compose.env` or `paperless.conf`.
- type: input
id: other
attributes:
label: Other
description: Any other relevant details

50
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,50 @@
---
name: Bug report
about: Something is not working
title: '[BUG] Concise description of the issue'
labels: ''
assignees: ''
---
<!---
=> Before opening an issue, please check the documentation and see if it helps you resolve your issue: https://paperless-ngx.readthedocs.io/en/latest/troubleshooting.html
=> Please also make sure that you followed the installation instructions.
=> Please search the issues and look for similar issues before opening a bug report.
=> If you would like to submit a feature request please submit one under https://github.com/paperless-ngx/paperless-ngx/discussions/categories/feature-requests
=> If you encounter issues while installing of configuring Paperless-ngx, please post that in the "Support" section of the discussions. Remember that Paperless successfully runs on a variety of different systems. If paperless does not start, it's probably an issue with your system, and not an issue of paperless.
=> Don't remove the [BUG] prefix from the title.
-->
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Webserver logs**
```
If available, post any logs from the web server related to your issue.
```
**Relevant information**
- Host OS of the machine running paperless: [e.g. Archlinux / Ubuntu 20.04]
- Browser [e.g. chrome, safari]
- Version [e.g. 1.0.0]
- Installation method: [docker / bare metal]
- Any configuration changes you made in `docker-compose.yml`, `docker-compose.env` or `paperless.conf`.

View File

@@ -1,11 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: 🤔 Questions and Help
url: https://github.com/paperless-ngx/paperless-ngx/discussions
about: This issue tracker is not for support questions. Please refer to our Discussions.
- name: 💬 Chat
url: https://matrix.to/#/#paperless:adnidor.de
about: Want to discuss Paperless-ngx with others? Check out our chat.
- name: 🚀 Feature Request
url: https://github.com/paperless-ngx/paperless-ngx/discussions/new?category=feature-requests
about: Remember to search for existing feature requests and "up-vote" any you like

19
.github/ISSUE_TEMPLATE/other.md vendored Normal file
View File

@@ -0,0 +1,19 @@
---
name: Other
about: Anything that is not a feature request or bug.
title: '[Other] Title of your issue'
labels: ''
assignees: ''
---
<!--
=> Discussions, Feedback and other suggestions belong in the "Discussion" section and not on the issue tracker.
=> If you would like to submit a feature request please submit one under https://github.com/paperless-ngx/paperless-ngx/discussions/categories/feature-requests
=> If you encounter issues while installing of configuring Paperless-ngx, please post that in the "Support" section of the discussions. Remember that Paperless successfully runs on a variety of different systems. If paperless does not start, it's probably is an issue with your system, and not an issue of paperless.
=> Don't remove the [Other] prefix from the title.
-->

View File

@@ -1,15 +1,23 @@
<!--
<!--
Note: All PRs with code changes should be targeted to the `dev` branch, pure documentation changes can target `main`
-->
## Proposed change
<!--
Please include a summary of the change and which issue is fixed (if any) and any relevant motivation / context. List any dependencies that are required for this change. If appropriate, please include an explanation of how your proposed change can be tested. Screenshots and / or videos can also be helpful if appropriate.
Please include a summary of the change and which issue is fixed (if any) and any relevant motivation / context. List any dependencies that are required for this change. If appropriate, please include an explanation of how your poposed change can be tested. Screenshots and / or videos can also be helpful if appropriate.
-->
Fixes # (issue)
<!--
Please also tag the relevant team to help with review. You can tag any of the following:
@paperless-ngx/backend (Python / django, database, etc.)
@paperless-ngx/frontend (JavaScript/Typescript, HTML, CSS, etc.)
@paperless-ngx/ci-cd (GitHub Actions, deployment)
@paperless-ngx/test (General testing for larger PRs)
-->
## Type of change
<!--

View File

@@ -1,37 +0,0 @@
categories:
- title: 'Breaking Changes'
labels:
- 'breaking-change'
- title: 'Features'
labels:
- 'enhancement'
- title: 'Bug Fixes'
labels:
- 'bug'
- title: 'Documentation'
label: 'documentation'
- title: 'Maintenance'
labels:
- 'chore'
- 'deployment'
- 'translation'
- title: 'Dependencies'
collapse-after: 3
label: 'dependencies'
include-labels:
- 'enhancement'
- 'bug'
- 'chore'
- 'deployment'
- 'translation'
- 'dependencies'
replacers: # Changes "Feature: Update checker" to "Update checker"
- search: '/Feature:|Feat:|\[feature\]/gi'
replace: ''
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
change-title-escapes: '\<*_&#@'
tag-prefix: "ngx-"
template: |
# Changelog
$CHANGES

7
.github/stale.yml vendored
View File

@@ -2,8 +2,11 @@
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
onlyLabels:
- unconfirmed
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- fixpending
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable

View File

@@ -132,7 +132,7 @@ jobs:
name: Install system dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript optipng libzbar0 poppler-utils
sudo apt-get install -qq --no-install-recommends unpaper tesseract-ocr imagemagick ghostscript optipng
-
name: Install Python dependencies
run: |
@@ -185,9 +185,6 @@ jobs:
# build and push image to docker hub.
build-docker-image:
if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/beta' || startsWith(github.ref, 'refs/tags/ngx-') || startsWith(github.ref, 'refs/tags/beta-'))
concurrency:
group: ${{ github.workflow }}-build-docker-image-${{ github.ref }}
cancel-in-progress: true
runs-on: ubuntu-20.04
needs: [tests-backend, tests-frontend]
steps:
@@ -198,8 +195,10 @@ jobs:
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=match,pattern=ngx-(\d.\d.\d),group=1
type=semver,pattern=ngx-{{version}}
type=semver,pattern=ngx-{{major}}.{{minor}}
type=ref,event=branch
type=ref,event=tag
-
name: Checkout
uses: actions/checkout@v3
@@ -329,22 +328,24 @@ jobs:
if [[ $GITHUB_REF == refs/tags/ngx-* ]]; then
echo ::set-output name=version::${GITHUB_REF#refs/tags/ngx-}
echo ::set-output name=prerelease::false
echo ::set-output name=body::"For a complete list of changes, see the changelog at https://paperless-ngx.readthedocs.io/en/latest/changelog.html"
elif [[ $GITHUB_REF == refs/tags/beta-* ]]; then
echo ::set-output name=version::${GITHUB_REF#refs/tags/beta-}
echo ::set-output name=prerelease::true
echo ::set-output name=body::"For a complete list of changes, see the changelog at https://github.com/paperless-ngx/paperless-ngx/blob/beta/docs/changelog.rst"
fi
-
name: Create Release and Changelog
id: create-release
uses: release-drafter/release-drafter@v5
with:
name: Paperless-ngx ${{ steps.get_version.outputs.version }}
tag: ngx-${{ steps.get_version.outputs.version }}
version: ${{ steps.get_version.outputs.version }}
prerelease: ${{ steps.get_version.outputs.prerelease }}
publish: true # ensures release is not marked as draft
name: Create release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ngx-${{ steps.get_version.outputs.version }}
release_name: Paperless-ngx ${{ steps.get_version.outputs.version }}
draft: false
prerelease: ${{ steps.get_version.outputs.prerelease }}
body: ${{ steps.get_version.outputs.body }}
-
name: Upload release archive
id: upload-release-asset
@@ -352,7 +353,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create-release.outputs.upload_url }}
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./paperless-ngx.tar.xz
asset_name: paperless-ngx-${{ steps.get_version.outputs.version }}.tar.xz
asset_content_type: application/x-xz

View File

@@ -5,7 +5,7 @@
repos:
# General hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
rev: v4.1.0
hooks:
- id: check-docstring-first
- id: check-json
@@ -27,7 +27,7 @@ repos:
- id: check-case-conflict
- id: detect-private-key
- repo: https://github.com/pre-commit/mirrors-prettier
rev: "v2.6.2"
rev: "v2.6.1"
hooks:
- id: prettier
types_or:
@@ -47,7 +47,7 @@ repos:
- id: yesqa
exclude: "(migrations)"
- repo: https://github.com/asottile/add-trailing-comma
rev: "v2.2.2"
rev: "v2.2.1"
hooks:
- id: add-trailing-comma
exclude: "(migrations)"

View File

@@ -1,10 +0,0 @@
/.github/workflows/ @paperless-ngx/ci-cd
/docker/ @paperless-ngx/ci-cd
/scripts/ @paperless-ngx/ci-cd
/src-ui/ @paperless-ngx/frontend
/src/ @paperless-ngx/backend
Pipfile* @paperless-ngx/backend
*.py @paperless-ngx/backend
requirements.txt @paperless-ngx/backend

View File

@@ -6,7 +6,7 @@ WORKDIR /src/src-ui
RUN npm update npm -g && npm ci --no-optional
RUN ./node_modules/.bin/ng build --configuration production
FROM ghcr.io/paperless-ngx/builder/ngx-base:1.7.0 as main-app
FROM ghcr.io/paperless-ngx/builder/ngx-base:1.0 as main-app
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
LABEL org.opencontainers.image.documentation="https://paperless-ngx.readthedocs.io/en/latest/"

View File

@@ -22,7 +22,7 @@ gunicorn = "*"
imap-tools = "*"
langdetect = "*"
pathvalidate = "*"
pillow = "~=9.1"
pillow = "~=9.0"
# Any version update to pikepdf requires a base image update
pikepdf = "~=5.1"
python-gnupg = "*"
@@ -51,8 +51,6 @@ concurrent-log-handler = "*"
"backports.zoneinfo" = {version = "*", markers = "python_version < '3.9'"}
"importlib-resources" = {version = "*", markers = "python_version < '3.9'"}
zipp = {version = "*", markers = "python_version < '3.9'"}
pyzbar = "*"
pdf2image = "*"
[dev-packages]
coveralls = "*"
@@ -64,7 +62,7 @@ pytest-django = "*"
pytest-env = "*"
pytest-sugar = "*"
pytest-xdist = "*"
sphinx = "~=4.5.0"
sphinx = "~=4.4.0"
sphinx_rtd_theme = "*"
tox = "*"
black = "*"

346
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "9573af313c811561d467d814c52c6bd1439bc48e3b31d7f56afed5f0ebe4b648"
"sha256": "f33c0aeb0cb880b444efd367ad5b47358dde2a9b18b55dcd6943dd81f6c7e2a6"
},
"pipfile-spec": 6,
"requires": {},
@@ -68,10 +68,10 @@
},
"autobahn": {
"hashes": [
"sha256:58a887c7a196bb08d8b6624cb3695f493a9e5c9f00fd350d8d6f829b47ff9036"
"sha256:60e1f4c602aacd052ffe3d46ae40b6b75f8286b3c46922c213b523162e58c17e"
],
"markers": "python_version >= '3.7'",
"version": "==22.3.2"
"version": "==22.2.2"
},
"automat": {
"hashes": [
@@ -99,6 +99,7 @@
"sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac",
"sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"
],
"index": "pypi",
"markers": "python_version < '3.9'",
"version": "==0.2.1"
},
@@ -206,11 +207,11 @@
},
"click": {
"hashes": [
"sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e",
"sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72"
"sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1",
"sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"
],
"markers": "python_version >= '3.7'",
"version": "==8.1.2"
"markers": "python_version >= '3.6'",
"version": "==8.0.4"
},
"coloredlogs": {
"hashes": [
@@ -279,11 +280,11 @@
},
"django": {
"hashes": [
"sha256:07c8638e7a7f548dc0acaaa7825d84b7bd42b10e8d22268b3d572946f1e9b687",
"sha256:4e8177858524417563cc0430f29ea249946d831eacb0068a1455686587df40b5"
"sha256:1239218849e922033a35d2a2f777cb8bee18bd725416744074f455f34ff50d0c",
"sha256:77ff2e7050e3324c9b67e29b6707754566f58514112a9ac73310f60cd5261930"
],
"index": "pypi",
"version": "==4.0.4"
"version": "==4.0.3"
},
"django-cors-headers": {
"hashes": [
@@ -487,15 +488,16 @@
},
"img2pdf": {
"hashes": [
"sha256:8ec898a9646523fd3862b154f3f47cd52609c24cc3e2dc1fb5f0168f0cbe793c"
"sha256:8e51c5043efa95d751481b516071a006f87c2a4059961a9ac43ec238915de09f"
],
"version": "==0.4.4"
"version": "==0.4.3"
},
"importlib-resources": {
"hashes": [
"sha256:1b93238cbf23b4cde34240dd8321d99e9bf2eb4bc91c0c99b2886283e7baad85",
"sha256:a9dd72f6cc106aeb50f6e66b86b69b454766dd6e39b69ac68450253058706bcc"
],
"index": "pypi",
"markers": "python_version < '3.9'",
"version": "==5.6.0"
},
@@ -671,11 +673,11 @@
},
"ocrmypdf": {
"hashes": [
"sha256:7f0a6165b80ba1b37ce5943cf5b4faf93bf98c04c8f5157ef83c5f292491485f",
"sha256:d52410bc38cf5b66da27668e38c66ac41fd3136457c1ec388b311f0a78ee213c"
"sha256:201ed2f589f851be73908fce35fbb6fb05e4739289d3cd8765f9519f49ea1cd9",
"sha256:f42e60bc2b6534634dd08928584275b1c556dc875c849650afcc38f7da9e2856"
],
"index": "pypi",
"version": "==13.4.2"
"version": "==13.4.1"
},
"packaging": {
"hashes": [
@@ -693,98 +695,87 @@
"index": "pypi",
"version": "==2.5.0"
},
"pdf2image": {
"hashes": [
"sha256:84f79f2b8fad943e36323ea4e937fcb05f26ded0caa0a01181df66049e42fb65",
"sha256:d58ed94d978a70c73c2bb7fdf8acbaf2a7089c29ff8141be5f45433c0c4293bb"
],
"index": "pypi",
"version": "==1.16.0"
},
"pdfminer.six": {
"hashes": [
"sha256:af0630f98a292bad4170f54e80f82ca81b916dd0b2c996437ec45c02f11d8762",
"sha256:eff2ce0abeaa4df94dc3461f70eab104487c7b4a2b3c7e9fd0aeec6c5f44d6a6"
"sha256:0351f17d362ee2d48b158be52bcde6576d96460efd038a3e89a043fba6d634d7",
"sha256:d3efb75c0249b51c1bf795e3a8bddf1726b276c77bf75fb136adea471ee2825b"
],
"index": "pypi",
"version": "==20220319"
"version": "==20211012"
},
"pikepdf": {
"hashes": [
"sha256:01be838a44430c4be84b748a33950fed09892472934a8041596c11189f365f7f",
"sha256:0cc95ef470169dfa5acc9196299bdba236716234a0d8b2746e2a563bc6f1f456",
"sha256:13e72d0aeeb3fc452569a3f7994acdd007de9aad804ced734d57cec269261b8b",
"sha256:2873503522ef26a09a6020c29c2efd221fa2ddc31e83bd902be27d317144cf63",
"sha256:2d5d6d3248b33ca5961d84bc3121a299cd27237fad56868d815e381c9a98d3d1",
"sha256:2f62e6c7bcf5d631e6ea74cf861f3e816f587c6ccb4ecbf6ac862e088ba2e4ac",
"sha256:51694d3d2f90510da6a8d7a4d07313ca868b373fffec6de270d9bbff1ce37180",
"sha256:5c23cbd7ae71f08fb5b5d9660eb0bc61abf345ada01bea6e1b6884c4261e17d6",
"sha256:6371bf02a436be2b7c63322b83a8e47523f2cd16438b2e93d546c7caf9ae308d",
"sha256:657293b74af8c7cf03f9905218a7935b26a4f3006803016b40b3db78e04cb35c",
"sha256:680d47377bb9fd6a36b6a81464ee269b4b29cbf29a84ae4f2ab8f6ea3665bf69",
"sha256:710535c679ab0d7b8249f72247832773e7a9a121dfbe9cad7f6465bd9bb45fae",
"sha256:7b4d7c09036d863915cb01007ca183d6fe64e2d57c0472453097bc9e029a58fb",
"sha256:978b6388ae99a024bdcae5a322c68e90c187cb568d09d43e6586b3479267121d",
"sha256:9917a03d500aab72715a9236136af7a5c8c7b26c034bf71ebdf028e177f0d25f",
"sha256:996faa6b119488f96d7271672a22af86e56e5544ec6b8eae6cd7d4432c70ae2d",
"sha256:9bac9e9d6b28dc0cc5a554051f183fbd070d0f9fe63c4e9aca939b8c44a5bb4d",
"sha256:aac14061de06843759ea6f5777fd8d7b71af808ed9264f57483a3311a09788ab",
"sha256:ad5361c3669fc0c8dbaf8fa0a590bddf59fad256bb2c527d5ce5cf991743a240",
"sha256:bc40b30c37f8f7c5bef873eca1f04e91ce34b6b74507d8d0019238a17d281fdc",
"sha256:bd9faae19787a5d05b9fcbe84d7cfe4d44e318068e06eca18906b9dba45425b6",
"sha256:c64e7905ec438b7a6c12626f2859df87f471892fab75b65b1441d9e1b38b4dde",
"sha256:d4db409b21a8ec0d3a79d2bbd894b997b13223c9ccf341cdc31b64360f1ee4c7",
"sha256:e0b635d6d9faefb4d0d32722279b8eb4e4d5d7b596c426f3433343de65e0c772",
"sha256:e62e9e8afe77fe2f06715faf10f38a4810d282d66f1e9e05208bb8d9723e6acf",
"sha256:f85d309bcfeeb3e2d344346a5050bfc41e332f19d390f79c20e4fc7de4b10a17",
"sha256:fe3fc2efe498aba6204b85c17c6a5d54ab7303354ecc5c3da624a6b6af0b3406"
"sha256:1567b74d15c16d1bef56a6d5f56fb6a35f4cc022ae252d35f33f56bb16b87966",
"sha256:179f24b4a4d0e89c7f592d85ba4daf7d34c709eb0a691425b881413fdce70734",
"sha256:22201b06db627a86cc91c1b76491dd9c57fce2df4e3bc8ba700ff66f7f7da04a",
"sha256:3e1a0b9ecf5d4aa106c3c0db558952f9a15f343f812c3bba6d6e1a56e25224ed",
"sha256:45ce2479e5ba74896ef389abf92831d7fbb34f25e6557adb9115710223a0bb13",
"sha256:4fa5c8494b011b19bd198dd9c3cd94676b905360b2231ad35171ae586644b823",
"sha256:559b3d502cc1a6813cbcb0766b0797fec034303f8f9b0734cf938fb1734e2b74",
"sha256:61731fceaab99850bc7045232301c2332bba727f78b53f7038fcbdcaf3d64309",
"sha256:6460d489341e7f8dc3f6b0dbf1f5a75a918ebd1e0ecb4c2b00877264a68ee1f4",
"sha256:716ca6fc8947502cb73a517c884066afa132ca998e085a309b8fb8c5796d6277",
"sha256:726450eb9baaad5697687c2621d481c80f868b68c06d2cff4be3f6a7ce28cfee",
"sha256:7e9c247ca384ad1606281eda4d841bc8cbff90875979ac3b520bcc5404bf9b26",
"sha256:8333d813b452daa4a066e135fd7ab6f7c07ccc02cb8381455d61d74f0a0ad0fe",
"sha256:83d0af374b103934de033f096205143fa9d6f78e789ba78c8aa6dfb0e5b73bc3",
"sha256:878c1c95298486d8cea7e8236c70613e7eae1426cbf362c3883ecd06e8f9c2d9",
"sha256:9abef24d929c4a08292dc4be4d6c4e5bf93832e747eef5f39e854348a332f46a",
"sha256:a474241dbeda246356b6448f607f4fb9fee5b9f5cb19511a768b88b471325865",
"sha256:a553fd06e5f6e78c5e840d066e7c8b1a988e16489fe0bd4a143ca601809ea4dd",
"sha256:a6154c6bb7606ef534444f54271a410a6337cee54dcdef20f0fa0686f622cf50",
"sha256:ac0082379cf6aa6c0c682bee4a3d2adbe6d60b9125e3632876d7c5e9665c07ab",
"sha256:c22e3fbfb76ad7838dde82c8d9fafd4c09fd419cee531b31d1b48a07344ac2b4",
"sha256:d42c52ff2e8fb00fad14182f67a8f076f38c75a874123b6776aa5c6af09e1126",
"sha256:d6ef14b722f80351e15c9163e0aa1f1df84f065c080765f8232ec51af6bd3368",
"sha256:d71a38445c80972572248815ebe61f9c814c53925a6b83b6596f3482a98a5f25",
"sha256:e81ebbdd53257f411827bbb301900cdbdca34ca60b4f7248f80c6e6980062498",
"sha256:f52f4ed655c25c408e8454cbcf0e7223b62f635d1e8ab738fd0c2d46531d28ed",
"sha256:fb407ae5820ee0bf71022d5a8f539d709dac590443270a13baf6a8872c76d46f"
],
"index": "pypi",
"version": "==5.1.1"
"version": "==5.1.0"
},
"pillow": {
"hashes": [
"sha256:01ce45deec9df310cbbee11104bae1a2a43308dd9c317f99235b6d3080ddd66e",
"sha256:0c51cb9edac8a5abd069fd0758ac0a8bfe52c261ee0e330f363548aca6893595",
"sha256:17869489de2fce6c36690a0c721bd3db176194af5f39249c1ac56d0bb0fcc512",
"sha256:21dee8466b42912335151d24c1665fcf44dc2ee47e021d233a40c3ca5adae59c",
"sha256:25023a6209a4d7c42154073144608c9a71d3512b648a2f5d4465182cb93d3477",
"sha256:255c9d69754a4c90b0ee484967fc8818c7ff8311c6dddcc43a4340e10cd1636a",
"sha256:35be4a9f65441d9982240e6966c1eaa1c654c4e5e931eaf580130409e31804d4",
"sha256:3f42364485bfdab19c1373b5cd62f7c5ab7cc052e19644862ec8f15bb8af289e",
"sha256:3fddcdb619ba04491e8f771636583a7cc5a5051cd193ff1aa1ee8616d2a692c5",
"sha256:463acf531f5d0925ca55904fa668bb3461c3ef6bc779e1d6d8a488092bdee378",
"sha256:4fe29a070de394e449fd88ebe1624d1e2d7ddeed4c12e0b31624561b58948d9a",
"sha256:55dd1cf09a1fd7c7b78425967aacae9b0d70125f7d3ab973fadc7b5abc3de652",
"sha256:5a3ecc026ea0e14d0ad7cd990ea7f48bfcb3eb4271034657dc9d06933c6629a7",
"sha256:5cfca31ab4c13552a0f354c87fbd7f162a4fafd25e6b521bba93a57fe6a3700a",
"sha256:66822d01e82506a19407d1afc104c3fcea3b81d5eb11485e593ad6b8492f995a",
"sha256:69e5ddc609230d4408277af135c5b5c8fe7a54b2bdb8ad7c5100b86b3aab04c6",
"sha256:6b6d4050b208c8ff886fd3db6690bf04f9a48749d78b41b7a5bf24c236ab0165",
"sha256:7a053bd4d65a3294b153bdd7724dce864a1d548416a5ef61f6d03bf149205160",
"sha256:82283af99c1c3a5ba1da44c67296d5aad19f11c535b551a5ae55328a317ce331",
"sha256:8782189c796eff29dbb37dd87afa4ad4d40fc90b2742704f94812851b725964b",
"sha256:8d79c6f468215d1a8415aa53d9868a6b40c4682165b8cb62a221b1baa47db458",
"sha256:97bda660702a856c2c9e12ec26fc6d187631ddfd896ff685814ab21ef0597033",
"sha256:a325ac71914c5c043fa50441b36606e64a10cd262de12f7a179620f579752ff8",
"sha256:a336a4f74baf67e26f3acc4d61c913e378e931817cd1e2ef4dfb79d3e051b481",
"sha256:a598d8830f6ef5501002ae85c7dbfcd9c27cc4efc02a1989369303ba85573e58",
"sha256:a5eaf3b42df2bcda61c53a742ee2c6e63f777d0e085bbc6b2ab7ed57deb13db7",
"sha256:aea7ce61328e15943d7b9eaca87e81f7c62ff90f669116f857262e9da4057ba3",
"sha256:af79d3fde1fc2e33561166d62e3b63f0cc3e47b5a3a2e5fea40d4917754734ea",
"sha256:c24f718f9dd73bb2b31a6201e6db5ea4a61fdd1d1c200f43ee585fc6dcd21b34",
"sha256:c5b0ff59785d93b3437c3703e3c64c178aabada51dea2a7f2c5eccf1bcf565a3",
"sha256:c7110ec1701b0bf8df569a7592a196c9d07c764a0a74f65471ea56816f10e2c8",
"sha256:c870193cce4b76713a2b29be5d8327c8ccbe0d4a49bc22968aa1e680930f5581",
"sha256:c9efef876c21788366ea1f50ecb39d5d6f65febe25ad1d4c0b8dff98843ac244",
"sha256:de344bcf6e2463bb25179d74d6e7989e375f906bcec8cb86edb8b12acbc7dfef",
"sha256:eb1b89b11256b5b6cad5e7593f9061ac4624f7651f7a8eb4dfa37caa1dfaa4d0",
"sha256:ed742214068efa95e9844c2d9129e209ed63f61baa4d54dbf4cf8b5e2d30ccf2",
"sha256:f401ed2bbb155e1ade150ccc63db1a4f6c1909d3d378f7d1235a44e90d75fb97",
"sha256:fb89397013cf302f282f0fc998bb7abf11d49dcff72c8ecb320f76ea6e2c5717"
"sha256:011233e0c42a4a7836498e98c1acf5e744c96a67dd5032a6f666cc1fb97eab97",
"sha256:0f29d831e2151e0b7b39981756d201f7108d3d215896212ffe2e992d06bfe049",
"sha256:12875d118f21cf35604176872447cdb57b07126750a33748bac15e77f90f1f9c",
"sha256:14d4b1341ac07ae07eb2cc682f459bec932a380c3b122f5540432d8977e64eae",
"sha256:1c3c33ac69cf059bbb9d1a71eeaba76781b450bc307e2291f8a4764d779a6b28",
"sha256:1d19397351f73a88904ad1aee421e800fe4bbcd1aeee6435fb62d0a05ccd1030",
"sha256:253e8a302a96df6927310a9d44e6103055e8fb96a6822f8b7f514bb7ef77de56",
"sha256:2632d0f846b7c7600edf53c48f8f9f1e13e62f66a6dbc15191029d950bfed976",
"sha256:335ace1a22325395c4ea88e00ba3dc89ca029bd66bd5a3c382d53e44f0ccd77e",
"sha256:413ce0bbf9fc6278b2d63309dfeefe452835e1c78398efb431bab0672fe9274e",
"sha256:5100b45a4638e3c00e4d2320d3193bdabb2d75e79793af7c3eb139e4f569f16f",
"sha256:514ceac913076feefbeaf89771fd6febde78b0c4c1b23aaeab082c41c694e81b",
"sha256:528a2a692c65dd5cafc130de286030af251d2ee0483a5bf50c9348aefe834e8a",
"sha256:6295f6763749b89c994fcb6d8a7f7ce03c3992e695f89f00b741b4580b199b7e",
"sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa",
"sha256:718856856ba31f14f13ba885ff13874be7fefc53984d2832458f12c38205f7f7",
"sha256:7f7609a718b177bf171ac93cea9fd2ddc0e03e84d8fa4e887bdfc39671d46b00",
"sha256:80ca33961ced9c63358056bd08403ff866512038883e74f3a4bf88ad3eb66838",
"sha256:80fe64a6deb6fcfdf7b8386f2cf216d329be6f2781f7d90304351811fb591360",
"sha256:81c4b81611e3a3cb30e59b0cf05b888c675f97e3adb2c8672c3154047980726b",
"sha256:855c583f268edde09474b081e3ddcd5cf3b20c12f26e0d434e1386cc5d318e7a",
"sha256:9bfdb82cdfeccec50aad441afc332faf8606dfa5e8efd18a6692b5d6e79f00fd",
"sha256:a5d24e1d674dd9d72c66ad3ea9131322819ff86250b30dc5821cbafcfa0b96b4",
"sha256:a9f44cd7e162ac6191491d7249cceb02b8116b0f7e847ee33f739d7cb1ea1f70",
"sha256:b5b3f092fe345c03bca1e0b687dfbb39364b21ebb8ba90e3fa707374b7915204",
"sha256:b9618823bd237c0d2575283f2939655f54d51b4527ec3972907a927acbcc5bfc",
"sha256:cef9c85ccbe9bee00909758936ea841ef12035296c748aaceee535969e27d31b",
"sha256:d21237d0cd37acded35154e29aec853e945950321dd2ffd1a7d86fe686814669",
"sha256:d3c5c79ab7dfce6d88f1ba639b77e77a17ea33a01b07b99840d6ed08031cb2a7",
"sha256:d9d7942b624b04b895cb95af03a23407f17646815495ce4547f0e60e0b06f58e",
"sha256:db6d9fac65bd08cea7f3540b899977c6dee9edad959fa4eaf305940d9cbd861c",
"sha256:ede5af4a2702444a832a800b8eb7f0a7a1c0eed55b644642e049c98d589e5092",
"sha256:effb7749713d5317478bb3acb3f81d9d7c7f86726d41c1facca068a04cf5bb4c",
"sha256:f154d173286a5d1863637a7dcd8c3437bb557520b01bddb0be0258dcb72696b5",
"sha256:f25ed6e28ddf50de7e7ea99d7a976d6a9c415f03adcaac9c41ff6ff41b6d86ac"
],
"index": "pypi",
"version": "==9.1.0"
"version": "==9.0.1"
},
"pluggy": {
"hashes": [
@@ -871,11 +862,11 @@
},
"pyparsing": {
"hashes": [
"sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954",
"sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"
"sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea",
"sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"
],
"markers": "python_full_version >= '3.6.8'",
"version": "==3.0.8"
"markers": "python_version >= '3.6'",
"version": "==3.0.7"
},
"python-dateutil": {
"hashes": [
@@ -968,15 +959,6 @@
],
"version": "==6.0"
},
"pyzbar": {
"hashes": [
"sha256:13e3ee5a2f3a545204a285f41814d5c0db571967e8d4af8699a03afc55182a9c",
"sha256:4559628b8192feb25766d954b36a3753baaf5c97c03135aec7e4a026036b475d",
"sha256:8f4c5264c9c7c6b9f20d01efc52a4eba1ded47d9ba857a94130afe33703eb518"
],
"index": "pypi",
"version": "==0.1.9"
},
"redis": {
"hashes": [
"sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2",
@@ -1191,11 +1173,11 @@
},
"setuptools": {
"hashes": [
"sha256:26ead7d1f93efc0f8c804d9fafafbe4a44b179580a7105754b245155f9af05a8",
"sha256:47c7b0c0f8fc10eec4cf1e71c6fdadf8decaa74ffa087e68cd1c20db7ad6a592"
"sha256:89eef7b71423ab7fccc7dfafdc145410ef170c4a89567427f932448135e08cdf",
"sha256:92b15f45ab164eb0c410d2bf661a6e9d15e3b78c0dffb0325f2bf0f313071cae"
],
"markers": "python_version >= '3.7'",
"version": "==62.1.0"
"version": "==61.1.1"
},
"six": {
"hashes": [
@@ -1238,22 +1220,22 @@
},
"tqdm": {
"hashes": [
"sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d",
"sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"
"sha256:4230a49119a416c88cc47d0d2d32d5d90f1a282d5e497d49801950704e49863d",
"sha256:6461b009d6792008d0000e1b0c7ca50195ec78c0e808a3a6b668a56a3236c3a5"
],
"index": "pypi",
"version": "==4.64.0"
"version": "==4.63.1"
},
"twisted": {
"extras": [
"tls"
],
"hashes": [
"sha256:a047990f57dfae1e0bd2b7df2526d4f16dcdc843774dc108b78c52f2a5f13680",
"sha256:f9f7a91f94932477a9fc3b169d57f54f96c6e74a23d78d9ce54039a7f48928a2"
"sha256:57f32b1f6838facb8c004c89467840367ad38e9e535f8252091345dba500b4f2",
"sha256:5c63c149eb6b8fe1e32a0215b1cef96fabdba04f705d8efb9174b1ccf5b49d49"
],
"markers": "python_full_version >= '3.6.7'",
"version": "==22.4.0"
"version": "==22.2.0"
},
"txaio": {
"hashes": [
@@ -1281,11 +1263,11 @@
},
"tzlocal": {
"hashes": [
"sha256:89885494684c929d9191c57aa27502afc87a579be5cdd3225c77c463ea043745",
"sha256:ee5842fa3a795f023514ac2d801c4a81d1743bbe642e3940143326b3a00addd7"
"sha256:0f28015ac68a5c067210400a9197fc5d36ba9bc3f8eaf1da3cbd59acdfed9e09",
"sha256:28ba8d9fcb6c9a782d6e0078b4f6627af1ea26aeaa32b4eab5324abc7df4149f"
],
"markers": "python_version >= '3.6'",
"version": "==4.2"
"version": "==4.1"
},
"urllib3": {
"hashes": [
@@ -1359,10 +1341,10 @@
},
"watchgod": {
"hashes": [
"sha256:2f3e8137d98f493ff58af54ea00f4d1433a6afe2ed08ab331a657df468c6bfce",
"sha256:cb11ff66657befba94d828e3b622d5fb76f22fbda1376f355f3e6e51e97d9450"
"sha256:4ba20c2fa3e63df706ab50e694b9453b05395fadb7cbbfd984d71fb1547d485d",
"sha256:c12d15f3df7d11e740704e45398277f75f1d78f46ad59ca9d7505bfd8b8d3086"
],
"version": "==0.8.2"
"version": "==0.8.1"
},
"wcwidth": {
"hashes": [
@@ -1443,11 +1425,12 @@
},
"zipp": {
"hashes": [
"sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad",
"sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"
"sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d",
"sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"
],
"index": "pypi",
"markers": "python_version < '3.9'",
"version": "==3.8.0"
"version": "==3.7.0"
},
"zope.interface": {
"hashes": [
@@ -1533,32 +1516,32 @@
},
"black": {
"hashes": [
"sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b",
"sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176",
"sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09",
"sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a",
"sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015",
"sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79",
"sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb",
"sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20",
"sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464",
"sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968",
"sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82",
"sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21",
"sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0",
"sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265",
"sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b",
"sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a",
"sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72",
"sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce",
"sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0",
"sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a",
"sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163",
"sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad",
"sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"
"sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2",
"sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71",
"sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6",
"sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5",
"sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912",
"sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866",
"sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d",
"sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0",
"sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321",
"sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8",
"sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd",
"sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3",
"sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba",
"sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0",
"sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5",
"sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a",
"sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28",
"sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c",
"sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1",
"sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab",
"sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f",
"sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61",
"sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"
],
"index": "pypi",
"version": "==22.3.0"
"version": "==22.1.0"
},
"certifi": {
"hashes": [
@@ -1585,15 +1568,15 @@
},
"click": {
"hashes": [
"sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e",
"sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72"
"sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1",
"sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"
],
"markers": "python_version >= '3.7'",
"version": "==8.1.2"
"markers": "python_version >= '3.6'",
"version": "==8.0.4"
},
"coverage": {
"extras": [
"toml"
],
"hashes": [
"sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9",
@@ -1688,11 +1671,11 @@
},
"faker": {
"hashes": [
"sha256:188961065fb5c78ea639f42176f55100f72c90c3a3179ac6c955c4bd712b0511",
"sha256:7758ece2593ce603db117db3d27393c31f4af03f783e176f3f0e14839a4f3426"
"sha256:5536ceb63380f0d598c026b7c330c17d719a19d1a495e9397ee8f5259420a696",
"sha256:85ed0cb379e3b7414bdb6b484994beaf9bddc74472c8c35f743c16cf5fc0c314"
],
"markers": "python_version >= '3.6'",
"version": "==13.3.4"
"version": "==13.3.3"
},
"filelock": {
"hashes": [
@@ -1726,6 +1709,14 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.3.0"
},
"importlib-metadata": {
"hashes": [
"sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6",
"sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"
],
"markers": "python_version < '3.10'",
"version": "==4.11.3"
},
"iniconfig": {
"hashes": [
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
@@ -1834,11 +1825,11 @@
},
"pre-commit": {
"hashes": [
"sha256:02226e69564ebca1a070bd1f046af866aa1c318dbc430027c50ab832ed2b73f2",
"sha256:5d445ee1fa8738d506881c5d84f83c62bb5be6b2838e32207433647e8e5ebe10"
"sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616",
"sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"
],
"index": "pypi",
"version": "==2.18.1"
"version": "==2.17.0"
},
"py": {
"hashes": [
@@ -1866,11 +1857,11 @@
},
"pyparsing": {
"hashes": [
"sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954",
"sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"
"sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea",
"sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"
],
"markers": "python_full_version >= '3.6.8'",
"version": "==3.0.8"
"markers": "python_version >= '3.6'",
"version": "==3.0.7"
},
"pytest": {
"hashes": [
@@ -2004,11 +1995,11 @@
},
"sphinx": {
"hashes": [
"sha256:7bf8ca9637a4ee15af412d1a1d9689fec70523a68ca9bb9127c2f3eeb344e2e6",
"sha256:ebf612653238bcc8f4359627a9b7ce44ede6fdd75d9d30f68255c7383d3a6226"
"sha256:5da895959511473857b6d0200f56865ed62c31e8f82dd338063b84ec022701fe",
"sha256:6caad9786055cb1fa22b4a365c1775816b876f91966481765d7d50e9f0dd35cc"
],
"index": "pypi",
"version": "==4.5.0"
"version": "==4.4.0"
},
"sphinx-rtd-theme": {
"hashes": [
@@ -2096,6 +2087,14 @@
"index": "pypi",
"version": "==3.24.5"
},
"typing-extensions": {
"hashes": [
"sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42",
"sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"
],
"markers": "python_version >= '3.6'",
"version": "==4.1.1"
},
"urllib3": {
"hashes": [
"sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
@@ -2106,11 +2105,20 @@
},
"virtualenv": {
"hashes": [
"sha256:e617f16e25b42eb4f6e74096b9c9e37713cf10bf30168fb4a739f3fa8f898a3a",
"sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"
"sha256:1e8588f35e8b42c6ec6841a13c5e88239de1e6e4e4cedfd3916b306dc826ec66",
"sha256:8e5b402037287126e81ccde9432b95a8be5b19d36584f64957060a3488c11ca8"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==20.14.1"
"version": "==20.14.0"
},
"zipp": {
"hashes": [
"sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d",
"sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"
],
"index": "pypi",
"markers": "python_version < '3.9'",
"version": "==3.7.0"
}
}
}

View File

@@ -22,10 +22,6 @@
# Docker setup does not use the configuration file.
# A few commonly adjusted settings are provided below.
# This is required if you will be exposing Paperless-ngx on a public domain
# (if doing so please consider security measures such as reverse proxy)
#PAPERLESS_URL=https://paperless.example.com
# Adjust this key if you plan to make paperless available publicly. It should
# be a very long sequence of random characters. You don't need to remember it.
#PAPERLESS_SECRET_KEY=change-me

View File

@@ -69,11 +69,10 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg:
image: gotenberg/gotenberg:7
image: thecodingmachine/gotenberg
restart: unless-stopped
command:
- "gotenberg"
- "--chromium-disable-routes=true"
environment:
DISABLE_GOOGLE_CHROME: 1
tika:
image: iwishiwasaneagle/apache-tika-arm@sha256:a78c25ffe57ecb1a194b2859d42a61af46e9e845191512b8f1a4bf90578ffdfd

View File

@@ -1,592 +0,0 @@
/* Variables */
:root {
--color-text-body: #5c5962;
--color-text-body-light: #fcfcfc;
--color-text-anchor: #7253ed;
--color-text-alt: rgba(0, 0, 0, 0.3);
--color-text-title: #27262b;
--color-text-code-inline: #e74c3c;
--color-text-code-nt: #062873;
--color-text-selection: #b19eff;
--color-bg-body: #fcfcfc;
--color-bg-body-alt: #f3f6f6;
--color-bg-side-nav: #f5f6fa;
--color-bg-side-nav-hover: #ebedf5;
--color-bg-code-block: var(--color-bg-side-nav);
--color-border: #eeebee;
--color-btn-neutral-bg: #f3f6f6;
--color-btn-neutral-bg-hover: #e5ebeb;
--color-success-title: #1abc9c;
--color-success-body: #dbfaf4;
--color-warning-title: #f0b37e;
--color-warning-body: #ffedcc;
--color-danger-title: #f29f97;
--color-danger-body: #fdf3f2;
--color-info-title: #6ab0de;
--color-info-body: #e7f2fa;
}
.dark-mode {
--color-text-body: #abb2bf;
--color-text-body-light: #9499a2;
--color-text-alt: rgba(0255, 255, 255, 0.5);
--color-text-title: var(--color-text-anchor);
--color-text-code-inline: #abb2bf;
--color-text-code-nt: #2063f3;
--color-text-selection: #030303;
--color-bg-body: #1d1d20 !important;
--color-bg-body-alt: #131315;
--color-bg-side-nav: #18181a;
--color-bg-side-nav-hover: #101216;
--color-bg-code-block: #101216;
--color-border: #47494f;
--color-btn-neutral-bg: #242529;
--color-btn-neutral-bg-hover: #101216;
--color-success-title: #02120f;
--color-success-body: #041b17;
--color-warning-title: #1b0e03;
--color-warning-body: #371d06;
--color-danger-title: #120902;
--color-danger-body: #1b0503;
--color-info-title: #020608;
--color-info-body: #06141e;
}
* {
transition: background-color 0.3s ease, border-color 0.3s ease;
}
/* Typography */
body {
font-family: system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
font-size: inherit;
line-height: 1.4;
color: var(--color-text-body);
}
h1, h2, h3, h4, h5, h6 {
font-family: inherit;
}
.rst-content .toctree-wrapper>p.caption, .rst-content h1, .rst-content h2, .rst-content h3, .rst-content h4, .rst-content h5, .rst-content h6 {
padding-top: .5em;
}
p, .main-content-wrap, .rst-content .section ul, .rst-content .toctree-wrapper ul, .rst-content section ul, .wy-plain-list-disc, article ul {
line-height: 1.6;
}
pre, .code, .rst-content .linenodiv pre, .rst-content div[class^=highlight] pre, .rst-content pre.literal-block {
font-family: "SFMono-Regular", Menlo,Consolas, Monospace;
font-size: 0.75em;
line-height: 1.8;
}
.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4 {
font-size: 1rem
}
.rst-versions {
font-family: inherit;
line-height: 1;
}
footer, footer p {
font-size: .8rem;
}
footer .rst-footer-buttons {
font-size: 1rem;
}
@media (max-width: 400px) {
/* break code lines on mobile */
pre, code {
word-break: break-word;
}
}
/* Layout */
.wy-side-nav-search, .wy-menu-vertical {
width: auto;
}
.wy-nav-side {
z-index: 0;
display: flex;
flex-wrap: wrap;
background-color: var(--color-bg-side-nav);
}
.wy-side-scroll {
width: 100%;
overflow-y: auto;
}
@media (min-width: 66.5rem) {
.wy-side-scroll {
width:264px
}
}
@media (min-width: 50rem) {
.wy-nav-side {
flex-wrap: nowrap;
position: fixed;
width: 248px;
height: 100%;
flex-direction: column;
border-right: 1px solid var(--color-border);
align-items:flex-end
}
}
@media (min-width: 66.5rem) {
.wy-nav-side {
width: calc((100% - 1064px) / 2 + 264px);
min-width:264px
}
}
@media (min-width: 50rem) {
.wy-nav-content-wrap {
position: relative;
max-width: 800px;
margin-left:248px
}
}
@media (min-width: 66.5rem) {
.wy-nav-content-wrap {
margin-left:calc((100% - 1064px) / 2 + 264px)
}
}
/* Colors */
body.wy-body-for-nav,
.wy-nav-content {
background: var(--color-bg-body);
}
.wy-nav-side {
border-right: 1px solid var(--color-border);
}
.wy-side-nav-search, .wy-nav-top {
background: var(--color-bg-side-nav);
border-bottom: 1px solid var(--color-border);
}
.wy-nav-content-wrap {
background: inherit;
}
.wy-side-nav-search > a, .wy-nav-top a, .wy-nav-top i {
color: var(--color-text-title);
}
.wy-side-nav-search > a:hover, .wy-nav-top a:hover {
background: transparent;
}
.wy-side-nav-search > div.version {
color: var(--color-text-alt)
}
.wy-side-nav-search > div[role="search"] {
border-top: 1px solid var(--color-border);
}
.wy-menu-vertical li.toctree-l2.current>a, .wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,
.wy-menu-vertical li.toctree-l3.current>a, .wy-menu-vertical li.toctree-l3.current li.toctree-l4>a {
background: var(--color-bg-side-nav);
}
.rst-content .highlighted {
background: #eedd85;
box-shadow: 0 0 0 2px #eedd85;
font-weight: 600;
}
.wy-side-nav-search input[type=text],
html.writer-html5 .rst-content table.docutils th {
color: var(--color-text-body);
}
.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,
.wy-table-backed,
.wy-table-odd td,
.wy-table-striped tr:nth-child(2n-1) td {
background-color: var(--color-bg-body-alt);
}
.rst-content table.docutils,
.wy-table-bordered-all,
html.writer-html5 .rst-content table.docutils th,
.rst-content table.docutils td,
.wy-table-bordered-all td,
hr {
border-color: var(--color-border) !important;
}
::selection {
background: var(--color-text-selection);
}
/* Ridiculous rules are taken from sphinx_rtd */
.rst-content .admonition-title,
.wy-alert-title {
color: var(--color-text-body-light);
}
.rst-content .hint,
.rst-content .important,
.rst-content .tip,
.rst-content .wy-alert-success,
.wy-alert.wy-alert-success {
background: var(--color-success-body);
}
.rst-content .hint .admonition-title,
.rst-content .hint .wy-alert-title,
.rst-content .important .admonition-title,
.rst-content .important .wy-alert-title,
.rst-content .tip .admonition-title,
.rst-content .tip .wy-alert-title,
.rst-content .wy-alert-success .admonition-title,
.rst-content .wy-alert-success .wy-alert-title,
.wy-alert.wy-alert-success .rst-content .admonition-title,
.wy-alert.wy-alert-success .wy-alert-title {
background-color: var(--color-success-title);
}
.rst-content .admonition-todo,
.rst-content .attention,
.rst-content .caution,
.rst-content .warning,
.rst-content .wy-alert-warning,
.wy-alert.wy-alert-warning {
background: var(--color-warning-body);
}
.rst-content .admonition-todo .admonition-title,
.rst-content .admonition-todo .wy-alert-title,
.rst-content .attention .admonition-title,
.rst-content .attention .wy-alert-title,
.rst-content .caution .admonition-title,
.rst-content .caution .wy-alert-title,
.rst-content .warning .admonition-title,
.rst-content .warning .wy-alert-title,
.rst-content .wy-alert-warning .admonition-title,
.rst-content .wy-alert-warning .wy-alert-title,
.rst-content .wy-alert.wy-alert-warning .admonition-title,
.wy-alert.wy-alert-warning .rst-content .admonition-title,
.wy-alert.wy-alert-warning .wy-alert-title {
background: var(--color-warning-title);
}
.rst-content .danger,
.rst-content .error,
.rst-content .wy-alert-danger,
.wy-alert.wy-alert-danger {
background: var(--color-danger-body);
}
.rst-content .danger .admonition-title,
.rst-content .danger .wy-alert-title,
.rst-content .error .admonition-title,
.rst-content .error .wy-alert-title,
.rst-content .wy-alert-danger .admonition-title,
.rst-content .wy-alert-danger .wy-alert-title,
.wy-alert.wy-alert-danger .rst-content .admonition-title,
.wy-alert.wy-alert-danger .wy-alert-title {
background: var(--color-danger-title);
}
.rst-content .note,
.rst-content .seealso,
.rst-content .wy-alert-info,
.wy-alert.wy-alert-info {
background: var(--color-info-body);
}
.rst-content .note .admonition-title,
.rst-content .note .wy-alert-title,
.rst-content .seealso .admonition-title,
.rst-content .seealso .wy-alert-title,
.rst-content .wy-alert-info .admonition-title,
.rst-content .wy-alert-info .wy-alert-title,
.wy-alert.wy-alert-info .rst-content .admonition-title,
.wy-alert.wy-alert-info .wy-alert-title {
background: var(--color-info-title);
}
/* Links */
a, a:visited,
.wy-menu-vertical a,
a.icon.icon-home,
.wy-menu-vertical li.toctree-l1.current > a.current {
color: var(--color-text-anchor);
text-decoration: none;
}
a:hover, .wy-breadcrumbs-aside a {
color: var(--color-text-anchor); /* reset */
}
.rst-versions a, .rst-versions .rst-current-version {
color: #var(--color-text-anchor);
}
.wy-nav-content a.reference, .wy-nav-content a:not([class]) {
background-image: linear-gradient(var(--color-border) 0%, var(--color-border) 100%);
background-repeat: repeat-x;
background-position: 0 100%;
background-size: 1px 1px;
}
.wy-nav-content a.reference:hover, .wy-nav-content a:not([class]):hover {
background-image: linear-gradient(rgba(114,83,237,0.45) 0%, rgba(114,83,237,0.45) 100%);
background-size: 1px 1px;
}
.wy-menu-vertical a:hover,
.wy-menu-vertical li.current a:hover,
.wy-menu-vertical a:active {
background: var(--color-bg-side-nav-hover) !important;
color: var(--color-text-body);
}
.wy-menu-vertical li.toctree-l1.current>a,
.wy-menu-vertical li.current>a,
.wy-menu-vertical li.on a {
background-color: var(--color-bg-side-nav-hover);
border: none;
font-weight: normal;
}
.wy-menu-vertical li.current {
background-color: inherit;
}
.wy-menu-vertical li.current a {
border-right: none;
}
.wy-menu-vertical li.toctree-l2 a,
.wy-menu-vertical li.toctree-l3 a,
.wy-menu-vertical li.toctree-l4 a,
.wy-menu-vertical li.toctree-l5 a,
.wy-menu-vertical li.toctree-l6 a,
.wy-menu-vertical li.toctree-l7 a,
.wy-menu-vertical li.toctree-l8 a,
.wy-menu-vertical li.toctree-l9 a,
.wy-menu-vertical li.toctree-l10 a {
color: var(--color-text-body);
}
a.image-reference, a.image-reference:hover {
background: none !important;
}
a.image-reference img {
cursor: zoom-in;
}
/* Code blocks */
.rst-content code, .rst-content tt, code {
padding: 0.25em;
font-weight: 400;
background-color: var(--color-bg-code-block);
border: 1px solid var(--color-border);
border-radius: 4px;
}
.rst-content div[class^=highlight], .rst-content pre.literal-block {
padding: 0.7rem;
margin-top: 0;
margin-bottom: 0.75rem;
overflow-x: auto;
background-color: var(--color-bg-side-nav);
border-color: var(--color-border);
border-radius: 4px;
box-shadow: none;
}
.rst-content .admonition-title,
.rst-content div.admonition,
.wy-alert-title {
padding: 10px 12px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.highlight .go {
color: inherit;
}
.highlight .nt {
color: var(--color-text-code-nt);
}
.rst-content code.literal,
.rst-content tt.literal {
border-color: var(--color-border);
background-color: var(--color-border);
color: var(--color-text-code-inline)
}
/* Search */
.wy-side-nav-search input[type=text] {
border: none;
border-radius: 0;
background-color: transparent;
font-family: inherit;
font-size: .85rem;
box-shadow: none;
padding: .7rem 1rem .7rem 2.8rem;
margin: 0;
}
#rtd-search-form {
position: relative;
}
#rtd-search-form:before {
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
content: "\f002";
color: var(--color-text-alt);
position: absolute;
left: 1.5rem;
top: .7rem;
}
/* Side nav */
.wy-side-nav-search {
padding: 1rem 0 0 0;
}
.wy-menu-vertical li a button.toctree-expand {
float: right;
margin-right: -1.5em;
padding: 0 .5em;
}
.wy-menu-vertical a,
.wy-menu-vertical li.current>a,
.wy-menu-vertical li.current li>a {
padding-right: 1.5em !important;
}
.wy-menu-vertical li.current li>a.current {
font-weight: 600;
}
/* Misc spacing */
.rst-content .admonition-title, .wy-alert-title {
padding: 10px 12px;
}
/* Buttons */
.btn {
display: inline-block;
box-sizing: border-box;
padding: 0.3em 1em;
margin: 0;
font-family: inherit;
font-size: inherit;
font-weight: 500;
line-height: 1.5;
color: #var(--color-text-anchor);
text-decoration: none;
vertical-align: baseline;
background-color: #f7f7f7;
border-width: 0;
border-radius: 4px;
box-shadow: 0 1px 2px rgba(0,0,0,0.12),0 3px 10px rgba(0,0,0,0.08);
appearance: none;
}
.btn:active {
padding: 0.3em 1em;
}
.rst-content .btn:focus {
outline: 1px solid #ccc;
}
.rst-content .btn-neutral, .rst-content .btn span.fa {
color: var(--color-text-body) !important;
}
.btn-neutral {
background-color: var(--color-btn-neutral-bg) !important;
color: var(--color-btn-neutral-text) !important;
border: 1px solid var(--color-btn-neutral-bg);
}
.btn:hover, .btn-neutral:hover {
background-color: var(--color-btn-neutral-bg-hover) !important;
}
/* Icon overrides */
.wy-side-nav-search a.icon-home:before {
display: none;
}
.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before {
content: "\f106"; /* fa-angle-up */
}
.fa-plus-square-o:before, .wy-menu-vertical li button.toctree-expand:before {
content: "\f107"; /* fa-angle-down */
}
/* Misc */
.wy-nav-top {
line-height: 36px;
}
.wy-nav-top > i {
font-size: 24px;
padding: 8px 0 0 2px;
color:#var(--color-text-anchor);
}
.rst-content table.docutils td,
.rst-content table.docutils th,
.rst-content table.field-list td,
.rst-content table.field-list th,
.wy-table td,
.wy-table th {
padding: 8px 14px;
}
.dark-mode-toggle {
position: absolute;
top: 14px;
right: 12px;
height: 20px;
width: 24px;
z-index: 10;
border: none;
background-color: transparent;
color: inherit;
opacity: 0.7;
}
.wy-nav-content-wrap {
z-index: 20;
}

14
docs/_static/custom.css vendored Normal file
View File

@@ -0,0 +1,14 @@
/* override table width restrictions */
@media screen and (min-width: 767px) {
.wy-table-responsive table td {
/* !important prevents the common CSS stylesheets from
overriding this as on RTD they are loaded after this stylesheet */
white-space: normal !important;
}
.wy-table-responsive {
overflow: visible !important;
}
}

View File

@@ -1,47 +0,0 @@
let toggleButton;
let icon;
function load() {
"use strict";
toggleButton = document.createElement("button");
toggleButton.setAttribute("title", "Toggle dark mode");
toggleButton.classList.add("dark-mode-toggle");
icon = document.createElement("i");
icon.classList.add("fa", darkModeState ? "fa-sun-o" : "fa-moon-o");
toggleButton.appendChild(icon);
document.body.prepend(toggleButton);
// Listen for changes in the OS settings
// addListener is used because older versions of Safari don't support addEventListener
// prefersDarkQuery set in <head>
if (prefersDarkQuery) {
prefersDarkQuery.addListener(function (evt) {
toggleDarkMode(evt.matches);
});
}
// Initial setting depending on the prefers-color-mode or localstorage
// darkModeState should be set in the document <head> to prevent flash
if (darkModeState == undefined) darkModeState = false;
toggleDarkMode(darkModeState);
// Toggles the "dark-mode" class on click and sets localStorage state
toggleButton.addEventListener("click", () => {
darkModeState = !darkModeState;
toggleDarkMode(darkModeState);
localStorage.setItem("dark-mode", darkModeState);
});
}
function toggleDarkMode(state) {
document.documentElement.classList.toggle("dark-mode", state);
document.documentElement.classList.toggle("light-mode", !state);
icon.classList.remove("fa-sun-o");
icon.classList.remove("fa-moon-o");
icon.classList.add(state ? "fa-sun-o" : "fa-moon-o");
darkModeState = state;
}
document.addEventListener("DOMContentLoaded", load);

BIN
docs/_static/screenshot.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 KiB

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 KiB

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 848 KiB

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 703 KiB

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 388 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 KiB

After

Width:  |  Height:  |  Size: 261 KiB

View File

@@ -1,13 +0,0 @@
{% extends "!layout.html" %}
{% block extrahead %}
<script>
// MediaQueryList object
const prefersDarkQuery = window.matchMedia("(prefers-color-scheme: dark)");
const lsDark = localStorage.getItem("dark-mode");
let darkModeState = lsDark !== null ? lsDark == "true" : prefersDarkQuery.matches;
document.documentElement.classList.toggle("dark-mode", darkModeState);
document.documentElement.classList.toggle("light-mode", !darkModeState);
</script>
{{ super() }}
{% endblock %}

View File

@@ -369,7 +369,7 @@ the naming scheme.
.. warning::
Since this command moves you documents around a lot, it is advised to to
Since this command moves you documents around alot, it is advised to to
a backup before. The renaming logic is robust and will never overwrite
or delete a file, but you can't ever be careful enough.

View File

@@ -200,7 +200,7 @@ Troubleshooting:
- Check your script's permission e.g. in case of permission error ``sudo chmod 755 post-consumption-example.sh``
- Pipe your scripts's output to a log file e.g. ``echo "${DOCUMENT_ID}" | tee --append /usr/src/paperless/scripts/post-consumption-example.log``
.. _post-consumption-example.sh: https://github.com/paperless-ngx/paperless-ngx/blob/main/scripts/post-consumption-example.sh
.. _post-consumption-example.sh: https://github.com/jonaswinkler/paperless-ngx/blob/master/scripts/post-consumption-example.sh
.. _advanced-file_name_handling:
@@ -243,7 +243,7 @@ will create a directory structure as follows:
last filename a document was stored as. If you do rename a file, paperless will
report your files as missing and won't be able to find them.
Paperless provides the following placeholders within filenames:
Paperless provides the following placeholders withing filenames:
* ``{asn}``: The archive serial number of the document, or "none".
* ``{correspondent}``: The name of the correspondent, or "none".

View File

@@ -5,87 +5,6 @@
Changelog
*********
paperless-ngx 1.7.0
###################
Breaking Changes
* ``PAPERLESS_URL`` is now required when using a reverse proxy. See `#674`_.
Features
* Allow setting more than one tag in mail rules `@jonasc`_ (#270)
* global drag'n'drop `@shamoon`_ (#283).
* Fix: download buttons should disable while waiting `@shamoon`_ (#630).
* Update checker `@shamoon`_ (#591).
* Show prompt on password-protected pdfs `@shamoon`_ (#564).
* Filtering query params aka browser navigation for filtering `@shamoon`_ (#540).
* Clickable tags in dashboard widgets `@shamoon`_ (#515).
* Add bottom pagination `@shamoon`_ (#372).
* Feature barcode splitter `@gador`_ (#532).
* App loading screen `@shamoon`_ (#298).
* Use progress bar for delayed buttons `@shamoon`_ (#415).
* Add minimum length for documents text filter `@shamoon`_ (#401).
* Added nav buttons in the document detail view `@GruberViktor`_ (#273).
* Improve date keyboard input `@shamoon`_ (#253).
* Color theming `@shamoon`_ (#243).
* Parse dates when entered without separators `@GruberViktor`_ (#250).
Bug Fixes
* add "localhost" to ALLOWED_HOSTS `@gador`_ (#700).
* Fix: scanners table `@qcasey`_ (#690).
* Adds wait for file before consuming `@stumpylog`_ (#483).
* Fix: frontend document editing erases time data `@shamoon`_ (#654).
* Increase length of SavedViewFilterRule `@stumpylog`_ (#612).
* Fixes attachment filename matching during mail fetching `@stumpylog`_ (#680).
* Add ``PAPERLESS_URL`` env variable & CSRF var `@shamoon`_ (#674).
* Fix: download buttons should disable while waiting `@shamoon`_ (#630).
* Fixes downloaded filename, add more consumer ignore settings `@stumpylog`_ (#599).
* FIX BUG: case-sensitive matching was not possible `@danielBreitlauch`_ (#594).
* uses shutil.move instead of rename `@gador`_ (#617).
* Fix npm deps 01.02.22 2 `@shamoon`_ (#610).
* Fix npm dependencies 01.02.22 `@shamoon`_ (#600).
* fix issue 416: implement PAPERLESS_OCR_MAX_IMAGE_PIXELS `@hacker-h`_ (#441).
* fix: exclude cypress from build in Dockerfile `@FrankStrieter`_ (#526).
* Corrections to pass pre-commit hooks `@schnuffle`_ (#454).
* Fix 311 unable to click checkboxes in document list `@shamoon`_ (#313).
* Fix imap tools bug `@stumpylog`_ (#393).
* Fix filterable dropdown buttons arent translated `@shamoon`_ (#366).
* Fix 224: "Auto-detected date is day before receipt date" `@a17t`_ (#246).
* Fix minor sphinx errors `@shamoon`_ (#322).
* Fix page links hidden `@shamoon`_ (#314).
* Fix: Include excluded items in dropdown count `@shamoon`_ (#263).
Translation
* `@miku323`_ contributed to Slovenian translation.
* `@FaintGhost`_ contributed to Chinese Simplified translation.
* `@DarkoBG79`_ contributed to Serbian translation.
* `Kemal Secer`_ contributed to Turkish translation.
* `@Prominence`_ contributed to Belarusian translation.
Documentation
* Fix: scanners table `@qcasey`_ (#690).
* Add `PAPERLESS_URL` env variable & CSRF var `@shamoon`_ (#674).
* Fixes downloaded filename, add more consumer ignore settings `@stumpylog`_ (#599).
* fix issue 416: implement ``PAPERLESS_OCR_MAX_IMAGE_PIXELS`` `@hacker-h`_ (#441).
* Fix minor sphinx errors `@shamoon`_ (#322).
Maintenance
* Add ``PAPERLESS_URL`` env variable & CSRF var `@shamoon`_ (#674).
* Chore: Implement release-drafter action for Changelogs `@qcasey`_ (#669).
* Chore: Add CODEOWNERS `@qcasey`_ (#667).
* Support docker-compose v2 in install `@stumpylog`_ (#611).
* Add Belarusian localization `@shamoon`_ (#588).
* Add Turkish localization `@shamoon`_ (#536).
* Add Serbian localization `@shamoon`_ (#504).
* Create PULL_REQUEST_TEMPLATE.md `@shamoon`_ (#304).
* Add Chinese localization `@shamoon`_ (#247).
* Add Slovenian language for frontend `@shamoon`_ (#315).
paperless-ngx 1.6.0
###################
@@ -116,10 +35,6 @@ Version 1.6.0 merges several pending PRs from jonaswinkler's repo and includes n
* `@shamoon`_ created a slick new logo (#165).
* `@tim-vogel`_ fixed exports missing groups (#193).
Known issues:
* 1.6.0 included a malformed package-lock.json, as a result users who want to build the docker image themselves need to change line 6 of the ``Dockerfile`` to ``RUN npm update npm -g && npm install --legacy-peer-deps``.
Thank you to the following people for their documentation updates, fixes, and comprehensive testing:
`@m0veax`_, `@a17t`_, `@fignew`_, `@muued`_, `@bauerj`_, `@isigmund`_, `@denilsonsa`_, `@mweimerskirch`_, `@alexander-bauer`_, `@apeltzer`_, `@tribut`_, `@yschroeder`_, `@gador`_, `@sAksham-Ar`_, `@sbrunner`_, `@philpagel`_, `@davemachado`_, `@2600box`_, `@qcasey`_, `@Nicarim`_, `@kpj`_, `@filcuk`_, `@Timoms`_, `@mattlamb99`_, `@padraigkitterick`_, `@ajkavanagh`_, `@Tooa`_, `@Unkn0wnCat`_, `@pewter77`_, `@stumpylog`_, `@Toxix`_, `@azapater`_, `@jschpp`_
@@ -225,7 +140,7 @@ paperless-ng 1.4.0
* New URL pattern for accessing documents by ASN directly (http://<paperless>/asn/123)
* Added logging when executing pre* and post-consume scripts.
* Added logging when executing pre- and post-consume scripts.
* Better error logging during document consumption.
@@ -1661,16 +1576,6 @@ bulk of the work on this big change.
.. _@azapater: https://github.com/azapater
.. _@tim-vogel: https://github.com/tim-vogel
.. _@jschpp: https://github.com/jschpp
.. _@schnuffle: https://github.com/schnuffle
.. _@GruberViktor: https://github.com/gruberviktor
.. _@hacker-h: https://github.com/hacker-h
.. _@danielBreitlauch: https://github.com/danielbreitlauch
.. _@miku323: https://github.com/miku323
.. _@FaintGhost: https://github.com/FaintGhost
.. _@DarkoBG79: https://github.com/DarkoBG79
.. _Kemal Secer: https://crowdin.com/profile/kemal.secer
.. _@Prominence: https://github.com/Prominence
.. _@jonasc: https://github.com/jonasc
.. _#20: https://github.com/the-paperless-project/paperless/issues/20
.. _#44: https://github.com/the-paperless-project/paperless/issues/44
@@ -1779,7 +1684,6 @@ bulk of the work on this big change.
.. _#488: https://github.com/the-paperless-project/paperless/pull/488
.. _#489: https://github.com/the-paperless-project/paperless/pull/489
.. _#492: https://github.com/the-paperless-project/paperless/pull/492
.. _#674: https://github.com/paperless-ngx/paperless-ngx/pull/674
.. _a new home on Docker Hub: https://hub.docker.com/r/danielquinn/paperless/
.. _optipng: http://optipng.sourceforge.net/

View File

@@ -15,7 +15,7 @@ extensions = [
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = ".rst"
@@ -119,16 +119,6 @@ html_theme_path = []
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# These paths are either relative to html_static_path
# or fully qualified paths (eg. https://...)
html_css_files = [
"css/custom.css",
]
html_js_files = [
"js/darkmode.js",
]
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.

View File

@@ -130,8 +130,6 @@ PAPERLESS_LOGROTATE_MAX_BACKUPS=<num>
Defaults to 20.
.. _hosting-and-security:
Hosting & Security
##################
@@ -144,24 +142,7 @@ PAPERLESS_SECRET_KEY=<key>
Default is listed in the file ``src/paperless/settings.py``.
PAPERLESS_URL=<url>
This setting can be used to set the three options below (ALLOWED_HOSTS,
CORS_ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS). If the other options are
set the values will be combined with this one. Do not include a trailing
slash. E.g. https://paperless.domain.com
Defaults to empty string, leaving the other settings unaffected.
PAPERLESS_CSRF_TRUSTED_ORIGINS=<comma-separated-list>
A list of trusted origins for unsafe requests (e.g. POST). As of Django 4.0
this is required to access the Django admin via the web.
See https://docs.djangoproject.com/en/4.0/ref/settings/#csrf-trusted-origins
Can also be set using PAPERLESS_URL (see above).
Defaults to empty string, which does not add any origins to the trusted list.
PAPERLESS_ALLOWED_HOSTS=<comma-separated-list>
PAPERLESS_ALLOWED_HOSTS<comma-separated-list>
If you're planning on putting Paperless on the open internet, then you
really should set this value to the domain name you're using. Failing to do
so leaves you open to HTTP host header attacks:
@@ -170,19 +151,12 @@ PAPERLESS_ALLOWED_HOSTS=<comma-separated-list>
Just remember that this is a comma-separated list, so "example.com" is fine,
as is "example.com,www.example.com", but NOT " example.com" or "example.com,"
Can also be set using PAPERLESS_URL (see above).
If manually set, please remember to include "localhost". Otherwise docker
healthcheck will fail.
Defaults to "*", which is all hosts.
PAPERLESS_CORS_ALLOWED_HOSTS=<comma-separated-list>
PAPERLESS_CORS_ALLOWED_HOSTS<comma-separated-list>
You need to add your servers to the list of allowed hosts that can do CORS
calls. Set this to your public domain name.
Can also be set using PAPERLESS_URL (see above).
Defaults to "http://localhost:8000".
PAPERLESS_FORCE_SCRIPT_NAME=<path>
@@ -211,7 +185,7 @@ PAPERLESS_AUTO_LOGIN_USERNAME=<username>
PAPERLESS_ADMIN_USER=<username>
If this environment variable is specified, Paperless automatically creates
a superuser with the provided username at start. This is useful in cases
where you can not run the `createsuperuser` command separately, such as Kubernetes
where you can not run the `createsuperuser` command seperately, such as Kubernetes
or AWS ECS.
Requires `PAPERLESS_ADMIN_PASSWORD` to be set.
@@ -566,10 +540,6 @@ PAPERLESS_WORKER_TIMEOUT=<num>
large documents within the default 1800 seconds. So extending this timeout
may prove to be useful on weak hardware setups.
PAPERLESS_WORKER_RETRY=<num>
If PAPERLESS_WORKER_TIMEOUT has been configured, the retry time for a task can
also be configured. By default, this value will be set to 10s more than the
worker timeout. This value should never be set less than the worker timeout.
PAPERLESS_TIME_ZONE=<timezone>
Set the time zone here.
@@ -618,27 +588,6 @@ PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=<bool>
Defaults to false.
PAPERLESS_CONSUMER_ENABLE_BARCODES=<bool>
Enables the scanning and page separation based on detected barcodes.
This allows for scanning and adding multiple documents per uploaded
file, which are separated by one or multiple barcode pages.
For ease of use, it is suggested to use a standardized separation page,
e.g. `here <https://www.alliancegroup.co.uk/patch-codes.htm>`_.
If no barcodes are detected in the uploaded file, no page separation
will happen.
Defaults to false.
PAPERLESS_CONSUMER_BARCODE_STRING=PATCHT
Defines the string to be detected as a separator barcode.
If paperless is used with the PATCH-T separator pages, users
shouldn't change this.
Defaults to "PATCHT"
PAPERLESS_CONVERT_MEMORY_LIMIT=<num>
On smaller systems, or even in the case of Very Large Documents, the consumer
@@ -722,7 +671,7 @@ PAPERLESS_CONSUMER_IGNORE_PATTERNS=<json>
This can be adjusted by configuring a custom json array with patterns to exclude.
Defaults to ``[".DS_STORE/*", "._*", ".stfolder/*", ".stversions/*", ".localized/*", "desktop.ini"]``.
Defautls to ``[".DS_STORE/*", "._*", ".stfolder/*"]``.
Binaries
########
@@ -815,26 +764,3 @@ PAPERLESS_OCR_LANGUAGES=<list>
PAPERLESS_OCR_LANGUAGE=tur
Defaults to none, which does not install any additional languages.
.. _configuration-update-checking:
Update Checking
###############
PAPERLESS_ENABLE_UPDATE_CHECK=<bool>
Enable (or disable) the automatic check for available updates. This feature is disabled
by default but if it is not explicitly set Paperless-ngx will show a message about this.
If enabled, the feature works by pinging the the Github API for the latest release e.g.
https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest
to determine whether a new version is available.
Actual updating of the app must still be performed manually.
Note that for users of thirdy-party containers e.g. linuxserver.io this notification
may be 'ahead' of a new release from the third-party maintainers.
In either case, no tracking data is collected by the app in any way.
Defaults to none, which disables the feature.

View File

@@ -7,7 +7,7 @@ Frequently asked questions
**A:** While Paperless-ngx is already considered largely "feature-complete" it is a community-driven
project and development will be guided in this way. New features can be submitted via
GitHub discussions and "up-voted" by the community but this is not a guarantee the feature
GitHub discussions and "up-voted" by the community but this is not a garauntee the feature
will be implemented. This project will always be open to collaboration in the form of PRs,
ideas etc.

View File

@@ -13,43 +13,43 @@ that works right for you based on recommendations from other Paperless users.
Physical scanners
=================
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brand | Model | Supports | Recommended By |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| | | FTP | SFTP | NFS | SMB | SMTP | API [1]_ | |
+=========+================+=====+======+=====+=====+======+==========+================+
| Brother | `ADS-1700W`_ | yes | | | yes | yes | |`holzhannes`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `ADS-1600W`_ | yes | | | yes | yes | |`holzhannes`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `ADS-1500W`_ | yes | | | yes | yes | |`danielquinn`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `ADS-1100W`_ | yes | | | | | |`ytzelf`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `ADS-2800W`_ | yes | yes | | yes | yes | |`philpagel`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `MFC-J6930DW`_ | yes | | | | | |`ayounggun`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `MFC-L5850DW`_ | yes | | | | yes | |`holzhannes`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `MFC-L2750DW`_ | yes | | | yes | yes | |`muued`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `MFC-J5910DW`_ | yes | | | | | |`bmsleight`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `MFC-8950DW`_ | yes | | | yes | yes | |`philpagel`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Brother | `MFC-9142CDN`_ | yes | | | yes | | |`REOLDEV`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Fujitsu | `ix500`_ | yes | | | yes | | |`eonist`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Epson | `ES-580W`_ | yes | | | yes | yes | |`fignew`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Epson | `WF-7710DWF`_ | yes | | | yes | | |`Skylinar`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Fujitsu | `S1300i`_ | yes | | | yes | | |`jonaswinkler`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
| Doxie | `Q2`_ | | | | | | yes |`Unkn0wnCat`_ |
+---------+----------------+-----+------+-----+-----+------+----------+----------------+
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brand | Model | Supports | Recommended By |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| | | FTP | NFS | SMB | SMTP | API [1]_ | |
+=========+================+=====+=====+=====+======+==========+================+
| Brother | `ADS-1700W`_ | yes | | yes | yes | |`holzhannes`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `ADS-1600W`_ | yes | | yes | yes | |`holzhannes`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `ADS-1500W`_ | yes | | yes | yes | |`danielquinn`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `ADS-1100W`_ | yes | | | | |`ytzelf`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `ADS-2800W`_ | yes | yes | | yes | yes |`philpagel`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `MFC-J6930DW`_ | yes | | | | |`ayounggun`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `MFC-L5850DW`_ | yes | | | yes | |`holzhannes`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `MFC-L2750DW`_ | yes | | yes | yes | |`muued`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `MFC-J5910DW`_ | yes | | | | |`bmsleight`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `MFC-8950DW`_ | yes | | | yes | yes |`philpagel`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Brother | `MFC-9142CDN`_ | yes | | yes | | |`REOLDEV`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Fujitsu | `ix500`_ | yes | | yes | | |`eonist`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Epson | `ES-580W`_ | yes | | yes | yes | |`fignew`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Epson | `WF-7710DWF`_ | yes | | yes | | |`Skylinar`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Fujitsu | `S1300i`_ | yes | | yes | | |`jonaswinkler`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
| Doxie | `Q2`_ | | | | | yes |`Unkn0wnCat`_ |
+---------+----------------+-----+-----+-----+------+----------+----------------+
.. _MFC-L5850DW: https://www.brother-usa.com/products/mfcl5850dw
.. _MFC-L2750DW: https://www.brother.de/drucker/laserdrucker/mfc-l2750dw
@@ -131,3 +131,4 @@ This part assumes your Doxie is connected to WiFi and you know its IP.
6. Click *Submit* at the bottom of the page
Congrats, you can now scan directly from your Doxie to your Paperless-ngx instance!

View File

@@ -4,60 +4,41 @@
Screenshots
***********
This is what Paperless-ngx looks like.
This is what paperless-ngx looks like. You shouldn't use paperless to index
research papers though, its a horrible tool for that job.
The dashboard shows customizable views on your document and allows document uploads:
.. image:: _static/screenshots/dashboard.png
:target: _static/screenshots/dashboard.png
The document list provides three different styles to scroll through your documents:
.. image:: _static/screenshots/documents-table.png
:target: _static/screenshots/documents-table.png
.. image:: _static/screenshots/documents-smallcards.png
:target: _static/screenshots/documents-smallcards.png
.. image:: _static/screenshots/documents-largecards.png
:target: _static/screenshots/documents-largecards.png
Paperless-ngx also supports "dark mode":
.. image:: _static/screenshots/documents-smallcards-dark.png
:target: _static/screenshots/documents-smallcards-dark.png
Extensive filtering mechanisms:
.. image:: _static/screenshots/documents-filter.png
:target: _static/screenshots/documents-filter.png
Bulk editing of document tags, correspondents, etc.:
.. image:: _static/screenshots/bulk-edit.png
:target: _static/screenshots/bulk-edit.png
Side-by-side editing of documents:
Side-by-side editing of documents. Optimized for 1080p.
.. image:: _static/screenshots/editing.png
:target: _static/screenshots/editing.png
Tag editing. This looks about the same for correspondents and document types.
.. image:: _static/screenshots/new-tag.png
:target: _static/screenshots/new-tag.png
Searching provides auto complete and highlights the results.
.. image:: _static/screenshots/search-preview.png
:target: _static/screenshots/search-preview.png
.. image:: _static/screenshots/search-results.png
:target: _static/screenshots/search-results.png
Fancy mail filters!
.. image:: _static/screenshots/mail-rules-edited.png
:target: _static/screenshots/mail-rules-edited.png
Mobile devices are supported.
Mobile support in the future? This kinda works, however some layouts are still
too wide.
.. image:: _static/screenshots/mobile.png
:target: _static/screenshots/mobile.png

View File

@@ -291,14 +291,12 @@ writing. Windows is not and will never be supported.
* ``libpq-dev`` for PostgreSQL
* ``libmagic-dev`` for mime type detection
* ``mime-support`` for mime type detection
* ``libzbar0`` for barcode detection
* ``poppler-utils`` for barcode detection
Use this list for your preferred package management:
.. code::
python3 python3-pip python3-dev imagemagick fonts-liberation optipng gnupg libpq-dev libmagic-dev mime-support libzbar0 poppler-utils
python3 python3-pip python3-dev imagemagick fonts-liberation optipng gnupg libpq-dev libmagic-dev mime-support
These dependencies are required for OCRmyPDF, which is used for text recognition.
@@ -347,8 +345,6 @@ writing. Windows is not and will never be supported.
paperless stores its data. If you like, you can point both to the same directory.
* ``PAPERLESS_SECRET_KEY`` should be a random sequence of characters. It's used for authentication. Failure
to do so allows third parties to forge authentication credentials.
* ``PAPERLESS_URL`` if you are behind a reverse proxy. This should point to your domain. Please see
:ref:`configuration` for more information.
Many more adjustments can be made to paperless, especially the OCR part. The following options are recommended
for everyone:
@@ -788,6 +784,4 @@ the following configuration is required for paperless to operate:
}
}
The ``PAPERLESS_URL`` configuration variable is also required when using a reverse proxy. Please refer to the :ref:`hosting-and-security` docs.
Also read `this <https://channels.readthedocs.io/en/stable/deploying.html#nginx-supervisor-ubuntu>`__, towards the end of the section.

View File

@@ -62,7 +62,7 @@ your documents:
1. OCR the document, if it has no text. Digital documents usually have text,
and this step will be skipped for those documents.
2. Paperless will create an archivable PDF/A document from your document.
2. Paperless will create an archiveable PDF/A document from your document.
If this document is coming from your scanner, it will have embedded selectable text.
3. Paperless performs automatic matching of tags, correspondents and types on the
document before storing it in the database.
@@ -102,14 +102,12 @@ files from the scanner. Typically, you're looking at an FTP server like
.. TODO: hyperref to configuration of the location of this magic folder.
Web UI Upload
=============
Dashboard upload
================
The dashboard has a file drop field to upload documents to paperless. Simply drag a file
onto this field or select a file with the file dialog. Multiple files are supported.
You can also upload documents on any other page of the web UI by dragging-and-dropping
files into your browser window.
.. _usage-mobile_upload:
@@ -180,14 +178,6 @@ These are as follows:
automatically or manually and tell paperless to move them to yet another folder
after consumption. It's up to you.
.. note::
When defining a mail rule with a folder, you may need to try different characters to
define how the sub-folders are separated. Common values include ".", "/" or "|", but
this varies by the mail server. Unfortunately, this isn't a value we can determine
automatically. Either check the documentation for your mail server, or check for
errors in the logs and try different folder separator values.
.. note::
Paperless will process the rules in the order defined in the admin page.

View File

@@ -47,29 +47,24 @@ if [[ $(id -u) == "0" ]] ; then
exit 1
fi
if ! command -v wget &> /dev/null ; then
if [[ -z $(which wget) ]] ; then
echo "wget executable not found. Is wget installed?"
exit 1
fi
if ! command -v docker &> /dev/null ; then
if [[ -z $(which docker) ]] ; then
echo "docker executable not found. Is docker installed?"
exit 1
fi
DOCKER_COMPOSE_CMD="docker-compose"
if ! command -v ${DOCKER_COMPOSE_CMD} ; then
if docker compose version &> /dev/null ; then
DOCKER_COMPOSE_CMD="docker compose"
else
echo "docker-compose executable not found. Is docker-compose installed?"
exit 1
fi
if [[ -z $(which docker-compose) ]] ; then
echo "docker-compose executable not found. Is docker-compose installed?"
exit 1
fi
# Check if user has permissions to run Docker by trying to get the status of Docker (docker status).
# If this fails, the user probably does not have permissions for Docker.
if ! docker stats --no-stream &> /dev/null ; then
if [ ! "$(docker stats --no-stream 2>/dev/null 1>&2)" ] ; then
echo ""
echo "WARN: It look like the current user does not have Docker permissions."
echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user."
@@ -92,14 +87,6 @@ echo ""
echo "1. Application configuration"
echo "============================"
echo ""
echo "The URL paperless will be available at. This is required if the"
echo "installation will be accessible via the web, otherwise can be left blank."
echo ""
ask "URL" ""
URL=$ask_result
echo ""
echo "The port on which the paperless webserver will listen for incoming"
echo "connections."
@@ -286,7 +273,6 @@ if [[ "$DATABASE_BACKEND" == "postgres" ]] ; then
fi
fi
echo ""
echo "URL: $URL"
echo "Port: $PORT"
echo "Database: $DATABASE_BACKEND"
echo "Tika enabled: $TIKA_ENABLED"
@@ -322,9 +308,6 @@ SECRET_KEY=$(tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 64 | head -n 1)
DEFAULT_LANGUAGES="deu eng fra ita spa"
{
if [[ ! $URL == "" ]] ; then
echo "PAPERLESS_URL=$URL"
fi
if [[ ! $USERMAP_UID == "1000" ]] ; then
echo "USERMAP_UID=$USERMAP_UID"
fi
@@ -368,8 +351,8 @@ if [ "$l1" -eq "$l2" ] ; then
fi
${DOCKER_COMPOSE_CMD} pull
docker-compose pull
${DOCKER_COMPOSE_CMD} run --rm -e DJANGO_SUPERUSER_PASSWORD="$PASSWORD" webserver createsuperuser --noinput --username "$USERNAME" --email "$EMAIL"
docker-compose run --rm -e DJANGO_SUPERUSER_PASSWORD="$PASSWORD" webserver createsuperuser --noinput --username "$USERNAME" --email "$EMAIL"
${DOCKER_COMPOSE_CMD} up -d
docker-compose up -d

View File

@@ -27,10 +27,8 @@
# Security and hosting
#PAPERLESS_SECRET_KEY=change-me
#PAPERLESS_URL=https://example.com
#PAPERLESS_CSRF_TRUSTED_ORIGINS=https://example.com # can be set using PAPERLESS_URL
#PAPERLESS_ALLOWED_HOSTS=example.com,www.example.com # can be set using PAPERLESS_URL
#PAPERLESS_CORS_ALLOWED_HOSTS=https://localhost:8080,https://example.com # can be set using PAPERLESS_URL
#PAPERLESS_ALLOWED_HOSTS=example.com,www.example.com
#PAPERLESS_CORS_ALLOWED_HOSTS=http://example.com,http://localhost:8000
#PAPERLESS_FORCE_SCRIPT_NAME=
#PAPERLESS_STATIC_URL=/static/
#PAPERLESS_AUTO_LOGIN_USERNAME=
@@ -60,10 +58,8 @@
#PAPERLESS_CONSUMER_POLLING=10
#PAPERLESS_CONSUMER_DELETE_DUPLICATES=false
#PAPERLESS_CONSUMER_RECURSIVE=false
#PAPERLESS_CONSUMER_IGNORE_PATTERNS=[".DS_STORE/*", "._*", ".stfolder/*", ".stversions/*", ".localized/*", "desktop.ini"]
#PAPERLESS_CONSUMER_IGNORE_PATTERNS=[".DS_STORE/*", "._*", ".stfolder/*"]
#PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=false
#PAPERLESS_CONSUMER_ENABLE_BARCODES=false
#PAPERLESS_CONSUMER_ENABLE_BARCODES=PATCHT
#PAPERLESS_OPTIMIZE_THUMBNAILS=true
#PAPERLESS_PRE_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh
#PAPERLESS_POST_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh
@@ -71,7 +67,6 @@
#PAPERLESS_FILENAME_PARSE_TRANSFORMS=[]
#PAPERLESS_THUMBNAIL_FONT_NAME=
#PAPERLESS_IGNORE_DATES=
#PAPERLESS_ENABLE_UPDATE_CHECK=
# Tika settings

View File

@@ -5,15 +5,15 @@
# pipenv lock --requirements
#
-i https://pypi.python.org/simple/
--extra-index-url https://www.piwheels.org/simple/
-i https://pypi.python.org/simple
--extra-index-url https://www.piwheels.org/simple
aioredis==1.3.1
anyio==3.5.0; python_full_version >= '3.6.2'
arrow==1.2.2; python_version >= '3.6'
asgiref==3.5.0; python_version >= '3.7'
async-timeout==4.0.2; python_version >= '3.6'
attrs==21.4.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
autobahn==22.3.2; python_version >= '3.7'
autobahn==22.2.2; python_version >= '3.7'
automat==20.2.0
backports.zoneinfo==0.2.1; python_version < '3.9'
blessed==1.19.1; python_version >= '2.7'
@@ -23,7 +23,7 @@ channels-redis==3.4.0
channels==3.0.4
chardet==4.0.0; python_version >= '3.1'
charset-normalizer==2.0.12; python_version >= '3'
click==8.1.2; python_version >= '3.7'
click==8.0.4; python_version >= '3.6'
coloredlogs==15.0.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
concurrent-log-handler==0.9.20
constantly==15.1.0
@@ -35,7 +35,7 @@ django-extensions==3.1.5
django-filter==21.1
django-picklefield==3.0.1; python_version >= '3'
django-q==1.3.9
django==4.0.4
django==4.0.3
djangorestframework==3.13.1
filelock==3.6.0
fuzzywuzzy[speedup]==0.18.0
@@ -47,7 +47,7 @@ humanfriendly==10.0; python_version >= '2.7' and python_version not in '3.0, 3.1
hyperlink==21.0.0
idna==3.3; python_version >= '3.5'
imap-tools==0.53.0
img2pdf==0.4.4
img2pdf==0.4.3
importlib-resources==5.6.0; python_version < '3.9'
incremental==21.3.0
inotify-simple==1.3.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
@@ -57,13 +57,12 @@ langdetect==1.0.9
lxml==4.8.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
msgpack==1.0.3
numpy==1.22.3; python_version >= '3.8'
ocrmypdf==13.4.2
ocrmypdf==13.4.1
packaging==21.3; python_version >= '3.6'
pathvalidate==2.5.0
pdf2image==1.16.0
pdfminer.six==20220319
pikepdf==5.1.1
pillow==9.1.0
pdfminer.six==20211012
pikepdf==5.1.0
pillow==9.0.1
pluggy==1.0.0; python_version >= '3.6'
portalocker==2.4.0; python_version >= '3'
psycopg2==2.9.3
@@ -71,7 +70,7 @@ pyasn1-modules==0.2.8
pyasn1==0.4.8
pycparser==2.21
pyopenssl==22.0.0
pyparsing==3.0.8; python_full_version >= '3.6.8'
pyparsing==3.0.7; python_version >= '3.6'
python-dateutil==2.8.2
python-dotenv==0.20.0
python-gnupg==0.4.8
@@ -80,7 +79,6 @@ python-magic==0.4.25
pytz-deprecation-shim==0.1.0.post0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
pytz==2022.1
pyyaml==6.0
pyzbar==0.1.9
redis==3.5.3
regex==2022.3.2; python_version >= '3.6'
reportlab==3.6.9; python_version >= '3.7' and python_version < '4'
@@ -88,26 +86,26 @@ requests==2.27.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3
scikit-learn==1.0.2
scipy==1.8.0; python_version < '3.11' and python_version >= '3.8'
service-identity==21.1.0
setuptools==62.1.0; python_version >= '3.7'
setuptools==61.1.1; python_version >= '3.7'
six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
sniffio==1.2.0; python_version >= '3.5'
sqlparse==0.4.2; python_version >= '3.5'
threadpoolctl==3.1.0; python_version >= '3.6'
tika==1.24
tqdm==4.64.0
twisted[tls]==22.4.0; python_full_version >= '3.6.7'
tqdm==4.63.1
twisted[tls]==22.2.0; python_full_version >= '3.6.7'
txaio==22.2.1; python_version >= '3.6'
typing-extensions==4.1.1; python_version >= '3.6'
tzdata==2022.1; python_version >= '3.6'
tzlocal==4.2; python_version >= '3.6'
tzlocal==4.1; python_version >= '3.6'
urllib3==1.26.9; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'
uvicorn[standard]==0.17.6
uvloop==0.16.0
watchdog==2.1.7
watchgod==0.8.2
watchgod==0.8.1
wcwidth==0.2.5
websockets==10.2
whitenoise==6.0.0
whoosh==2.7.4
zipp==3.8.0; python_version < '3.9'
zipp==3.7.0; python_version < '3.9'
zope.interface==5.4.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'

View File

@@ -1,13 +1,3 @@
<app-toasts></app-toasts>
<ngx-file-drop dropZoneClassName="main-dropzone" contentClassName="main-content" [disabled]="!dragDropEnabled"
(onFileDrop)="dropped($event)" (onFileOver)="fileOver()" (onFileLeave)="fileLeave()">
<ng-template ngx-file-drop-content-tmp>
<div class="global-dropzone-overlay fade" [class.show]="fileIsOver" [class.hide]="hidden">
<h2 i18n>Drop files to begin upload</h2>
</div>
<div [class.inert]="fileIsOver">
<router-outlet></router-outlet>
</div>
</ng-template>
</ngx-file-drop>
<router-outlet></router-outlet>

View File

@@ -4,8 +4,6 @@ import { Router } from '@angular/router'
import { Subscription } from 'rxjs'
import { ConsumerStatusService } from './services/consumer-status.service'
import { ToastService } from './services/toast.service'
import { NgxFileDropEntry } from 'ngx-file-drop'
import { UploadDocumentsService } from './services/upload-documents.service'
@Component({
selector: 'app-root',
@@ -17,16 +15,11 @@ export class AppComponent implements OnInit, OnDestroy {
successSubscription: Subscription
failedSubscription: Subscription
private fileLeaveTimeoutID: any
fileIsOver: boolean = false
hidden: boolean = true
constructor(
private settings: SettingsService,
private consumerStatusService: ConsumerStatusService,
private toastService: ToastService,
private router: Router,
private uploadDocumentsService: UploadDocumentsService
private router: Router
) {
let anyWindow = window as any
anyWindow.pdfWorkerSrc = 'assets/js/pdf.worker.min.js'
@@ -107,36 +100,4 @@ export class AppComponent implements OnInit, OnDestroy {
}
})
}
public get dragDropEnabled(): boolean {
return !this.router.url.includes('dashboard')
}
public fileOver() {
// allows transition
setTimeout(() => {
this.fileIsOver = true
}, 1)
this.hidden = false
// stop fileLeave timeout
clearTimeout(this.fileLeaveTimeoutID)
}
public fileLeave(immediate: boolean = false) {
const ms = immediate ? 0 : 500
this.fileLeaveTimeoutID = setTimeout(() => {
this.fileIsOver = false
// await transition completed
setTimeout(() => {
this.hidden = true
}, 150)
}, ms)
}
public dropped(files: NgxFileDropEntry[]) {
this.fileLeave(true)
this.uploadDocumentsService.uploadFiles(files)
this.toastService.showInfo($localize`Initiating upload...`, 3000)
}
}

View File

@@ -12,7 +12,7 @@
</a>
<div class="search-form-container flex-grow-1 py-2 pb-3 pb-sm-2 px-3 ps-md-4 me-sm-auto order-3 order-sm-1">
<form (ngSubmit)="search()" class="form-inline flex-grow-1">
<svg width="1em" height="1em" fill="currentColor">
<svg width="1em" height="1em">
<use xlink:href="assets/bootstrap-icons.svg#search"/>
</svg>
<input class="form-control form-control-sm" type="text" placeholder="Search documents" aria-label="Search"
@@ -25,7 +25,7 @@
<span *ngIf="displayName" class="navbar-text small me-2 text-light d-none d-sm-inline">
{{displayName}}
</span>
<svg width="1.3em" height="1.3em" fill="currentColor">
<svg width="1.3em" height="1.3em">
<use xlink:href="assets/bootstrap-icons.svg#person-circle"/>
</svg>
</button>
@@ -170,46 +170,21 @@
<li class="nav-item">
<div class="d-flex w-100 flex-wrap">
<a class="nav-link pe-2 pb-1" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#github" />
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="sidebaricon bi bi-github" viewBox="0 0 16 16">
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
</svg>&nbsp;<ng-container i18n>GitHub</ng-container>
</a>
<a class="nav-link-additional small text-muted ms-3" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/discussions/categories/feature-requests" title="Suggest an idea">
<svg xmlns="http://www.w3.org/2000/svg" width="1.1em" height="1.1em" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#lightbulb" />
<svg xmlns="http://www.w3.org/2000/svg" width="1.3em" height="1.3em" fill="currentColor" class="bi bi-lightbulb pe-1" viewBox="0 0 16 16">
<path d="M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13a.5.5 0 0 1 0 1 .5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1 0-1 .5.5 0 0 1 0-1 .5.5 0 0 1-.46-.302l-.761-1.77a1.964 1.964 0 0 0-.453-.618A5.984 5.984 0 0 1 2 6zm6-5a5 5 0 0 0-3.479 8.592c.263.254.514.564.676.941L5.83 12h4.342l.632-1.467c.162-.377.413-.687.676-.941A5 5 0 0 0 8 1z"/>
</svg>
<ng-container i18n>Suggest an idea</ng-container>
</a>
</div>
</li>
<li class="nav-item mt-2">
<div class="px-3 py-2 text-muted small d-flex align-items-center flex-wrap">
<div class="me-3">{{ versionString }}</div>
<div *ngIf="appRemoteVersion" class="version-check">
<ng-template #updateAvailablePopContent>
<span class="small">Paperless-ngx v{{ appRemoteVersion.version }} <ng-container i18n>is available.</ng-container><br/><ng-container i18n>Click to view.</ng-container></span>
</ng-template>
<ng-template #updateCheckingNotEnabledPopContent>
<span class="small"><ng-container i18n>Checking for updates is disabled.</ng-container><br/><ng-container i18n>Click for more information.</ng-container></span>
</ng-template>
<ng-container *ngIf="appRemoteVersion.feature_is_set; else updateCheckNotSet">
<a *ngIf="appRemoteVersion.update_available" class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://github.com/paperless-ngx/paperless-ngx/releases"
[ngbPopover]="updateAvailablePopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body">
<svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#info-circle" />
</svg>
<ng-container *ngIf="appRemoteVersion?.update_available" i18n>Update available</ng-container>
</a>
</ng-container>
<ng-template #updateCheckNotSet>
<a class="small text-decoration-none" target="_blank" rel="noopener noreferrer" href="https://paperless-ngx.readthedocs.io/en/latest/configuration.html#update-checking"
[ngbPopover]="updateCheckingNotEnabledPopContent" popoverClass="shadow" triggers="mouseenter:mouseleave" container="body">
<svg fill="currentColor" class="me-1" width="1.2em" height="1.2em" style="vertical-align: text-top;" viewBox="0 0 16 16">
<use xlink:href="assets/bootstrap-icons.svg#info-circle" />
</svg>
</a>
</ng-template>
</div>
<div class="px-3 py-2 text-muted small">
{{versionString}}
</div>
</li>
</ul>

View File

@@ -1,3 +1,4 @@
@import "/src/theme";
/*
* Sidebar
*/
@@ -34,24 +35,20 @@
.sidebar .nav-link {
font-weight: 500;
}
&:hover, &.active, &:focus {
color: var(--bs-primary);
}
.sidebar .nav-link .sidebaricon {
margin-right: 4px;
}
&:focus-visible {
outline: none;
background-color: var(--bs-body-bg);
}
.sidebar .nav-link.active {
color: var(--bs-primary);
font-weight: bold;
}
&.active {
font-weight: bold;
}
.sidebaricon {
margin-right: 4px;
color: inherit;
}
.sidebar .nav-link.active .sidebaricon,
.sidebar .nav-link:hover .sidebaricon {
color: inherit;
}
.sidebar-heading {
@@ -174,28 +171,8 @@
&:focus {
background-color: rgba(0, 0, 0, 0.3);
color: var(--bs-light);
flex-grow: 1;
padding-left: 0.5rem;
}
}
}
.version-check {
animation: pulse 2s ease-in-out 0s 1;
}
@keyframes pulse {
0% {
opacity: 0;
}
25% {
opacity: 100%;
}
75% {
opacity: 0;
}
100% {
opacity: 100%;
}
}

View File

@@ -18,10 +18,6 @@ import { DocumentDetailComponent } from '../document-detail/document-detail.comp
import { Meta } from '@angular/platform-browser'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
import {
RemoteVersionService,
AppRemoteVersion,
} from 'src/app/services/rest/remote-version.service'
@Component({
selector: 'app-app-frame',
@@ -36,18 +32,10 @@ export class AppFrameComponent {
private searchService: SearchService,
public savedViewService: SavedViewService,
private list: DocumentListViewService,
private meta: Meta,
private remoteVersionService: RemoteVersionService
) {
this.remoteVersionService
.checkForUpdates()
.subscribe((appRemoteVersion: AppRemoteVersion) => {
this.appRemoteVersion = appRemoteVersion
})
}
private meta: Meta
) {}
versionString = `${environment.appTitle} ${environment.version}`
appRemoteVersion
isMenuCollapsed: boolean = true

View File

@@ -1,3 +1,5 @@
@import "/src/theme";
.badge-corner {
position: absolute;
top: -8px;
@@ -40,7 +42,7 @@
filter: brightness(0.5);
&.active {
background-color: var(--pngx-primary-lighten-30);
background-color: var(--ngx-primary-lighten-30);
}
}

View File

@@ -2,7 +2,7 @@
*ngFor="let toast of toasts"
[header]="toast.title" [autohide]="true" [delay]="toast.delay"
[class]="toast.classname"
(hidden)="toastService.closeToast(toast)">
(hide)="toastService.closeToast(toast)">
<p>{{toast.content}}</p>
<p *ngIf="toast.action"><button class="btn btn-sm btn-outline-secondary" (click)="toastService.closeToast(toast); toast.action()">{{toast.actionName}}</button></p>
</ngb-toast>

View File

@@ -5,7 +5,3 @@
margin: 0.5em;
z-index: 1200;
}
.toast:not(.show) {
display: block; // this corrects an ng-bootstrap bug that prevented animations
}

View File

@@ -1,3 +1,5 @@
@import "/src/theme";
form {
position: relative;
}
@@ -31,7 +33,3 @@ form {
mix-blend-mode: soft-light;
pointer-events: none;
}
::ng-deep .ngx-file-drop__drop-zone--over {
background-color: var(--pngx-primary-faded) !important;
}

View File

@@ -6,7 +6,7 @@ import {
FileStatus,
FileStatusPhase,
} from 'src/app/services/consumer-status.service'
import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
import { DocumentService } from 'src/app/services/rest/document.service'
const MAX_ALERTS = 5
@@ -19,8 +19,8 @@ export class UploadFileWidgetComponent implements OnInit {
alertsExpanded = false
constructor(
private consumerStatusService: ConsumerStatusService,
private uploadDocumentsService: UploadDocumentsService
private documentService: DocumentService,
private consumerStatusService: ConsumerStatusService
) {}
getStatus() {
@@ -116,6 +116,48 @@ export class UploadFileWidgetComponent implements OnInit {
public fileLeave(event) {}
public dropped(files: NgxFileDropEntry[]) {
this.uploadDocumentsService.uploadFiles(files)
for (const droppedFile of files) {
if (droppedFile.fileEntry.isFile) {
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry
fileEntry.file((file: File) => {
let formData = new FormData()
formData.append('document', file, file.name)
let status = this.consumerStatusService.newFileUpload(file.name)
status.message = $localize`Connecting...`
this.documentService.uploadDocument(formData).subscribe(
(event) => {
if (event.type == HttpEventType.UploadProgress) {
status.updateProgress(
FileStatusPhase.UPLOADING,
event.loaded,
event.total
)
status.message = $localize`Uploading...`
} else if (event.type == HttpEventType.Response) {
status.taskId = event.body['task_id']
status.message = $localize`Upload complete, waiting...`
}
},
(error) => {
switch (error.status) {
case 400: {
this.consumerStatusService.fail(status, error.error.document)
break
}
default: {
this.consumerStatusService.fail(
status,
$localize`HTTP error: ${error.status} ${error.statusText}`
)
break
}
}
}
)
})
}
}
}
}

View File

@@ -135,27 +135,20 @@
</li>
<li [ngbNavItem]="4" class="d-md-none">
<a ngbNavLink>Preview</a>
<ng-template ngbNavContent *ngIf="pdfPreview.offsetParent == undefined">
<div class="position-relative">
<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>
<ng-template #nativePdfViewer>
<object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object>
</ng-template>
</ng-container>
<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">
<form>
<input autocomplete="" class="form-control" i18n-placeholder placeholder="Enter Password" type="password" (keyup)="onPasswordKeyUp($event)" />
</form>
</div>
<a ngbNavLink>Preview</a>
<ng-template ngbNavContent *ngIf="pdfPreview.offsetParent == undefined">
<ng-container *ngIf="getContentType() == 'application/pdf'">
<div class="preview-sticky pdf-viewer-container" *ngIf="!useNativePdfViewer ; else nativePdfViewer">
<pdf-viewer [src]="previewUrl" [original-size]="false" [show-borders]="true" [show-all]="true" [render-text-mode]="2"></pdf-viewer>
</div>
</ng-template>
<ng-template #nativePdfViewer>
<object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object>
</ng-template>
</ng-container>
<ng-container *ngIf="getContentType() == 'text/plain'">
<object [data]="previewUrl | safeUrl" type="text/plain" class="preview-sticky bg-white" width="100%"></object>
</ng-container>
</ng-template>
</li>
</ul>
@@ -167,10 +160,10 @@
</form>
</div>
<div class="col-md-6 col-xl-8 mb-3 d-none d-md-block position-relative" #pdfPreview>
<div class="col-md-6 col-xl-8 mb-3 d-none d-md-block" #pdfPreview>
<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>
<pdf-viewer [src]="previewUrl" [original-size]="false" [show-borders]="true" [show-all]="true" [(page)]="previewCurrentPage" [render-text-mode]="2" (after-load-complete)="pdfPreviewLoaded($event)"></pdf-viewer>
</div>
<ng-template #nativePdfViewer>
<object [data]="previewUrl | safeUrl" class="preview-sticky" width="100%"></object>
@@ -179,11 +172,5 @@
<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">
<form>
<input autocomplete="" class="form-control" i18n-placeholder placeholder="Enter Password" type="password" (keyup)="onPasswordKeyUp($event)" />
</form>
</div>
</div>
</div>

View File

@@ -17,10 +17,3 @@
--page-margin: 1px 0 -8px;
width: 100% !important;
}
.password-prompt {
position: absolute;
top: 30%;
left: 30%;
right: 30%;
}

View File

@@ -1,4 +1,10 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'
import {
Component,
OnInit,
OnDestroy,
ViewChild,
ElementRef,
} from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { ActivatedRoute, Router } from '@angular/router'
import { NgbModal, NgbNav } from '@ng-bootstrap/ng-bootstrap'
@@ -84,11 +90,6 @@ export class DocumentDetailComponent
isDirty$: Observable<boolean>
unsubscribeNotifier: Subject<any> = new Subject()
requiresPassword: boolean = false
password: string
ogDate: Date
@ViewChild('nav') nav: NgbNav
@ViewChild('pdfPreview') set pdfPreview(element) {
// this gets called when compontent added or removed from DOM
@@ -144,21 +145,7 @@ export class DocumentDetailComponent
ngOnInit(): void {
this.documentForm.valueChanges
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe((changes) => {
if (this.ogDate) {
let newDate = new Date(changes['created'])
newDate.setHours(
this.ogDate.getHours(),
this.ogDate.getMinutes(),
this.ogDate.getSeconds(),
this.ogDate.getMilliseconds()
)
this.documentForm.patchValue(
{ created: this.formatDate(newDate) },
{ emitEvent: false }
)
}
.subscribe((wow) => {
Object.assign(this.document, this.documentForm.value)
})
@@ -199,25 +186,17 @@ export class DocumentDetailComponent
this.updateComponent(doc)
}
this.ogDate = new Date(doc.created)
// Initialize dirtyCheck
this.store = new BehaviorSubject({
title: doc.title,
content: doc.content,
created: this.formatDate(this.ogDate),
created: doc.created,
correspondent: doc.correspondent,
document_type: doc.document_type,
archive_serial_number: doc.archive_serial_number,
tags: [...doc.tags],
})
// ensure we're always starting with 24-char ISO8601 string
this.documentForm.patchValue(
{ created: this.formatDate(this.ogDate) },
{ emitEvent: false }
)
this.isDirty$ = dirtyCheck(
this.documentForm,
this.store.asObservable()
@@ -471,22 +450,5 @@ export class DocumentDetailComponent
pdfPreviewLoaded(pdf: PDFDocumentProxy) {
this.previewNumPages = pdf.numPages
if (this.password) this.requiresPassword = false
}
onError(event) {
if (event.name == 'PasswordException') {
this.requiresPassword = true
}
}
onPasswordKeyUp(event: KeyboardEvent) {
if ('Enter' == event.key) {
this.password = (event.target as HTMLInputElement).value
}
}
formatDate(date: Date): string {
return date.toISOString().split('.')[0] + 'Z'
}
}

View File

@@ -57,18 +57,13 @@
</div>
<div class="col-auto ms-auto mb-2 mb-xl-0 d-flex">
<div class="btn-group btn-group-sm me-2">
<button type="button" [disabled]="awaitingDownload" class="btn btn-outline-primary btn-sm" (click)="downloadSelected()">
<svg *ngIf="!awaitingDownload" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
<button type="button" class="btn btn-outline-primary btn-sm" (click)="downloadSelected()">
<svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#download" />
</svg>
<div *ngIf="awaitingDownload" class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Preparing download...</span>
</div>
&nbsp;
<ng-container i18n>Download</ng-container>
</svg>&nbsp;<ng-container i18n>Download</ng-container>
</button>
<div class="btn-group" ngbDropdown role="group" aria-label="Button group with nested dropdown">
<button [disabled]="awaitingDownload" class="btn btn-outline-primary btn-sm dropdown-toggle-split" ngbDropdownToggle></button>
<button class="btn btn-outline-primary btn-sm dropdown-toggle-split" ngbDropdownToggle></button>
<div class="dropdown-menu shadow" ngbDropdownMenu>
<button ngbDropdownItem i18n (click)="downloadSelected('originals')">Download originals</button>
</div>

View File

@@ -39,7 +39,6 @@ export class BulkEditorComponent {
tagSelectionModel = new FilterableDropdownSelectionModel()
correspondentSelectionModel = new FilterableDropdownSelectionModel()
documentTypeSelectionModel = new FilterableDropdownSelectionModel()
awaitingDownload: boolean
constructor(
private documentTypeService: DocumentTypeService,
@@ -318,12 +317,10 @@ export class BulkEditorComponent {
}
downloadSelected(content = 'archive') {
this.awaitingDownload = true
this.documentService
.bulkDownload(Array.from(this.list.selected), content)
.subscribe((result: any) => {
saveAs(result, 'documents.zip')
this.awaitingDownload = false
})
}
}

View File

@@ -1,3 +1,5 @@
@import "/src/theme";
.result-content {
overflow-wrap: anywhere;
}
@@ -58,7 +60,7 @@
}
.doc-img-background-selected {
background-color: var(--pngx-primary-faded);
background-color: var(--ngx-primary-faded);
}
.card-info {

View File

@@ -1,3 +1,5 @@
@import "/src/theme";
.card-text {
font-size: 90%;
}
@@ -43,7 +45,7 @@
}
.doc-img-background-selected {
background-color: var(--pngx-primary-faded);
background-color: var(--ngx-primary-faded);
}
.card-info {

View File

@@ -75,7 +75,7 @@
</app-page-header>
<div class="row sticky-top pt-4 pb-2 pb-lg-4 bg-body">
<div class="sticky-top py-2 mt-n2 mt-sm-n3 py-sm-4 bg-body mx-n3 px-3">
<app-filter-editor [hidden]="isBulkEditing" [(filterRules)]="list.filterRules" [unmodifiedFilterRules]="unmodifiedFilterRules" #filterEditor></app-filter-editor>
<app-bulk-editor [hidden]="!isBulkEditing"></app-bulk-editor>
</div>
@@ -100,7 +100,7 @@
<ng-container *ngTemplateOutlet="pagination"></ng-container>
<ng-container *ngIf="list.error ; else documentListNoError">
<div class="alert alert-danger" role="alert"><ng-container i18n>Error while loading documents</ng-container>: {{list.error}}</div>
<div class="alert alert-danger" role="alert">Error while loading documents: {{list.error}}</div>
</ng-container>
<ng-template #documentListNoError>
@@ -185,7 +185,7 @@
</tbody>
</table>
<div class="row row-cols-paperless-cards" *ngIf="displayMode == 'smallCards'">
<div class="m-n2 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)" (clickDocumentType)="clickDocumentType($event)"></app-document-card-small>
</div>
<div *ngIf="list.documents?.length > 15" class="mt-3">

View File

@@ -1,13 +1,11 @@
::ng-deep app-document-list app-page-header > div.mb-3 {
margin-bottom: 0 !important;
}
@import "/src/theme";
tr {
user-select: none;
}
.table-row-selected {
background-color: var(--pngx-primary-faded);
background-color: var(--ngx-primary-faded);
}
$paperless-card-breakpoints: (

View File

@@ -243,12 +243,8 @@ export class DocumentListComponent implements OnInit, OnDestroy, AfterViewInit {
$localize`View "${savedView.name}" created successfully.`
)
},
error: (httpError) => {
let error = httpError.error
if (error.filter_rules) {
error.filter_rules = error.filter_rules.map((r) => r.value)
}
modal.componentInstance.error = error
error: (error) => {
modal.componentInstance.error = error.error
modal.componentInstance.buttonsEnabled = true
},
})

View File

@@ -50,7 +50,7 @@
</div>
</div>
<div class="w-100 d-xl-none"></div>
<div class="col col-xl-auto">
<div class="col col-xl-auto mb-2 mb-xl-0">
<button class="btn btn-link btn-sm px-0 mx-0 ms-xl-n3" [disabled]="!rulesModified" (click)="resetSelected()">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x me-1" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<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"/>

View File

@@ -8,11 +8,6 @@
<app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text>
<app-input-check i18n-title title="Show in sidebar" formControlName="showInSideBar"></app-input-check>
<app-input-check i18n-title title="Show on dashboard" formControlName="showOnDashboard"></app-input-check>
<div *ngIf="error?.filter_rules" class="alert alert-danger" role="alert">
<h6 class="alert-heading" i18n>Filter rules error occurred while saving this view</h6>
<ng-container i18n>The error returned was</ng-container>:<br/>
{{ error.filter_rules }}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="cancel()" i18n [disabled]="!buttonsEnabled">Cancel</button>

View File

@@ -7,7 +7,7 @@
<ul ngbNav #nav="ngbNav" class="nav-tabs">
<li [ngbNavItem]="1">
<a ngbNavLink i18n>General</a>
<a ngbNavLink i18n>General settings</a>
<ng-template ngbNavContent>
<h4 i18n>Appearance</h4>
@@ -104,7 +104,7 @@
<div class="col-md-3 col-form-label">
<span i18n>Theme Color</span>
</div>
<div class="col col-md-3">
<div class="col-3">
<app-input-color i18n-title formControlName="themeColor" [error]="error?.color"></app-input-color>
</div>
<div class="col-2">

View File

@@ -9,7 +9,7 @@ import {
import { PaperlessDocument } from '../data/paperless-document'
import { PaperlessSavedView } from '../data/paperless-saved-view'
import { DOCUMENT_LIST_SERVICE } from '../data/storage-keys'
import { DocumentService, DOCUMENT_SORT_FIELDS } from './rest/document.service'
import { DocumentService } from './rest/document.service'
import { SettingsService, SETTINGS_KEYS } from './settings.service'
/**
@@ -160,24 +160,7 @@ export class DocumentListViewService {
activeListViewState.currentPage = 1
this.reload()
} else {
let errorMessage
if (
typeof error.error !== 'string' &&
Object.keys(error.error).length > 0
) {
// e.g. { archive_serial_number: Array<string> }
errorMessage = Object.keys(error.error)
.map((fieldName) => {
const fieldError: Array<string> = error.error[fieldName]
return `${
DOCUMENT_SORT_FIELDS.find((f) => f.field == fieldName)?.name
}: ${fieldError[0]}`
})
.join(', ')
} else {
errorMessage = error.error
}
this.error = errorMessage
this.error = error.error
}
},
})

View File

@@ -1,23 +0,0 @@
import { HttpClient } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { map, Observable } from 'rxjs'
import { environment } from 'src/environments/environment'
export interface AppRemoteVersion {
version: string
update_available: boolean
feature_is_set: boolean
}
@Injectable({
providedIn: 'root',
})
export class RemoteVersionService {
constructor(private http: HttpClient) {}
public checkForUpdates(): Observable<AppRemoteVersion> {
return this.http.get<AppRemoteVersion>(
`${environment.apiBaseUrl}remote_version/`
)
}
}

View File

@@ -9,11 +9,7 @@ import {
} from '@angular/core'
import { Meta } from '@angular/platform-browser'
import { CookieService } from 'ngx-cookie-service'
import {
BRIGHTNESS,
estimateBrightnessForColor,
hexToHsl,
} from 'src/app/utils/color'
import { hexToHsl } from 'src/app/utils/color'
export interface PaperlessSettings {
key: string
@@ -136,41 +132,28 @@ export class SettingsService {
: this.renderer.removeClass(this.document.body, 'color-scheme-dark')
}
// remove these in case they were there
this.renderer.removeClass(this.document.body, 'primary-dark')
this.renderer.removeClass(this.document.body, 'primary-light')
if (themeColor) {
const hsl = hexToHsl(themeColor)
const bgBrightnessEstimate = estimateBrightnessForColor(themeColor)
if (bgBrightnessEstimate == BRIGHTNESS.DARK) {
this.renderer.addClass(this.document.body, 'primary-dark')
this.renderer.removeClass(this.document.body, 'primary-light')
} else {
this.renderer.addClass(this.document.body, 'primary-light')
this.renderer.removeClass(this.document.body, 'primary-dark')
}
this.renderer.setStyle(
document.body,
document.documentElement,
'--pngx-primary',
`${+hsl.h * 360},${hsl.s * 100}%`,
RendererStyleFlags2.DashCase
)
this.renderer.setStyle(
document.body,
document.documentElement,
'--pngx-primary-lightness',
`${hsl.l * 100}%`,
RendererStyleFlags2.DashCase
)
} else {
this.renderer.removeStyle(
document.body,
document.documentElement,
'--pngx-primary',
RendererStyleFlags2.DashCase
)
this.renderer.removeStyle(
document.body,
document.documentElement,
'--pngx-primary-lightness',
RendererStyleFlags2.DashCase
)

View File

@@ -1,74 +0,0 @@
import { Injectable } from '@angular/core'
import { HttpEventType } from '@angular/common/http'
import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop'
import {
ConsumerStatusService,
FileStatusPhase,
} from './consumer-status.service'
import { DocumentService } from './rest/document.service'
import { Subscription } from 'rxjs'
@Injectable({
providedIn: 'root',
})
export class UploadDocumentsService {
private uploadSubscriptions: Array<Subscription> = []
constructor(
private documentService: DocumentService,
private consumerStatusService: ConsumerStatusService
) {}
uploadFiles(files: NgxFileDropEntry[]) {
for (const droppedFile of files) {
if (droppedFile.fileEntry.isFile) {
const fileEntry = droppedFile.fileEntry as FileSystemFileEntry
fileEntry.file((file: File) => {
let formData = new FormData()
formData.append('document', file, file.name)
let status = this.consumerStatusService.newFileUpload(file.name)
status.message = $localize`Connecting...`
this.uploadSubscriptions[file.name] = this.documentService
.uploadDocument(formData)
.subscribe({
next: (event) => {
if (event.type == HttpEventType.UploadProgress) {
status.updateProgress(
FileStatusPhase.UPLOADING,
event.loaded,
event.total
)
status.message = $localize`Uploading...`
} else if (event.type == HttpEventType.Response) {
status.taskId = event.body['task_id']
status.message = $localize`Upload complete, waiting...`
this.uploadSubscriptions[file.name]?.complete()
}
},
error: (error) => {
switch (error.status) {
case 400: {
this.consumerStatusService.fail(
status,
error.error.document
)
break
}
default: {
this.consumerStatusService.fail(
status,
$localize`HTTP error: ${error.status} ${error.statusText}`
)
break
}
}
this.uploadSubscriptions[file.name]?.complete()
},
})
})
}
}
}
}

View File

@@ -1,9 +1,4 @@
import { HSL, RGB } from 'ngx-color'
export const BRIGHTNESS = {
LIGHT: 'light',
DARK: 'dark',
}
import { HSL } from 'ngx-color'
function componentToHex(c) {
var hex = Math.floor(c).toString(16)
@@ -91,42 +86,14 @@ export function rgbToHsl(r, g, b) {
}
export function hexToHsl(hex: string): HSL {
const rgb = hexToRGB(hex)
const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b)
return { h: hsl[0], s: hsl[1], l: hsl[2] }
}
export function hexToRGB(hex: string): RGB {
hex = hex.replace('#', '')
let aRgbHex = hex.match(/.{1,2}/g)
return {
r: parseInt(aRgbHex[0], 16),
g: parseInt(aRgbHex[1], 16),
b: parseInt(aRgbHex[2], 16),
}
}
export function computeLuminance(color: RGB) {
// Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
const colorKeys = Object.keys(color)
for (var i = 0; i < 3; i++) {
var rgb = color[colorKeys[i]]
rgb /= 255
rgb = rgb < 0.03928 ? rgb / 12.92 : Math.pow((rgb + 0.055) / 1.055, 2.4)
color[i] = rgb
}
return 0.2126 * color[0] + 0.7152 * color[1] + 0.0722 * color[2]
}
export function estimateBrightnessForColor(colorHex: string) {
// See <https://www.w3.org/TR/WCAG20/#contrast-ratiodef>
// Adapted from https://api.flutter.dev/flutter/material/ThemeData/estimateBrightnessForColor.html
const rgb = hexToRGB(colorHex)
const luminance = computeLuminance(rgb)
const kThreshold = 0.15
return (luminance + 0.05) * (luminance + 0.05) > kThreshold
? BRIGHTNESS.LIGHT
: BRIGHTNESS.DARK
const hsl = rgbToHsl(
parseInt(aRgbHex[0], 16),
parseInt(aRgbHex[1], 16),
parseInt(aRgbHex[2], 16)
)
return { h: hsl[0], s: hsl[1], l: hsl[2] }
}
export function randomColor() {

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 861 KiB

After

Width:  |  Height:  |  Size: 579 KiB

View File

@@ -5,7 +5,7 @@ export const environment = {
apiBaseUrl: document.baseURI + 'api/',
apiVersion: '2',
appTitle: 'Paperless-ngx',
version: '1.7.0',
version: '1.6.0',
webSocketHost: window.location.host,
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
webSocketBaseUrl: base_url.pathname + 'ws/',

View File

@@ -616,7 +616,7 @@
<context context-type="linenumber">4</context>
</context-group>
<note priority="1" from="description">This button dismisses all status messages about processed documents on the dashboard (failed and successful)</note>
<target state="translated">Ryd afsluttede</target>
<target state="translated">Afskedig afsluttede</target>
</trans-unit>
<trans-unit id="118343233500414755" datatype="html">
<source>Drop documents here or</source>
@@ -2016,7 +2016,7 @@
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">17</context>
</context-group>
<target state="translated">Visningssprog</target>
<target state="translated">Visning sprog</target>
</trans-unit>
<trans-unit id="53523152145406584" datatype="html">
<source>You need to reload the page after applying a new language.</source>
@@ -2232,7 +2232,7 @@
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">163</context>
</context-group>
<target state="translated">Ingen gemte visninger.</target>
<target state="translated">Ingen gemte visninger angivet.</target>
</trans-unit>
<trans-unit id="5610279464668232148" datatype="html" approved="yes">
<source>Saved view &quot;<x id="PH" equiv-text="savedView.name"/>&quot; deleted.</source>

View File

@@ -2136,7 +2136,7 @@
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
<context context-type="linenumber">103</context>
</context-group>
<target state="final">Édition en masse</target>
<target state="final">Edition en masse</target>
</trans-unit>
<trans-unit id="8158899674926420054" datatype="html" approved="yes">
<source>Show confirmation dialogs</source>

View File

@@ -2452,7 +2452,7 @@
<context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
<context context-type="linenumber">97</context>
</context-group>
<target state="translated">Masz niezapisane zmiany.</target>
<target state="translated">Istnieją niezapisane zmiany.</target>
</trans-unit>
<trans-unit id="3305084982600522070" datatype="html">
<source>Are you sure you want to leave?</source>

View File

@@ -54,14 +54,14 @@
</context-group>
<target state="final">Документ <x id="PH" equiv-text="status.filename"/> обрабатывается paperless</target>
</trans-unit>
<trans-unit id="2173456130768795374" datatype="html" approved="yes">
<trans-unit id="2173456130768795374" datatype="html">
<source>Paperless-ngx</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">11</context>
</context-group>
<note priority="1" from="description">app title</note>
<target state="final">Paperless-ngx</target>
<target state="translated">Paperless-ngx</target>
</trans-unit>
<trans-unit id="7100953725264790651" datatype="html" approved="yes">
<source>Search documents</source>
@@ -433,7 +433,7 @@
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
<context context-type="linenumber">45</context>
</context-group>
<target state="translated">Нажмите еще раз, чтобы исключить элементы.</target>
<target state="translated">Нажмите снова, чтобы исключить элементы.</target>
</trans-unit>
<trans-unit id="7593728289020204896" datatype="html" approved="yes">
<source>Not assigned</source>

View File

@@ -1352,7 +1352,7 @@
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
<context context-type="linenumber">202</context>
</context-group>
<target state="translated">Ova radnja će trajno obrisati <x id="PH" equiv-text="this.list.selected.size"/> selektovan(a) dokument(a).</target>
<target state="translated">Ova radnja će trajno obrisati <x id="PH" equiv-text="this.list.selected.size"/> selektovane dokumente.</target>
</trans-unit>
<trans-unit id="5641451190833696892" datatype="html">
<source>This operation cannot be undone.</source>

View File

@@ -4,146 +4,27 @@ $enable-negative-margins: true;
@import "node_modules/bootstrap/scss/bootstrap";
@import "~@ng-select/ng-select/themes/default.theme.css";
@import "theme";
@import "theme_dark";
@import "print";
// Paperless-ngx styles
.toolbaricon {
width: 1.2em;
height: 1.2em;
}
.buttonicon {
width: 1.2em;
height: 1.2em;
}
.sidebaricon {
width: 16px;
height: 16px;
vertical-align: text-bottom;
}
body {
font-size: 0.875rem;
height: 100vh;
}
* {
transition: background-color 0.3s ease, border-color 0.3s ease;
}
svg.logo {
.leaf {
fill: var(--bs-primary) !important;
}
.text {
fill: var(--bs-body-color) !important;
}
}
.navbar.bg-primary {
--bs-primary: hsl(var(--pngx-primary),var(--pngx-primary-lightness));
--bs-primary-rgb: var(--bs-primary);
}
.border {
border-color: var(--bs-border-color) !important;
}
.border-end {
border-right: 1px solid var(--bs-border-color) !important;
}
.border-start {
border-left: 1px solid var(--bs-border-color) !important;
}
.border-bottom {
border-bottom: 1px solid var(--bs-border-color) !important;
}
.nav-link, .list-group-item {
color: var(--bs-body-color);
}
.bg-body {
background-color: var(--bs-body-bg);
}
.bg-primary {
background-color: var(--bs-primary) !important;
color: var(--pngx-primary-text-contrast);
}
.navbar-brand {
color: var(--pngx-primary-text-contrast) !important;
}
.navbar .dropdown .btn {
color: var(--pngx-primary-text-contrast) !important;
}
.btn-primary {
color: var(--pngx-primary-text-contrast);
background-color: var(--bs-primary);
border-color: var(--bs-primary);
&:hover, &:focus {
background-color: var(--pngx-primary-darken-5);
border-color: var(--pngx-primary-darken-5);
}
&:disabled, &.disabled {
color: var(--pngx-primary-text-contrast);
background-color: var(--pngx-primary-darken-5) !important;
border-color: var(--pngx-primary-darken-5) !important;
}
}
.btn-outline-primary {
border-color: var(--bs-primary) !important;
color: var(--bs-primary) !important;
&:hover, &:focus, &.active, &:active {
background-color: var(--bs-primary) !important;
color: var(--bs-light) !important;
}
}
.btn-outline-secondary {
color: var(--bs-secondary);
&:hover {
color: var(--bs-light);
}
}
.nav-item .sidebaricon {
color: var(--bs-secondary);
}
.btn:focus,
.btn:active:focus,
.dropdown-item:focus,
.btn-check:focus + .btn,
.form-control:focus,
.form-check-input:focus,
.form-check-radio:focus,
.form-select:focus {
box-shadow: 0 0 0 0.25rem hsla(var(--pngx-primary), var(--pngx-primary-lightness), var(--pngx-focus-alpha));
}
.form-switch .form-check-input:focus {
background-image: escape-svg(url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#bbb'/></svg>"));
}
.nav-item a:focus-visible {
outline: none;
background-color: var(--bs-body-bg);
}
a.navbar-brand:focus-visible {
outline: none;
color: var(--pngx-primary-darken-5);
}
.dropdown.show {
> .btn-primary {
background-color: var(--bs-primary);
border-color: var(--bs-primary);
}
> .btn-outline-primary {
color: var(--pngx-primary-text-contrast) !important;
}
}
a, a:hover, .btn-link, .btn-link:hover {
color: var(--bs-primary);
}
.form-control-dark {
@@ -235,251 +116,6 @@ a, a:hover, .btn-link, .btn-link:hover {
}
}
.form-control:not(.btn),
input,
select,
textarea,
.form-select:not(.is-invalid):not(:disabled),
.form-check-input,
.ng-select .ng-select-container .ng-value-container .ng-input > input {
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
border-color: var(--bs-border-color);
&:focus {
background-color: var(--pngx-bg-darker);
color: var(--bs-body-color);
}
}
.form-check-input:checked {
background-color: var(--bs-primary);
border-color: var(--bs-primary);
}
.form-check-input:focus {
border-color: var(--bs-primary);
}
.page-link {
color: var(--bs-secondary);
background-color: var(--bs-body-bg);
border-color: var(--bs-border-color) !important;
&:hover, &:focus {
background-color: var(--bs-primary) !important;
color: var(--bs-light) !important;
}
}
.page-item.active .page-link {
background-color: var(--bs-primary);
border-color: var(--bs-primary) !important;
color: var(--bs-light);
}
.page-item.disabled .page-link {
background-color: var(--pngx-bg-darker);
}
.nav-tabs {
border-bottom: 1px solid var(--bs-border-color);
.nav-link {
color: var(--bs-primary);
&.active, &:hover {
border-color: var(--bs-border-color);
background-color: var(--bs-body-bg);
color: var(--bs-body-color);
border-bottom: 1px solid transparent;
}
&:focus {
border-color: var(--bs-border-color);
}
&.active:focus, &:active {
border-bottom: 1px solid transparent;
}
}
}
.ng-select-container,
.ng-select.ng-select-opened > .ng-select-container,
.ng-dropdown-panel,
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option {
background-color: var(--bs-body-bg) !important;
color: var(--bs-body-color) !important;
border-color: var(--bs-border-color) !important;
input:focus {
background-color: transparent !important;
}
}
.input-group-text {
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
border-color: var(--bs-border-color);
}
.list-group-item {
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
border-color: var(--bs-border-color);
&:hover, &:focus {
background-color: var(--bs-body-bg);
}
}
.dropdown-menu {
background-color: var(--bs-body-bg);
.dropdown-divider {
border-color: var(--bs-border-color);
}
.dropdown-item {
color: var(--bs-body-color);
&:hover, &:focus {
background-color: var(--pngx-bg-darker);
color: var(--bs-body-color);
}
&.active {
background-color: var(--bs-primary);
color: var(--pngx-primary-text-contrast);
}
}
}
.doc-img-container {
border: none !important;
border-top-left-radius: .25rem;
border-top-right-radius: .25rem;
overflow: hidden;
}
// icons
.toolbaricon {
width: 1.2em;
height: 1.2em;
}
.buttonicon {
width: 1.2em;
height: 1.2em;
}
.sidebaricon {
width: 16px;
height: 16px;
vertical-align: text-bottom;
}
table.table {
color: var(--bs-body-color);
.des,.asc {
background-color: var(--bs-body-bg) !important;
}
}
.close {
color: var(--bs-body-color);
}
.modal .btn-close {
color: var(--bs-body-color);
}
.main-dropzone {
height: 100%;
width: 100%;
&.ngx-file-drop__drop-zone--over {
background-color: transparent !important;
}
}
.global-dropzone-overlay {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(23, 84, 31, .8);
z-index: 1055; // $zindex-modal
pointer-events: none !important;
user-select: none !important;
text-align: center;
padding-top: 25%;
&.show {
opacity: 1 !important;
}
&.hide {
display: none;
}
}
.ngx-file-drop__drop-zone--over .global-dropzone-overlay {
opacity: 0;
}
.inert {
pointer-events: none !important;
user-select: none !important;
}
.alert-danger {
color: var(--bs-body-color);
background-color: var(--bs-danger);
border-color: var(--bs-danger);
}
.progress {
background-color: var(--bs-body-bg);
}
.ngb-dp-header,
.ngb-dp-weekdays,
.ngb-dp-month {
background-color: var(--bs-body-bg);
}
.popover {
.popover-header,
.popover-body {
background-color: var(--pngx-bg-alt);
border-color: var(--bs-border-color);
color: var(--bs-body-color);
}
}
// fix popover carat colors
.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="left"] {
border-left-color: var(--pngx-bg-alt);
}
.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="right"] {
border-right-color: var(--pngx-bg-alt);
}
.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="top"] {
border-top-color: var(--pngx-bg-alt);
}
.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="bottom"] {
border-bottom-color: var(--pngx-bg-alt);
}
.bs-popover-bottom .popover-header::before,
.bs-popover-auto[x-placement^=bottom] .popover-header::before {
border-bottom-color: var(--pngx-bg-alt);
}
// Bootstrap 5 tweaks
a.badge {
text-decoration: none;

View File

@@ -1,222 +1,294 @@
@mixin paperless-green {
:root {
// base color e.g. #17541f = hsl(128, 57%, 21%)
--pngx-primary: 128, 57%;
--pngx-primary-lightness: 21%;
}
body {
@include paperless-green;
--pngx-primary-text-contrast: var(--bs-light);
--bs-primary: hsl(var(--pngx-primary), var(--pngx-primary-lightness));
--bs-border-color: var(--bs-gray-400);
--pngx-primary-faded: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 72%));
--pngx-primary-lighten-30: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 30%));
--pngx-primary-darken-5: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 5%));
--pngx-primary-darken-15: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 15%));
--pngx-primary-darken-18: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 18%));
--pngx-bg-alt: #fff;
--pngx-bg-darker: var(--bs-gray-100);
--pngx-focus-alpha: 0.3;
--ngx-primary-faded: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 72%));
--ngx-primary-lighten-10: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 10%));
--ngx-primary-lighten-30: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 30%));
--ngx-primary-darken-10: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 10%));
--ngx-primary-darken-15: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 15%));
--ngx-primary-darken-18: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 18%));
--ngx-bg-darker: var(--bs-gray-100);
--ngx-focus-alpha: 0.3;
}
// Dark text colors allow for maintain contrast with theme color changes
$text-color-light-bg: #212529;
$text-color-dark-bg: #abb2bf;
$text-color-dark-bg-accent: lighten($text-color-dark-bg, 10%);
// Taken from bootstrap
$form-check-input-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'><path fill='none' stroke='#{$text-color-light-bg}' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/></svg>");
$form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='2' fill='#{$text-color-light-bg}'/></svg>");
.primary-light {
--pngx-primary-text-contrast: #{$text-color-light-bg} !important;
.form-check-input:checked[type=checkbox] {
background-image: escape-svg($form-check-input-checked-bg-image-dark);
svg.logo {
.leaf {
fill: var(--bs-primary) !important;
}
.form-check-input:checked[type=radio] {
background-image: escape-svg($form-check-radio-checked-bg-image-dark);
}
.btn-close {
filter: none !important;
.text {
fill: var(--bs-body-color) !important;
}
}
.primary-dark {
--pngx-primary-text-contrast: #{$text-color-dark-bg} !important;
.nav-link, .list-group-item {
color: var(--bs-body-color);
}
// Dark mode
@mixin paperless-green-dark-mode {
--pngx-primary-lightness: 31%;
.bg-body {
background-color: var(--bs-body-bg);
}
@mixin dark-mode {
--bs-body-color: #{$text-color-dark-bg};
--pngx-body-color-accent: #{$text-color-dark-bg-accent};
--bs-danger: #b71631;
--bs-danger-rgb: 183, 22, 49;
--bs-body-bg: #161618;
--bs-body-bg-rgb: 22, 22, 24;
--bs-light: #1c1c1f;
--bs-light-rgb: 28, 28, 31;
--bs-border-color: #47494f;
--pngx-bg-darker: #101216;
--pngx-bg-alt: #242529;
--pngx-focus-alpha: 0.6;
--pngx-primary-faded: var(--pngx-primary-darken-15);
--pngx-primary-text-contrast: var(--bs-body-color);
.bg-primary {
background-color: var(--bs-primary) !important;
}
.text-dark, .text-light {
.btn-primary {
background-color: var(--bs-primary);
border-color: var(--bs-primary);
&:hover, &:focus {
background-color: var(--ngx-primary-darken-10);
border-color: var(--ngx-primary-darken-10);
}
&:disabled, &.disabled {
background-color: var(--ngx-primary-darken-10) !important;
border-color: var(--ngx-primary-darken-10) !important;
}
}
.btn-outline-primary {
border-color: var(--bs-primary) !important;
color: var(--bs-primary) !important;
&:hover, &:focus, &.active, &:active {
background-color: var(--bs-primary) !important;
color: var(--bs-light) !important;
}
}
.btn-outline-secondary {
color: var(--bs-secondary);
}
.nav-item .sidebaricon {
color: var(--bs-secondary);
}
.btn:focus,
.btn:active:focus,
.dropdown-item:focus,
.btn-check:focus + .btn,
.form-control:focus,
.form-check-input:focus,
.form-check-radio:focus,
.form-select:focus {
box-shadow: 0 0 0 0.25rem hsla(var(--pngx-primary), var(--pngx-primary-lightness), var(--ngx-focus-alpha));
}
.form-switch .form-check-input:focus {
background-image: escape-svg(url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#bbb'/></svg>"));
}
.nav-link:focus-visible, .nav-item a:focus-visible {
outline: none;
background-color: var(--ngx-bg-darker);
}
a.navbar-brand:focus-visible {
outline: none;
color: var(--ngx-primary-darken-10);
}
.dropdown.show {
> .btn-primary {
background-color: var(--bs-primary);
border-color: var(--bs-primary);
}
> .btn-outline-primary {
color: var(--bs-body-color) !important;
}
}
.btn-primary {
&:hover, &:focus, &.active, &:active {
color: var(--bs-body-color) !important;
a, a:hover, .btn-link, .btn-link:hover {
color: var(--bs-primary);
}
.form-control:not(.btn),
input,
select,
textarea,
.form-select:not(.is-invalid):not(:disabled),
.form-check-input {
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
border-color: var(--bs-border-color);
&:focus {
background-color: var(--ngx-bg-darker);
color: var(--bs-body-color) !important;
}
}
.form-check-input:checked {
background-color: var(--bs-primary);
border-color: var(--bs-primary);
}
.form-check-input:focus {
border-color: var(--bs-primary);
}
.page-link {
color: var(--bs-secondary);
background-color: var(--bs-body-bg);
border-color: var(--bs-border-color) !important;
&:hover, &:focus {
background-color: var(--bs-primary) !important;
color: var(--bs-light) !important;
}
}
.page-item.active .page-link {
background-color: var(--bs-primary);
border-color: var(--bs-primary) !important;
color: var(--bs-light);
}
.page-item.disabled .page-link {
background-color: var(--ngx-bg-darker);
}
.nav-tabs {
border-bottom: 1px solid var(--bs-border-color);
.nav-link {
color: var(--bs-primary);
&.active, &:hover {
border-color: var(--bs-border-color);
background-color: var(--bs-body-bg);
color: var(--bs-body-color);
border-bottom: 1px solid transparent;
}
&:focus {
border-color: var(--bs-border-color);
}
&.active:focus, &:active {
border-bottom: 1px solid transparent;
}
}
}
.btn-outline-primary {
&:hover, &:focus, &.active, &:active {
color: var(--bs-light) !important;
}
.ng-select-container,
.ng-select.ng-select-opened > .ng-select-container,
.ng-dropdown-panel,
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option {
background-color: var(--bs-body-bg) !important;
color: var(--bs-body-color) !important;
border-color: var(--bs-border-color) !important;
input:focus {
background-color: transparent !important;
}
}
.btn-outline-secondary {
&:hover, &:focus, &.active, &:active {
background-color: var(--pngx-bg-darker);
color: var(--bs-primary);
}
}
.input-group-text {
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
border-color: var(--bs-border-color);
}
.btn-light {
color: var(--bs-body-color);
}
.list-group-item {
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
border-color: var(--bs-border-color);
.btn .progress {
background-color: var(--pngx-body-color-accent);
}
.search-form-container {
input, input:focus {
color: var(--bs-body-color) !important;
}
}
.card {
&:hover, &:focus {
background-color: var(--bs-body-bg);
.card-header {
background-color: rgba(0, 0, 0, 0.12);
}
}
}
.modal-content, .modal-header, .modal-body, .modal-footer {
background-color: var(--bs-body-bg);
.dropdown-menu {
background-color: var(--bs-body-bg);
.dropdown-divider {
border-color: var(--bs-border-color);
}
app-tag .badge {
filter: brightness(.8);
}
.doc-img {
mix-blend-mode: normal;
border-radius: 0;
border-color: var(--bs-border-color);
filter: invert(10%);
&.border-end {
border-right: none !important;
}
}
.doc-img.inverted {
filter: invert(95%) hue-rotate(180deg);
}
.card-selected .doc-img {
mix-blend-mode: luminosity;
}
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option:hover,
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked {
background-color: var(--bs-light);
}
table {
.des,
.asc {
&::after {
filter: invert(0.8); /* arrow is a black inline png bkgd image (!) so use filter */
}
}
&.table-hover > tbody > tr:hover > * {
background-color: var(--bs-light);
color: var(--pngx-body-color-accent);
}
}
.alert-secondary {
background-color: var(--bs-light);
border-color: var(--pngx-bg-darker);
.dropdown-item {
color: var(--bs-body-color);
}
.table-striped > tbody > tr:nth-of-type(odd) > * {
color: var(--pngx-body-color-accent);
}
.close, .modal .btn-close, .alert .btn-close {
text-shadow: 0 1px 0 #666;
}
.modal .btn-close, .alert .btn-close, .toast .btn-close {
filter: invert(1) grayscale(100%) brightness(200%);
}
.toast, .toast-header {
background-color: hsla(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 15%), 0.8);
}
.toast,
.toast .toast-header,
.toast .btn-close {
color: var(--pngx-primary-text-contrast);
}
}
body.color-scheme-dark {
// no custom theme color
&:not(.primary-light):not(.primary-dark) {
@include paperless-green-dark-mode;
.navbar.bg-primary {
// navbar is og green in dark mode
@include paperless-green;
}
}
@include dark-mode;
}
@media (prefers-color-scheme: dark) {
body.color-scheme-system {
// no custom theme color
&:not(.primary-light):not(.primary-dark) {
@include paperless-green-dark-mode;
.navbar.bg-primary {
// navbar is og green in dark mode
@include paperless-green;
}
&:hover {
background-color: var(--ngx-bg-darker);
color: var(--bs-body-color);
}
@include dark-mode;
&.active {
background-color: var(--bs-primary);
}
}
}
table.table {
color: var(--bs-body-color);
.des,.asc {
background-color: var(--bs-body-bg) !important;
}
}
.close {
color: var(--bs-body-color);
}
.modal .btn-close {
color: var(--bs-body-color);
}
.ngx-file-drop__drop-zone--over {
background-color: var(--ngx-primary-faded) !important;
}
.alert-danger {
color: var(--bs-body-color);
background-color: var(--bs-danger);
border-color: var(--bs-danger);
}
.alert-secondary {
background-color: var(--ngx-primary-darken-18);
border-color: var(--ngx-primary-darken-15);
color: var(--bs-body-color);
}
.ngb-dp-header,
.ngb-dp-weekdays,
.ngb-dp-month {
background-color: var(--bs-body-bg);
}
.popover {
.popover-header,
.popover-body {
background-color: var(--ngx-bg-alt);
border-color: var(--bs-border-color);
}
}
// fix popover carat colors
.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="left"] {
border-left-color: var(--ngx-bg-alt);
}
.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="right"] {
border-right-color: var(--ngx-bg-alt);
}
.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="top"] {
border-top-color: var(--ngx-bg-alt);
}
.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^="bottom"] {
border-bottom-color: var(--ngx-bg-alt);
}
.bs-popover-bottom .popover-header::before,
.bs-popover-auto[x-placement^=bottom] .popover-header::before {
border-bottom-color: var(--ngx-bg-alt);
}

168
src-ui/src/theme_dark.scss Normal file
View File

@@ -0,0 +1,168 @@
$primary-dark-mode: #45973a;
$primary-dark-mode-rgb: 69, 151, 58;
$primary-dark-mode-darken-10: darken($primary-dark-mode, 10%);
$danger-dark-mode: #b71631;
$danger-dark-mode-rgb: 183, 22, 49;
$bg-dark-mode: #161618;
$bg-dark-mode-rgb: 22, 22, 24;
$bg-dark-mode-accent: #101216;
$bg-dark-mode-alt: #242529;
$bg-light-dark-mode: #1c1c1f;
$bg-light-dark-mode-rgb: 28, 28, 31;
$text-color-dark-mode: #abb2bf;
$text-color-dark-mode-accent: lighten($text-color-dark-mode, 10%);
$border-color-dark-mode: #47494f;
* {
transition: background-color 0.3s ease, border-color 0.3s ease;
}
@mixin dark-mode {
--bs-primary: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 10%));
--bs-danger: #{$danger-dark-mode};
--bs-danger-rgb: #{$danger-dark-mode-rgb};
--bs-body-bg: #{$bg-dark-mode};
--bs-body-bg-rgb: #{$bg-dark-mode-rgb};
--bs-body-color: #{$text-color-dark-mode};
--bs-light: #{$bg-light-dark-mode};
--bs-light-rgb: #{$bg-light-dark-mode-rgb};
--bs-border-color: #{$border-color-dark-mode};
--ngx-bg-darker: #{$bg-dark-mode-accent};
--ngx-bg-alt: #{$bg-dark-mode-alt};
--ngx-body-color-accent: #{$text-color-dark-mode-accent};
--ngx-focus-alpha: 0.7;
--ngx-primary-faded: var(--ngx-primary-darken-15);
.navbar.bg-primary{
--bs-primary: hsl(var(--pngx-primary),var(--pngx-primary-lightness));
--bs-primary-rgb: var(--bs-primary);
}
.navbar-brand {
color: var(--bs-body-color);
}
.border {
border-color: var(--bs-border-color) !important;
}
.border-end {
border-right: 1px solid var(--bs-border-color) !important;
}
.border-start {
border-left: 1px solid var(--bs-border-color) !important;
}
.border-bottom {
border-bottom: 1px solid var(--bs-border-color) !important;
}
.text-dark, .text-light {
color: var(--bs-body-color) !important;
}
.btn-outline-primary, .btn-primary {
&:hover, &:focus, &.active, &:active {
color: var(--ngx-body-color-accent) !important;
}
}
.btn-outline-secondary {
&:hover, &:focus, &.active, &:active {
background-color: var(--ngx-bg-darker);
color: var(--bs-primary);
}
}
.card {
background-color: var(--bs-body-bg);
.card-header {
background-color: rgba(0, 0, 0, 0.12);
}
}
.modal-content, .modal-header, .modal-body, .modal-footer {
background-color: var(--bs-body-bg);
border-color: var(--bs-border-color);
}
app-tag .badge {
filter: brightness(.8);
}
.doc-img-container {
border: none !important;
border-top-left-radius: .25rem;
border-top-right-radius: .25rem;
overflow: hidden;
}
.doc-img {
mix-blend-mode: normal;
border-radius: 0;
border-color: var(--bs-border-color);
filter: invert(10%);
&.border-end {
border-right: none !important;
}
}
.doc-img.inverted {
filter: invert(95%) hue-rotate(180deg);
}
.card-selected .doc-img {
mix-blend-mode: luminosity;
}
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option:hover,
.ng-dropdown-panel .ng-dropdown-panel-items .ng-option.ng-option-marked {
background-color: $bg-light-dark-mode;
}
table {
.des,
.asc {
&::after {
filter: invert(0.8); /* arrow is a black inline png bkgd image (!) so use filter */
}
}
&.table-hover > tbody > tr:hover > * {
background-color: $bg-light-dark-mode;
color: $text-color-dark-mode-accent;
}
}
.table-striped > tbody > tr:nth-of-type(odd) > * {
color: $text-color-dark-mode-accent;
}
.close, .modal .btn-close {
text-shadow: 0 1px 0 #666;
}
.modal .btn-close {
filter: invert(1) grayscale(100%) brightness(200%);
}
.toast {
background-color: hsla(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 18%), 0.9);
}
.toast-header {
background-color: hsla(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 10%), 0.9);
}
}
body.color-scheme-dark {
@include dark-mode;
}
body.color-scheme-system {
@media (prefers-color-scheme: dark) {
@include dark-mode;
}
}

View File

@@ -3,9 +3,7 @@ import os
from pathlib import Path
from pathlib import PurePath
from threading import Thread
from time import monotonic
from time import sleep
from typing import Final
from django.conf import settings
from django.core.management.base import BaseCommand
@@ -55,25 +53,6 @@ def _consume(filepath):
logger.warning(f"Not consuming file {filepath}: Unknown file extension.")
return
# Total wait time: up to 500ms
os_error_retry_count: Final[int] = 50
os_error_retry_wait: Final[float] = 0.01
read_try_count = 0
file_open_ok = False
while (read_try_count < os_error_retry_count) and not file_open_ok:
try:
with open(filepath, "rb"):
file_open_ok = True
except OSError:
read_try_count += 1
sleep(os_error_retry_wait)
if read_try_count >= os_error_retry_count:
logger.warning(f"Not consuming file {filepath}: OS reports file as busy still")
return
tag_ids = None
try:
if settings.CONSUMER_SUBDIRS_AS_TAGS:
@@ -102,23 +81,19 @@ def _consume_wait_unmodified(file):
logger.debug(f"Waiting for file {file} to remain unmodified")
mtime = -1
size = -1
current_try = 0
while current_try < settings.CONSUMER_POLLING_RETRY_COUNT:
try:
stat_data = os.stat(file)
new_mtime = stat_data.st_mtime
new_size = stat_data.st_size
new_mtime = os.stat(file).st_mtime
except FileNotFoundError:
logger.debug(
f"File {file} moved while waiting for it to remain " f"unmodified.",
)
return
if new_mtime == mtime and new_size == size:
if new_mtime == mtime:
_consume(file)
return
mtime = new_mtime
size = new_size
sleep(settings.CONSUMER_POLLING_DELAY)
current_try += 1
@@ -207,32 +182,14 @@ class Command(BaseCommand):
descriptor = inotify.add_watch(directory, inotify_flags)
try:
inotify_debounce: Final[float] = 0.5
notified_files = {}
while not self.stop_flag:
for event in inotify.read(timeout=1000):
if recursive:
path = inotify.get_path(event.wd)
else:
path = directory
filepath = os.path.join(path, event.name)
notified_files[filepath] = monotonic()
# Check the files against the timeout
still_waiting = {}
for filepath in notified_files:
# Time of the last inotify event for this file
last_event_time = notified_files[filepath]
if (monotonic() - last_event_time) > inotify_debounce:
_consume(filepath)
else:
still_waiting[filepath] = last_event_time
# These files are still waiting to hit the timeout
notified_files = still_waiting
_consume(filepath)
except KeyboardInterrupt:
pass

View File

@@ -60,7 +60,7 @@ def match_tags(document, classifier):
def matches(matching_model, document):
search_kwargs = {}
document_content = document.content
document_content = document.content.lower()
# Check that match is not empty
if matching_model.match.strip() == "":

View File

@@ -1,20 +0,0 @@
# Generated by Django 4.0.3 on 2022-04-01 22:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("documents", "1017_alter_savedviewfilterrule_rule_type"),
]
operations = [
migrations.AlterField(
model_name="savedviewfilterrule",
name="value",
field=models.CharField(
blank=True, max_length=255, null=True, verbose_name="value"
),
),
]

View File

@@ -375,7 +375,7 @@ class SavedViewFilterRule(models.Model):
rule_type = models.PositiveIntegerField(_("rule type"), choices=RULE_TYPES)
value = models.CharField(_("value"), max_length=255, blank=True, null=True)
value = models.CharField(_("value"), max_length=128, blank=True, null=True)
class Meta:
verbose_name = _("filter rule")

View File

@@ -23,7 +23,6 @@ from documents.signals import document_consumer_declaration
# - XX. MONTH ZZZZ with XX being 1 or 2 and ZZZZ being 2 or 4 digits
# - MONTH ZZZZ, with ZZZZ being 4 digits
# - MONTH XX, ZZZZ with XX being 1 or 2 and ZZZZ being 4 digits
# - XX MON ZZZZ with XX being 1 or 2 and ZZZZ being 4 digits. MONTH is 3 letters
# TODO: isnt there a date parsing library for this?
@@ -32,8 +31,7 @@ DATE_REGEX = re.compile(
r"(\b|(?!=([_-])))([0-9]{4}|[0-9]{2})[\.\/-]([0-9]{1,2})[\.\/-]([0-9]{1,2})(\b|(?=([_-])))|" # noqa: E501
r"(\b|(?!=([_-])))([0-9]{1,2}[\. ]+[^ ]{3,9} ([0-9]{4}|[0-9]{2}))(\b|(?=([_-])))|" # noqa: E501
r"(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{1,2}, ([0-9]{4}))(\b|(?=([_-])))|"
r"(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{4})(\b|(?=([_-])))|"
r"(\b|(?!=([_-])))(\b[0-9]{1,2}[ \.\/-][A-Z]{3}[ \.\/-][0-9]{4})(\b|(?=([_-])))", # noqa: E501
r"(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{4})(\b|(?=([_-])))",
)

View File

@@ -1,12 +1,6 @@
import logging
import os
import shutil
import tempfile
from typing import List # for type hinting. Can be removed, if only Python >3.8 is used
import tqdm
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.conf import settings
from django.db.models.signals import post_save
from documents import index
@@ -20,12 +14,8 @@ from documents.models import Document
from documents.models import DocumentType
from documents.models import Tag
from documents.sanity_checker import SanityCheckFailedException
from pdf2image import convert_from_path
from pikepdf import Pdf
from pyzbar import pyzbar
from whoosh.writing import AsyncWriter
logger = logging.getLogger("paperless.tasks")
@@ -72,115 +62,6 @@ def train_classifier():
logger.warning("Classifier error: " + str(e))
def barcode_reader(image) -> List[str]:
"""
Read any barcodes contained in image
Returns a list containing all found barcodes
"""
barcodes = []
# Decode the barcode image
detected_barcodes = pyzbar.decode(image)
if detected_barcodes:
# Traverse through all the detected barcodes in image
for barcode in detected_barcodes:
if barcode.data:
decoded_barcode = barcode.data.decode("utf-8")
barcodes.append(decoded_barcode)
logger.debug(
f"Barcode of type {str(barcode.type)} found: {decoded_barcode}",
)
return barcodes
def scan_file_for_separating_barcodes(filepath: str) -> List[int]:
"""
Scan the provided file for page separating barcodes
Returns a list of pagenumbers, which separate the file
"""
separator_page_numbers = []
separator_barcode = str(settings.CONSUMER_BARCODE_STRING)
# use a temporary directory in case the file os too big to handle in memory
with tempfile.TemporaryDirectory() as path:
pages_from_path = convert_from_path(filepath, output_folder=path)
for current_page_number, page in enumerate(pages_from_path):
current_barcodes = barcode_reader(page)
if separator_barcode in current_barcodes:
separator_page_numbers.append(current_page_number)
return separator_page_numbers
def separate_pages(filepath: str, pages_to_split_on: List[int]) -> List[str]:
"""
Separate the provided file on the pages_to_split_on.
The pages which are defined by page_numbers will be removed.
Returns a list of (temporary) filepaths to consume.
These will need to be deleted later.
"""
os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
tempdir = tempfile.mkdtemp(prefix="paperless-", dir=settings.SCRATCH_DIR)
fname = os.path.splitext(os.path.basename(filepath))[0]
pdf = Pdf.open(filepath)
document_paths = []
logger.debug(f"Temp dir is {str(tempdir)}")
if not pages_to_split_on:
logger.warning("No pages to split on!")
else:
# go from the first page to the first separator page
dst = Pdf.new()
for n, page in enumerate(pdf.pages):
if n < pages_to_split_on[0]:
dst.pages.append(page)
output_filename = "{}_document_0.pdf".format(fname)
savepath = os.path.join(tempdir, output_filename)
with open(savepath, "wb") as out:
dst.save(out)
document_paths = [savepath]
# iterate through the rest of the document
for count, page_number in enumerate(pages_to_split_on):
logger.debug(f"Count: {str(count)} page_number: {str(page_number)}")
dst = Pdf.new()
try:
next_page = pages_to_split_on[count + 1]
except IndexError:
next_page = len(pdf.pages)
# skip the first page_number. This contains the barcode page
for page in range(page_number + 1, next_page):
logger.debug(
f"page_number: {str(page_number)} next_page: {str(next_page)}",
)
dst.pages.append(pdf.pages[page])
output_filename = "{}_document_{}.pdf".format(fname, str(count + 1))
logger.debug(f"pdf no:{str(count)} has {str(len(dst.pages))} pages")
savepath = os.path.join(tempdir, output_filename)
with open(savepath, "wb") as out:
dst.save(out)
document_paths.append(savepath)
logger.debug(f"Temp files are {str(document_paths)}")
return document_paths
def save_to_dir(
filepath: str,
newname: str = None,
target_dir: str = settings.CONSUMPTION_DIR,
):
"""
Copies filepath to target_dir.
Optionally rename the file.
"""
if os.path.isfile(filepath) and os.path.isdir(target_dir):
dst = shutil.copy(filepath, target_dir)
logging.debug(f"saved {str(filepath)} to {str(dst)}")
if newname:
dst_new = os.path.join(target_dir, newname)
logger.debug(f"moving {str(dst)} to {str(dst_new)}")
os.rename(dst, dst_new)
else:
logger.warning(f"{str(filepath)} or {str(target_dir)} don't exist.")
def consume_file(
path,
override_filename=None,
@@ -191,48 +72,6 @@ def consume_file(
task_id=None,
):
# check for separators in current document
if settings.CONSUMER_ENABLE_BARCODES:
separators = []
document_list = []
separators = scan_file_for_separating_barcodes(path)
if separators:
logger.debug(f"Pages with separators found in: {str(path)}")
document_list = separate_pages(path, separators)
if document_list:
for n, document in enumerate(document_list):
# save to consumption dir
# rename it to the original filename with number prefix
if override_filename:
newname = f"{str(n)}_" + override_filename
else:
newname = None
save_to_dir(document, newname=newname)
# if we got here, the document was successfully split
# and can safely be deleted
logger.debug("Deleting file {}".format(path))
os.unlink(path)
# notify the sender, otherwise the progress bar
# in the UI stays stuck
payload = {
"filename": override_filename,
"task_id": task_id,
"current_progress": 100,
"max_progress": 100,
"status": "SUCCESS",
"message": "finished",
}
try:
async_to_sync(get_channel_layer().group_send)(
"status_updates",
{"type": "status_update", "data": payload},
)
except OSError as e:
logger.warning("OSError. It could be, the broker cannot be reached.")
logger.warning(str(e))
return "File successfully split"
# continue with consumption if no barcode was found
document = Consumer().try_consume_file(
path,
override_filename=override_filename,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 891 B

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Some files were not shown because too many files have changed in this diff Show More