Merge branch 'main' into dev
91
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
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
@ -1,50 +0,0 @@
|
|||||||
---
|
|
||||||
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`.
|
|
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
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
@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
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.
|
|
||||||
|
|
||||||
-->
|
|
5
.github/release-drafter.yml
vendored
@ -1,4 +1,7 @@
|
|||||||
categories:
|
categories:
|
||||||
|
- title: 'Breaking Changes'
|
||||||
|
labels:
|
||||||
|
- 'breaking-change'
|
||||||
- title: 'Features'
|
- title: 'Features'
|
||||||
labels:
|
labels:
|
||||||
- 'enhancement'
|
- 'enhancement'
|
||||||
@ -29,6 +32,6 @@ change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
|||||||
change-title-escapes: '\<*_&#@'
|
change-title-escapes: '\<*_&#@'
|
||||||
tag-prefix: "ngx-"
|
tag-prefix: "ngx-"
|
||||||
template: |
|
template: |
|
||||||
## Changelog
|
# Changelog
|
||||||
|
|
||||||
$CHANGES
|
$CHANGES
|
||||||
|
7
.github/stale.yml
vendored
@ -2,11 +2,8 @@
|
|||||||
daysUntilStale: 30
|
daysUntilStale: 30
|
||||||
# Number of days of inactivity before a stale issue is closed
|
# Number of days of inactivity before a stale issue is closed
|
||||||
daysUntilClose: 7
|
daysUntilClose: 7
|
||||||
# Issues with these labels will never be considered stale
|
onlyLabels:
|
||||||
exemptLabels:
|
- unconfirmed
|
||||||
- pinned
|
|
||||||
- security
|
|
||||||
- fixpending
|
|
||||||
# Label to use when marking an issue as stale
|
# Label to use when marking an issue as stale
|
||||||
staleLabel: stale
|
staleLabel: stale
|
||||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
4
.github/workflows/ci.yml
vendored
@ -185,6 +185,9 @@ jobs:
|
|||||||
# build and push image to docker hub.
|
# build and push image to docker hub.
|
||||||
build-docker-image:
|
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-'))
|
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
|
runs-on: ubuntu-20.04
|
||||||
needs: [tests-backend, tests-frontend]
|
needs: [tests-backend, tests-frontend]
|
||||||
steps:
|
steps:
|
||||||
@ -195,7 +198,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
images: ghcr.io/${{ github.repository }}
|
images: ghcr.io/${{ github.repository }}
|
||||||
tags: |
|
tags: |
|
||||||
type=match,pattern=ngx-(\d.\d.\d),group=1
|
|
||||||
type=ref,event=branch
|
type=ref,event=branch
|
||||||
type=ref,event=tag
|
type=ref,event=tag
|
||||||
-
|
-
|
||||||
|
@ -6,7 +6,7 @@ WORKDIR /src/src-ui
|
|||||||
RUN npm update npm -g && npm ci --no-optional
|
RUN npm update npm -g && npm ci --no-optional
|
||||||
RUN ./node_modules/.bin/ng build --configuration production
|
RUN ./node_modules/.bin/ng build --configuration production
|
||||||
|
|
||||||
FROM ghcr.io/paperless-ngx/builder/ngx-base:dev as main-app
|
FROM ghcr.io/paperless-ngx/builder/ngx-base:1.7.0 as main-app
|
||||||
|
|
||||||
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
|
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
|
||||||
LABEL org.opencontainers.image.documentation="https://paperless-ngx.readthedocs.io/en/latest/"
|
LABEL org.opencontainers.image.documentation="https://paperless-ngx.readthedocs.io/en/latest/"
|
||||||
|
@ -69,10 +69,11 @@ services:
|
|||||||
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
PAPERLESS_TIKA_ENDPOINT: http://tika:9998
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: thecodingmachine/gotenberg
|
image: gotenberg/gotenberg:7
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
command:
|
||||||
DISABLE_GOOGLE_CHROME: 1
|
- "gotenberg"
|
||||||
|
- "--chromium-disable-routes=true"
|
||||||
|
|
||||||
tika:
|
tika:
|
||||||
image: iwishiwasaneagle/apache-tika-arm@sha256:a78c25ffe57ecb1a194b2859d42a61af46e9e845191512b8f1a4bf90578ffdfd
|
image: iwishiwasaneagle/apache-tika-arm@sha256:a78c25ffe57ecb1a194b2859d42a61af46e9e845191512b8f1a4bf90578ffdfd
|
||||||
|
592
docs/_static/css/custom.css
vendored
Normal file
@ -0,0 +1,592 @@
|
|||||||
|
/* 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
@ -1,14 +0,0 @@
|
|||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
47
docs/_static/js/darkmode.js
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
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
Before Width: | Height: | Size: 445 KiB |
BIN
docs/_static/screenshots/bulk-edit.png
vendored
Normal file
After Width: | Height: | Size: 661 KiB |
BIN
docs/_static/screenshots/correspondents.png
vendored
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 457 KiB |
BIN
docs/_static/screenshots/dashboard.png
vendored
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 436 KiB |
BIN
docs/_static/screenshots/documents-filter.png
vendored
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 462 KiB |
BIN
docs/_static/screenshots/documents-largecards.png
vendored
Before Width: | Height: | Size: 306 KiB After Width: | Height: | Size: 608 KiB |
BIN
docs/_static/screenshots/documents-smallcards-dark.png
vendored
Normal file
After Width: | Height: | Size: 698 KiB |
BIN
docs/_static/screenshots/documents-smallcards.png
vendored
Before Width: | Height: | Size: 410 KiB After Width: | Height: | Size: 706 KiB |
BIN
docs/_static/screenshots/documents-table.png
vendored
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 480 KiB |
BIN
docs/_static/screenshots/editing.png
vendored
Before Width: | Height: | Size: 293 KiB After Width: | Height: | Size: 848 KiB |
BIN
docs/_static/screenshots/logs.png
vendored
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 703 KiB |
BIN
docs/_static/screenshots/mobile.png
vendored
Before Width: | Height: | Size: 158 KiB After Width: | Height: | Size: 388 KiB |
BIN
docs/_static/screenshots/new-tag.png
vendored
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 26 KiB |
BIN
docs/_static/screenshots/search-preview.png
vendored
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 54 KiB |
BIN
docs/_static/screenshots/search-results.png
vendored
Before Width: | Height: | Size: 261 KiB After Width: | Height: | Size: 517 KiB |
13
docs/_templates/layout.html
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{% 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 %}
|
@ -369,7 +369,7 @@ the naming scheme.
|
|||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
Since this command moves you documents around alot, it is advised to to
|
Since this command moves you documents around a lot, it is advised to to
|
||||||
a backup before. The renaming logic is robust and will never overwrite
|
a backup before. The renaming logic is robust and will never overwrite
|
||||||
or delete a file, but you can't ever be careful enough.
|
or delete a file, but you can't ever be careful enough.
|
||||||
|
|
||||||
|
@ -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
|
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.
|
report your files as missing and won't be able to find them.
|
||||||
|
|
||||||
Paperless provides the following placeholders withing filenames:
|
Paperless provides the following placeholders within filenames:
|
||||||
|
|
||||||
* ``{asn}``: The archive serial number of the document, or "none".
|
* ``{asn}``: The archive serial number of the document, or "none".
|
||||||
* ``{correspondent}``: The name of the correspondent, or "none".
|
* ``{correspondent}``: The name of the correspondent, or "none".
|
||||||
|
@ -5,6 +5,87 @@
|
|||||||
Changelog
|
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
|
paperless-ngx 1.6.0
|
||||||
###################
|
###################
|
||||||
|
|
||||||
@ -35,6 +116,10 @@ Version 1.6.0 merges several pending PRs from jonaswinkler's repo and includes n
|
|||||||
* `@shamoon`_ created a slick new logo (#165).
|
* `@shamoon`_ created a slick new logo (#165).
|
||||||
* `@tim-vogel`_ fixed exports missing groups (#193).
|
* `@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:
|
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`_
|
`@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`_
|
||||||
@ -140,7 +225,7 @@ paperless-ng 1.4.0
|
|||||||
|
|
||||||
* New URL pattern for accessing documents by ASN directly (http://<paperless>/asn/123)
|
* 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.
|
* Better error logging during document consumption.
|
||||||
|
|
||||||
@ -1576,6 +1661,16 @@ bulk of the work on this big change.
|
|||||||
.. _@azapater: https://github.com/azapater
|
.. _@azapater: https://github.com/azapater
|
||||||
.. _@tim-vogel: https://github.com/tim-vogel
|
.. _@tim-vogel: https://github.com/tim-vogel
|
||||||
.. _@jschpp: https://github.com/jschpp
|
.. _@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
|
.. _#20: https://github.com/the-paperless-project/paperless/issues/20
|
||||||
.. _#44: https://github.com/the-paperless-project/paperless/issues/44
|
.. _#44: https://github.com/the-paperless-project/paperless/issues/44
|
||||||
@ -1684,6 +1779,7 @@ bulk of the work on this big change.
|
|||||||
.. _#488: https://github.com/the-paperless-project/paperless/pull/488
|
.. _#488: https://github.com/the-paperless-project/paperless/pull/488
|
||||||
.. _#489: https://github.com/the-paperless-project/paperless/pull/489
|
.. _#489: https://github.com/the-paperless-project/paperless/pull/489
|
||||||
.. _#492: https://github.com/the-paperless-project/paperless/pull/492
|
.. _#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/
|
.. _a new home on Docker Hub: https://hub.docker.com/r/danielquinn/paperless/
|
||||||
.. _optipng: http://optipng.sourceforge.net/
|
.. _optipng: http://optipng.sourceforge.net/
|
||||||
|
12
docs/conf.py
@ -15,7 +15,7 @@ extensions = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
# templates_path = ['_templates']
|
templates_path = ["_templates"]
|
||||||
|
|
||||||
# The suffix of source filenames.
|
# The suffix of source filenames.
|
||||||
source_suffix = ".rst"
|
source_suffix = ".rst"
|
||||||
@ -119,6 +119,16 @@ html_theme_path = []
|
|||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ["_static"]
|
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
|
# Add any extra paths that contain custom files (such as robots.txt or
|
||||||
# .htaccess) here, relative to this directory. These files are copied
|
# .htaccess) here, relative to this directory. These files are copied
|
||||||
# directly to the root of the documentation.
|
# directly to the root of the documentation.
|
||||||
|
@ -130,6 +130,8 @@ PAPERLESS_LOGROTATE_MAX_BACKUPS=<num>
|
|||||||
|
|
||||||
Defaults to 20.
|
Defaults to 20.
|
||||||
|
|
||||||
|
.. _hosting-and-security:
|
||||||
|
|
||||||
Hosting & Security
|
Hosting & Security
|
||||||
##################
|
##################
|
||||||
|
|
||||||
@ -170,6 +172,9 @@ PAPERLESS_ALLOWED_HOSTS=<comma-separated-list>
|
|||||||
|
|
||||||
Can also be set using PAPERLESS_URL (see above).
|
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.
|
Defaults to "*", which is all hosts.
|
||||||
|
|
||||||
PAPERLESS_CORS_ALLOWED_HOSTS=<comma-separated-list>
|
PAPERLESS_CORS_ALLOWED_HOSTS=<comma-separated-list>
|
||||||
@ -206,7 +211,7 @@ PAPERLESS_AUTO_LOGIN_USERNAME=<username>
|
|||||||
PAPERLESS_ADMIN_USER=<username>
|
PAPERLESS_ADMIN_USER=<username>
|
||||||
If this environment variable is specified, Paperless automatically creates
|
If this environment variable is specified, Paperless automatically creates
|
||||||
a superuser with the provided username at start. This is useful in cases
|
a superuser with the provided username at start. This is useful in cases
|
||||||
where you can not run the `createsuperuser` command seperately, such as Kubernetes
|
where you can not run the `createsuperuser` command separately, such as Kubernetes
|
||||||
or AWS ECS.
|
or AWS ECS.
|
||||||
|
|
||||||
Requires `PAPERLESS_ADMIN_PASSWORD` to be set.
|
Requires `PAPERLESS_ADMIN_PASSWORD` to be set.
|
||||||
|
@ -7,7 +7,7 @@ Frequently asked questions
|
|||||||
|
|
||||||
**A:** While Paperless-ngx is already considered largely "feature-complete" it is a community-driven
|
**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
|
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 garauntee the feature
|
GitHub discussions and "up-voted" by the community but this is not a guarantee the feature
|
||||||
will be implemented. This project will always be open to collaboration in the form of PRs,
|
will be implemented. This project will always be open to collaboration in the form of PRs,
|
||||||
ideas etc.
|
ideas etc.
|
||||||
|
|
||||||
|
@ -4,41 +4,60 @@
|
|||||||
Screenshots
|
Screenshots
|
||||||
***********
|
***********
|
||||||
|
|
||||||
This is what paperless-ngx looks like. You shouldn't use paperless to index
|
This is what Paperless-ngx looks like.
|
||||||
research papers though, its a horrible tool for that job.
|
|
||||||
|
|
||||||
The dashboard shows customizable views on your document and allows document uploads:
|
The dashboard shows customizable views on your document and allows document uploads:
|
||||||
|
|
||||||
.. image:: _static/screenshots/dashboard.png
|
.. image:: _static/screenshots/dashboard.png
|
||||||
|
:target: _static/screenshots/dashboard.png
|
||||||
|
|
||||||
The document list provides three different styles to scroll through your documents:
|
The document list provides three different styles to scroll through your documents:
|
||||||
|
|
||||||
.. image:: _static/screenshots/documents-table.png
|
.. image:: _static/screenshots/documents-table.png
|
||||||
|
:target: _static/screenshots/documents-table.png
|
||||||
.. image:: _static/screenshots/documents-smallcards.png
|
.. image:: _static/screenshots/documents-smallcards.png
|
||||||
|
:target: _static/screenshots/documents-smallcards.png
|
||||||
.. image:: _static/screenshots/documents-largecards.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:
|
Extensive filtering mechanisms:
|
||||||
|
|
||||||
.. image:: _static/screenshots/documents-filter.png
|
.. image:: _static/screenshots/documents-filter.png
|
||||||
|
:target: _static/screenshots/documents-filter.png
|
||||||
|
|
||||||
Side-by-side editing of documents. Optimized for 1080p.
|
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:
|
||||||
|
|
||||||
.. image:: _static/screenshots/editing.png
|
.. image:: _static/screenshots/editing.png
|
||||||
|
:target: _static/screenshots/editing.png
|
||||||
|
|
||||||
Tag editing. This looks about the same for correspondents and document types.
|
Tag editing. This looks about the same for correspondents and document types.
|
||||||
|
|
||||||
.. image:: _static/screenshots/new-tag.png
|
.. image:: _static/screenshots/new-tag.png
|
||||||
|
:target: _static/screenshots/new-tag.png
|
||||||
|
|
||||||
Searching provides auto complete and highlights the results.
|
Searching provides auto complete and highlights the results.
|
||||||
|
|
||||||
.. image:: _static/screenshots/search-preview.png
|
.. image:: _static/screenshots/search-preview.png
|
||||||
|
:target: _static/screenshots/search-preview.png
|
||||||
.. image:: _static/screenshots/search-results.png
|
.. image:: _static/screenshots/search-results.png
|
||||||
|
:target: _static/screenshots/search-results.png
|
||||||
|
|
||||||
Fancy mail filters!
|
Fancy mail filters!
|
||||||
|
|
||||||
.. image:: _static/screenshots/mail-rules-edited.png
|
.. image:: _static/screenshots/mail-rules-edited.png
|
||||||
|
:target: _static/screenshots/mail-rules-edited.png
|
||||||
|
|
||||||
Mobile support in the future? This kinda works, however some layouts are still
|
Mobile devices are supported.
|
||||||
too wide.
|
|
||||||
|
|
||||||
.. image:: _static/screenshots/mobile.png
|
.. image:: _static/screenshots/mobile.png
|
||||||
|
:target: _static/screenshots/mobile.png
|
||||||
|
@ -291,12 +291,14 @@ writing. Windows is not and will never be supported.
|
|||||||
* ``libpq-dev`` for PostgreSQL
|
* ``libpq-dev`` for PostgreSQL
|
||||||
* ``libmagic-dev`` for mime type detection
|
* ``libmagic-dev`` for mime type detection
|
||||||
* ``mime-support`` 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:
|
Use this list for your preferred package management:
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
python3 python3-pip python3-dev imagemagick fonts-liberation optipng gnupg libpq-dev libmagic-dev mime-support
|
python3 python3-pip python3-dev imagemagick fonts-liberation optipng gnupg libpq-dev libmagic-dev mime-support libzbar0 poppler-utils
|
||||||
|
|
||||||
These dependencies are required for OCRmyPDF, which is used for text recognition.
|
These dependencies are required for OCRmyPDF, which is used for text recognition.
|
||||||
|
|
||||||
@ -345,6 +347,8 @@ 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 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
|
* ``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.
|
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
|
Many more adjustments can be made to paperless, especially the OCR part. The following options are recommended
|
||||||
for everyone:
|
for everyone:
|
||||||
@ -784,4 +788,6 @@ 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.
|
Also read `this <https://channels.readthedocs.io/en/stable/deploying.html#nginx-supervisor-ubuntu>`__, towards the end of the section.
|
||||||
|
@ -62,7 +62,7 @@ your documents:
|
|||||||
|
|
||||||
1. OCR the document, if it has no text. Digital documents usually have text,
|
1. OCR the document, if it has no text. Digital documents usually have text,
|
||||||
and this step will be skipped for those documents.
|
and this step will be skipped for those documents.
|
||||||
2. Paperless will create an archiveable PDF/A document from your document.
|
2. Paperless will create an archivable PDF/A document from your document.
|
||||||
If this document is coming from your scanner, it will have embedded selectable text.
|
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
|
3. Paperless performs automatic matching of tags, correspondents and types on the
|
||||||
document before storing it in the database.
|
document before storing it in the database.
|
||||||
@ -102,12 +102,14 @@ files from the scanner. Typically, you're looking at an FTP server like
|
|||||||
|
|
||||||
.. TODO: hyperref to configuration of the location of this magic folder.
|
.. TODO: hyperref to configuration of the location of this magic folder.
|
||||||
|
|
||||||
Dashboard upload
|
Web UI Upload
|
||||||
================
|
=============
|
||||||
|
|
||||||
The dashboard has a file drop field to upload documents to paperless. Simply drag a file
|
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.
|
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:
|
.. _usage-mobile_upload:
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
@import "/src/theme";
|
|
||||||
/*
|
/*
|
||||||
* Sidebar
|
* Sidebar
|
||||||
*/
|
*/
|
||||||
@ -36,10 +35,15 @@
|
|||||||
.sidebar .nav-link {
|
.sidebar .nav-link {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
||||||
&:hover, &.active {
|
&:hover, &.active, &:focus {
|
||||||
color: var(--bs-primary);
|
color: var(--bs-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
@import "/src/theme";
|
|
||||||
|
|
||||||
.badge-corner {
|
.badge-corner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -8px;
|
top: -8px;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
@import "/src/theme";
|
|
||||||
|
|
||||||
form {
|
form {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
@import "/src/theme";
|
|
||||||
|
|
||||||
.result-content {
|
.result-content {
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
@import "/src/theme";
|
|
||||||
|
|
||||||
.card-text {
|
.card-text {
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
@import "/src/theme";
|
|
||||||
|
|
||||||
::ng-deep app-document-list app-page-header > div.mb-3 {
|
::ng-deep app-document-list app-page-header > div.mb-3 {
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<ul ngbNav #nav="ngbNav" class="nav-tabs">
|
<ul ngbNav #nav="ngbNav" class="nav-tabs">
|
||||||
<li [ngbNavItem]="1">
|
<li [ngbNavItem]="1">
|
||||||
<a ngbNavLink i18n>General settings</a>
|
<a ngbNavLink i18n>General</a>
|
||||||
<ng-template ngbNavContent>
|
<ng-template ngbNavContent>
|
||||||
|
|
||||||
<h4 i18n>Appearance</h4>
|
<h4 i18n>Appearance</h4>
|
||||||
@ -104,7 +104,7 @@
|
|||||||
<div class="col-md-3 col-form-label">
|
<div class="col-md-3 col-form-label">
|
||||||
<span i18n>Theme Color</span>
|
<span i18n>Theme Color</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3">
|
<div class="col col-md-3">
|
||||||
<app-input-color i18n-title formControlName="themeColor" [error]="error?.color"></app-input-color>
|
<app-input-color i18n-title formControlName="themeColor" [error]="error?.color"></app-input-color>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
|
@ -9,7 +9,11 @@ import {
|
|||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { Meta } from '@angular/platform-browser'
|
import { Meta } from '@angular/platform-browser'
|
||||||
import { CookieService } from 'ngx-cookie-service'
|
import { CookieService } from 'ngx-cookie-service'
|
||||||
import { hexToHsl } from 'src/app/utils/color'
|
import {
|
||||||
|
BRIGHTNESS,
|
||||||
|
estimateBrightnessForColor,
|
||||||
|
hexToHsl,
|
||||||
|
} from 'src/app/utils/color'
|
||||||
|
|
||||||
export interface PaperlessSettings {
|
export interface PaperlessSettings {
|
||||||
key: string
|
key: string
|
||||||
@ -132,28 +136,41 @@ export class SettingsService {
|
|||||||
: this.renderer.removeClass(this.document.body, 'color-scheme-dark')
|
: 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) {
|
if (themeColor) {
|
||||||
const hsl = hexToHsl(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(
|
this.renderer.setStyle(
|
||||||
document.documentElement,
|
document.body,
|
||||||
'--pngx-primary',
|
'--pngx-primary',
|
||||||
`${+hsl.h * 360},${hsl.s * 100}%`,
|
`${+hsl.h * 360},${hsl.s * 100}%`,
|
||||||
RendererStyleFlags2.DashCase
|
RendererStyleFlags2.DashCase
|
||||||
)
|
)
|
||||||
this.renderer.setStyle(
|
this.renderer.setStyle(
|
||||||
document.documentElement,
|
document.body,
|
||||||
'--pngx-primary-lightness',
|
'--pngx-primary-lightness',
|
||||||
`${hsl.l * 100}%`,
|
`${hsl.l * 100}%`,
|
||||||
RendererStyleFlags2.DashCase
|
RendererStyleFlags2.DashCase
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
this.renderer.removeStyle(
|
this.renderer.removeStyle(
|
||||||
document.documentElement,
|
document.body,
|
||||||
'--pngx-primary',
|
'--pngx-primary',
|
||||||
RendererStyleFlags2.DashCase
|
RendererStyleFlags2.DashCase
|
||||||
)
|
)
|
||||||
this.renderer.removeStyle(
|
this.renderer.removeStyle(
|
||||||
document.documentElement,
|
document.body,
|
||||||
'--pngx-primary-lightness',
|
'--pngx-primary-lightness',
|
||||||
RendererStyleFlags2.DashCase
|
RendererStyleFlags2.DashCase
|
||||||
)
|
)
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { HSL } from 'ngx-color'
|
import { HSL, RGB } from 'ngx-color'
|
||||||
|
|
||||||
|
export const BRIGHTNESS = {
|
||||||
|
LIGHT: 'light',
|
||||||
|
DARK: 'dark',
|
||||||
|
}
|
||||||
|
|
||||||
function componentToHex(c) {
|
function componentToHex(c) {
|
||||||
var hex = Math.floor(c).toString(16)
|
var hex = Math.floor(c).toString(16)
|
||||||
@ -86,14 +91,42 @@ export function rgbToHsl(r, g, b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function hexToHsl(hex: string): HSL {
|
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('#', '')
|
hex = hex.replace('#', '')
|
||||||
let aRgbHex = hex.match(/.{1,2}/g)
|
let aRgbHex = hex.match(/.{1,2}/g)
|
||||||
const hsl = rgbToHsl(
|
return {
|
||||||
parseInt(aRgbHex[0], 16),
|
r: parseInt(aRgbHex[0], 16),
|
||||||
parseInt(aRgbHex[1], 16),
|
g: parseInt(aRgbHex[1], 16),
|
||||||
parseInt(aRgbHex[2], 16)
|
b: parseInt(aRgbHex[2], 16),
|
||||||
)
|
}
|
||||||
return { h: hsl[0], s: hsl[1], l: hsl[2] }
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
export function randomColor() {
|
export function randomColor() {
|
||||||
|
@ -5,7 +5,7 @@ export const environment = {
|
|||||||
apiBaseUrl: document.baseURI + 'api/',
|
apiBaseUrl: document.baseURI + 'api/',
|
||||||
apiVersion: '2',
|
apiVersion: '2',
|
||||||
appTitle: 'Paperless-ngx',
|
appTitle: 'Paperless-ngx',
|
||||||
version: '1.6.0',
|
version: '1.7.0',
|
||||||
webSocketHost: window.location.host,
|
webSocketHost: window.location.host,
|
||||||
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
|
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
|
||||||
webSocketBaseUrl: base_url.pathname + 'ws/',
|
webSocketBaseUrl: base_url.pathname + 'ws/',
|
||||||
|
@ -616,7 +616,7 @@
|
|||||||
<context context-type="linenumber">4</context>
|
<context context-type="linenumber">4</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">This button dismisses all status messages about processed documents on the dashboard (failed and successful)</note>
|
<note priority="1" from="description">This button dismisses all status messages about processed documents on the dashboard (failed and successful)</note>
|
||||||
<target state="translated">Afskedig afsluttede</target>
|
<target state="translated">Ryd afsluttede</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="118343233500414755" datatype="html">
|
<trans-unit id="118343233500414755" datatype="html">
|
||||||
<source>Drop documents here or</source>
|
<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="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">17</context>
|
<context context-type="linenumber">17</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<target state="translated">Visning sprog</target>
|
<target state="translated">Visningssprog</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="53523152145406584" datatype="html">
|
<trans-unit id="53523152145406584" datatype="html">
|
||||||
<source>You need to reload the page after applying a new language.</source>
|
<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="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">163</context>
|
<context context-type="linenumber">163</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<target state="translated">Ingen gemte visninger angivet.</target>
|
<target state="translated">Ingen gemte visninger.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5610279464668232148" datatype="html" approved="yes">
|
<trans-unit id="5610279464668232148" datatype="html" approved="yes">
|
||||||
<source>Saved view "<x id="PH" equiv-text="savedView.name"/>" deleted.</source>
|
<source>Saved view "<x id="PH" equiv-text="savedView.name"/>" deleted.</source>
|
||||||
|
@ -2136,7 +2136,7 @@
|
|||||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||||
<context context-type="linenumber">103</context>
|
<context context-type="linenumber">103</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<target state="final">Edition en masse</target>
|
<target state="final">Édition en masse</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="8158899674926420054" datatype="html" approved="yes">
|
<trans-unit id="8158899674926420054" datatype="html" approved="yes">
|
||||||
<source>Show confirmation dialogs</source>
|
<source>Show confirmation dialogs</source>
|
||||||
|
@ -2452,7 +2452,7 @@
|
|||||||
<context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
|
<context context-type="sourcefile">src/app/services/open-documents.service.ts</context>
|
||||||
<context context-type="linenumber">97</context>
|
<context context-type="linenumber">97</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<target state="translated">Istnieją niezapisane zmiany.</target>
|
<target state="translated">Masz niezapisane zmiany.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3305084982600522070" datatype="html">
|
<trans-unit id="3305084982600522070" datatype="html">
|
||||||
<source>Are you sure you want to leave?</source>
|
<source>Are you sure you want to leave?</source>
|
||||||
|
@ -54,14 +54,14 @@
|
|||||||
</context-group>
|
</context-group>
|
||||||
<target state="final">Документ <x id="PH" equiv-text="status.filename"/> обрабатывается paperless</target>
|
<target state="final">Документ <x id="PH" equiv-text="status.filename"/> обрабатывается paperless</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="2173456130768795374" datatype="html">
|
<trans-unit id="2173456130768795374" datatype="html" approved="yes">
|
||||||
<source>Paperless-ngx</source>
|
<source>Paperless-ngx</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
|
||||||
<context context-type="linenumber">11</context>
|
<context context-type="linenumber">11</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">app title</note>
|
<note priority="1" from="description">app title</note>
|
||||||
<target state="translated">Paperless-ngx</target>
|
<target state="final">Paperless-ngx</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7100953725264790651" datatype="html" approved="yes">
|
<trans-unit id="7100953725264790651" datatype="html" approved="yes">
|
||||||
<source>Search documents</source>
|
<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="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
|
||||||
<context context-type="linenumber">45</context>
|
<context context-type="linenumber">45</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<target state="translated">Нажмите снова, чтобы исключить элементы.</target>
|
<target state="translated">Нажмите еще раз, чтобы исключить элементы.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7593728289020204896" datatype="html" approved="yes">
|
<trans-unit id="7593728289020204896" datatype="html" approved="yes">
|
||||||
<source>Not assigned</source>
|
<source>Not assigned</source>
|
||||||
|
@ -1352,7 +1352,7 @@
|
|||||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||||
<context context-type="linenumber">202</context>
|
<context context-type="linenumber">202</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<target state="translated">Ova radnja će trajno obrisati <x id="PH" equiv-text="this.list.selected.size"/> selektovane dokumente.</target>
|
<target state="translated">Ova radnja će trajno obrisati <x id="PH" equiv-text="this.list.selected.size"/> selektovan(a) dokument(a).</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5641451190833696892" datatype="html">
|
<trans-unit id="5641451190833696892" datatype="html">
|
||||||
<source>This operation cannot be undone.</source>
|
<source>This operation cannot be undone.</source>
|
||||||
|
@ -4,7 +4,6 @@ $enable-negative-margins: true;
|
|||||||
@import "node_modules/bootstrap/scss/bootstrap";
|
@import "node_modules/bootstrap/scss/bootstrap";
|
||||||
@import "~@ng-select/ng-select/themes/default.theme.css";
|
@import "~@ng-select/ng-select/themes/default.theme.css";
|
||||||
@import "theme";
|
@import "theme";
|
||||||
@import "theme_dark";
|
|
||||||
@import "print";
|
@import "print";
|
||||||
|
|
||||||
// Paperless-ngx styles
|
// Paperless-ngx styles
|
||||||
@ -26,6 +25,27 @@ svg.logo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.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 {
|
.nav-link, .list-group-item {
|
||||||
color: var(--bs-body-color);
|
color: var(--bs-body-color);
|
||||||
}
|
}
|
||||||
@ -36,20 +56,31 @@ svg.logo {
|
|||||||
|
|
||||||
.bg-primary {
|
.bg-primary {
|
||||||
background-color: var(--bs-primary) !important;
|
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 {
|
.btn-primary {
|
||||||
|
color: var(--pngx-primary-text-contrast);
|
||||||
background-color: var(--bs-primary);
|
background-color: var(--bs-primary);
|
||||||
border-color: var(--bs-primary);
|
border-color: var(--bs-primary);
|
||||||
|
|
||||||
&:hover, &:focus {
|
&:hover, &:focus {
|
||||||
background-color: var(--pngx-primary-darken-10);
|
background-color: var(--pngx-primary-darken-5);
|
||||||
border-color: var(--pngx-primary-darken-10);
|
border-color: var(--pngx-primary-darken-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled, &.disabled {
|
&:disabled, &.disabled {
|
||||||
background-color: var(--pngx-primary-darken-10) !important;
|
color: var(--pngx-primary-text-contrast);
|
||||||
border-color: var(--pngx-primary-darken-10) !important;
|
background-color: var(--pngx-primary-darken-5) !important;
|
||||||
|
border-color: var(--pngx-primary-darken-5) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,14 +121,14 @@ svg.logo {
|
|||||||
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>"));
|
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 {
|
.nav-item a:focus-visible {
|
||||||
outline: none;
|
outline: none;
|
||||||
background-color: var(--pngx-bg-darker);
|
background-color: var(--bs-body-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
a.navbar-brand:focus-visible {
|
a.navbar-brand:focus-visible {
|
||||||
outline: none;
|
outline: none;
|
||||||
color: var(--pngx-primary-darken-10);
|
color: var(--pngx-primary-darken-5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown.show {
|
.dropdown.show {
|
||||||
@ -209,7 +240,8 @@ input,
|
|||||||
select,
|
select,
|
||||||
textarea,
|
textarea,
|
||||||
.form-select:not(.is-invalid):not(:disabled),
|
.form-select:not(.is-invalid):not(:disabled),
|
||||||
.form-check-input {
|
.form-check-input,
|
||||||
|
.ng-select .ng-select-container .ng-value-container .ng-input > input {
|
||||||
color: var(--bs-body-color);
|
color: var(--bs-body-color);
|
||||||
background-color: var(--bs-body-bg);
|
background-color: var(--bs-body-bg);
|
||||||
border-color: var(--bs-border-color);
|
border-color: var(--bs-border-color);
|
||||||
@ -324,6 +356,13 @@ textarea,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.doc-img-container {
|
||||||
|
border: none !important;
|
||||||
|
border-top-left-radius: .25rem;
|
||||||
|
border-top-right-radius: .25rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
// icons
|
// icons
|
||||||
.toolbaricon {
|
.toolbaricon {
|
||||||
width: 1.2em;
|
width: 1.2em;
|
||||||
@ -403,10 +442,8 @@ table.table {
|
|||||||
border-color: var(--bs-danger);
|
border-color: var(--bs-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert-secondary {
|
.progress {
|
||||||
background-color: var(--pngx-primary-darken-18);
|
background-color: var(--bs-body-bg);
|
||||||
border-color: var(--pngx-primary-darken-15);
|
|
||||||
color: var(--bs-body-color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ngb-dp-header,
|
.ngb-dp-header,
|
||||||
|
@ -1,17 +1,222 @@
|
|||||||
:root {
|
@mixin paperless-green {
|
||||||
// base color e.g. #17541f = hsl(128, 57%, 21%)
|
// base color e.g. #17541f = hsl(128, 57%, 21%)
|
||||||
--pngx-primary: 128, 57%;
|
--pngx-primary: 128, 57%;
|
||||||
--pngx-primary-lightness: 21%;
|
--pngx-primary-lightness: 21%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@include paperless-green;
|
||||||
--pngx-primary-text-contrast: var(--bs-light);
|
--pngx-primary-text-contrast: var(--bs-light);
|
||||||
|
|
||||||
--bs-primary: hsl(var(--pngx-primary), var(--pngx-primary-lightness));
|
--bs-primary: hsl(var(--pngx-primary), var(--pngx-primary-lightness));
|
||||||
--bs-border-color: var(--bs-gray-400);
|
--bs-border-color: var(--bs-gray-400);
|
||||||
--pngx-primary-faded: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 72%));
|
--pngx-primary-faded: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 72%));
|
||||||
--pngx-primary-lighten-10: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 10%));
|
|
||||||
--pngx-primary-lighten-30: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 30%));
|
--pngx-primary-lighten-30: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) + 30%));
|
||||||
--pngx-primary-darken-10: hsl(var(--pngx-primary), calc(var(--pngx-primary-lightness) - 10%));
|
--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-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-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-bg-darker: var(--bs-gray-100);
|
||||||
--pngx-focus-alpha: 0.3;
|
--pngx-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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-check-input:checked[type=radio] {
|
||||||
|
background-image: escape-svg($form-check-radio-checked-bg-image-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-close {
|
||||||
|
filter: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-dark {
|
||||||
|
--pngx-primary-text-contrast: #{$text-color-dark-bg} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode
|
||||||
|
@mixin paperless-green-dark-mode {
|
||||||
|
--pngx-primary-lightness: 31%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
|
||||||
|
.text-dark, .text-light {
|
||||||
|
color: var(--bs-body-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
&:hover, &:focus, &.active, &:active {
|
||||||
|
color: var(--bs-body-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary {
|
||||||
|
&:hover, &:focus, &.active, &:active {
|
||||||
|
color: var(--bs-light) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-secondary {
|
||||||
|
&:hover, &:focus, &.active, &:active {
|
||||||
|
background-color: var(--pngx-bg-darker);
|
||||||
|
color: var(--bs-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-light {
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn .progress {
|
||||||
|
background-color: var(--pngx-body-color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form-container {
|
||||||
|
input, input:focus {
|
||||||
|
color: var(--bs-body-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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 {
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include dark-mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
$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;
|
|
||||||
|
|
||||||
@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};
|
|
||||||
--pngx-bg-darker: #{$bg-dark-mode-accent};
|
|
||||||
--pngx-bg-alt: #{$bg-dark-mode-alt};
|
|
||||||
--pngx-body-color-accent: #{$text-color-dark-mode-accent};
|
|
||||||
--pngx-focus-alpha: 0.7;
|
|
||||||
--pngx-primary-faded: var(--pngx-primary-darken-15);
|
|
||||||
--pngx-primary-text-contrast: var(--bs-body-color);
|
|
||||||
|
|
||||||
.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(--bs-light) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-outline-secondary {
|
|
||||||
&:hover, &:focus, &.active, &:active {
|
|
||||||
background-color: var(--pngx-bg-darker);
|
|
||||||
color: var(--bs-primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-form-container {
|
|
||||||
input, input:focus {
|
|
||||||
color: var(--bs-body-color) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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, .alert .btn-close {
|
|
||||||
text-shadow: 0 1px 0 #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .btn-close, .alert .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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: paperless-ngx\n"
|
"Project-Id-Version: paperless-ngx\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2022-03-02 11:20-0800\n"
|
"POT-Creation-Date: 2022-03-02 11:20-0800\n"
|
||||||
"PO-Revision-Date: 2022-03-14 23:41\n"
|
"PO-Revision-Date: 2022-04-12 15:26\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: Polish\n"
|
"Language-Team: Polish\n"
|
||||||
"Language: pl_PL\n"
|
"Language: pl_PL\n"
|
||||||
@ -60,7 +60,7 @@ msgstr "algorytm dopasowania"
|
|||||||
|
|
||||||
#: documents/models.py:48
|
#: documents/models.py:48
|
||||||
msgid "is insensitive"
|
msgid "is insensitive"
|
||||||
msgstr "bez rozróżniania wielkości liter"
|
msgstr "bez rozróżniania wielkości znaków"
|
||||||
|
|
||||||
#: documents/models.py:61 documents/models.py:104
|
#: documents/models.py:61 documents/models.py:104
|
||||||
msgid "correspondent"
|
msgid "correspondent"
|
||||||
@ -240,7 +240,7 @@ msgstr "użytkownik"
|
|||||||
|
|
||||||
#: documents/models.py:317
|
#: documents/models.py:317
|
||||||
msgid "show on dashboard"
|
msgid "show on dashboard"
|
||||||
msgstr "pokaż na pulpicie"
|
msgstr "pokaż na stronie głównej"
|
||||||
|
|
||||||
#: documents/models.py:320
|
#: documents/models.py:320
|
||||||
msgid "show in sidebar"
|
msgid "show in sidebar"
|
||||||
@ -638,7 +638,7 @@ msgstr "konto"
|
|||||||
|
|
||||||
#: paperless_mail/models.py:119
|
#: paperless_mail/models.py:119
|
||||||
msgid "folder"
|
msgid "folder"
|
||||||
msgstr "katalog"
|
msgstr "folder"
|
||||||
|
|
||||||
#: paperless_mail/models.py:122
|
#: paperless_mail/models.py:122
|
||||||
msgid "Subfolders must be separated by dots."
|
msgid "Subfolders must be separated by dots."
|
||||||
|
@ -251,7 +251,8 @@ if _paperless_url:
|
|||||||
if _allowed_hosts:
|
if _allowed_hosts:
|
||||||
ALLOWED_HOSTS.append(_paperless_uri.hostname)
|
ALLOWED_HOSTS.append(_paperless_uri.hostname)
|
||||||
else:
|
else:
|
||||||
ALLOWED_HOSTS = [_paperless_uri.hostname]
|
# always allow localhost. Necessary e.g. for healthcheck in docker.
|
||||||
|
ALLOWED_HOSTS = [_paperless_uri.hostname] + ["localhost"]
|
||||||
|
|
||||||
# The secret key has a default that should be fine so long as you're hosting
|
# The secret key has a default that should be fine so long as you're hosting
|
||||||
# Paperless on a closed network. However, if you're putting this anywhere
|
# Paperless on a closed network. However, if you're putting this anywhere
|
||||||
|
@ -1 +1 @@
|
|||||||
__version__ = (1, 6, 0)
|
__version__ = (1, 7, 0)
|
||||||
|
@ -82,7 +82,7 @@ class MailRuleAdmin(admin.ModelAdmin):
|
|||||||
),
|
),
|
||||||
"fields": (
|
"fields": (
|
||||||
"assign_title_from",
|
"assign_title_from",
|
||||||
"assign_tag",
|
"assign_tags",
|
||||||
"assign_document_type",
|
"assign_document_type",
|
||||||
"assign_correspondent_from",
|
"assign_correspondent_from",
|
||||||
"assign_correspondent",
|
"assign_correspondent",
|
||||||
|
@ -18,6 +18,7 @@ from imap_tools import MailboxFolderSelectError
|
|||||||
from imap_tools import MailBoxUnencrypted
|
from imap_tools import MailBoxUnencrypted
|
||||||
from imap_tools import MailMessage
|
from imap_tools import MailMessage
|
||||||
from imap_tools import MailMessageFlags
|
from imap_tools import MailMessageFlags
|
||||||
|
from imap_tools.mailbox import MailBoxTls
|
||||||
from paperless_mail.models import MailAccount
|
from paperless_mail.models import MailAccount
|
||||||
from paperless_mail.models import MailRule
|
from paperless_mail.models import MailRule
|
||||||
|
|
||||||
@ -61,13 +62,13 @@ class FlagMailAction(BaseMailAction):
|
|||||||
|
|
||||||
|
|
||||||
def get_rule_action(rule):
|
def get_rule_action(rule):
|
||||||
if rule.action == MailRule.AttachmentAction.FLAG:
|
if rule.action == MailRule.MailAction.FLAG:
|
||||||
return FlagMailAction()
|
return FlagMailAction()
|
||||||
elif rule.action == MailRule.AttachmentAction.DELETE:
|
elif rule.action == MailRule.MailAction.DELETE:
|
||||||
return DeleteMailAction()
|
return DeleteMailAction()
|
||||||
elif rule.action == MailRule.AttachmentAction.MOVE:
|
elif rule.action == MailRule.MailAction.MOVE:
|
||||||
return MoveMailAction()
|
return MoveMailAction()
|
||||||
elif rule.action == MailRule.AttachmentAction.MARK_READ:
|
elif rule.action == MailRule.MailAction.MARK_READ:
|
||||||
return MarkReadMailAction()
|
return MarkReadMailAction()
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Unknown action.") # pragma: nocover
|
raise NotImplementedError("Unknown action.") # pragma: nocover
|
||||||
@ -92,7 +93,7 @@ def get_mailbox(server, port, security):
|
|||||||
if security == MailAccount.ImapSecurity.NONE:
|
if security == MailAccount.ImapSecurity.NONE:
|
||||||
mailbox = MailBoxUnencrypted(server, port)
|
mailbox = MailBoxUnencrypted(server, port)
|
||||||
elif security == MailAccount.ImapSecurity.STARTTLS:
|
elif security == MailAccount.ImapSecurity.STARTTLS:
|
||||||
mailbox = MailBox(server, port, starttls=True)
|
mailbox = MailBoxTls(server, port)
|
||||||
elif security == MailAccount.ImapSecurity.SSL:
|
elif security == MailAccount.ImapSecurity.SSL:
|
||||||
mailbox = MailBox(server, port)
|
mailbox = MailBox(server, port)
|
||||||
else:
|
else:
|
||||||
@ -280,7 +281,7 @@ class MailAccountHandler(LoggingMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
correspondent = self.get_correspondent(message, rule)
|
correspondent = self.get_correspondent(message, rule)
|
||||||
tag = rule.assign_tag
|
tag_ids = [tag.id for tag in rule.assign_tags.all()]
|
||||||
doc_type = rule.assign_document_type
|
doc_type = rule.assign_document_type
|
||||||
|
|
||||||
processed_attachments = 0
|
processed_attachments = 0
|
||||||
@ -343,7 +344,7 @@ class MailAccountHandler(LoggingMixin):
|
|||||||
if correspondent
|
if correspondent
|
||||||
else None,
|
else None,
|
||||||
override_document_type_id=doc_type.id if doc_type else None,
|
override_document_type_id=doc_type.id if doc_type else None,
|
||||||
override_tag_ids=[tag.id] if tag else None,
|
override_tag_ids=tag_ids,
|
||||||
task_name=att.filename[:100],
|
task_name=att.filename[:100],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
23
src/paperless_mail/migrations/0009_mailrule_assign_tags.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 3.2.12 on 2022-03-11 15:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("paperless_mail", "0008_auto_20210516_0940"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="assign_tags",
|
||||||
|
field=models.ManyToManyField(
|
||||||
|
blank=True,
|
||||||
|
related_name="mail_rules_multi",
|
||||||
|
to="documents.Tag",
|
||||||
|
verbose_name="assign this tag",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
40
src/paperless_mail/migrations/0010_auto_20220311_1602.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Generated by Django 3.2.12 on 2022-03-11 15:02
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_tag_to_tags(apps, schema_editor):
|
||||||
|
# Manual data migration, see
|
||||||
|
# https://docs.djangoproject.com/en/3.2/topics/migrations/#data-migrations
|
||||||
|
#
|
||||||
|
# Copy the assign_tag property to the new assign_tags set if it exists.
|
||||||
|
MailRule = apps.get_model("paperless_mail", "MailRule")
|
||||||
|
for mail_rule in MailRule.objects.all():
|
||||||
|
if mail_rule.assign_tag:
|
||||||
|
mail_rule.assign_tags.add(mail_rule.assign_tag)
|
||||||
|
mail_rule.save()
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_tags_to_tag(apps, schema_editor):
|
||||||
|
# Manual data migration, see
|
||||||
|
# https://docs.djangoproject.com/en/3.2/topics/migrations/#data-migrations
|
||||||
|
#
|
||||||
|
# Copy the unique value in the assign_tags set to the old assign_tag property.
|
||||||
|
# Do nothing if the tag is not unique.
|
||||||
|
MailRule = apps.get_model("paperless_mail", "MailRule")
|
||||||
|
for mail_rule in MailRule.objects.all():
|
||||||
|
tags = mail_rule.assign_tags.all()
|
||||||
|
if len(tags) == 1:
|
||||||
|
mail_rule.assign_tag = tags[0]
|
||||||
|
mail_rule.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("paperless_mail", "0009_mailrule_assign_tags"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(migrate_tag_to_tags, migrate_tags_to_tag),
|
||||||
|
]
|
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 3.2.12 on 2022-03-11 15:18
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("paperless_mail", "0010_auto_20220311_1602"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="assign_tag",
|
||||||
|
),
|
||||||
|
]
|
@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 3.2.12 on 2022-03-11 16:21
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("paperless_mail", "0011_remove_mailrule_assign_tag"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="assign_tags",
|
||||||
|
field=models.ManyToManyField(
|
||||||
|
blank=True, to="documents.Tag", verbose_name="assign this tag"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
13
src/paperless_mail/migrations/0013_merge_20220412_1051.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Generated by Django 4.0.4 on 2022-04-12 08:51
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("paperless_mail", "0009_alter_mailrule_action_alter_mailrule_folder"),
|
||||||
|
("paperless_mail", "0012_alter_mailrule_assign_tags"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = []
|
27
src/paperless_mail/migrations/0014_alter_mailrule_action.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 4.0.4 on 2022-04-18 22:57
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("paperless_mail", "0013_merge_20220412_1051"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="mailrule",
|
||||||
|
name="action",
|
||||||
|
field=models.PositiveIntegerField(
|
||||||
|
choices=[
|
||||||
|
(1, "Delete"),
|
||||||
|
(2, "Move to specified folder"),
|
||||||
|
(3, "Mark as read, don't process read mails"),
|
||||||
|
(4, "Flag the mail, don't process flagged mails"),
|
||||||
|
],
|
||||||
|
default=3,
|
||||||
|
verbose_name="action",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -60,11 +60,11 @@ class MailRule(models.Model):
|
|||||||
ATTACHMENTS_ONLY = 1, _("Only process attachments.")
|
ATTACHMENTS_ONLY = 1, _("Only process attachments.")
|
||||||
EVERYTHING = 2, _("Process all files, including 'inline' " "attachments.")
|
EVERYTHING = 2, _("Process all files, including 'inline' " "attachments.")
|
||||||
|
|
||||||
class AttachmentAction(models.IntegerChoices):
|
class MailAction(models.IntegerChoices):
|
||||||
DELETE = 1, _("Mark as read, don't process read mails")
|
DELETE = 1, _("Delete")
|
||||||
MOVE = 2, _("Flag the mail, don't process flagged mails")
|
MOVE = 2, _("Move to specified folder")
|
||||||
MARK_READ = 3, _("Move to specified folder")
|
MARK_READ = 3, _("Mark as read, don't process read mails")
|
||||||
FLAG = 4, _("Delete")
|
FLAG = 4, _("Flag the mail, don't process flagged mails")
|
||||||
|
|
||||||
class TitleSource(models.IntegerChoices):
|
class TitleSource(models.IntegerChoices):
|
||||||
FROM_SUBJECT = 1, _("Use subject as title")
|
FROM_SUBJECT = 1, _("Use subject as title")
|
||||||
@ -146,8 +146,8 @@ class MailRule(models.Model):
|
|||||||
|
|
||||||
action = models.PositiveIntegerField(
|
action = models.PositiveIntegerField(
|
||||||
_("action"),
|
_("action"),
|
||||||
choices=AttachmentAction.choices,
|
choices=MailAction.choices,
|
||||||
default=AttachmentAction.MARK_READ,
|
default=MailAction.MARK_READ,
|
||||||
)
|
)
|
||||||
|
|
||||||
action_parameter = models.CharField(
|
action_parameter = models.CharField(
|
||||||
@ -169,11 +169,9 @@ class MailRule(models.Model):
|
|||||||
default=TitleSource.FROM_SUBJECT,
|
default=TitleSource.FROM_SUBJECT,
|
||||||
)
|
)
|
||||||
|
|
||||||
assign_tag = models.ForeignKey(
|
assign_tags = models.ManyToManyField(
|
||||||
document_models.Tag,
|
document_models.Tag,
|
||||||
null=True,
|
|
||||||
blank=True,
|
blank=True,
|
||||||
on_delete=models.SET_NULL,
|
|
||||||
verbose_name=_("assign this tag"),
|
verbose_name=_("assign this tag"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -308,10 +308,12 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
account = MailAccount()
|
account = MailAccount()
|
||||||
|
account.save()
|
||||||
rule = MailRule(
|
rule = MailRule(
|
||||||
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
||||||
account=account,
|
account=account,
|
||||||
)
|
)
|
||||||
|
rule.save()
|
||||||
|
|
||||||
result = self.mail_account_handler.handle_message(message, rule)
|
result = self.mail_account_handler.handle_message(message, rule)
|
||||||
|
|
||||||
@ -355,10 +357,12 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
account = MailAccount()
|
account = MailAccount()
|
||||||
|
account.save()
|
||||||
rule = MailRule(
|
rule = MailRule(
|
||||||
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
||||||
account=account,
|
account=account,
|
||||||
)
|
)
|
||||||
|
rule.save()
|
||||||
|
|
||||||
result = self.mail_account_handler.handle_message(message, rule)
|
result = self.mail_account_handler.handle_message(message, rule)
|
||||||
|
|
||||||
@ -381,10 +385,12 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
account = MailAccount()
|
account = MailAccount()
|
||||||
|
account.save()
|
||||||
rule = MailRule(
|
rule = MailRule(
|
||||||
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
||||||
account=account,
|
account=account,
|
||||||
)
|
)
|
||||||
|
rule.save()
|
||||||
|
|
||||||
result = self.mail_account_handler.handle_message(message, rule)
|
result = self.mail_account_handler.handle_message(message, rule)
|
||||||
|
|
||||||
@ -406,11 +412,13 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
account = MailAccount()
|
account = MailAccount()
|
||||||
|
account.save()
|
||||||
rule = MailRule(
|
rule = MailRule(
|
||||||
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
||||||
account=account,
|
account=account,
|
||||||
attachment_type=MailRule.AttachmentProcessing.EVERYTHING,
|
attachment_type=MailRule.AttachmentProcessing.EVERYTHING,
|
||||||
)
|
)
|
||||||
|
rule.save()
|
||||||
|
|
||||||
result = self.mail_account_handler.handle_message(message, rule)
|
result = self.mail_account_handler.handle_message(message, rule)
|
||||||
|
|
||||||
@ -440,12 +448,15 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
for (pattern, matches) in tests:
|
for (pattern, matches) in tests:
|
||||||
matches.sort()
|
matches.sort()
|
||||||
self.async_task.reset_mock()
|
self.async_task.reset_mock()
|
||||||
account = MailAccount()
|
account = MailAccount(name=str(uuid.uuid4()))
|
||||||
|
account.save()
|
||||||
rule = MailRule(
|
rule = MailRule(
|
||||||
|
name=str(uuid.uuid4()),
|
||||||
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
assign_title_from=MailRule.TitleSource.FROM_FILENAME,
|
||||||
account=account,
|
account=account,
|
||||||
filter_attachment_filename=pattern,
|
filter_attachment_filename=pattern,
|
||||||
)
|
)
|
||||||
|
rule.save()
|
||||||
|
|
||||||
result = self.mail_account_handler.handle_message(message, rule)
|
result = self.mail_account_handler.handle_message(message, rule)
|
||||||
|
|
||||||
@ -467,7 +478,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
_ = MailRule.objects.create(
|
_ = MailRule.objects.create(
|
||||||
name="testrule",
|
name="testrule",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.MARK_READ,
|
action=MailRule.MailAction.MARK_READ,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(len(self.bogus_mailbox.messages), 3)
|
self.assertEqual(len(self.bogus_mailbox.messages), 3)
|
||||||
@ -490,7 +501,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
_ = MailRule.objects.create(
|
_ = MailRule.objects.create(
|
||||||
name="testrule",
|
name="testrule",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.DELETE,
|
action=MailRule.MailAction.DELETE,
|
||||||
filter_subject="Invoice",
|
filter_subject="Invoice",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -511,7 +522,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
_ = MailRule.objects.create(
|
_ = MailRule.objects.create(
|
||||||
name="testrule",
|
name="testrule",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.FLAG,
|
action=MailRule.MailAction.FLAG,
|
||||||
filter_subject="Invoice",
|
filter_subject="Invoice",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -534,7 +545,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
_ = MailRule.objects.create(
|
_ = MailRule.objects.create(
|
||||||
name="testrule",
|
name="testrule",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.MOVE,
|
action=MailRule.MailAction.MOVE,
|
||||||
action_parameter="spam",
|
action_parameter="spam",
|
||||||
filter_subject="Claim",
|
filter_subject="Claim",
|
||||||
)
|
)
|
||||||
@ -580,7 +591,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
_ = MailRule.objects.create(
|
_ = MailRule.objects.create(
|
||||||
name="testrule",
|
name="testrule",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.MOVE,
|
action=MailRule.MailAction.MOVE,
|
||||||
action_parameter="spam",
|
action_parameter="spam",
|
||||||
filter_subject="Claim",
|
filter_subject="Claim",
|
||||||
)
|
)
|
||||||
@ -601,7 +612,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
_ = MailRule.objects.create(
|
_ = MailRule.objects.create(
|
||||||
name="testrule",
|
name="testrule",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.MOVE,
|
action=MailRule.MailAction.MOVE,
|
||||||
action_parameter="spam",
|
action_parameter="spam",
|
||||||
filter_subject="Claim",
|
filter_subject="Claim",
|
||||||
order=1,
|
order=1,
|
||||||
@ -610,7 +621,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
_ = MailRule.objects.create(
|
_ = MailRule.objects.create(
|
||||||
name="testrule2",
|
name="testrule2",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.MOVE,
|
action=MailRule.MailAction.MOVE,
|
||||||
action_parameter="spam",
|
action_parameter="spam",
|
||||||
filter_subject="Claim",
|
filter_subject="Claim",
|
||||||
order=2,
|
order=2,
|
||||||
@ -706,7 +717,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
_ = MailRule.objects.create(
|
_ = MailRule.objects.create(
|
||||||
name="testrule",
|
name="testrule",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.MOVE,
|
action=MailRule.MailAction.MOVE,
|
||||||
action_parameter="spam",
|
action_parameter="spam",
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -731,7 +742,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
name="testrule",
|
name="testrule",
|
||||||
filter_from="amazon@amazon.de",
|
filter_from="amazon@amazon.de",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.MOVE,
|
action=MailRule.MailAction.MOVE,
|
||||||
action_parameter="spam",
|
action_parameter="spam",
|
||||||
assign_correspondent_from=MailRule.CorrespondentSource.FROM_EMAIL,
|
assign_correspondent_from=MailRule.CorrespondentSource.FROM_EMAIL,
|
||||||
)
|
)
|
||||||
@ -768,7 +779,7 @@ class TestMail(DirectoriesMixin, TestCase):
|
|||||||
rule = MailRule.objects.create(
|
rule = MailRule.objects.create(
|
||||||
name="testrule3",
|
name="testrule3",
|
||||||
account=account,
|
account=account,
|
||||||
action=MailRule.AttachmentAction.DELETE,
|
action=MailRule.MailAction.DELETE,
|
||||||
filter_subject="Claim",
|
filter_subject="Claim",
|
||||||
)
|
)
|
||||||
|
|
||||||
|