Compare commits
121
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e79429dfb | ||
|
|
ab04817bea | ||
|
|
8dde7fa043 | ||
|
|
edfebe18a2 | ||
|
|
0b07c3bd8f | ||
|
|
1483f450ad | ||
|
|
600c13204e | ||
|
|
2073279473 | ||
|
|
d126ecfa4d | ||
|
|
944aaf5438 | ||
|
|
277bf0eb83 | ||
|
|
d57c237980 | ||
|
|
37ade0c6b2 | ||
|
|
28a2479f24 | ||
|
|
6e688a5b82 | ||
|
|
2104e65462 | ||
|
|
c0882e74e2 | ||
|
|
ced6a61869 | ||
|
|
25a0845efd | ||
|
|
f95fd4ced2 | ||
|
|
227f7b6946 | ||
|
|
1a0b954397 | ||
|
|
87ad2ce1d6 | ||
|
|
63cd16e29e | ||
|
|
66fc9e9111 | ||
|
|
43bee38b3c | ||
|
|
18ebb58dc9 | ||
|
|
099b84a66d | ||
|
|
3b22d7131d | ||
|
|
11f9616411 | ||
|
|
10b53bdfd0 | ||
|
|
aeb10d3407 | ||
|
|
ddf386c4d4 | ||
|
|
9575b0fe32 | ||
|
|
6c7290bd34 | ||
|
|
19032fe433 | ||
|
|
8fce8d4677 | ||
|
|
2333ea9dc7 | ||
|
|
937cdaccad | ||
|
|
54916fec2e | ||
|
|
03e159f67c | ||
|
|
482ef6313d | ||
|
|
43685baff3 | ||
|
|
f6a2cc74e8 | ||
|
|
9f1436a865 | ||
|
|
020df6c8ea | ||
|
|
08046cb83f | ||
|
|
f06edebbd8 | ||
|
|
c808011b75 | ||
|
|
da60fc8150 | ||
|
|
04a0d01b21 | ||
|
|
02a066f8f3 | ||
|
|
7834d369c6 | ||
|
|
5e841531be | ||
|
|
5355f2b027 | ||
|
|
e1533202fc | ||
|
|
0b949a14c0 | ||
|
|
5175206179 | ||
|
|
0af0dffeda | ||
|
|
74a1b36005 | ||
|
|
8884a4dfdf | ||
|
|
19f73f5782 | ||
|
|
8e95310602 | ||
|
|
fe31b8d160 | ||
|
|
6515d1ae85 | ||
|
|
a8ec9f29cd | ||
|
|
bab53838ec | ||
|
|
bf2e98527c | ||
|
|
e1e11de2b5 | ||
|
|
4fda8f3348 | ||
|
|
8427d58337 | ||
|
|
385076cf28 | ||
|
|
b9725437d9 | ||
|
|
72616def4f | ||
|
|
6c972bd08a | ||
|
|
a71a991084 | ||
|
|
0927f9d477 | ||
|
|
a68b858733 | ||
|
|
08199f09b6 | ||
|
|
5f8151282b | ||
|
|
efde4828d6 | ||
|
|
016c168404 | ||
|
|
741ccfa280 | ||
|
|
31b74515b6 | ||
|
|
b9a2f82ce0 | ||
|
|
3c59d9b787 | ||
|
|
f919ec0d57 | ||
|
|
9c380f5aac | ||
|
|
fc5def157d | ||
|
|
d5eff386db | ||
|
|
864e421cd3 | ||
|
|
2c9825193b | ||
|
|
98f3e99e2e | ||
|
|
961354aa8a | ||
|
|
fa27c895ed | ||
|
|
c31b6e63f5 | ||
|
|
ef924e896b | ||
|
|
5b1619cba3 | ||
|
|
a3a9949ebc | ||
|
|
ada8493838 | ||
|
|
cc06d528cb | ||
|
|
0926266663 | ||
|
|
59008ea765 | ||
|
|
01cd4c7546 | ||
|
|
8d606b9f34 | ||
|
|
86376c8c5f | ||
|
|
48220ceeb8 | ||
|
|
f94da1cf27 | ||
|
|
6d786f8987 | ||
|
|
8d433ac0de | ||
|
|
4b42c97d0a | ||
|
|
d8637ff4b1 | ||
|
|
342e9c9734 | ||
|
|
af1d084391 | ||
|
|
e6961d5287 | ||
|
|
7562bdb218 | ||
|
|
cb94e15ba7 | ||
|
|
c8b0674b93 | ||
|
|
4fa1779ef3 | ||
|
|
db23a5cf37 | ||
|
|
7cc3c73994 |
No files matched your search
@@ -0,0 +1,42 @@
|
||||
---
|
||||
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-ng.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.
|
||||
-->
|
||||
|
||||
**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`.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[Feature Request] Consice and clear description of your feature request"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
=> We already have lots of feature requests open. Please search the existing requests first and look for feature requests similar to yours.
|
||||
-->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Other
|
||||
about: Anything that is not a feature request or bug.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
=> Discussions, Feedback and other suggestions belong in the "Disussion" section and not on the issue tracker.
|
||||
|
||||
-->
|
||||
@@ -26,10 +26,15 @@ jobs:
|
||||
docker --version
|
||||
molecule --version
|
||||
python --version
|
||||
- name: Test with molecule
|
||||
- name: Test fresh installation with molecule
|
||||
run: |
|
||||
cd ansible
|
||||
molecule test
|
||||
molecule test -s fresh
|
||||
working-directory: "${{ github.repository }}"
|
||||
- name: Test release update with molecule
|
||||
run: |
|
||||
cd ansible
|
||||
molecule test -s update
|
||||
working-directory: "${{ github.repository }}"
|
||||
# # https://galaxy.ansible.com/docs/contributing/importing.html
|
||||
# release:
|
||||
|
||||
+13
-10
@@ -208,7 +208,7 @@ jobs:
|
||||
tag_name: ng-${{ steps.get_version.outputs.version }}
|
||||
release_name: Paperless-ng ${{ steps.get_version.outputs.version }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
prerelease: false
|
||||
body: |
|
||||
For a complete list of changes, see the changelog at https://paperless-ng.readthedocs.io/en/latest/changelog.html.
|
||||
-
|
||||
@@ -225,7 +225,7 @@ jobs:
|
||||
|
||||
# build and push image to docker hub.
|
||||
build-docker-image:
|
||||
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/ng-'))
|
||||
if: github.event_name == 'push' && (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/ng-'))
|
||||
runs-on: ubuntu-latest
|
||||
needs: [frontend, tests]
|
||||
steps:
|
||||
@@ -233,15 +233,18 @@ jobs:
|
||||
name: Prepare
|
||||
id: prepare
|
||||
run: |
|
||||
VERSION=edge
|
||||
IMAGE_NAME=jonaswinkler/paperless-ng
|
||||
if [[ $GITHUB_REF == refs/tags/ng-* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/ng-}
|
||||
elif [[ $GITHUB_REF == refs/heads/master ]]; then
|
||||
VERSION=latest
|
||||
TAGS=${IMAGE_NAME}:${GITHUB_REF#refs/tags/ng-},${IMAGE_NAME}:latest
|
||||
INSPECT_TAG=${IMAGE_NAME}:latest
|
||||
elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/heads/}
|
||||
TAGS=${IMAGE_NAME}:${GITHUB_REF#refs/heads/}
|
||||
INSPECT_TAG=${TAGS}
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
echo ::set-output name=version::${VERSION}
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
echo ::set-output name=inspect_tag::${INSPECT_TAG}
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -279,10 +282,10 @@ jobs:
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
push: true
|
||||
tags: jonaswinkler/paperless-ng:${{ steps.prepare.outputs.version }}
|
||||
tags: ${{ steps.prepare.outputs.tags }}
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||
-
|
||||
name: Inspect image
|
||||
run: |
|
||||
docker buildx imagetools inspect jonaswinkler/paperless-ng:${{ steps.prepare.outputs.version }}
|
||||
docker buildx imagetools inspect ${{ steps.prepare.outputs.inspect_tag }}
|
||||
@@ -1,4 +1,4 @@
|
||||

|
||||
[](https://github.com/jonaswinkler/paperless-ng/actions)
|
||||
[](https://paperless-ng.readthedocs.io/en/latest/?badge=latest)
|
||||
[](https://gitter.im/paperless-ng/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://hub.docker.com/r/jonaswinkler/paperless-ng)
|
||||
@@ -10,7 +10,9 @@
|
||||
|
||||
Paperless-ng is a fork of the original project, adding a new interface and many other changes under the hood. For a detailed list of changes, have a look at the changelog in the documentation.
|
||||
|
||||
This project is still in development and some things may not work as expected.
|
||||
# Survey
|
||||
|
||||
If you already used Paperless-ng for a bit, would like to give some anonymous feedback, and help me decide on what to focus on next: I've created a survey, [see here](https://github.com/jonaswinkler/paperless-ng/issues/402). Thank you!
|
||||
|
||||
# How it Works
|
||||
|
||||
@@ -31,6 +33,8 @@ Here's what you get:
|
||||
# Features
|
||||
|
||||
* Performs OCR on your documents, adds selectable text to image only documents and adds tags, correspondents and document types to your documents.
|
||||
* Supports PDF documents, images, plain text files, and Office documents (Word, Excel, Powerpoint, and LibreOffice equivalents).
|
||||
* Office document support is optional and provided by Apache Tika (see [configuration](https://paperless-ng.readthedocs.io/en/latest/configuration.html#tika-settings))
|
||||
* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and can be configured freely.
|
||||
* Single page application front end. Should be pretty snappy. Will be mobile friendly in the future.
|
||||
* Includes a dashboard that shows basic statistics and has document upload.
|
||||
@@ -48,43 +52,10 @@ Here's what you get:
|
||||
* Paperless learns from your documents and will be able to automatically assign tags, correspondents and types to documents once you've stored a few documents in paperless.
|
||||
* A task processor that processes documents in parallel and also tells you when something goes wrong. On modern multi core systems, consumption is blazing fast.
|
||||
|
||||
If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html).
|
||||
If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html). However, some parts of the UI have changed since I took these.
|
||||
|
||||
For a complete list of changes from paperless, check out the [changelog](https://paperless-ng.readthedocs.io/en/latest/changelog.html)
|
||||
|
||||
# Roadmap for 1.0
|
||||
|
||||
- Make the front end nice (except mobile).
|
||||
- Fix whatever bugs I and you find.
|
||||
- Make the documentation nice.
|
||||
|
||||
## Roadmap for versions beyond 1.0
|
||||
|
||||
These are things that I want to add to paperless eventually. They are sorted by priority.
|
||||
|
||||
- **More search.** The search backend is incredibly versatile and customizable. Searching is the most important feature of this project and thus, I want to implement things like:
|
||||
- Group and limit search results by correspondent, show “more from this” links in the results.
|
||||
- **Nested tags**. Organize tags in a hierarchical structure. This will combine the benefits of folders and tags in one coherent system.
|
||||
- **An interactive consumer** that shows its progress for documents it processes on the web page.
|
||||
- With live updates and websockets. This already works on a dev branch, but requires a lot of new dependencies, which I'm not particularly happy about.
|
||||
- Notifications when a document was added with buttons to open the new document right away.
|
||||
- **Arbitrary tag colors**. Allow the selection of any color with a color picker.
|
||||
|
||||
Apart from that, paperless is pretty much feature complete.
|
||||
|
||||
## On the chopping block.
|
||||
|
||||
- **GnuPG encrypion.** [Here's a note about encryption in paperless](https://paperless-ng.readthedocs.io/en/latest/administration.html#managing-encryption). The gist of it is that I don't see which attacks this implementation protects against. It gives a false sense of security to users who don't care about how it works.
|
||||
|
||||
## Wont-do list.
|
||||
|
||||
These features will probably never make it into paperless, since paperless is meant to be an easy to use set-and-forget solution.
|
||||
|
||||
- **Document versions.** I might consider adding the ability to update a document with a newer version, but that's about it. The kind of documents that get added to paperless usually don't change at all.
|
||||
- **Workflows.** I don't see a use case for these, yet.
|
||||
- **Folders.** Tags are superior in just about every way.
|
||||
- **Apps / extension support.** Again, paperless is meant to be simple.
|
||||
|
||||
# Getting started
|
||||
|
||||
The recommended way to deploy paperless is docker-compose. The files in the /docker/hub directory are configured to pull the image from Docker Hub.
|
||||
|
||||
+92
-16
@@ -1,38 +1,114 @@
|
||||
Role Name
|
||||
=========
|
||||
Ansible Role: paperless-ng
|
||||
==========================
|
||||
|
||||
A brief description of the role goes here.
|
||||
Installs and configures paperless-ng EDMS on Debian/Ubuntu systems.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||
No special system requirements. Ansible 2.7 or newer is required.
|
||||
|
||||
Note that this role requires root access, so either run it in a playbook with a global `become: yes`, or invoke the role in your playbook like:
|
||||
|
||||
- hosts: all
|
||||
roles:
|
||||
- role: ansible
|
||||
become: yes
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||
Most configuration variables from paperless-ng itself are available and accept their respective arguments.
|
||||
Every `PAPERLESS_*` configuration varaible is lowercased and instead prefixed with `paperlessng_*` in `defaults/main.yml`.
|
||||
|
||||
For a full listing including explainations and allowed values, see the current [documentation](https://paperless-ng.readthedocs.io/en/ng-0.9.14/configuration.html).
|
||||
|
||||
Additional variables available in this role are listed below, along with default values:
|
||||
|
||||
paperlessng_version: 0.9.14
|
||||
|
||||
The [release](https://github.com/jonaswinkler/paperless-ng/releases) archive version of paperless-ng to install.
|
||||
|
||||
paperlessng_redis_host: localhost
|
||||
paperlessng_redis_port: 6379
|
||||
|
||||
Seperate configuration values that combine into `PAPERLESS_REDIS`.
|
||||
|
||||
paperlessng_db_type: sqlite
|
||||
|
||||
Database to use. Default is file-based SQLite.
|
||||
|
||||
paperlessng_db_host: localhost
|
||||
paperlessng_db_port: 5432
|
||||
paperlessng_db_name: paperlessng
|
||||
paperlessng_db_user: paperlessng
|
||||
paperlessng_db_pass: paperlessng
|
||||
paperlessng_db_sslmode: prefer
|
||||
|
||||
Database configuration (only applicable if `paperlessng_db_type == 'postgresql'`).
|
||||
|
||||
paperlessng_directory: /opt/paperless-ng
|
||||
|
||||
Root directory paperless-ng is installed into.
|
||||
|
||||
paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv"
|
||||
|
||||
Directory used for the virtual environment for paperless-ng.
|
||||
|
||||
paperlessng_ocr_languages:
|
||||
- eng
|
||||
|
||||
List of OCR languages to install and configure (`apt search tesseract-ocr-*`).
|
||||
|
||||
paperlessng_use_jbig2enc: True
|
||||
|
||||
Whether to install and use [jbig2enc](https://github.com/agl/jbig2enc) for OCRmyPDF.
|
||||
|
||||
paperlessng_big2enc_lossy: False
|
||||
|
||||
Whether to use jbig2enc's lossy compression mode.
|
||||
|
||||
paperlessng_superuser_name: paperlessng
|
||||
paperlessng_superuser_email: paperlessng@example.com
|
||||
paperlessng_superuser_password: paperlessng
|
||||
|
||||
Credentials of the initial superuser in paperless-ng.
|
||||
|
||||
paperlessng_system_user: paperlessng
|
||||
paperlessng_system_group: paperlessng
|
||||
|
||||
System user and group to run the paperless-ng services as (will be created if required).
|
||||
|
||||
paperlessng_listen_address: 127.0.0.1
|
||||
paperlessng_listen_port: 8000
|
||||
|
||||
Address and port for the paperless-ng service to listen on.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||
No ansible dependencies.
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
`playbook.yml`:
|
||||
|
||||
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||
|
||||
- hosts: servers
|
||||
- hosts: all
|
||||
become: yes
|
||||
vars_files:
|
||||
- vars/main.yml
|
||||
roles:
|
||||
- { role: username.rolename, x: 42 }
|
||||
- ansible
|
||||
|
||||
License
|
||||
-------
|
||||
`vars/main.yml`:
|
||||
|
||||
BSD
|
||||
paperlessng_media_root: /mnt/media/smbshare
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
paperlessng_db_type: postgresql
|
||||
paperlessng_db_pass: PLEASEPROVIDEASTRONGPASSWORDHERE
|
||||
|
||||
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
||||
paperless_secret_key: AGAINPLEASECHANGETHISNOW
|
||||
|
||||
paperlessng_ocr_languages:
|
||||
- eng
|
||||
- deu
|
||||
+53
-18
@@ -1,5 +1,19 @@
|
||||
---
|
||||
paperlessng_version: 0.9.13
|
||||
paperlessng_version: 0.9.14
|
||||
|
||||
# Required services
|
||||
paperlessng_redis_host: localhost
|
||||
paperlessng_redis_port: 6379
|
||||
paperlessng_db_type: sqlite # or postgresql
|
||||
# Below entries only apply for paperlessng_db_type=='postgresql'
|
||||
paperlessng_db_host: localhost
|
||||
paperlessng_db_port: 5432
|
||||
paperlessng_db_name: paperlessng
|
||||
paperlessng_db_user: paperlessng
|
||||
paperlessng_db_pass: paperlessng
|
||||
paperlessng_db_sslmode: prefer
|
||||
|
||||
# Paths and folders
|
||||
paperlessng_directory: /opt/paperless-ng
|
||||
paperlessng_consumption_dir: "{{ paperlessng_directory }}/consumption"
|
||||
paperlessng_data_dir: "{{ paperlessng_directory }}/data"
|
||||
@@ -8,36 +22,57 @@ paperlessng_static_dir: "{{ paperlessng_directory }}/static"
|
||||
paperlessng_filename_format:
|
||||
paperlessng_virtualenv: "{{ paperlessng_directory }}/.venv"
|
||||
|
||||
# Hosting & Security
|
||||
paperless_secret_key: PLEASECHANGETHISFORTHELOVEOFGOD
|
||||
paperless_allowed_hosts: "*"
|
||||
paperless_cors_allowed_hosts: http://localhost:8000
|
||||
paperless_force_script_name:
|
||||
paperless_static_url: /static/
|
||||
paperless_auto_login_username:
|
||||
paperless_cookie_prefix: ""
|
||||
paperless_enable_http_remote_user: False
|
||||
|
||||
# OCR settings
|
||||
paperlessng_ocr_languages:
|
||||
- eng
|
||||
paperlessng_time_zone: Europe/Berlin
|
||||
paperlessng_ocr_mode: skip
|
||||
paperlessng_ocr_output_type: pdfa
|
||||
paperlessng_ocr_pages: 0
|
||||
paperlessng_ocr_image_dpi:
|
||||
# see https://ocrmypdf.readthedocs.io/en/latest/api.html#ocrmypdf.ocr
|
||||
paperlessng_ocrmypdf_args:
|
||||
#- "deskew": true # https://github.com/jonaswinkler/paperless-ng/issues/231
|
||||
paperlessng_ocr_user_args:
|
||||
#- "deskew": True # https://github.com/jonaswinkler/paperless-ng/issues/231
|
||||
- "optimize": 1
|
||||
paperlessng_use_jbig2enc: true
|
||||
paperlessng_big2enc_lossy: false
|
||||
paperlessng_tika_enabled: false
|
||||
paperlessng_use_jbig2enc: True
|
||||
paperlessng_big2enc_lossy: False
|
||||
|
||||
# Tika settings
|
||||
paperlessng_tika_enabled: False
|
||||
paperlessng_tika_endpoint: http://localhost:9998
|
||||
paperlessng_tika_gotenberg_endpoint: http://localhost:3000
|
||||
|
||||
# Software tweaks
|
||||
paperlessng_time_zone: Europe/Berlin
|
||||
paperlessng_consumer_polling: 0
|
||||
paperlessng_consumer_delete_duplicates: False
|
||||
paperlessng_consumer_recursive: False
|
||||
paperlessng_consumer_subdirs_as_tags: False
|
||||
paperlessng_optimize_thumbnails: True
|
||||
paperlessng_post_consume_script:
|
||||
paperlessng_filename_date_order:
|
||||
paperlessng_filename_parse_transforms:
|
||||
paperlessng_thumbnail_font_name: /usr/share/fonts/liberation/LiberationSerif-Regular.ttf
|
||||
paperlessng_ignore_dates: ""
|
||||
|
||||
# Superuser settings
|
||||
paperlessng_superuser_name: paperlessng
|
||||
paperlessng_superuser_email: paperlessng@example.com
|
||||
paperlessng_superuser_password: paperlessng
|
||||
|
||||
# System user settings
|
||||
paperlessng_system_user: paperlessng
|
||||
paperlessng_system_group: paperlessng
|
||||
|
||||
# Webserver settings
|
||||
paperlessng_listen_address: 127.0.0.1
|
||||
paperlessng_listen_port: 8000
|
||||
|
||||
paperlessng_redis_host: localhost
|
||||
paperlessng_redis_port: 6379
|
||||
|
||||
paperlessng_db_type: sqlite # or postgresql
|
||||
# Below entries only apply for paperlessng_db_type=='postgresql'
|
||||
paperlessng_db_host: localhost
|
||||
paperlessng_db_port: 5432
|
||||
paperlessng_db_name: paperlessng
|
||||
paperlessng_db_user: paperlessng
|
||||
paperlessng_db_pass: paperlessng
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: "Include ansible"
|
||||
include_role:
|
||||
name: "ansible"
|
||||
@@ -1,14 +0,0 @@
|
||||
---
|
||||
# This is an example playbook to execute Ansible tests.
|
||||
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: check if webserver is up
|
||||
uri:
|
||||
url: http://localhost:8000
|
||||
status_code: [200, 302]
|
||||
return_content: yes
|
||||
register: landingpage
|
||||
failed_when: "'Sign in</button>' not in landingpage.content"
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: fresh installation
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: install paperless-ng with default parameters
|
||||
include_role:
|
||||
name: ansible
|
||||
File renamed without changes.
@@ -0,0 +1,60 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
|
||||
vars_files:
|
||||
- ../../defaults/main.yml
|
||||
|
||||
tasks:
|
||||
- name: check if webserver is up
|
||||
uri:
|
||||
url: http://localhost:8000
|
||||
status_code: [200, 302]
|
||||
return_content: yes
|
||||
register: landingpage
|
||||
failed_when: "'Sign in</button>' not in landingpage.content"
|
||||
|
||||
- name: check if document posting works
|
||||
uri:
|
||||
url: http://localhost:8000/api/documents/post_document/
|
||||
method: POST
|
||||
body_format: form-multipart
|
||||
body:
|
||||
document:
|
||||
content: FOO
|
||||
filename: document.txt
|
||||
mime_type: text/plain
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: post_document
|
||||
failed_when: "'OK' not in post_document.content"
|
||||
|
||||
- name: verify uploaded document has been accepted
|
||||
uri:
|
||||
url: http://localhost:8000/api/logs/
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: logs
|
||||
failed_when: "'Consuming document.txt' not in logs.content"
|
||||
|
||||
# assumes txt consumption finished by now, might have to sleep a bit
|
||||
- name: verify uploaded document has been consumed
|
||||
uri:
|
||||
url: http://localhost:8000/api/logs/
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: logs
|
||||
failed_when: "'document consumption finished' not in logs.content"
|
||||
|
||||
- name: verify uploaded document is avaiable
|
||||
uri:
|
||||
url: http://localhost:8000/api/documents/1/
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: document
|
||||
failed_when: "'Not found.' in document.content or 'FOO' not in document.content"
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: update previous release to newest release
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: set current version as installation target
|
||||
set_fact:
|
||||
paperlessng_version: 0.9.14
|
||||
|
||||
- name: update to newest paperless-ng release
|
||||
include_role:
|
||||
name: ansible
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: ubuntu_focal
|
||||
image: jrei/systemd-ubuntu:20.04
|
||||
privileged: true
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
tmpfs:
|
||||
- /tmp
|
||||
- /run
|
||||
- /run/lock
|
||||
override_command: False
|
||||
# ubuntu 18.04 bionic works except that
|
||||
# the default redis configuration expects IPv6 which is not enabled in docker by default
|
||||
# the default Python environment is configured for ASCII instead of UTF-8
|
||||
# ubuntu 16.04 xenial only has Python 3.5 which is EOL and breaks multiple dependencies
|
||||
- name: debian_buster
|
||||
image: jrei/systemd-debian:10
|
||||
privileged: true
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
||||
tmpfs:
|
||||
- /tmp
|
||||
- /run
|
||||
- /run/lock
|
||||
override_command: False
|
||||
# debian 9 stretch only has Python 3.5 which is EOL and breaks multiple dependencies
|
||||
provisioner:
|
||||
name: ansible
|
||||
verifier:
|
||||
name: ansible
|
||||
@@ -0,0 +1,10 @@
|
||||
- name: install previous release
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: set previous version as installation target
|
||||
set_fact:
|
||||
paperlessng_version: 0.9.13
|
||||
|
||||
- name: install previous paperless-ng release
|
||||
include_role:
|
||||
name: ansible
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
|
||||
vars_files:
|
||||
- ../../defaults/main.yml
|
||||
|
||||
tasks:
|
||||
- name: check if webserver is up
|
||||
uri:
|
||||
url: http://localhost:8000
|
||||
status_code: [200, 302]
|
||||
return_content: yes
|
||||
register: landingpage
|
||||
failed_when: "'Sign in</button>' not in landingpage.content"
|
||||
|
||||
- name: check if document posting works
|
||||
uri:
|
||||
url: http://localhost:8000/api/documents/post_document/
|
||||
method: POST
|
||||
body_format: form-multipart
|
||||
body:
|
||||
document:
|
||||
content: FOO
|
||||
filename: document.txt
|
||||
mime_type: text/plain
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: post_document
|
||||
failed_when: "'OK' not in post_document.content"
|
||||
|
||||
- name: verify uploaded document has been accepted
|
||||
uri:
|
||||
url: http://localhost:8000/api/logs/
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: logs
|
||||
failed_when: "'Consuming document.txt' not in logs.content"
|
||||
|
||||
# assumes txt consumption finished by now, might have to sleep a bit
|
||||
- name: verify uploaded document has been consumed
|
||||
uri:
|
||||
url: http://localhost:8000/api/logs/
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: logs
|
||||
failed_when: "'document consumption finished' not in logs.content"
|
||||
|
||||
- name: verify uploaded document is avaiable
|
||||
uri:
|
||||
url: http://localhost:8000/api/documents/1/
|
||||
headers:
|
||||
Authorization: 'Basic {{ (paperlessng_superuser_name + ":" + paperlessng_superuser_password) | b64encode }}'
|
||||
return_content: yes
|
||||
register: document
|
||||
failed_when: "'Not found.' in document.content or 'FOO' not in document.content"
|
||||
+102
-76
@@ -9,26 +9,26 @@
|
||||
update_cache: yes
|
||||
pkg:
|
||||
# paperless-ng
|
||||
- python3-dev
|
||||
- python3-pip
|
||||
- gettext
|
||||
- python3-dev
|
||||
- fonts-liberation
|
||||
- imagemagick
|
||||
- unpaper
|
||||
- ghostscript
|
||||
- optipng
|
||||
- tesseract-ocr
|
||||
- gnupg
|
||||
- libpoppler-cpp-dev
|
||||
- libmagic-dev
|
||||
- libpq-dev
|
||||
- libmagic-dev
|
||||
- mime-support
|
||||
# OCRmyPDF
|
||||
- unpaper
|
||||
- ghostscript
|
||||
- icc-profiles-free
|
||||
- qpdf
|
||||
- liblept5
|
||||
- libxml2
|
||||
- pngquant
|
||||
- zlib1g
|
||||
- tesseract-ocr
|
||||
# dev
|
||||
- sudo
|
||||
- build-essential
|
||||
@@ -105,46 +105,64 @@
|
||||
ignore_errors: yes
|
||||
register: paperlessng_current_version
|
||||
|
||||
- name: register current state
|
||||
set_fact:
|
||||
fresh_installation: '{{ "No such file or directory" in paperlessng_current_version.stderr }}'
|
||||
update_installation: '{{ "No such file or directory" not in paperlessng_current_version.stderr and paperlessng_current_version.stdout != paperlessng_version | string }}'
|
||||
reconfigure_only: '{{ paperlessng_current_version.stdout == paperlessng_version | string }}'
|
||||
|
||||
- name: backup current paperless-ng installation
|
||||
copy:
|
||||
src: "{{ paperlessng_directory }}"
|
||||
remote_src: yes
|
||||
dest: "{{ paperlessng_directory }}-{{ ansible_date_time.iso8601 }}/"
|
||||
when: '"No such file or directory" not in paperlessng_current_version.stderr and paperlessng_current_version.stdout != paperlessng_version | string'
|
||||
when: update_installation
|
||||
|
||||
- name: remove current paperless sources
|
||||
file:
|
||||
path: "{{ paperlessng_directory }}/{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- docker
|
||||
- docs
|
||||
- scripts
|
||||
- src
|
||||
- static
|
||||
when: update_installation
|
||||
|
||||
- name: create temporary directory
|
||||
tempfile:
|
||||
state: directory
|
||||
register: tempdir
|
||||
when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
|
||||
when: not reconfigure_only
|
||||
|
||||
- name: extract paperless-ng
|
||||
unarchive:
|
||||
src: "https://github.com/jonaswinkler/paperless-ng/releases/download/ng-{{ paperlessng_version }}/paperless-ng-{{ paperlessng_version }}.tar.xz"
|
||||
remote_src: yes
|
||||
dest: "{{ tempdir.path }}"
|
||||
when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
|
||||
when: not reconfigure_only
|
||||
|
||||
- name: change owner and permissions of paperless-ng
|
||||
command:
|
||||
cmd: "{{ item }}"
|
||||
warn: false
|
||||
with_items:
|
||||
- "chown -R {{ paperlessng_system_user }}:{{ paperlessng_system_group }} {{ tempdir.path }}"
|
||||
- "find {{ tempdir.path }} -type d -exec chmod 0750 {} ;"
|
||||
- "find {{ tempdir.path }} -type f -exec chmod 0640 {} ;"
|
||||
when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
|
||||
- "chown -R {{ paperlessng_system_user }}:{{ paperlessng_system_group }} {{ tempdir.path }}"
|
||||
- "find {{ tempdir.path }} -type d -exec chmod 0750 {} ;"
|
||||
- "find {{ tempdir.path }} -type f -exec chmod 0640 {} ;"
|
||||
when: not reconfigure_only
|
||||
|
||||
- name: move paperless-ng
|
||||
command:
|
||||
cmd: "cp -a {{ tempdir.path }}/paperless-ng/ {{ paperlessng_directory }}"
|
||||
when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
|
||||
cmd: "cp -a {{ tempdir.path }}/paperless-ng/. {{ paperlessng_directory }}"
|
||||
when: not reconfigure_only
|
||||
|
||||
- name: remove temporary directory
|
||||
file:
|
||||
path: "{{ tempdir.path }}"
|
||||
state: absent
|
||||
when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string'
|
||||
when: not reconfigure_only
|
||||
|
||||
- name: create paperless-ng directories and set permissions
|
||||
file:
|
||||
@@ -154,7 +172,7 @@
|
||||
group: "{{ paperlessng_system_group }}"
|
||||
mode: "750"
|
||||
with_items:
|
||||
- "{{ paperlessng_directory }}" # ansible `copy:` does not set correct permissions on `dest:` for recursive copies
|
||||
- "{{ paperlessng_directory }}"
|
||||
- "{{ paperlessng_consumption_dir }}"
|
||||
- "{{ paperlessng_data_dir }}"
|
||||
- "{{ paperlessng_media_root }}"
|
||||
@@ -171,8 +189,10 @@
|
||||
regexp: "^#?{{ item.regexp }}="
|
||||
line: "{{ item.line }}"
|
||||
with_items:
|
||||
# Required services
|
||||
- regexp: PAPERLESS_REDIS
|
||||
line: "PAPERLESS_REDIS=redis://{{ paperlessng_redis_host }}:{{ paperlessng_redis_port }}"
|
||||
# Paths and folders
|
||||
- regexp: PAPERLESS_CONSUMPTION_DIR
|
||||
line: "PAPERLESS_CONSUMPTION_DIR={{ paperlessng_consumption_dir }}"
|
||||
- regexp: PAPERLESS_DATA_DIR
|
||||
@@ -183,26 +203,65 @@
|
||||
line: "PAPERLESS_STATICDIR={{ paperlessng_static_dir }}"
|
||||
- regexp: PAPERLESS_FILENAME_FORMAT
|
||||
line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}"
|
||||
# Hosting & Security
|
||||
- regexp: PAPERLESS_SECRET_KEY
|
||||
line: "PAPERLESS_SECRET_KEY={{ paperless_secret_key }}"
|
||||
- regexp: PAPERLESS_ALLOWED_HOSTS
|
||||
line: "PAPERLESS_ALLOWED_HOSTS={{ paperless_allowed_hosts }}"
|
||||
- regexp: PAPERLESS_CORS_ALLOWED_HOSTS
|
||||
line: "PAPERLESS_CORS_ALLOWED_HOSTS={{ paperless_cors_allowed_hosts }}"
|
||||
- regexp: PAPERLESS_FORCE_SCRIPT_NAME
|
||||
line: "PAPERLESS_FORCE_SCRIPT_NAME={{ paperless_force_script_name }}"
|
||||
- regexp: PAPERLESS_STATIC_URL
|
||||
line: "PAPERLESS_STATIC_URL={{ paperless_static_url }}"
|
||||
- regexp: PAPERLESS_AUTO_LOGIN_USERNAME
|
||||
line: "PAPERLESS_AUTO_LOGIN_USERNAME={{ paperless_auto_login_username }}"
|
||||
- regexp: PAPERLESS_COOKIE_PREFIX
|
||||
line: "PAPERLESS_COOKIE_PREFIX={{ paperless_cookie_prefix }}"
|
||||
- regexp: PAPERLESS_ENABLE_HTTP_REMOTE_USER
|
||||
line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperless_enable_http_remote_user }}"
|
||||
# OCR settings
|
||||
- regexp: PAPERLESS_OCR_LANGUAGE
|
||||
line: "PAPERLESS_OCR_LANGUAGE={{ paperlessng_ocr_languages | join('+') }}"
|
||||
- regexp: PAPERLESS_OCR_MODE
|
||||
line: "PAPERLESS_OCR_MODE={{ paperlessng_ocr_mode }}"
|
||||
- regexp: PAPERLESS_OCR_OUTPUT_TYPE
|
||||
line: "PAPERLESS_OCR_OUTPUT_TYPE={{ paperlessng_ocr_output_type }}"
|
||||
- regexp: PAPERLESS_OCR_PAGES
|
||||
line: "PAPERLESS_OCR_PAGES={{ paperlessng_ocr_pages }}"
|
||||
- regexp: PAPERLESS_OCR_IMAGE_DPI
|
||||
line: "PAPERLESS_OCR_IMAGE_DPI={{ paperlessng_ocr_image_dpi }}"
|
||||
- regexp: PAPERLESS_OCR_USER_ARGS
|
||||
line: "PAPERLESS_OCR_USER_ARGS={{ paperlessng_ocrmypdf_args | combine({'jbig2_lossy': true} if paperlessng_big2enc_lossy else {}) | to_json }}"
|
||||
- regexp: PAPERLESS_TIME_ZONE
|
||||
line: "PAPERLESS_TIME_ZONE={{ paperlessng_time_zone }}"
|
||||
line: "PAPERLESS_OCR_USER_ARGS={{ paperlessng_ocr_user_args | combine({'jbig2_lossy': true} if paperlessng_big2enc_lossy else {}) | to_json }}"
|
||||
# Tika settings
|
||||
- regexp: PAPERLESS_TIKA_ENABLED
|
||||
line: "PAPERLESS_TIKA_ENABLED={{ paperlessng_tika_enabled }}"
|
||||
no_log: yes
|
||||
|
||||
- name: configure paperless-ng [tika]
|
||||
lineinfile:
|
||||
path: "{{ paperlessng_directory }}/paperless.conf.template"
|
||||
regexp: "^#?{{ item.regexp }}="
|
||||
line: "'{{ item.line }}' if paperlessng_tika_enabled else '#{{ item.line }}'"
|
||||
with_items:
|
||||
- regexp: PAPERLESS_TIKA_ENDPOINT
|
||||
line: "PAPERLESS_TIKA_ENDPOINT={{ paperlessng_tika_endpoint }}"
|
||||
- regexp: PAPERLESS_TIKA_GOTENBERG_ENDPOINT
|
||||
line: "PAPERLESS_TIKA_GOTENBERG_ENDPOINT={{ paperlessng_tika_endpoint }}"
|
||||
# Software tweaks
|
||||
- regexp: PAPERLESS_TIME_ZONE
|
||||
line: "PAPERLESS_TIME_ZONE={{ paperlessng_time_zone }}"
|
||||
- regexp: PAPERLESS_CONSUMER_POLLING
|
||||
line: "PAPERLESS_CONSUMER_POLLING={{ paperlessng_consumer_polling }}"
|
||||
- regexp: PAPERLESS_CONSUMER_DELETE_DUPLICATES
|
||||
line: "PAPERLESS_CONSUMER_DELETE_DUPLICATES={{ paperlessng_consumer_delete_duplicates }}"
|
||||
- regexp: PAPERLESS_CONSUMER_RECURSIVE
|
||||
line: "PAPERLESS_CONSUMER_RECURSIVE={{ paperlessng_consumer_recursive }}"
|
||||
- regexp: PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS
|
||||
line: "PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS={{ paperlessng_consumer_subdirs_as_tags }}"
|
||||
- regexp: PAPERLESS_OPTIMIZE_THUMBNAILS
|
||||
line: "PAPERLESS_OPTIMIZE_THUMBNAILS={{ paperlessng_optimize_thumbnails }}"
|
||||
- regexp: PAPERLESS_POST_CONSUME_SCRIPT
|
||||
line: "PAPERLESS_POST_CONSUME_SCRIPT={{ paperlessng_post_consume_script }}"
|
||||
- regexp: PAPERLESS_FILENAME_DATE_ORDER
|
||||
line: "PAPERLESS_FILENAME_DATE_ORDER={{ paperlessng_filename_date_order }}"
|
||||
- regexp: PAPERLESS_THUMBNAIL_FONT_NAME
|
||||
line: "PAPERLESS_THUMBNAIL_FONT_NAME={{ paperlessng_thumbnail_font_name }}"
|
||||
- regexp: PAPERLESS_IGNORE_DATES
|
||||
line: "PAPERLESS_IGNORE_DATES={{ paperlessng_ignore_dates }}"
|
||||
no_log: yes
|
||||
|
||||
- name: configure paperless-ng database [sqlite]
|
||||
lineinfile:
|
||||
@@ -228,6 +287,8 @@
|
||||
line: "PAPERLESS_DBUSER={{ paperlessng_db_user }}"
|
||||
- regexp: PAPERLESS_DBPASS
|
||||
line: "PAPERLESS_DBPASS={{ paperlessng_db_pass }}"
|
||||
- regexp: PAPERLESS_DBSSLMODE
|
||||
line: "PAPERLESS_DBSSLMODE={{ paperlessng_db_sslmode }}"
|
||||
when: paperlessng_db_type == 'postgresql'
|
||||
no_log: yes
|
||||
|
||||
@@ -238,7 +299,7 @@
|
||||
dest: /etc/paperless.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
mode: "0644"
|
||||
register: configuration
|
||||
|
||||
- name: create paperlessng venv
|
||||
@@ -256,29 +317,15 @@
|
||||
requirements: "{{ paperlessng_directory }}/requirements.txt"
|
||||
executable: "{{ paperlessng_virtualenv }}/bin/pip3"
|
||||
extra_args: --upgrade
|
||||
when: paperlessng_current_version.stdout != paperlessng_version | string
|
||||
when: not reconfigure_only
|
||||
|
||||
- name: collect static files
|
||||
become: yes
|
||||
become_user: "{{ paperlessng_system_user }}"
|
||||
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py collectstatic --no-input"
|
||||
when: paperlessng_current_version.stdout != paperlessng_version | string
|
||||
register: static_files
|
||||
changed_when: static_files.stdout is not match("0 static files copied .*")
|
||||
|
||||
- name: create database schema
|
||||
- name: migrate database schema
|
||||
become: yes
|
||||
become_user: "{{ paperlessng_system_user }}"
|
||||
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py migrate"
|
||||
when: paperlessng_current_version.stdout != paperlessng_version | string
|
||||
register: database_schema
|
||||
changed_when: '"No migrations to apply." not in database_schema.stdout'
|
||||
|
||||
- name: compile translations
|
||||
become: yes
|
||||
become_user: "{{ paperlessng_system_user }}"
|
||||
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py compilemessages"
|
||||
when: paperlessng_current_version.stdout != paperlessng_version | string
|
||||
when: not reconfigure_only
|
||||
|
||||
- name: configure paperless superuser
|
||||
become: yes
|
||||
@@ -307,7 +354,7 @@
|
||||
else:
|
||||
User.objects.create_superuser('{{ paperlessng_superuser_name }}', '{{ paperlessng_superuser_email }}', '{{ paperlessng_superuser_password }}')
|
||||
print('changed')
|
||||
command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py shell -c \"{{ creation_script }}\""
|
||||
command: '{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py shell -c "{{ creation_script }}"'
|
||||
register: superuser
|
||||
changed_when: superuser.stdout == 'changed'
|
||||
no_log: yes
|
||||
@@ -320,7 +367,7 @@
|
||||
owner: "{{ paperlessng_system_user }}"
|
||||
group: "{{ paperlessng_system_group }}"
|
||||
mode: g-w,o-rwx
|
||||
when: venv.changed or paperlessng_current_version.stdout != paperlessng_version | string
|
||||
when: venv.changed or not reconfigure_only
|
||||
|
||||
- name: configure ghostscript for PDF
|
||||
lineinfile:
|
||||
@@ -343,34 +390,13 @@
|
||||
]
|
||||
- [
|
||||
# https://www.freedesktop.org/software/systemd/man/systemd.exec.html
|
||||
{
|
||||
option: "User",
|
||||
value: "{{ paperlessng_system_user }}",
|
||||
},
|
||||
{
|
||||
option: "Group",
|
||||
value: "{{ paperlessng_system_group }}",
|
||||
},
|
||||
{
|
||||
option: "WorkingDirectory",
|
||||
value: "{{ paperlessng_directory }}/src",
|
||||
},
|
||||
{
|
||||
option: "ProtectSystem",
|
||||
value: "full",
|
||||
},
|
||||
{
|
||||
option: "NoNewPrivileges",
|
||||
value: "true",
|
||||
},
|
||||
{
|
||||
option: "PrivateUsers",
|
||||
value: "true",
|
||||
},
|
||||
{
|
||||
option: "PrivateDevices",
|
||||
value: "true",
|
||||
}
|
||||
{ option: "User", value: "{{ paperlessng_system_user }}" },
|
||||
{ option: "Group", value: "{{ paperlessng_system_group }}" },
|
||||
{ option: "WorkingDirectory", value: "{{ paperlessng_directory }}/src", },
|
||||
{ option: "ProtectSystem", value: "full" },
|
||||
{ option: "NoNewPrivileges", value: "true" },
|
||||
{ option: "PrivateUsers", value: "true" },
|
||||
{ option: "PrivateDevices", value: "true" },
|
||||
]
|
||||
|
||||
- name: configure paperless-consumer service
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
files:
|
||||
- source: /src/locale/en-us/LC_MESSAGES/django.po
|
||||
translation: /src/locale/%two_letters_code%/LC_MESSAGES/django.po
|
||||
- source: /src-ui/messages.xlf
|
||||
translation: /src-ui/src/locale/messages.%two_letters_code%.xlf
|
||||
@@ -79,7 +79,11 @@ initialize() {
|
||||
fi
|
||||
done
|
||||
|
||||
echo "creating directory /tmp/paperless"
|
||||
mkdir -p /tmp/paperless
|
||||
|
||||
chown -R paperless:paperless ../
|
||||
chown -R paperless:paperless /tmp/paperless
|
||||
|
||||
migrations
|
||||
|
||||
|
||||
+94
-27
@@ -20,6 +20,8 @@ Options available to any installation of paperless:
|
||||
metadata to a specific folder. You may import your documents into a
|
||||
fresh instance of paperless again or store your documents in another
|
||||
DMS with this export.
|
||||
* The document exporter is also able to update an already existing export.
|
||||
Therefore, incremental backups with ``rsync`` are entirely possible.
|
||||
|
||||
Options available to docker installations:
|
||||
|
||||
@@ -53,6 +55,9 @@ Restoring
|
||||
Updating Paperless
|
||||
##################
|
||||
|
||||
Docker Route
|
||||
============
|
||||
|
||||
If a new release of paperless-ng is available, upgrading depends on how you
|
||||
installed paperless-ng in the first place. The releases are available at the
|
||||
`release page <https://github.com/jonaswinkler/paperless-ng/releases>`_.
|
||||
@@ -72,10 +77,10 @@ A. If you pull the image from the docker hub, all you need to do is:
|
||||
|
||||
$ docker-compose pull
|
||||
$ docker-compose up
|
||||
|
||||
|
||||
The docker-compose files refer to the ``latest`` version, which is always the latest
|
||||
stable release.
|
||||
|
||||
|
||||
B. If you built the image yourself, do the following:
|
||||
|
||||
.. code:: shell-session
|
||||
@@ -85,45 +90,87 @@ B. If you built the image yourself, do the following:
|
||||
$ docker-compose build
|
||||
$ docker-compose up
|
||||
|
||||
Running `docker-compose up` will also apply any new database migrations.
|
||||
Running ``docker-compose up`` will also apply any new database migrations.
|
||||
If you see everything working, press CTRL+C once to gracefully stop paperless.
|
||||
Then you can start paperless-ng with ``-d`` to have it run in the background.
|
||||
|
||||
Updating paperless without docker
|
||||
=================================
|
||||
.. note::
|
||||
|
||||
In version 0.9.14, the update process was changed. In 0.9.13 and earlier, the
|
||||
docker-compose files specified exact versions and pull won't automatically
|
||||
update to newer versions. In order to enable updates as described above, either
|
||||
get the new ``docker-compose.yml`` file from `here <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
|
||||
or edit the ``docker-compose.yml`` file, find the line that says
|
||||
|
||||
.. code::
|
||||
|
||||
image: jonaswinkler/paperless-ng:0.9.x
|
||||
|
||||
and replace the version with ``latest``:
|
||||
|
||||
.. code::
|
||||
|
||||
image: jonaswinkler/paperless-ng:latest
|
||||
|
||||
Bare Metal Route
|
||||
================
|
||||
|
||||
After grabbing the new release and unpacking the contents, do the following:
|
||||
|
||||
1. Update dependencies. New paperless version may require additional
|
||||
dependencies. The dependencies required are listed in the section about
|
||||
dependencies. The dependencies required are listed in the section about
|
||||
:ref:`bare metal installations <setup-bare_metal>`.
|
||||
|
||||
2. Update python requirements. If you use Pipenv, this is done with the following steps.
|
||||
2. Update python requirements. Keep in mind to activate your virtual environment
|
||||
before that, if you use one.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ pip install --upgrade pipenv
|
||||
$ cd /path/to/paperless
|
||||
$ pipenv clean
|
||||
$ pipenv install
|
||||
$ pip install -r requirements.txt
|
||||
|
||||
This creates a new virtual environment (or uses your existing environment)
|
||||
and installs all dependencies into it.
|
||||
|
||||
You can also use the included ``requirements.txt`` file instead and create the virtual
|
||||
environment yourself. This file includes exactly the same dependencies.
|
||||
|
||||
3. Migrate the database.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd src
|
||||
$ pipenv run python3 manage.py migrate
|
||||
|
||||
$ python3 manage.py migrate
|
||||
|
||||
This might not actually do anything. Not every new paperless version comes with new
|
||||
database migrations.
|
||||
|
||||
|
||||
|
||||
Ansible Route
|
||||
=============
|
||||
|
||||
Most of the update process is automated when using the ansible role.
|
||||
|
||||
1. Backup your defined role variables file outside the paperless source-tree:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ cp ansible/vars.yml ~/vars.yml.old
|
||||
|
||||
2. Pull the release tag you want to update to:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ git fetch --all
|
||||
$ git checkout ng-0.9.14
|
||||
|
||||
3. Update the role variable definitions ``ansible/vars.yml`` (where appropriate).
|
||||
|
||||
4. Run the ansible playbook you created created during :ref:`installation <setup-ansible>` again:
|
||||
|
||||
.. note::
|
||||
|
||||
When ansible detects that an update run is in progress, it backs up the entire ``paperlessng_directory`` to ``paperlessng_directory-TIMESTAMP``.
|
||||
Updates can be rolled back by simply moving the timestamped folder back to the original location.
|
||||
If the update succeeds and you want to continue using the new release, please don't forget to delete the backup folder.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ ansible-playbook playbook.yml
|
||||
|
||||
|
||||
Management utilities
|
||||
####################
|
||||
|
||||
@@ -140,7 +187,7 @@ or
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd /path/to/paperless/src
|
||||
$ pipenv run python manage.py <command> <arguments>
|
||||
$ python3 manage.py <command> <arguments>
|
||||
|
||||
depending on whether you use docker or not.
|
||||
|
||||
@@ -157,8 +204,13 @@ backup or migration to another DMS.
|
||||
|
||||
.. code::
|
||||
|
||||
document_exporter target
|
||||
document_exporter target [-c] [-f] [-d]
|
||||
|
||||
optional arguments:
|
||||
-c, --compare-checksums
|
||||
-f, --use-filename-format
|
||||
-d, --delete
|
||||
|
||||
``target`` is a folder to which the data gets written. This includes documents,
|
||||
thumbnails and a ``manifest.json`` file. The manifest contains all metadata from
|
||||
the database (correspondents, tags, etc).
|
||||
@@ -167,6 +219,24 @@ When you use the provided docker compose script, specify ``../export`` as the
|
||||
target. This path inside the container is automatically mounted on your host on
|
||||
the folder ``export``.
|
||||
|
||||
If the target directory already exists and contains files, paperless will assume
|
||||
that the contents of the export directory are a previous export and will attempt
|
||||
to update the previous export. Paperless will only export changed and added files.
|
||||
Paperless determines whether a file has changed by inspecting the file attributes
|
||||
"date/time modified" and "size". If that does not work out for you, specify
|
||||
``--compare-checksums`` and paperless will attempt to compare file checksums instead.
|
||||
This is slower.
|
||||
|
||||
Paperless will not remove any existing files in the export directory. If you want
|
||||
paperless to also remove files that do not belong to the current export such as files
|
||||
from deleted documents, specify ``--delete``. Be careful when pointing paperless to
|
||||
a directory that already contains other files.
|
||||
|
||||
The filenames generated by this command follow the format
|
||||
``[date created] [correspondent] [title].[extension]``.
|
||||
If you want paperless to use ``PAPERLESS_FILENAME_FORMAT`` for exported filenames
|
||||
instead, specify ``--use-filename-format``.
|
||||
|
||||
|
||||
.. _utilities-importer:
|
||||
|
||||
@@ -361,7 +431,7 @@ Documents can be stored in Paperless using GnuPG encryption.
|
||||
Furthermore, the entire text content of the documents is stored plain in the
|
||||
database, even if your documents are encrypted. Filenames are not encrypted as
|
||||
well.
|
||||
|
||||
|
||||
Also, the web server provides transparent access to your encrypted documents.
|
||||
|
||||
Consider running paperless on an encrypted filesystem instead, which will then
|
||||
@@ -384,6 +454,3 @@ Basic usage to disable encryption of your document store:
|
||||
.. code::
|
||||
|
||||
decrypt_documents [--passphrase SECR3TP4SSPHRA$E]
|
||||
|
||||
|
||||
.. _Pipenv: https://pipenv.pypa.io/en/latest/
|
||||
+29
-6
@@ -5,6 +5,29 @@
|
||||
Changelog
|
||||
*********
|
||||
|
||||
paperless-ng 1.0.0
|
||||
##################
|
||||
|
||||
Nothing special about this release, but since there are relatively few bug reports coming in, I think that this is reasonably stable.
|
||||
|
||||
* Document export
|
||||
|
||||
* The document exporter has been rewritten to support updating an already existing export in place.
|
||||
This enables incremental backups with ``rsync``.
|
||||
* The document exporter supports naming exported files according to ``PAPERLESS_FILENAME_FORMAT``.
|
||||
* The document exporter locks the media directory and the database during execution to ensure that
|
||||
the resulting export is consistent.
|
||||
* See the :ref:`updated documentation <utilities-exporter>` for more details.
|
||||
|
||||
* Other changes and additions
|
||||
|
||||
* Added a language selector to the settings.
|
||||
* Added date format options to the settings.
|
||||
* Range selection with shift clicking is now possible in the document list.
|
||||
* Filtering correspondent, type and tag management pages by name.
|
||||
* Focus "Name" field in dialogs by default.
|
||||
|
||||
|
||||
paperless-ng 0.9.14
|
||||
###################
|
||||
|
||||
@@ -116,7 +139,7 @@ paperless-ng 0.9.10
|
||||
* There are some configuration options in the settings to alter the behavior.
|
||||
|
||||
* Other changes and additions
|
||||
|
||||
|
||||
* Thanks to `zjean`_, paperless now publishes a webmanifest, which is useful for adding the application to home screens on mobile devices.
|
||||
* The Paperless-ng logo now navigates to the dashboard.
|
||||
* Filter for documents that don't have any correspondents, types or tags assigned.
|
||||
@@ -136,7 +159,7 @@ paperless-ng 0.9.10
|
||||
The bulk delete operations did not update the search index. Therefore, documents that you deleted remained in the index and
|
||||
caused the search to return messages about missing documents when searching. Further bulk operations will properly update
|
||||
the index.
|
||||
|
||||
|
||||
However, this change is not retroactive: If you used the delete method of the bulk editor, you need to reindex your search index
|
||||
by :ref:`running the management command document_index with the argument reindex <administration-index>`.
|
||||
|
||||
@@ -191,12 +214,12 @@ paperless-ng 0.9.7
|
||||
|
||||
* Thanks to the hard work of `Michael Shamoon`_, paperless now comes with a much more streamlined UI for
|
||||
filtering documents.
|
||||
|
||||
|
||||
* `Michael Shamoon`_ replaced the document preview with another component. This should fix compatibility with Safari browsers.
|
||||
|
||||
* Added buttons to the management pages to quickly show all documents with one specific tag, correspondent, or title.
|
||||
|
||||
* Paperless now stores your saved views on the server and associates them with your user account.
|
||||
|
||||
* Paperless now stores your saved views on the server and associates them with your user account.
|
||||
This means that you can access your views on multiple devices and have separate views for different users.
|
||||
You will have to recreate your views.
|
||||
|
||||
@@ -214,7 +237,7 @@ paperless-ng 0.9.7
|
||||
This option enables you to be logged in into multiple instances by specifying different cookie names for each instance.
|
||||
|
||||
* Fixes
|
||||
|
||||
|
||||
* Sometimes paperless would assign dates in the future to newly consumed documents.
|
||||
* The filename format fields ``{created_month}`` and ``{created_day}`` now use a leading zero for single digit values.
|
||||
* The filename format field ``{tags}`` can no longer be used without arguments.
|
||||
|
||||
@@ -267,7 +267,7 @@ PAPERLESS_OCR_IMAGE_DPI=<num>
|
||||
present in an image.
|
||||
|
||||
|
||||
PAPERLESS_OCR_USER_ARG=<json>
|
||||
PAPERLESS_OCR_USER_ARGS=<json>
|
||||
OCRmyPDF offers many more options. Use this parameter to specify any
|
||||
additional arguments you wish to pass to OCRmyPDF. Since Paperless uses
|
||||
the API of OCRmyPDF, you have to specify these in a format that can be
|
||||
@@ -376,25 +376,24 @@ PAPERLESS_THREADS_PER_WORKER=<num>
|
||||
use a higher thread per worker count.
|
||||
|
||||
The default is a balance between the two, according to your CPU core count,
|
||||
with a slight favor towards threads per worker, and leaving at least one core
|
||||
free for other tasks:
|
||||
with a slight favor towards threads per worker:
|
||||
|
||||
+----------------+---------+---------+
|
||||
| CPU core count | Workers | Threads |
|
||||
+----------------+---------+---------+
|
||||
| 1 | 1 | 1 |
|
||||
+----------------+---------+---------+
|
||||
| 2 | 1 | 1 |
|
||||
| 2 | 2 | 1 |
|
||||
+----------------+---------+---------+
|
||||
| 4 | 1 | 3 |
|
||||
| 4 | 2 | 2 |
|
||||
+----------------+---------+---------+
|
||||
| 6 | 2 | 2 |
|
||||
| 6 | 2 | 3 |
|
||||
+----------------+---------+---------+
|
||||
| 8 | 2 | 3 |
|
||||
| 8 | 2 | 4 |
|
||||
+----------------+---------+---------+
|
||||
| 12 | 3 | 3 |
|
||||
| 12 | 3 | 4 |
|
||||
+----------------+---------+---------+
|
||||
| 16 | 3 | 5 |
|
||||
| 16 | 4 | 4 |
|
||||
+----------------+---------+---------+
|
||||
|
||||
If you only specify PAPERLESS_TASK_WORKERS, paperless will adjust
|
||||
|
||||
+189
-49
@@ -20,45 +20,45 @@ Paperless consists of the following components:
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd /path/to/paperless/src/
|
||||
$ pipenv run gunicorn -c /usr/src/paperless/gunicorn.conf.py -b 0.0.0.0:8000 paperless.wsgi
|
||||
$ gunicorn -c ../gunicorn.conf.py -b 0.0.0.0:8000 paperless.wsgi
|
||||
|
||||
or by any other means such as Apache ``mod_wsgi``.
|
||||
|
||||
* **The consumer:** This is what watches your consumption folder for documents.
|
||||
However, the consumer itself does not consume really consume your documents anymore.
|
||||
It rather notifies a task processor that a new file is ready for consumption.
|
||||
However, the consumer itself does not really consume your documents.
|
||||
Now it notifies a task processor that a new file is ready for consumption.
|
||||
I suppose it should be named differently.
|
||||
This also used to check your emails, but that's now gone elsewhere as well.
|
||||
This was also used to check your emails, but that's now done elsewhere as well.
|
||||
|
||||
Start the consumer with the management command ``document_consumer``:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd /path/to/paperless/src/
|
||||
$ pipenv run python3 manage.py document_consumer
|
||||
$ python3 manage.py document_consumer
|
||||
|
||||
.. _setup-task_processor:
|
||||
|
||||
* **The task processor:** Paperless relies on `Django Q <https://django-q.readthedocs.io/en/latest/>`_
|
||||
for doing much of the heavy lifting. This is a task queue that accepts tasks from
|
||||
multiple sources and processes tasks in parallel. It also comes with a scheduler that executes
|
||||
for doing most of the heavy lifting. This is a task queue that accepts tasks from
|
||||
multiple sources and processes these in parallel. It also comes with a scheduler that executes
|
||||
certain commands periodically.
|
||||
|
||||
This task processor is responsible for:
|
||||
|
||||
* Consuming documents. When the consumer finds new documents, it notifies the task processor to
|
||||
start a consumption task.
|
||||
* Consuming emails. It periodically checks your configured accounts for new mails and
|
||||
produces consumption tasks for any documents it finds.
|
||||
* The task processor also performs the consumption of any documents you upload through
|
||||
the web interface.
|
||||
* Maintain the search index and the automatic matching algorithm. These are things that paperless
|
||||
* Consuming emails. It periodically checks your configured accounts for new emails and
|
||||
notifies the task processor to consume the attachment of an email.
|
||||
* Maintaining the search index and the automatic matching algorithm. These are things that paperless
|
||||
needs to do from time to time in order to operate properly.
|
||||
|
||||
This allows paperless to process multiple documents from your consumption folder in parallel! On
|
||||
a modern multi core system, consumption with full ocr is blazing fast.
|
||||
a modern multi core system, this makes the consumption process with full OCR blazingly fast.
|
||||
|
||||
The task processor comes with a built-in admin interface that you can use to see whenever any of the
|
||||
The task processor comes with a built-in admin interface that you can use to check whenever any of the
|
||||
tasks fail and inspect the errors (i.e., wrong email credentials, errors during consuming a specific
|
||||
file, etc).
|
||||
|
||||
@@ -67,11 +67,11 @@ Paperless consists of the following components:
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd /path/to/paperless/src/
|
||||
$ pipenv run python3 manage.py qcluster
|
||||
$ python3 manage.py qcluster
|
||||
|
||||
* A `redis <https://redis.io/>`_ message broker: This is a really lightweight service that is responsible
|
||||
for getting the tasks from the webserver and consumer to the task scheduler. These run in different
|
||||
processes (maybe even on different machines!), and therefore, this is necessary.
|
||||
for getting the tasks from the webserver and the consumer to the task scheduler. These run in a different
|
||||
process (maybe even on different machines!), and therefore, this is necessary.
|
||||
|
||||
* Optional: A database server. Paperless supports both PostgreSQL and SQLite for storing its data.
|
||||
|
||||
@@ -79,28 +79,39 @@ Paperless consists of the following components:
|
||||
Installation
|
||||
############
|
||||
|
||||
You can go multiple routes with setting up and running Paperless:
|
||||
You can go multiple routes to setup and run Paperless:
|
||||
|
||||
* :ref:`Pull the image from Docker Hub <setup-docker_hub>`
|
||||
* :ref:`Build the Docker image yourself <setup-docker_build>`
|
||||
* :ref:`Install Paperless directly on your system (bare metal) <setup-bare_metal>`
|
||||
* :ref:`Install Paperless directly on your system manually (bare metal) <setup-bare_metal>`
|
||||
* :ref:`Use ansible to install Paperless on your system automatically (bare metal) <setup-ansible>`
|
||||
|
||||
The Docker routes are quick & easy. These are the recommended routes. This configures all the stuff
|
||||
from above automatically so that it just works and uses sensible defaults for all configuration options.
|
||||
from the above automatically so that it just works and uses sensible defaults for all configuration options.
|
||||
Here you find a cheat-sheet for docker beginners: `CLI Basics <https://sehn.tech/post/devops-with-docker/>`_
|
||||
|
||||
The bare metal route is more complicated to setup but makes it easier
|
||||
The bare metal route is complicated to setup but makes it easier
|
||||
should you want to contribute some code back. You need to configure and
|
||||
run the above mentioned components yourself.
|
||||
|
||||
The ansible route combines benefits of both options:
|
||||
the setup process is fully automated, reproducible and `idempotent <https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#Idempotency>`_,
|
||||
it includes the same sensible defaults, and it simultaneously provides the flexibility of a bare metal installation.
|
||||
|
||||
.. _CLI Basics: https://sehn.tech/post/devops-with-docker/
|
||||
.. _idempotent: https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#Idempotency
|
||||
|
||||
.. _setup-docker_hub:
|
||||
|
||||
Install Paperless from Docker Hub
|
||||
=================================
|
||||
|
||||
1. Go to the `/docker/compose directory on the project page <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
|
||||
and download one of the ``docker-compose.*.yml`` files, depending on which database backend you
|
||||
1. Login with your user and create a folder in your home-directory `mkdir -v ~/paperless-ng` to have a place for your configuration files and consumption directory.
|
||||
|
||||
2. Go to the `/docker/compose directory on the project page <https://github.com/jonaswinkler/paperless-ng/tree/master/docker/compose>`_
|
||||
and download one of the `docker-compose.*.yml` files, depending on which database backend you
|
||||
want to use. Rename this file to `docker-compose.yml`.
|
||||
If you want to enable optional support for Office documents, download a file with ``-tika`` in its name.
|
||||
If you want to enable optional support for Office documents, download a file with `-tika` in the file name.
|
||||
Download the ``docker-compose.env`` file and the ``.env`` file as well and store them
|
||||
in the same directory.
|
||||
|
||||
@@ -109,25 +120,26 @@ Install Paperless from Docker Hub
|
||||
For new installations, it is recommended to use PostgreSQL as the database
|
||||
backend.
|
||||
|
||||
2. Install `Docker`_ and `docker-compose`_.
|
||||
3. Install `Docker`_ and `docker-compose`_.
|
||||
|
||||
.. caution::
|
||||
|
||||
If you want to use the included ``docker-compose.*.yml`` file, you
|
||||
need to have at least Docker version **17.09.0** and docker-compose
|
||||
version **1.17.0**.
|
||||
version **1.17.0**.
|
||||
To check do: `docker-compose -v` or `docker -v`
|
||||
|
||||
See the `Docker installation guide`_ on how to install the current
|
||||
version of Docker for your operating system or Linux distribution of
|
||||
choice. To get an up-to-date version of docker-compose, follow the
|
||||
choice. To get the latest version of docker-compose, follow the
|
||||
`docker-compose installation guide`_ if your package repository doesn't
|
||||
include it.
|
||||
|
||||
.. _Docker installation guide: https://docs.docker.com/engine/installation/
|
||||
.. _docker-compose installation guide: https://docs.docker.com/compose/install/
|
||||
|
||||
3. Modify ``docker-compose.yml`` to your preferences. You may want to change the path
|
||||
to the consumption directory in this file. Find the line that specifies where
|
||||
4. Modify ``docker-compose.yml`` to your preferences. You may want to change the path
|
||||
to the consumption directory. Find the line that specifies where
|
||||
to mount the consumption directory:
|
||||
|
||||
.. code::
|
||||
@@ -143,31 +155,35 @@ Install Paperless from Docker Hub
|
||||
Don't change the part after the colon or paperless wont find your documents.
|
||||
|
||||
|
||||
4. Modify ``docker-compose.env``, following the comments in the file. The
|
||||
5. Modify ``docker-compose.env``, following the comments in the file. The
|
||||
most important change is to set ``USERMAP_UID`` and ``USERMAP_GID``
|
||||
to the uid and gid of your user on the host system. This ensures that
|
||||
both the docker container and you on the host machine have write access
|
||||
to the consumption directory. If your UID and GID on the host system is
|
||||
1000 (the default for the first normal user on most systems), it will
|
||||
work out of the box without any modifications.
|
||||
work out of the box without any modifications. `id "username"` to check.
|
||||
|
||||
.. note::
|
||||
|
||||
You can use any settings from the file ``paperless.conf.example`` in this file.
|
||||
Have a look at :ref:`configuration` to see whats available.
|
||||
You can copy any setting from the file ``paperless.conf.example`` and paste it here.
|
||||
Have a look at :ref:`configuration` to see what's available.
|
||||
|
||||
.. caution::
|
||||
|
||||
Certain file systems such as NFS network shares don't support file system
|
||||
Some file systems such as NFS network shares don't support file system
|
||||
notifications with ``inotify``. When storing the consumption directory
|
||||
on such a file system, paperless will be unable to pick up new files
|
||||
on such a file system, paperless will not pick up new files
|
||||
with the default configuration. You will need to use ``PAPERLESS_CONSUMER_POLLING``,
|
||||
which will disable inotify. See :ref:`here <configuration-polling>`.
|
||||
|
||||
6. Now head over to: https://hub.docker.com/r/jonaswinkler/paperless-ng and choose your preferred
|
||||
image and copy the link. To download this image do a `docker pull` followed by the link. Do this within the directory with the .yml files.
|
||||
Depending on your network connection and CPU this will take a while. You have time to get a beverage.
|
||||
|
||||
5. Run ``docker-compose up -d``. This will create and start the necessary
|
||||
containers.
|
||||
7. Run ``docker-compose up -d``. This will create and start the necessary
|
||||
containers, but your are not done yet!
|
||||
|
||||
6. To be able to login, you will need a super user. To create it, execute the
|
||||
8. To be able to login, you will need a super user. To create it, execute the
|
||||
following command:
|
||||
|
||||
.. code-block:: shell-session
|
||||
@@ -175,12 +191,12 @@ Install Paperless from Docker Hub
|
||||
$ docker-compose run --rm webserver createsuperuser
|
||||
|
||||
This will prompt you to set a username, an optional e-mail address and
|
||||
finally a password.
|
||||
finally a password (at least 8 characters).
|
||||
|
||||
7. The default ``docker-compose.yml`` exports the webserver on your local port
|
||||
9. The default ``docker-compose.yml`` exports the webserver on your local port
|
||||
8000. If you haven't adapted this, you should now be able to visit your
|
||||
Paperless instance at ``http://127.0.0.1:8000``. You can login with the
|
||||
user and password you just created.
|
||||
Paperless instance at ``http://127.0.0.1:8000`` or your servers IP-Address:8000.
|
||||
Use the login credentials you have created with the previous step.
|
||||
|
||||
.. _Docker: https://www.docker.com/
|
||||
.. _docker-compose: https://docs.docker.com/compose/install/
|
||||
@@ -239,7 +255,7 @@ writing. Windows is not and will never be supported.
|
||||
1. Install dependencies. Paperless requires the following packages.
|
||||
|
||||
* ``python3`` 3.6, 3.7, 3.8 (3.9 is untested).
|
||||
* ``python3-pip``, optionally ``pipenv`` for package installation
|
||||
* ``python3-pip``
|
||||
* ``python3-dev``
|
||||
|
||||
* ``fonts-liberation`` for generating thumbnails for plain text files
|
||||
@@ -308,8 +324,13 @@ writing. Windows is not and will never be supported.
|
||||
|
||||
Adjust as necessary if you configured different folders.
|
||||
|
||||
7. Install python requirements. Paperless comes with both Pipfiles for ``pipenv`` as well as with a ``requirements.txt``.
|
||||
Both will install exactly the same requirements. It is up to you if you wish to use a virtual environment or not.
|
||||
7. Install python requirements from the ``requirements.txt`` file.
|
||||
It is up to you if you wish to use a virtual environment or not.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
pip3 install -r requirements.txt
|
||||
|
||||
|
||||
8. Go to ``/opt/paperless/src``, and execute the following commands:
|
||||
|
||||
@@ -333,7 +354,8 @@ writing. Windows is not and will never be supported.
|
||||
.. warning::
|
||||
|
||||
This is a development server which should not be used in
|
||||
production.
|
||||
production. It is not audited for security and performance
|
||||
is inferior to production ready web servers.
|
||||
|
||||
.. hint::
|
||||
|
||||
@@ -348,6 +370,11 @@ writing. Windows is not and will never be supported.
|
||||
``consumer`` script to watch the input folder, and the ``scheduler``
|
||||
script to run tasks such as email checking and document consumption.
|
||||
|
||||
You may need to adjust the path to the ``gunicorn`` executable. This
|
||||
will be installed as part of the python dependencies, and is either located
|
||||
in the ``bin`` folder of your virtual environment, or in ``~/.local/bin/`` if
|
||||
no virtual environment is used.
|
||||
|
||||
These services rely on redis and optionally the database server, but
|
||||
don't need to be started in any particular order. The example files
|
||||
depend on redis being started. If you use a database server, you should
|
||||
@@ -392,6 +419,121 @@ writing. Windows is not and will never be supported.
|
||||
to compile this by yourself, because this software has been patented until around 2017 and
|
||||
binary packages are not available for most distributions.
|
||||
|
||||
.. _setup-ansible:
|
||||
|
||||
Install Paperless using ansible
|
||||
===============================
|
||||
.. note::
|
||||
|
||||
This role currently only supports Debian 10 Buster and Ubuntu 20.04 Focal or later as target hosts.
|
||||
|
||||
1. Install ansible 2.7+ on the management node.
|
||||
This may be the target host paperless-ng is being installed on or any remote host which can access the target host.
|
||||
For further details, check the ansible `inventory <https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html>`_ documentation.
|
||||
|
||||
On Debian and Ubuntu, the official repositories should provide a suitable version:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
apt install ansible
|
||||
ansible --version
|
||||
|
||||
Alternatively, you can install the most recent ansible release using PyPI:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
python3 -m pip install ansible
|
||||
ansible --version
|
||||
|
||||
Make sure your taget hosts are accessible:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
ansible -m ping YourAnsibleTargetHostGoesHere
|
||||
|
||||
2. Clone the repository of paperless-ng:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
git clone https://github.com/jonaswinkler/paperless-ng
|
||||
|
||||
Checkout the latest release tag:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
cd paperless-ng
|
||||
git checkout ng-0.9.14
|
||||
|
||||
3. Create an ansible ``playbook.yml`` in the paperless-ng root directory:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
- hosts: YourAnsibleTargetHostGoesHere
|
||||
become: yes
|
||||
vars_files:
|
||||
- ansible/vars.yml
|
||||
roles:
|
||||
- ansible
|
||||
|
||||
Optional: If you also want to use PostgreSQL on the target system, install and add (for example) the `geerlingguy.postgresql <https://github.com/geerlingguy/ansible-role-postgresql>`_ role:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
ansible-galaxy install geerlingguy.postgresql
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
- hosts: YourAnsibleTargetHostGoesHere
|
||||
become: yes
|
||||
vars_files:
|
||||
- ansible/vars.yml
|
||||
roles:
|
||||
- geerlingguy.postgresql
|
||||
- ansible
|
||||
|
||||
Optional: If you also want to use a reverse proxy on the target system, install and add (for example) the `geerlingguy.nginx <https://github.com/geerlingguy/ansible-role-nginx>`_ role:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
ansible-galaxy install geerlingguy.nginx
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
- hosts: YourAnsibleTargetHostGoesHere
|
||||
become: yes
|
||||
vars_files:
|
||||
- ansible/vars.yml
|
||||
roles:
|
||||
- geerlingguy.postgresql
|
||||
- ansible
|
||||
- geerlingguy.nginx
|
||||
|
||||
4. Create ``ansible/vars.yml`` to configure your ansible deployment:
|
||||
|
||||
.. code:: yaml
|
||||
|
||||
paperless_secret_key: PleaseGenerateAStrongKeyForThis
|
||||
|
||||
paperlessng_superuser_name: YourUserName
|
||||
paperlessng_superuser_email: name@domain.tld
|
||||
paperlessng_superuser_password: YourDesiredPasswordUsedForFirstLogin
|
||||
|
||||
paperlessng_ocr_languages:
|
||||
- eng
|
||||
- deu
|
||||
|
||||
For all of the available options, please check ``ansible/README.md`` and :ref:`configuration`.
|
||||
|
||||
Optional configurations for the above-mentioned PostgreSQL and nginx roles would also go here.
|
||||
|
||||
5. Run the ansible playbook from the management node:
|
||||
|
||||
.. code:: sh
|
||||
|
||||
ansible-playbook playbook.yml
|
||||
|
||||
When this step completes successfully, paperless-ng will be available on the target host at ``http://127.0.0.1:8000`` (or the address you configured).
|
||||
|
||||
Migration to paperless-ng
|
||||
#########################
|
||||
|
||||
@@ -517,14 +659,12 @@ management commands as below.
|
||||
|
||||
This will launch the container and initialize the PostgreSQL database.
|
||||
|
||||
b) Without docker, open a shell in your virtual environment, switch to
|
||||
b) Without docker, remember to activate any virtual environment, switch to
|
||||
the ``src`` directory and create the database schema:
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
$ cd /path/to/paperless
|
||||
$ pipenv shell
|
||||
$ cd src
|
||||
$ cd /path/to/paperless/src
|
||||
$ python3 manage.py migrate
|
||||
|
||||
This will not copy any data yet.
|
||||
@@ -541,7 +681,7 @@ management commands as below.
|
||||
|
||||
$ python3 manage.py loaddata data.json
|
||||
|
||||
6. Exit the shell.
|
||||
6. If operating inside Docker, you may exit the shell now.
|
||||
|
||||
.. code:: shell-session
|
||||
|
||||
|
||||
@@ -30,13 +30,22 @@ Consumer fails to pickup any new files
|
||||
######################################
|
||||
|
||||
If you notice that the consumer will only pickup files in the consumption
|
||||
directory at startup, but won't find any other files added later, check out
|
||||
the configuration file and enable filesystem polling with the setting
|
||||
``PAPERLESS_CONSUMER_POLLING``.
|
||||
directory at startup, but won't find any other files added later, you will need to
|
||||
enable filesystem polling with the configuration option
|
||||
``PAPERLESS_CONSUMER_POLLING``, see :ref:`here <configuration-polling>`.
|
||||
|
||||
This will disable listening to filesystem changes with inotify and paperless will
|
||||
manually check the consumption directory for changes instead.
|
||||
|
||||
|
||||
Paperless always redirects to /admin
|
||||
####################################
|
||||
|
||||
You probably had the old paperless installed at some point. Paperless installed
|
||||
a permanent redirect to /admin in your browser, and you need to clear your
|
||||
browsing data / cache to fix that.
|
||||
|
||||
|
||||
Operation not permitted
|
||||
#######################
|
||||
|
||||
@@ -64,6 +73,24 @@ This may have two reasons:
|
||||
with Inbox tags. Verify that there are documents in your archive without inbox tags.
|
||||
The algorithm will only learn from documents not in your inbox.
|
||||
|
||||
UserWarning in sklearn on every single document
|
||||
###############################################
|
||||
|
||||
You may encounter warnings like this:
|
||||
|
||||
.. code::
|
||||
|
||||
/usr/local/lib/python3.7/site-packages/sklearn/base.py:315:
|
||||
UserWarning: Trying to unpickle estimator CountVectorizer from version 0.23.2 when using version 0.24.0.
|
||||
This might lead to breaking code or invalid results. Use at your own risk.
|
||||
|
||||
This happens when certain dependencies of paperless that are responsible for the auto matching algorithm are
|
||||
updated. After updating these, your current training data *might* not be compatible anymore. This can be ignored
|
||||
in most cases. This warning will disappear automatically when paperless updates the training data.
|
||||
|
||||
If you want to get rid of the warning or actually experience issues with automatic matching, delete
|
||||
the file ``classification_model.pickle`` in the data directory and let paperless recreate it.
|
||||
|
||||
Permission denied errors in the consumption directory
|
||||
#####################################################
|
||||
|
||||
@@ -78,3 +105,47 @@ Ensure that ``USERMAP_UID`` and ``USERMAP_GID`` are set to the user id and group
|
||||
different from ``1000``. See :ref:`setup-docker_hub`.
|
||||
|
||||
Also ensure that you are able to read and write to the consumption directory on the host.
|
||||
|
||||
Web-UI stuck at "Loading..."
|
||||
############################
|
||||
|
||||
This might have multiple reasons.
|
||||
|
||||
|
||||
1. If you built the docker image yourself or deployed using the bare metal route,
|
||||
make sure that there are files in ``<paperless-root>/static/frontend/<lang-code>/``.
|
||||
If there are no files, make sure that you executed ``collectstatic`` successfully, either
|
||||
manually or as part of the docker image build.
|
||||
|
||||
If the front end is still missing, make sure that the front end is compiled (files present in
|
||||
``src/documents/static/frontend``). If it is not, you need to compile the front end yourself
|
||||
or download the release archive instead of cloning the repository.
|
||||
|
||||
2. Check the output of the web server. You might see errors like this:
|
||||
|
||||
|
||||
.. code::
|
||||
|
||||
[2021-01-25 10:08:04 +0000] [40] [ERROR] Socket error processing request.
|
||||
Traceback (most recent call last):
|
||||
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 134, in handle
|
||||
self.handle_request(listener, req, client, addr)
|
||||
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 190, in handle_request
|
||||
util.reraise(*sys.exc_info())
|
||||
File "/usr/local/lib/python3.7/site-packages/gunicorn/util.py", line 625, in reraise
|
||||
raise value
|
||||
File "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", line 178, in handle_request
|
||||
resp.write_file(respiter)
|
||||
File "/usr/local/lib/python3.7/site-packages/gunicorn/http/wsgi.py", line 396, in write_file
|
||||
if not self.sendfile(respiter):
|
||||
File "/usr/local/lib/python3.7/site-packages/gunicorn/http/wsgi.py", line 386, in sendfile
|
||||
sent += os.sendfile(sockno, fileno, offset + sent, count)
|
||||
OSError: [Errno 22] Invalid argument
|
||||
|
||||
To fix this issue, add
|
||||
|
||||
.. code::
|
||||
|
||||
SENDFILE=0
|
||||
|
||||
to your `docker-compose.env` file.
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#PAPERLESS_SECRET_KEY=change-me
|
||||
#PAPERLESS_ALLOWED_HOSTS=example.com,www.example.com
|
||||
#PAPERLESS_CORS_ALLOWED_HOSTS=localhost:8080,example.com,localhost:8000
|
||||
#PAPERLESS_CORS_ALLOWED_HOSTS=http://example.com,http://localhost:8000
|
||||
#PAPERLESS_FORCE_SCRIPT_NAME=
|
||||
#PAPERLESS_STATIC_URL=/static/
|
||||
#PAPERLESS_AUTO_LOGIN_USERNAME=
|
||||
|
||||
+138
-47
@@ -394,53 +394,60 @@
|
||||
<context context-type="linenumber">2</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ddb40946e790522301687ecddb9ce1cb8ad40dd1" datatype="html">
|
||||
<source>Filter by:</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">8</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="cff1428d10d59d14e45edec3c735a27b5482db59" datatype="html">
|
||||
<source>Name</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8fa4d523f7b91df4390120b85ed0406138273e1a" datatype="html">
|
||||
<source>Color</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">14</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="d0c4488f742efeba0915e90e285a022da813deff" datatype="html">
|
||||
<source>Matching</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="9bcf8d20d23c111eca1431abd2d2ce0de324499c" datatype="html">
|
||||
<source>Document count</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">16</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="030b4423b92167200e39519599f9b863b4f7c62c" datatype="html">
|
||||
<source>Actions</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
<context context-type="linenumber">23</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="9d51b3c90afda70700229d1b8a55371c13cb3bce" datatype="html">
|
||||
<source>Documents</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">32</context>
|
||||
<context context-type="linenumber">38</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="28f86ffd419b869711aa13f5e5ff54be6d70731c" datatype="html">
|
||||
<source>Edit</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4990731724078522539" datatype="html">
|
||||
@@ -475,21 +482,35 @@
|
||||
<source>Saved view "<x id="PH" equiv-text="savedView.name"/>" deleted.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
<context context-type="linenumber">63</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5647210819299459618" datatype="html">
|
||||
<source>Settings saved successfully.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">68</context>
|
||||
<context context-type="linenumber">79</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6839066544204061364" datatype="html">
|
||||
<source>Use system language</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7729897675462249787" datatype="html">
|
||||
<source>Use date format of display language</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8488620293789898901" datatype="html">
|
||||
<source>Error while storing settings on server: <x id="PH" equiv-text="JSON.stringify(error.error)"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">80</context>
|
||||
<context context-type="linenumber">103</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="121cc5391cd2a5115bc2b3160379ee5b36cd7716" datatype="html">
|
||||
@@ -510,7 +531,7 @@
|
||||
<source>Saved views</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">64</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13" datatype="html">
|
||||
@@ -520,109 +541,158 @@
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="75f8908d266f7cc9b9e68e0be906fd080a223606" datatype="html">
|
||||
<source>Display language</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1284a077dc18a2d1ff1b744f16f1797eea28ae37" datatype="html">
|
||||
<source>You need to reload the page after applying a new language.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="0cd55822928764cc82a62ee3e6f3adbc1c630479" datatype="html">
|
||||
<source>Date display</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">32</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7df4de0d0704a06a302d853e31f2580eba98f127" datatype="html">
|
||||
<source>Date format</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">45</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="69852399a18b8ca4fca8c5bfddd3f00a6d137593" datatype="html">
|
||||
<source>Short: <x id="INTERPOLATION" equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="d01a59285e711252b98c4f193394e4b854615c78" datatype="html">
|
||||
<source>Medium: <x id="INTERPOLATION" equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="00481629776699b1caebd12b3b1176e2e23740a8" datatype="html">
|
||||
<source>Long: <x id="INTERPOLATION" equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">59</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2045151788cbdda7512752e408da59a6b54a8ef0" datatype="html">
|
||||
<source>Items per page</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="c4435e56bf0289e78fedc462f1d21fb30b9de55d" datatype="html">
|
||||
<source>Document editor</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4903e521c9bfd11ce88e7a5575106ef638912e0d" datatype="html">
|
||||
<source>Use PDF viewer provided by the browser</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="d7db07023e53f8396d18d375c2b78c25fc81c197" datatype="html">
|
||||
<source>This is usually faster for displaying large PDF documents, but it might not work on some browsers.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="9ee5d1cbfd6ee168dae37aaba2b59b50bcabb2ff" datatype="html">
|
||||
<source>Dark mode</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">44</context>
|
||||
<context context-type="linenumber">94</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="f8cb5506e70fd71fddc9bb71cee18bfff7b29637" datatype="html">
|
||||
<source>Use system settings</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8ee474504043fa89821d626e4f3413240fa91b53" datatype="html">
|
||||
<source>Enable dark mode</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">48</context>
|
||||
<context context-type="linenumber">98</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3863a86cd9e69a61d143d3daf51df44203df4a82" datatype="html">
|
||||
<source>Bulk editing</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">52</context>
|
||||
<context context-type="linenumber">102</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586" datatype="html">
|
||||
<source>Show confirmation dialogs</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">56</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a" datatype="html">
|
||||
<source>Deleting documents will always ask for confirmation.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">56</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8cfddc13e04f5545ac63f419ef363505d6f78c2e" datatype="html">
|
||||
<source>Apply on close</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">57</context>
|
||||
<context context-type="linenumber">107</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8cb90334f5dfd7fc67205085f59381e2a334ccfc" datatype="html">
|
||||
<source>Appears on</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
<context context-type="linenumber">126</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5" datatype="html">
|
||||
<source>Show on dashboard</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">79</context>
|
||||
<context context-type="linenumber">129</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="541bfc5b123b3f8867fd681eaceefb663a811973" datatype="html">
|
||||
<source>Show in sidebar</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
<context context-type="linenumber">133</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="abba764a7a595d04dc8c3b26e04b3780d4fdb540" datatype="html">
|
||||
<source>No saved views defined.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">93</context>
|
||||
<context context-type="linenumber">143</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ef60a738a565f498b858e903e42bc5ffc3cc1299" datatype="html">
|
||||
@@ -650,7 +720,7 @@
|
||||
<source>Last correspondence</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1234709746630139322" datatype="html">
|
||||
@@ -692,21 +762,21 @@
|
||||
<source>Matching algorithm</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="eab7fc7cf2d663e54de934b779fce4275a303f0f" datatype="html">
|
||||
<source>Matching pattern</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="632e529f49cf3d367dfbd15bd055e9b53aef30fb" datatype="html">
|
||||
<source>Case insensitive</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="9153094873118985366" datatype="html">
|
||||
@@ -913,13 +983,6 @@
|
||||
<context context-type="linenumber">46</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ddb40946e790522301687ecddb9ce1cb8ad40dd1" datatype="html">
|
||||
<source>Filter by:</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">4</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="02d184c288f567825a1fcbf83bcd3099a10853d5" datatype="html">
|
||||
<source>Filter tags</source>
|
||||
<context-group purpose="location">
|
||||
@@ -1019,8 +1082,8 @@
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1b29a8153575e5ad26cc7dd8bd75c4f45f6bfe7e" datatype="html">
|
||||
<source>Created: <x id="INTERPOLATION" equiv-text="{{document.created | date}}"/></source>
|
||||
<trans-unit id="849b42384616374df49bd8b3711ec159cb10b845" datatype="html">
|
||||
<source>Created: <x id="INTERPOLATION" equiv-text="{{document.created | customDate}}"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
@@ -1400,6 +1463,34 @@
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1206520795340730278" datatype="html">
|
||||
<source>English (US)</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1858110241312746425" datatype="html">
|
||||
<source>German</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3071065188816255493" datatype="html">
|
||||
<source>Dutch</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7633754075223722162" datatype="html">
|
||||
<source>French</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">77</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1519954996184640001" datatype="html">
|
||||
<source>Error</source>
|
||||
<context-group purpose="location">
|
||||
@@ -1558,56 +1649,56 @@
|
||||
<source>Create new item</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5324147361912094446" datatype="html">
|
||||
<source>Edit item</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1699589597032579396" datatype="html">
|
||||
<source>Could not save element: <x id="PH" equiv-text="error"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
<context context-type="linenumber">58</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="810888510148304696" datatype="html">
|
||||
<source>Automatic</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5044611416737085530" datatype="html">
|
||||
<source>Do you really want to delete this element?</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8371896857609524947" datatype="html">
|
||||
<source>Associated documents will not be deleted.</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
<context context-type="linenumber">104</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7022070615528435141" datatype="html">
|
||||
<source>Delete</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">85</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5467489005440577210" datatype="html">
|
||||
<source>Error while deleting element: <x id="PH" equiv-text="JSON.stringify(error.error)"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">93</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5851669019930456395" datatype="html">
|
||||
|
||||
@@ -13,7 +13,7 @@ import { DocumentTypeListComponent } from './components/manage/document-type-lis
|
||||
import { LogsComponent } from './components/manage/logs/logs.component';
|
||||
import { SettingsComponent } from './components/manage/settings/settings.component';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { DatePipe, registerLocaleData } from '@angular/common';
|
||||
import { NotFoundComponent } from './components/not-found/not-found.component';
|
||||
import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component';
|
||||
import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component';
|
||||
@@ -59,6 +59,15 @@ import { SelectDialogComponent } from './components/common/select-dialog/select-
|
||||
import { NgSelectModule } from '@ng-select/ng-select';
|
||||
import { NumberComponent } from './components/common/input/number/number.component';
|
||||
import { SafePipe } from './pipes/safe.pipe';
|
||||
import { CustomDatePipe } from './pipes/custom-date.pipe';
|
||||
|
||||
import localeFr from '@angular/common/locales/fr';
|
||||
import localeNl from '@angular/common/locales/nl';
|
||||
import localeDe from '@angular/common/locales/de';
|
||||
|
||||
registerLocaleData(localeFr)
|
||||
registerLocaleData(localeNl)
|
||||
registerLocaleData(localeDe)
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -108,7 +117,8 @@ import { SafePipe } from './pipes/safe.pipe';
|
||||
MetadataCollapseComponent,
|
||||
SelectDialogComponent,
|
||||
NumberComponent,
|
||||
SafePipe
|
||||
SafePipe,
|
||||
CustomDatePipe
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@@ -27,6 +27,8 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
|
||||
|
||||
networkActive = false
|
||||
|
||||
closeEnabled = false
|
||||
|
||||
error = null
|
||||
|
||||
abstract getForm(): FormGroup
|
||||
@@ -37,6 +39,11 @@ export abstract class EditDialogComponent<T extends ObjectWithId> implements OnI
|
||||
if (this.object != null) {
|
||||
this.objectForm.patchValue(this.object)
|
||||
}
|
||||
|
||||
// wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
|
||||
setTimeout(() => {
|
||||
this.closeEnabled = true
|
||||
});
|
||||
}
|
||||
|
||||
getCreateTitle() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<app-page-header title="Dashboard" [subTitle]="subtitle" i18n-title>
|
||||
<svg class="logo d-none d-md-block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2897.4 896.64" height="5rem" width="auto">
|
||||
<svg class="logo d-none d-md-block" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2897.4 896.64" height="5rem">
|
||||
<path class="leaf" d="M730.71,0C616.3,266.24,66.21,299.25,242.24,728.31c2.2,5.5-40.71,63.81-69.31,112.22-6.6-24.21-14.3-50.61-13.2-52.81C302.75,618.29,57.41,522.58,44.21,384-17.4,494-39.4,686.5,146.53,797.62c1.1,0,9.9,41.81,14.3,62.71-4.4,8.8-8.8,17.6-11,24.2-4.4,11,28.6,9.91,28.6,12.11,3.3-1.1,81.41-138.63,83.61-139.73C704.31,655.7,782.42,257.44,730.71,0ZM105.82,544.58C145.43,580.89,211.44,692,155.33,756.91,168.53,727.21,172.93,660.1,105.82,544.58ZM269.74,670C200.43,506.08,404,325.65,505.18,280.54,298.35,465.37,263.14,602.89,269.74,670Z" transform="translate(0)" style="fill:#17541f"/>
|
||||
<path class="text" style="fill:#000" d="M1169.27,231.44q.7,17.06.71,38.73t-.71,38a112,112,0,0,1-6.76,38A100.16,100.16,0,0,1,1142.25,380a93.87,93.87,0,0,1-33.76,23.46q-20.25,8.54-46.56,8.53h-36.25V597.5q0,9.94,2.85,14.57c1.89,3.08,3.79,4.86,5.68,5.33a10.54,10.54,0,0,0,9.24,0v18.48h-86V617.4q5.68,2.13,8.53,0,3.56-.71,7.1-5.33t3.56-14.57V166q0-10.66-3.56-15.28t-7.1-6q-2.85-1.42-8.53,0V127.66h104.48q26.31,0,46.56,8.53a93.75,93.75,0,0,1,33.76,23.46,101,101,0,0,1,20.26,33.4A111.05,111.05,0,0,1,1169.27,231.44ZM1118.8,215.8q0-18.48-6-32t-14.22-21.32a74.68,74.68,0,0,0-17.06-12.44q-8.88-4.62-15.28-4.62h-40.52V392.79h40.52q6.39,0,15.28-3.56t17.06-12.44a86,86,0,0,0,14.22-21.67q6-12.8,6-32Zm337.63,389.52a27.35,27.35,0,0,1-2.85,11.72,40.58,40.58,0,0,1-8.17,11.38,54.8,54.8,0,0,1-13.5,9.59,40.07,40.07,0,0,1-18.84,4.27,60,60,0,0,1-30.57-7.82q-13.5-7.82-18.48-20.61a56.12,56.12,0,0,1-6.39-16.35q-9.24,19.18-28.08,32a72.42,72.42,0,0,1-41.58,12.8q-28.44,0-49.76-20.61-12.07-10.67-19.9-27.73t-9.24-39.09q-.71-2.13-.71-8.53v-.71a124.43,124.43,0,0,1,4.26-25.23,113.22,113.22,0,0,1,11-26.3,149.46,149.46,0,0,1,17.77-24.88,128.6,128.6,0,0,1,22.39-20.26,17.39,17.39,0,0,0,4.62-3.2,28.53,28.53,0,0,1,5.33-3.91l2.13-1.06,2.14-1.07q12.8-7.81,22.39-14.22t17.41-12.08a97.25,97.25,0,0,0,8.53-6.4c2.84-2.37,5.93-4.73,9.24-7.1a79.43,79.43,0,0,0,12.09-10,36.51,36.51,0,0,0,8.53-13.5q4.26-7.11,4.26-17.06a67.74,67.74,0,0,0-2.13-17.77,49.09,49.09,0,0,0-14.57-27q-11.73-11.37-26-14.93a38.26,38.26,0,0,0-12.08-2.13q-19.9,0-34.12,7.11-9.24,4.26-13.51,9.59t-4.62,10a10.73,10.73,0,0,0,1.78,7.46,12.91,12.91,0,0,0,5.69,4.26,6,6,0,0,0,1.78.71,6.28,6.28,0,0,1,1.77.72,32.21,32.21,0,0,1,15.28,12.43,34,34,0,0,1,6,19.55,32.78,32.78,0,0,1-32.7,32.7,33.53,33.53,0,0,1-24.17-9.6,30.94,30.94,0,0,1-10-23.1,106.83,106.83,0,0,1,3.2-25.23,84.65,84.65,0,0,1,9.6-23.81q13.5-19.91,32.34-28.79a96.24,96.24,0,0,1,41.58-8.89,93.82,93.82,0,0,1,33.76,6.4,96.18,96.18,0,0,1,29.86,18.13,103.38,103.38,0,0,1,22,28.07q8.88,16.35,8.88,37.68v209q0,12.79.36,16.7t.36,5.34h0q0,9.24,5.68,14.92a18.45,18.45,0,0,0,13.51,5.69q9.24,0,14.92-5.33a20.62,20.62,0,0,0,6.4-13.86Zm-111.6-17.77a52.58,52.58,0,0,0,6.76-21.33q1.07-11.37,1.06-21.32V413.4a63.92,63.92,0,0,1-6,5.33,74,74,0,0,0-6.75,6q-20.62,14.93-35.19,27.37a164.06,164.06,0,0,0-24.52,25.59,113,113,0,0,0-15.64,28.07,121,121,0,0,0-7.11,34.12v10.67q.72,27,14.58,42.29t39.44,15.28a38.56,38.56,0,0,0,20.62-5.69q9.24-6.39,12.79-14.92Zm244.52-304.23q40.51.72,64.68,24.53t24.17,75.7V542.77q0,51.88-24.17,75.34T1589.35,643h-2.13a80.27,80.27,0,0,1-22-2.84h-.71a.63.63,0,0,1-.71-.71,91.74,91.74,0,0,1-28.43-15.64l-6.76-6.76-5.33-7.46v118q0,10,2.85,14.58c1.89,3.08,3.79,5.09,5.68,6a10.54,10.54,0,0,0,9.24,0V766h-86V748.19q5.68,2.13,8.53,0,3.56-1.43,6.75-6t3.2-14.58V326.68q0-9.94-3.2-14.57t-6.75-6q-2.85-1.43-8.53,0V289h68.23v27.72a68.68,68.68,0,0,1,12.09-14.21,75.62,75.62,0,0,1,28.43-15.64.63.63,0,0,1,.71-.71h.71a90.55,90.55,0,0,1,22-2.85Zm35.54,84.59v-.71q-.72-36.26-8.89-51.18t-26.65-16.35a63.12,63.12,0,0,0-24.17,3.2,48,48,0,0,0-19.9,13.15q-8.54,9.24-14.22,24.88t-7.82,37V548.45q2.13,22,7.82,37.32t14.22,24.17a53.23,53.23,0,0,0,19.9,13.15,58.92,58.92,0,0,0,24.17,3.55q18.48-1.42,26.65-16.35t8.89-51.18V367.91Zm179.83-84.59a105.36,105.36,0,0,1,33.41,5.69,86.38,86.38,0,0,1,30.56,17.77,88.06,88.06,0,0,1,21.68,30.57q8.18,18.48,8.18,44.06v90.28H1757.1V544.9q0,44.07,15.28,62.9t38,18.13a113.94,113.94,0,0,0,26.65-4.62,54.83,54.83,0,0,0,22.75-13.15q9.59-9.24,16-26.3t6.4-44.78h16.35V547a153.56,153.56,0,0,1-4.27,36.61A72,72,0,0,1,1852,634.1q-16.71,7.47-40.87,8.18a102.25,102.25,0,0,1-34.83-6.4,96,96,0,0,1-32-19.19,108.91,108.91,0,0,1-23.45-30.21q-9.24-17.41-9.25-39.45v-16.7s-.11-9.12-.35-27.37-.36-29.5-.36-33.76-.11-16-.35-35.19-.36-29.49-.36-30.92v-21q0-25.59,8.89-44.43t22-30.56A90.72,90.72,0,0,1,1771,289.37a98.55,98.55,0,0,1,33.76-6.05Zm46.2,169.89V397.05q0-27-3.55-45.13t-9.6-29.15q-6-11-14.57-15.28a42.36,42.36,0,0,0-19.19-4.26q-22.76,0-34.48,19.9t-11.72,73.92v56.16Zm279.35-113.73a42.31,42.31,0,0,0,.71,5.68,17.92,17.92,0,0,1,0,7.11q-.72,14.93-10.31,24.52a33,33,0,0,1-46.91-.35,33.52,33.52,0,0,1-9.6-24.17q0-17.76,13.51-27a8.33,8.33,0,0,0,1.77-1.06l1.07-1.07a16.22,16.22,0,0,0,6.4-10.31q1.41-6.75-8.53-11.73a26.26,26.26,0,0,0-12.8-3.55q-21.32-1.43-37.67,12.8t-22,49.75V597.5q0,9.94,2.84,14.57t5.69,5.33a10.54,10.54,0,0,0,9.24,0v18.48h-86V617.4q5.68,2.13,8.53,0,3.55-.71,7.11-5.33t3.55-14.57V326.68q0-9.94-3.55-14.57t-7.11-6q-2.85-1.43-8.53,0V288.3h68.24v29.14q5-7.81,9.95-14.57a48.44,48.44,0,0,1,11.72-11.37,59.79,59.79,0,0,1,15.64-7.46q8.89-2.85,22.39-2.85,20.61,0,38.39,14.93t26.3,43.36Zm95.95,258q0,9.94,3.2,14.57c2.13,3.08,4.15,4.86,6,5.33q3.55,2.13,9.24,0v18.48h-86V617.4q5.68,2.13,10,0,2.13-.71,5.69-5.33t3.55-14.57V166.75q0-10.65-3.55-15.28a28.82,28.82,0,0,0-5.69-6q-4.26-2.13-10,0V128.37h67.52V597.5Zm154.25-314.18a105.25,105.25,0,0,1,33.4,5.69,86.33,86.33,0,0,1,30.57,17.77,88.19,88.19,0,0,1,21.68,30.57q8.18,18.48,8.17,44.06v90.28H2332.84V544.9q0,44.07,15.29,62.9t38,18.13a113.93,113.93,0,0,0,Line truncated
|
||||
</svg>
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let doc of documents" routerLink="/documents/{{doc.id}}">
|
||||
<td>{{doc.created | date}}</td>
|
||||
<td>{{doc.created | customDate}}</td>
|
||||
<td>{{doc.title | documentTitle}}<app-tag [tag]="t" *ngFor="let t of doc.tags$ | async" class="ml-1"></app-tag>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ export class SavedViewWidgetComponent implements OnInit {
|
||||
private documentService: DocumentService,
|
||||
private router: Router,
|
||||
private list: DocumentListViewService) { }
|
||||
|
||||
|
||||
@Input()
|
||||
savedView: PaperlessSavedView
|
||||
|
||||
@@ -34,7 +34,7 @@ export class SavedViewWidgetComponent implements OnInit {
|
||||
} else {
|
||||
this.list.load(this.savedView)
|
||||
this.router.navigate(["documents"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -85,11 +85,11 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td i18n>Date modified</td>
|
||||
<td>{{document.modified | date:'medium'}}</td>
|
||||
<td>{{document.modified | customDate}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n>Date added</td>
|
||||
<td>{{document.added | date:'medium'}}</td>
|
||||
<td>{{document.added | customDate}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td i18n>Media filename</td>
|
||||
@@ -141,6 +141,10 @@
|
||||
<ng-template #nativePdfViewer>
|
||||
<object [data]="previewUrl | safe" type="application/pdf" class="preview-sticky" width="100%"></object>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="getContentType() == 'text/plain'">
|
||||
<object [data]="previewUrl | safe" type="text/plain" class="preview-sticky" width="100%"></object>
|
||||
</ng-container>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
+6
-6
@@ -1,11 +1,11 @@
|
||||
<div class="card mb-3 shadow-sm" [class.card-selected]="selected" [class.document-card]="selectable">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-md-2 d-none d-lg-block doc-img-background rounded-left" [class.doc-img-background-selected]="selected">
|
||||
<img [src]="getThumbUrl()" class="card-img doc-img border-right rounded-left" (click)="setSelected(selectable ? !selected : false)">
|
||||
<div class="col-md-2 d-none d-lg-block doc-img-background rounded-left" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
|
||||
<img [src]="getThumbUrl()" class="card-img doc-img border-right rounded-left">
|
||||
|
||||
<div style="top: 0; left: 0" class="position-absolute border-right border-bottom bg-light p-1" [class.document-card-check]="!selected">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="smallCardCheck{{document.id}}" [checked]="selected" (change)="setSelected($event.target.checked)">
|
||||
<input type="checkbox" class="custom-control-input" id="smallCardCheck{{document.id}}" [checked]="selected" (click)="this.toggleSelected.emit($event)">
|
||||
<label class="custom-control-label" for="smallCardCheck{{document.id}}"></label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -17,11 +17,11 @@
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title">
|
||||
<ng-container *ngIf="document.correspondent">
|
||||
<a *ngIf="clickCorrespondent.observers.length ; else nolink" [routerLink]="" title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent)" class="font-weight-bold">{{(document.correspondent$ | async)?.name}}</a>
|
||||
<a *ngIf="clickCorrespondent.observers.length ; else nolink" [routerLink]="" title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="font-weight-bold">{{(document.correspondent$ | async)?.name}}</a>
|
||||
<ng-template #nolink>{{(document.correspondent$ | async)?.name}}</ng-template>:
|
||||
</ng-container>
|
||||
{{document.title | documentTitle}}
|
||||
<app-tag [tag]="t" linkTitle="Filter by tag" i18n-linkTitle *ngFor="let t of document.tags$ | async" class="ml-1" (click)="clickTag.emit(t.id)" [clickable]="clickTag.observers.length"></app-tag>
|
||||
<app-tag [tag]="t" linkTitle="Filter by tag" i18n-linkTitle *ngFor="let t of document.tags$ | async" class="ml-1" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="clickTag.observers.length"></app-tag>
|
||||
</h5>
|
||||
<h5 class="card-title" *ngIf="document.archive_serial_number">#{{document.archive_serial_number}}</h5>
|
||||
</div>
|
||||
@@ -64,7 +64,7 @@
|
||||
<ngb-progressbar [type]="searchScoreClass" [value]="searchScore" class="search-score-bar mx-2" [max]="1"></ngb-progressbar>
|
||||
</div>
|
||||
|
||||
<small class="text-muted" [class.ml-auto]="!searchScore" i18n>Created: {{document.created | date}}</small>
|
||||
<small class="text-muted" [class.ml-auto]="!searchScore" i18n>Created: {{document.created | customDate}}</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
+2
-7
@@ -15,16 +15,11 @@ export class DocumentCardLargeComponent implements OnInit {
|
||||
@Input()
|
||||
selected = false
|
||||
|
||||
setSelected(value: boolean) {
|
||||
this.selected = value
|
||||
this.selectedChange.emit(value)
|
||||
}
|
||||
|
||||
@Output()
|
||||
selectedChange = new EventEmitter<boolean>()
|
||||
toggleSelected = new EventEmitter()
|
||||
|
||||
get selectable() {
|
||||
return this.selectedChange.observers.length > 0
|
||||
return this.toggleSelected.observers.length > 0
|
||||
}
|
||||
|
||||
@Input()
|
||||
|
||||
+7
-7
@@ -1,18 +1,18 @@
|
||||
<div class="col p-2 h-100">
|
||||
<div class="card h-100 shadow-sm document-card" [class.card-selected]="selected">
|
||||
<div class="border-bottom doc-img-container" [class.doc-img-background-selected]="selected">
|
||||
<img class="card-img doc-img rounded-top" [src]="getThumbUrl()" (click)="setSelected(!selected)">
|
||||
<div class="border-bottom doc-img-container" [class.doc-img-background-selected]="selected" (click)="this.toggleSelected.emit($event)">
|
||||
<img class="card-img doc-img rounded-top" [src]="getThumbUrl()">
|
||||
|
||||
<div class="border-right border-bottom bg-light p-1 rounded document-card-check">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="smallCardCheck{{document.id}}" [checked]="selected" (change)="setSelected($event.target.checked)">
|
||||
<input type="checkbox" class="custom-control-input" id="smallCardCheck{{document.id}}" [checked]="selected" (click)="this.toggleSelected.emit($event)">
|
||||
<label class="custom-control-label" for="smallCardCheck{{document.id}}"></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="top: 0; right: 0; font-size: large" class="text-right position-absolute mr-1">
|
||||
<div *ngFor="let t of getTagsLimited$() | async">
|
||||
<app-tag [tag]="t" (click)="clickTag.emit(t.id)" [clickable]="true" linkTitle="Filter by tag" i18n-linkTitle></app-tag>
|
||||
<app-tag [tag]="t" (click)="clickTag.emit(t.id);$event.stopPropagation()" [clickable]="true" linkTitle="Filter by tag" i18n-linkTitle></app-tag>
|
||||
</div>
|
||||
<div *ngIf="moreTags">
|
||||
<span class="badge badge-secondary">+ {{moreTags}}</span>
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="card-body p-2">
|
||||
<p class="card-text">
|
||||
<ng-container *ngIf="document.correspondent">
|
||||
<a [routerLink]="" title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent)" class="font-weight-bold">{{(document.correspondent$ | async)?.name}}</a>:
|
||||
<a [routerLink]="" title="Filter by correspondent" i18n-title (click)="clickCorrespondent.emit(document.correspondent);$event.stopPropagation()" class="font-weight-bold">{{(document.correspondent$ | async)?.name}}</a>:
|
||||
</ng-container>
|
||||
{{document.title | documentTitle}} <span *ngIf="document.archive_serial_number">(#{{document.archive_serial_number}})</span>
|
||||
</p>
|
||||
@@ -43,14 +43,14 @@
|
||||
<path fill-rule="evenodd" d="M6.5 12a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM13 6.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" i18n-title>
|
||||
<a [href]="getDownloadUrl()" class="btn btn-sm btn-outline-secondary" title="Download" (click)="$event.stopPropagation()" i18n-title>
|
||||
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-download" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
||||
<path fill-rule="evenodd" d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<small class="text-muted pl-1">{{document.created | date}}</small>
|
||||
<small class="text-muted pl-1">{{document.created | customDate:'shortDate'}}</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
+2
-7
@@ -14,14 +14,9 @@ export class DocumentCardSmallComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
selected = false
|
||||
|
||||
setSelected(value: boolean) {
|
||||
this.selected = value
|
||||
this.selectedChange.emit(value)
|
||||
}
|
||||
|
||||
|
||||
@Output()
|
||||
selectedChange = new EventEmitter<boolean>()
|
||||
toggleSelected = new EventEmitter()
|
||||
|
||||
@Input()
|
||||
document: PaperlessDocument
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
</div>
|
||||
|
||||
<div *ngIf="displayMode == 'largeCards'">
|
||||
<app-document-card-large [selected]="list.isSelected(d)" (selectedChange)="list.setSelected(d, $event)" *ngFor="let d of list.documents; trackBy: trackByDocumentId" [document]="d" [details]="d.content" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)">
|
||||
<app-document-card-large [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" *ngFor="let d of list.documents; trackBy: trackByDocumentId" [document]="d" [details]="d.content" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)">
|
||||
</app-document-card-large>
|
||||
</div>
|
||||
|
||||
@@ -135,10 +135,10 @@
|
||||
i18n>Added</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let d of list.documents; trackBy: trackByDocumentId" [ngClass]="list.isSelected(d) ? 'table-row-selected' : ''">
|
||||
<tr *ngFor="let d of list.documents; trackBy: trackByDocumentId" (click)="toggleSelected(d, $event)" [ngClass]="list.isSelected(d) ? 'table-row-selected' : ''">
|
||||
<td>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="docCheck{{d.id}}" [checked]="list.isSelected(d)" (change)="list.setSelected(d, $event.target.checked)">
|
||||
<input type="checkbox" class="custom-control-input" id="docCheck{{d.id}}" [checked]="list.isSelected(d)" (click)="toggleSelected(d, $event)">
|
||||
<label class="custom-control-label" for="docCheck{{d.id}}"></label>
|
||||
</div>
|
||||
</td>
|
||||
@@ -147,28 +147,28 @@
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<ng-container *ngIf="d.correspondent">
|
||||
<a [routerLink]="" (click)="clickCorrespondent(d.correspondent)" title="Filter by correspondent">{{(d.correspondent$ | async)?.name}}</a>
|
||||
<a [routerLink]="" (click)="clickCorrespondent(d.correspondent);$event.stopPropagation()" title="Filter by correspondent">{{(d.correspondent$ | async)?.name}}</a>
|
||||
</ng-container>
|
||||
</td>
|
||||
<td>
|
||||
<a routerLink="/documents/{{d.id}}" title="Edit document" style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a>
|
||||
<app-tag [tag]="t" *ngFor="let t of d.tags$ | async" class="ml-1" clickable="true" linkTitle="Filter by tag" (click)="clickTag(t.id)"></app-tag>
|
||||
<app-tag [tag]="t" *ngFor="let t of d.tags$ | async" class="ml-1" clickable="true" linkTitle="Filter by tag" (click)="clickTag(t.id);$event.stopPropagation()"></app-tag>
|
||||
</td>
|
||||
<td class="d-none d-xl-table-cell">
|
||||
<ng-container *ngIf="d.document_type">
|
||||
<a [routerLink]="" (click)="clickDocumentType(d.document_type)" title="Filter by document type">{{(d.document_type$ | async)?.name}}</a>
|
||||
<a [routerLink]="" (click)="clickDocumentType(d.document_type);$event.stopPropagation()" title="Filter by document type">{{(d.document_type$ | async)?.name}}</a>
|
||||
</ng-container>
|
||||
</td>
|
||||
<td>
|
||||
{{d.created | date}}
|
||||
{{d.created | customDate}}
|
||||
</td>
|
||||
<td class="d-none d-xl-table-cell">
|
||||
{{d.added | date}}
|
||||
{{d.added | customDate}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="m-n2 row row-cols-paperless-cards" *ngIf="displayMode == 'smallCards'">
|
||||
<app-document-card-small [selected]="list.isSelected(d)" (selectedChange)="list.setSelected(d, $event)" [document]="d" *ngFor="let d of list.documents; trackBy: trackByDocumentId" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)"></app-document-card-small>
|
||||
<app-document-card-small [selected]="list.isSelected(d)" (toggleSelected)="toggleSelected(d, $event)" [document]="d" *ngFor="let d of list.documents; trackBy: trackByDocumentId" (clickTag)="clickTag($event)" (clickCorrespondent)="clickCorrespondent($event)"></app-document-card-small>
|
||||
</div>
|
||||
@@ -1,5 +1,9 @@
|
||||
@import "/src/theme";
|
||||
|
||||
tr {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.table-row-selected {
|
||||
background-color: $primaryFaded;
|
||||
}
|
||||
|
||||
@@ -160,6 +160,11 @@ export class DocumentListComponent implements OnInit {
|
||||
this.filterRulesModified = modified
|
||||
}
|
||||
|
||||
toggleSelected(document: PaperlessDocument, event: MouseEvent): void {
|
||||
if (!event.shiftKey) this.list.toggleSelected(document)
|
||||
else this.list.selectRangeTo(document)
|
||||
}
|
||||
|
||||
clickTag(tagID: number) {
|
||||
this.list.selectNone()
|
||||
setTimeout(() => {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<form [formGroup]="saveViewConfigForm" (ngSubmit)="save()">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title" i18n>Save current view</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="cancel()">
|
||||
<button type="button" [disabled]="!closeEnabled" class="close" aria-label="Close" (click)="cancel()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
+7
-1
@@ -20,6 +20,8 @@ export class SaveViewConfigDialogComponent implements OnInit {
|
||||
@Input()
|
||||
buttonsEnabled = true
|
||||
|
||||
closeEnabled = false
|
||||
|
||||
_defaultName = ""
|
||||
|
||||
get defaultName() {
|
||||
@@ -31,7 +33,7 @@ export class SaveViewConfigDialogComponent implements OnInit {
|
||||
this._defaultName = value
|
||||
this.saveViewConfigForm.patchValue({name: value})
|
||||
}
|
||||
|
||||
|
||||
saveViewConfigForm = new FormGroup({
|
||||
name: new FormControl(''),
|
||||
showInSideBar: new FormControl(false),
|
||||
@@ -39,6 +41,10 @@ export class SaveViewConfigDialogComponent implements OnInit {
|
||||
})
|
||||
|
||||
ngOnInit(): void {
|
||||
// wait to enable close button so it doesnt steal focus from input since its the first clickable element in the DOM
|
||||
setTimeout(() => {
|
||||
this.closeEnabled = true
|
||||
});
|
||||
}
|
||||
|
||||
save() {
|
||||
|
||||
+1
-2
@@ -1,12 +1,11 @@
|
||||
<form [formGroup]="objectForm" (ngSubmit)="save()">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="cancel()">
|
||||
<button type="button" [disabled]="!closeEnabled" class="close" aria-label="Close" (click)="cancel()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<app-input-text i18n-title title="Name" formControlName="name" [error]="error?.name"></app-input-text>
|
||||
<app-input-select i18n-title title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select>
|
||||
<app-input-text *ngIf="patternRequired" i18n-title title="Matching pattern" formControlName="match"></app-input-text>
|
||||
|
||||
+10
-3
@@ -2,8 +2,15 @@
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" (click)="openCreateDialog()" i18n>Create</button>
|
||||
</app-page-header>
|
||||
|
||||
<div class="row m-0 justify-content-end">
|
||||
<ngb-pagination [pageSize]="25" [collectionSize]="collectionSize" [(page)]="page" (pageChange)="reloadData()" aria-label="Default pagination"></ngb-pagination>
|
||||
<div class="row">
|
||||
<div class="col-md mb-2 mb-xl-0">
|
||||
<div class="form-inline d-flex align-items-center">
|
||||
<label class="text-muted mr-2 mb-0" i18n>Filter by:</label>
|
||||
<input class="form-control form-control-sm flex-fill w-auto" type="text" [(ngModel)]="nameFilter" placeholder="Name" i18n-placeholder>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ngb-pagination class="col-auto" [pageSize]="25" [collectionSize]="collectionSize" [(page)]="page" (pageChange)="reloadData()" aria-label="Default pagination"></ngb-pagination>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped border shadow-sm">
|
||||
@@ -21,7 +28,7 @@
|
||||
<td scope="row">{{ correspondent.name }}</td>
|
||||
<td scope="row">{{ getMatching(correspondent) }}</td>
|
||||
<td scope="row">{{ correspondent.document_count }}</td>
|
||||
<td scope="row">{{ correspondent.last_correspondence | date }}</td>
|
||||
<td scope="row">{{ correspondent.last_correspondence | customDate }}</td>
|
||||
<td scope="row">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-outline-secondary" (click)="filterDocuments(correspondent)">
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<form [formGroup]="objectForm" (ngSubmit)="save()">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="cancel()">
|
||||
<button type="button" [disabled]="!closeEnabled" class="close" aria-label="Close" (click)="cancel()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
+9
-3
@@ -2,9 +2,15 @@
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" (click)="openCreateDialog()" i18n>Create</button>
|
||||
</app-page-header>
|
||||
|
||||
<div class="row m-0 justify-content-end">
|
||||
<ngb-pagination [pageSize]="25" [collectionSize]="collectionSize" [(page)]="page" (pageChange)="reloadData()"
|
||||
aria-label="Default pagination"></ngb-pagination>
|
||||
<div class="row">
|
||||
<div class="col-md mb-2 mb-xl-0">
|
||||
<div class="form-inline d-flex align-items-center">
|
||||
<label class="text-muted mr-2 mb-0" i18n>Filter by:</label>
|
||||
<input class="form-control form-control-sm flex-fill w-auto" type="text" [(ngModel)]="nameFilter" placeholder="Name" i18n-placeholder>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ngb-pagination class="col-auto" [pageSize]="25" [collectionSize]="collectionSize" [(page)]="page" (pageChange)="reloadData()" aria-label="Default pagination"></ngb-pagination>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped border shadow-sm">
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { Directive, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { Directive, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||
import { MatchingModel, MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model';
|
||||
import { ObjectWithId } from 'src/app/data/object-with-id';
|
||||
import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive';
|
||||
import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service';
|
||||
import { AbstractNameFilterService } from 'src/app/services/rest/abstract-name-filter-service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component';
|
||||
|
||||
@Directive()
|
||||
export abstract class GenericListComponent<T extends ObjectWithId> implements OnInit {
|
||||
export abstract class GenericListComponent<T extends ObjectWithId> implements OnInit, OnDestroy {
|
||||
|
||||
constructor(
|
||||
private service: AbstractPaperlessService<T>,
|
||||
private service: AbstractNameFilterService<T>,
|
||||
private modalService: NgbModal,
|
||||
private editDialogComponent: any,
|
||||
private toastService: ToastService) {
|
||||
@@ -28,6 +30,10 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
|
||||
public sortField: string
|
||||
public sortReverse: boolean
|
||||
|
||||
private nameFilterDebounce: Subject<string>
|
||||
private subscription: Subscription
|
||||
private _nameFilter: string
|
||||
|
||||
getMatching(o: MatchingModel) {
|
||||
if (o.matching_algorithm == MATCH_AUTO) {
|
||||
return $localize`Automatic`
|
||||
@@ -44,12 +50,27 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
|
||||
this.reloadData()
|
||||
}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
this.reloadData()
|
||||
|
||||
this.nameFilterDebounce = new Subject<string>()
|
||||
|
||||
this.subscription = this.nameFilterDebounce.pipe(
|
||||
debounceTime(400),
|
||||
distinctUntilChanged()
|
||||
).subscribe(title => {
|
||||
this._nameFilter = title
|
||||
this.reloadData()
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe()
|
||||
}
|
||||
|
||||
reloadData() {
|
||||
this.service.list(this.page, null, this.sortField, this.sortReverse).subscribe(c => {
|
||||
this.service.listFiltered(this.page, null, this.sortField, this.sortReverse, this._nameFilter).subscribe(c => {
|
||||
this.data = c.results
|
||||
this.collectionSize = c.count
|
||||
});
|
||||
@@ -95,4 +116,12 @@ export abstract class GenericListComponent<T extends ObjectWithId> implements On
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
get nameFilter() {
|
||||
return this._nameFilter
|
||||
}
|
||||
|
||||
set nameFilter(nameFilter: string) {
|
||||
this.nameFilterDebounce.next(nameFilter)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
<p
|
||||
class="m-0 p-0 log-entry-{{log.level}}"
|
||||
*ngFor="let log of logs">
|
||||
{{log.created | date:'short'}}
|
||||
{{log.created | customDate:'short'}}
|
||||
{{getLevelText(log.level)}}
|
||||
{{log.message}}
|
||||
</p>
|
||||
|
||||
@@ -12,6 +12,56 @@
|
||||
|
||||
<h4 i18n>Appearance</h4>
|
||||
|
||||
<div class="form-row form-group">
|
||||
<div class="col-md-3 col-form-label">
|
||||
<span i18n>Display language</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
||||
<select class="form-control" formControlName="displayLanguage">
|
||||
<option *ngFor="let lang of displayLanguageOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code && currentLocale != 'en-US'"> - {{lang.englishName}}</span></option>
|
||||
</select>
|
||||
|
||||
<small class="form-text text-muted" i18n>You need to reload the page after applying a new language.</small>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row form-group">
|
||||
<div class="col-md-3 col-form-label">
|
||||
<span i18n>Date display</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
||||
<select class="form-control" formControlName="dateLocale">
|
||||
<option *ngFor="let lang of dateLocaleOptions" [ngValue]="lang.code">{{lang.name}}<span *ngIf="lang.code"> - {{today | date:'shortDate':null:lang.code}}</span></option>
|
||||
</select>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row form-group">
|
||||
<div class="col-md-3 col-form-label">
|
||||
<span i18n>Date format</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
||||
<div class="custom-control custom-radio">
|
||||
<input type="radio" id="dateFormatShort" name="dateFormat" class="custom-control-input" formControlName="dateFormat" value="shortDate">
|
||||
<label class="custom-control-label" for="dateFormatShort" i18n>Short: {{today | customDate:'shortDate':null:computedDateLocale}}</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio">
|
||||
<input type="radio" id="dateFormatMedium" name="dateFormat" class="custom-control-input" formControlName="dateFormat" value="mediumDate">
|
||||
<label class="custom-control-label" for="dateFormatMedium" i18n>Medium: {{today | customDate:'mediumDate':null:computedDateLocale}}</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio">
|
||||
<input type="radio" id="dateFormatLong" name="dateFormat" class="custom-control-input" formControlName="dateFormat" value="longDate">
|
||||
<label class="custom-control-label" for="dateFormatLong" i18n>Long: {{today | customDate:'longDate':null:computedDateLocale}}</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row form-group">
|
||||
<div class="col-md-3 col-form-label">
|
||||
<span i18n>Items per page</span>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Component, OnInit, Renderer2 } from '@angular/core';
|
||||
import { Component, Inject, LOCALE_ID, OnInit, Renderer2 } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
|
||||
import { DocumentListViewService } from 'src/app/services/document-list-view.service';
|
||||
import { SavedViewService } from 'src/app/services/rest/saved-view.service';
|
||||
import { SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
|
||||
import { LanguageOption, SettingsService, SETTINGS_KEYS } from 'src/app/services/settings.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
|
||||
@Component({
|
||||
@@ -22,16 +22,24 @@ export class SettingsComponent implements OnInit {
|
||||
'darkModeUseSystem': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM)),
|
||||
'darkModeEnabled': new FormControl(this.settings.get(SETTINGS_KEYS.DARK_MODE_ENABLED)),
|
||||
'useNativePdfViewer': new FormControl(this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER)),
|
||||
'savedViews': this.savedViewGroup
|
||||
'savedViews': this.savedViewGroup,
|
||||
'displayLanguage': new FormControl(this.settings.getLanguage()),
|
||||
'dateLocale': new FormControl(this.settings.get(SETTINGS_KEYS.DATE_LOCALE)),
|
||||
'dateFormat': new FormControl(this.settings.get(SETTINGS_KEYS.DATE_FORMAT)),
|
||||
})
|
||||
|
||||
savedViews: PaperlessSavedView[]
|
||||
|
||||
get computedDateLocale(): string {
|
||||
return this.settingsForm.value.dateLocale || this.settingsForm.value.displayLanguage
|
||||
}
|
||||
|
||||
constructor(
|
||||
public savedViewService: SavedViewService,
|
||||
private documentListViewService: DocumentListViewService,
|
||||
private toastService: ToastService,
|
||||
private settings: SettingsService
|
||||
private settings: SettingsService,
|
||||
@Inject(LOCALE_ID) public currentLocale: string
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
@@ -63,11 +71,26 @@ export class SettingsComponent implements OnInit {
|
||||
this.settings.set(SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, this.settingsForm.value.darkModeUseSystem)
|
||||
this.settings.set(SETTINGS_KEYS.DARK_MODE_ENABLED, (this.settingsForm.value.darkModeEnabled == true).toString())
|
||||
this.settings.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, this.settingsForm.value.useNativePdfViewer)
|
||||
this.settings.set(SETTINGS_KEYS.DATE_LOCALE, this.settingsForm.value.dateLocale)
|
||||
this.settings.set(SETTINGS_KEYS.DATE_FORMAT, this.settingsForm.value.dateFormat)
|
||||
this.settings.setLanguage(this.settingsForm.value.displayLanguage)
|
||||
this.documentListViewService.updatePageSize()
|
||||
this.settings.updateDarkModeSettings()
|
||||
this.toastService.showInfo($localize`Settings saved successfully.`)
|
||||
}
|
||||
|
||||
get displayLanguageOptions(): LanguageOption[] {
|
||||
return [{code: "", name: $localize`Use system language`}].concat(this.settings.getLanguageOptions())
|
||||
}
|
||||
|
||||
get dateLocaleOptions(): LanguageOption[] {
|
||||
return [{code: "", name: $localize`Use date format of display language`}].concat(this.settings.getLanguageOptions())
|
||||
}
|
||||
|
||||
get today() {
|
||||
return new Date()
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
let x = []
|
||||
for (let id in this.savedViewGroup.value) {
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<form [formGroup]="objectForm" (ngSubmit)="save()">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{getTitle()}}</h4>
|
||||
<button type="button" class="close" aria-label="Close" (click)="cancel()">
|
||||
<button type="button" [disabled]="!closeEnabled" class="close" aria-label="Close" (click)="cancel()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -2,9 +2,15 @@
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" (click)="openCreateDialog()" i18n>Create</button>
|
||||
</app-page-header>
|
||||
|
||||
<div class="row m-0 justify-content-end">
|
||||
<ngb-pagination [pageSize]="25" [collectionSize]="collectionSize" [(page)]="page" (pageChange)="reloadData()"
|
||||
aria-label="Default pagination"></ngb-pagination>
|
||||
<div class="row">
|
||||
<div class="col-md mb-2 mb-xl-0">
|
||||
<div class="form-inline d-flex align-items-center">
|
||||
<label class="text-muted mr-2 mb-0" i18n>Filter by:</label>
|
||||
<input class="form-control form-control-sm flex-fill w-auto" type="text" [(ngModel)]="nameFilter" placeholder="Name" i18n-placeholder>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ngb-pagination class="col-auto" [pageSize]="25" [collectionSize]="collectionSize" [(page)]="page" (pageChange)="reloadData()" aria-label="Default pagination"></ngb-pagination>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped border shadow-sm">
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { CustomDatePipe } from './custom-date.pipe';
|
||||
|
||||
describe('CustomDatePipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new CustomDatePipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core';
|
||||
import { SettingsService, SETTINGS_KEYS } from '../services/settings.service';
|
||||
|
||||
@Pipe({
|
||||
name: 'customDate'
|
||||
})
|
||||
export class CustomDatePipe extends DatePipe implements PipeTransform {
|
||||
|
||||
constructor(@Inject(LOCALE_ID) locale: string, private settings: SettingsService) {
|
||||
super(settings.get(SETTINGS_KEYS.DATE_LOCALE) || locale)
|
||||
|
||||
}
|
||||
|
||||
transform(value: any, format?: string, timezone?: string, locale?: string): string | null {
|
||||
return super.transform(value, format || this.settings.get(SETTINGS_KEYS.DATE_FORMAT), timezone, locale)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,6 +27,8 @@ export class DocumentListViewService {
|
||||
currentPage = 1
|
||||
currentPageSize: number = this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE)
|
||||
collectionSize: number
|
||||
rangeSelectionAnchorIndex: number
|
||||
lastRangeSelectionToIndex: number
|
||||
|
||||
/**
|
||||
* This is the current config for the document list. The service will always remember the last settings used for the document list.
|
||||
@@ -108,6 +110,7 @@ export class DocumentListViewService {
|
||||
if (onFinish) {
|
||||
onFinish()
|
||||
}
|
||||
this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null
|
||||
this.isReloading = false
|
||||
},
|
||||
error => {
|
||||
@@ -218,6 +221,7 @@ export class DocumentListViewService {
|
||||
|
||||
selectNone() {
|
||||
this.selected.clear()
|
||||
this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null
|
||||
}
|
||||
|
||||
reduceSelectionToFilter() {
|
||||
@@ -249,14 +253,39 @@ export class DocumentListViewService {
|
||||
return this.selected.has(d.id)
|
||||
}
|
||||
|
||||
setSelected(d: PaperlessDocument, value: boolean) {
|
||||
if (value) {
|
||||
this.selected.add(d.id)
|
||||
} else if (!value) {
|
||||
this.selected.delete(d.id)
|
||||
toggleSelected(d: PaperlessDocument): void {
|
||||
if (this.selected.has(d.id)) this.selected.delete(d.id)
|
||||
else this.selected.add(d.id)
|
||||
this.rangeSelectionAnchorIndex = this.documentIndexInCurrentView(d.id)
|
||||
this.lastRangeSelectionToIndex = null
|
||||
}
|
||||
|
||||
selectRangeTo(d: PaperlessDocument) {
|
||||
if (this.rangeSelectionAnchorIndex !== null) {
|
||||
const documentToIndex = this.documentIndexInCurrentView(d.id)
|
||||
const fromIndex = Math.min(this.rangeSelectionAnchorIndex, documentToIndex)
|
||||
const toIndex = Math.max(this.rangeSelectionAnchorIndex, documentToIndex)
|
||||
|
||||
if (this.lastRangeSelectionToIndex !== null) {
|
||||
// revert the old selection
|
||||
this.documents.slice(Math.min(this.rangeSelectionAnchorIndex, this.lastRangeSelectionToIndex), Math.max(this.rangeSelectionAnchorIndex, this.lastRangeSelectionToIndex) + 1).forEach(d => {
|
||||
this.selected.delete(d.id)
|
||||
})
|
||||
}
|
||||
|
||||
this.documents.slice(fromIndex, toIndex + 1).forEach(d => {
|
||||
this.selected.add(d.id)
|
||||
})
|
||||
this.lastRangeSelectionToIndex = documentToIndex
|
||||
} else { // e.g. shift key but was first click
|
||||
this.toggleSelected(d)
|
||||
}
|
||||
}
|
||||
|
||||
documentIndexInCurrentView(documentID: number): number {
|
||||
return this.documents.map(d => d.id).indexOf(documentID)
|
||||
}
|
||||
|
||||
constructor(private documentService: DocumentService, private settings: SettingsService, private router: Router) {
|
||||
let documentListViewConfigJson = sessionStorage.getItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG)
|
||||
if (documentListViewConfigJson) {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ObjectWithId } from 'src/app/data/object-with-id'
|
||||
import { AbstractPaperlessService } from './abstract-paperless-service'
|
||||
|
||||
export abstract class AbstractNameFilterService<T extends ObjectWithId> extends AbstractPaperlessService<T> {
|
||||
|
||||
listFiltered(page?: number, pageSize?: number, sortField?: string, sortReverse?: boolean, nameFilter?: string) {
|
||||
let params = {}
|
||||
if (nameFilter) {
|
||||
params = {'name__icontains': nameFilter}
|
||||
}
|
||||
return this.list(page, pageSize, sortField, sortReverse, params)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
|
||||
import { AbstractPaperlessService } from './abstract-paperless-service';
|
||||
import { AbstractNameFilterService } from './abstract-name-filter-service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CorrespondentService extends AbstractPaperlessService<PaperlessCorrespondent> {
|
||||
export class CorrespondentService extends AbstractNameFilterService<PaperlessCorrespondent> {
|
||||
|
||||
constructor(http: HttpClient) {
|
||||
super(http, 'correspondents')
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
|
||||
import { AbstractPaperlessService } from './abstract-paperless-service';
|
||||
import { AbstractNameFilterService } from './abstract-name-filter-service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DocumentTypeService extends AbstractPaperlessService<PaperlessDocumentType> {
|
||||
export class DocumentTypeService extends AbstractNameFilterService<PaperlessDocumentType> {
|
||||
|
||||
constructor(http: HttpClient) {
|
||||
super(http, 'document_types')
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PaperlessTag } from 'src/app/data/paperless-tag';
|
||||
import { AbstractPaperlessService } from './abstract-paperless-service';
|
||||
import { AbstractNameFilterService } from './abstract-name-filter-service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TagService extends AbstractPaperlessService<PaperlessTag> {
|
||||
export class TagService extends AbstractNameFilterService<PaperlessTag> {
|
||||
|
||||
constructor(http: HttpClient) {
|
||||
super(http, 'tags')
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
|
||||
import { Meta } from '@angular/platform-browser';
|
||||
import { CookieService } from 'ngx-cookie-service';
|
||||
|
||||
export interface PaperlessSettings {
|
||||
key: string
|
||||
@@ -7,13 +9,21 @@ export interface PaperlessSettings {
|
||||
default: any
|
||||
}
|
||||
|
||||
export interface LanguageOption {
|
||||
code: string,
|
||||
name: string,
|
||||
englishName?: string
|
||||
}
|
||||
|
||||
export const SETTINGS_KEYS = {
|
||||
BULK_EDIT_CONFIRMATION_DIALOGS: 'general-settings:bulk-edit:confirmation-dialogs',
|
||||
BULK_EDIT_APPLY_ON_CLOSE: 'general-settings:bulk-edit:apply-on-close',
|
||||
DOCUMENT_LIST_SIZE: 'general-settings:documentListSize',
|
||||
DARK_MODE_USE_SYSTEM: 'general-settings:dark-mode:use-system',
|
||||
DARK_MODE_ENABLED: 'general-settings:dark-mode:enabled',
|
||||
USE_NATIVE_PDF_VIEWER: 'general-settings:document-details:native-pdf-viewer'
|
||||
USE_NATIVE_PDF_VIEWER: 'general-settings:document-details:native-pdf-viewer',
|
||||
DATE_LOCALE: 'general-settings:date-display:date-locale',
|
||||
DATE_FORMAT: 'general-settings:date-display:date-format'
|
||||
}
|
||||
|
||||
const SETTINGS: PaperlessSettings[] = [
|
||||
@@ -22,7 +32,9 @@ const SETTINGS: PaperlessSettings[] = [
|
||||
{key: SETTINGS_KEYS.DOCUMENT_LIST_SIZE, type: "number", default: 50},
|
||||
{key: SETTINGS_KEYS.DARK_MODE_USE_SYSTEM, type: "boolean", default: true},
|
||||
{key: SETTINGS_KEYS.DARK_MODE_ENABLED, type: "boolean", default: false},
|
||||
{key: SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, type: "boolean", default: false}
|
||||
{key: SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, type: "boolean", default: false},
|
||||
{key: SETTINGS_KEYS.DATE_LOCALE, type: "string", default: ""},
|
||||
{key: SETTINGS_KEYS.DATE_FORMAT, type: "string", default: "mediumDate"}
|
||||
]
|
||||
|
||||
@Injectable({
|
||||
@@ -34,7 +46,9 @@ export class SettingsService {
|
||||
|
||||
constructor(
|
||||
private rendererFactory: RendererFactory2,
|
||||
@Inject(DOCUMENT) private document
|
||||
@Inject(DOCUMENT) private document,
|
||||
private cookieService: CookieService,
|
||||
private meta: Meta
|
||||
) {
|
||||
this.renderer = rendererFactory.createRenderer(null, null);
|
||||
|
||||
@@ -55,6 +69,35 @@ export class SettingsService {
|
||||
|
||||
}
|
||||
|
||||
getLanguageOptions(): LanguageOption[] {
|
||||
return [
|
||||
{code: "en-US", name: $localize`English (US)`, englishName: "English (US)"},
|
||||
{code: "de", name: $localize`German`, englishName: "German"},
|
||||
{code: "nl", name: $localize`Dutch`, englishName: "Dutch"},
|
||||
{code: "fr", name: $localize`French`, englishName: "French"}
|
||||
]
|
||||
}
|
||||
|
||||
private getLanguageCookieName() {
|
||||
let prefix = ""
|
||||
if (this.meta.getTag('name=cookie_prefix')) {
|
||||
prefix = this.meta.getTag('name=cookie_prefix').content
|
||||
}
|
||||
return `${prefix || ''}django_language`
|
||||
}
|
||||
|
||||
getLanguage(): string {
|
||||
return this.cookieService.get(this.getLanguageCookieName())
|
||||
}
|
||||
|
||||
setLanguage(language: string) {
|
||||
if (language) {
|
||||
this.cookieService.set(this.getLanguageCookieName(), language)
|
||||
} else {
|
||||
this.cookieService.delete(this.getLanguageCookieName())
|
||||
}
|
||||
}
|
||||
|
||||
get(key: string): any {
|
||||
let setting = SETTINGS.find(s => s.key == key)
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@ export const environment = {
|
||||
production: true,
|
||||
apiBaseUrl: "/api/",
|
||||
appTitle: "Paperless-ng",
|
||||
version: "0.9.14"
|
||||
version: "1.0.0"
|
||||
};
|
||||
@@ -449,12 +449,20 @@
|
||||
<context context-type="linenumber">2</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ddb40946e790522301687ecddb9ce1cb8ad40dd1">
|
||||
<source>Filter by:</source>
|
||||
<target>Filtern nach:</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">8</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="cff1428d10d59d14e45edec3c735a27b5482db59">
|
||||
<source>Name</source>
|
||||
<target>Name</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8fa4d523f7b91df4390120b85ed0406138273e1a">
|
||||
@@ -462,7 +470,7 @@
|
||||
<target>Farbe</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">14</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="d0c4488f742efeba0915e90e285a022da813deff">
|
||||
@@ -470,7 +478,7 @@
|
||||
<target>Zuweisung</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9bcf8d20d23c111eca1431abd2d2ce0de324499c">
|
||||
@@ -478,7 +486,7 @@
|
||||
<target>Anzahl Dokumente</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">16</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="030b4423b92167200e39519599f9b863b4f7c62c">
|
||||
@@ -486,7 +494,7 @@
|
||||
<target>Aktionen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
<context context-type="linenumber">23</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9d51b3c90afda70700229d1b8a55371c13cb3bce">
|
||||
@@ -494,7 +502,7 @@
|
||||
<target>Dokumente</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">32</context>
|
||||
<context context-type="linenumber">38</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="28f86ffd419b869711aa13f5e5ff54be6d70731c">
|
||||
@@ -502,7 +510,7 @@
|
||||
<target>Bearbeiten</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="4990731724078522539">
|
||||
@@ -542,7 +550,7 @@
|
||||
<target>Gespeicherte Ansicht "<x equiv-text="savedView.name" id="PH"/>" gelöscht.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
<context context-type="linenumber">63</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5647210819299459618">
|
||||
@@ -550,7 +558,23 @@
|
||||
<target>Einstellungen erfolgreich gespeichert.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">68</context>
|
||||
<context context-type="linenumber">79</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6839066544204061364">
|
||||
<source>Use system language</source>
|
||||
<target>Benutze Systemsprache</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7729897675462249787">
|
||||
<source>Use date format of display language</source>
|
||||
<target>Benutze Datumsformat der Anzeigesprache</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8488620293789898901">
|
||||
@@ -558,7 +582,7 @@
|
||||
<target>Fehler beim Speichern der Einstellungen auf dem Server: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">80</context>
|
||||
<context context-type="linenumber">103</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
|
||||
@@ -582,7 +606,7 @@
|
||||
<target>Gespeicherte Ansichten</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">64</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13">
|
||||
@@ -593,12 +617,68 @@
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="75f8908d266f7cc9b9e68e0be906fd080a223606">
|
||||
<source>Display language</source>
|
||||
<target>Anzeigesprache</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1284a077dc18a2d1ff1b744f16f1797eea28ae37">
|
||||
<source>You need to reload the page after applying a new language.</source>
|
||||
<target>Nachdem Sie eine neue Sprache ausgewählt haben, müssen Sie die Seite neu laden.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="0cd55822928764cc82a62ee3e6f3adbc1c630479">
|
||||
<source>Date display</source>
|
||||
<target>Datumsanzeige</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">32</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7df4de0d0704a06a302d853e31f2580eba98f127">
|
||||
<source>Date format</source>
|
||||
<target>Datumsformat</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">45</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="69852399a18b8ca4fca8c5bfddd3f00a6d137593">
|
||||
<source>Short: <x equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}" id="INTERPOLATION"/></source>
|
||||
<target>Kurz: <x equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="d01a59285e711252b98c4f193394e4b854615c78">
|
||||
<source>Medium: <x equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}" id="INTERPOLATION"/></source>
|
||||
<target>Mittel: <x equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="00481629776699b1caebd12b3b1176e2e23740a8">
|
||||
<source>Long: <x equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}" id="INTERPOLATION"/></source>
|
||||
<target>Lang: <x equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">59</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2045151788cbdda7512752e408da59a6b54a8ef0">
|
||||
<source>Items per page</source>
|
||||
<target>Dokumente pro Seite</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="c4435e56bf0289e78fedc462f1d21fb30b9de55d">
|
||||
@@ -606,7 +686,7 @@
|
||||
<target>Dokumenteditor</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="4903e521c9bfd11ce88e7a5575106ef638912e0d">
|
||||
@@ -614,7 +694,7 @@
|
||||
<target>Benutze PDF-Betrachter des Web Browsers</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="d7db07023e53f8396d18d375c2b78c25fc81c197">
|
||||
@@ -622,7 +702,7 @@
|
||||
<target>Der integrierte PDF-Betrachter des Web-Browsers ist in der Regel schneller bei der Anzeige besonders großer Dokumente, funktioniert aber nicht in allen Browsern.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9ee5d1cbfd6ee168dae37aaba2b59b50bcabb2ff">
|
||||
@@ -630,7 +710,7 @@
|
||||
<target>Dunkler Modus</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">44</context>
|
||||
<context context-type="linenumber">94</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="f8cb5506e70fd71fddc9bb71cee18bfff7b29637">
|
||||
@@ -638,7 +718,7 @@
|
||||
<target>Benutze Systemeinstellungen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8ee474504043fa89821d626e4f3413240fa91b53">
|
||||
@@ -646,7 +726,7 @@
|
||||
<target>Dunklen Modus aktivieren</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">48</context>
|
||||
<context context-type="linenumber">98</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82">
|
||||
@@ -654,7 +734,7 @@
|
||||
<target>Massenbearbeitung</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">52</context>
|
||||
<context context-type="linenumber">102</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586">
|
||||
@@ -662,7 +742,7 @@
|
||||
<target>Bestätigungsdialoge anzeigen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">56</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a">
|
||||
@@ -670,7 +750,7 @@
|
||||
<target>Beim Löschen von Dokumenten wird immer nach einer Bestätigung gefragt.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">56</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e">
|
||||
@@ -678,7 +758,7 @@
|
||||
<target>Anwenden beim Schließen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">57</context>
|
||||
<context context-type="linenumber">107</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8cb90334f5dfd7fc67205085f59381e2a334ccfc">
|
||||
@@ -686,7 +766,7 @@
|
||||
<target>Erscheint auf</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
<context context-type="linenumber">126</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5">
|
||||
@@ -694,7 +774,7 @@
|
||||
<target>Auf Startseite zeigen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">79</context>
|
||||
<context context-type="linenumber">129</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="541bfc5b123b3f8867fd681eaceefb663a811973">
|
||||
@@ -702,7 +782,7 @@
|
||||
<target>In Seitenleiste zeigen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
<context context-type="linenumber">133</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="abba764a7a595d04dc8c3b26e04b3780d4fdb540">
|
||||
@@ -710,7 +790,7 @@
|
||||
<target>Keine gespeicherten Ansichten vorhanden.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">93</context>
|
||||
<context context-type="linenumber">143</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ef60a738a565f498b858e903e42bc5ffc3cc1299">
|
||||
@@ -742,7 +822,7 @@
|
||||
<target>Letzter Kontakt</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1234709746630139322">
|
||||
@@ -790,7 +870,7 @@
|
||||
<target>Zuweisungsalgorithmus</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="eab7fc7cf2d663e54de934b779fce4275a303f0f">
|
||||
@@ -798,7 +878,7 @@
|
||||
<target>Zuweisungsmuster</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="632e529f49cf3d367dfbd15bd055e9b53aef30fb">
|
||||
@@ -806,7 +886,7 @@
|
||||
<target>Groß-/Kleinschreibung irrelevant</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9153094873118985366">
|
||||
@@ -1042,14 +1122,6 @@
|
||||
<context context-type="linenumber">46</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ddb40946e790522301687ecddb9ce1cb8ad40dd1">
|
||||
<source>Filter by:</source>
|
||||
<target>Filtern nach:</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">4</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="02d184c288f567825a1fcbf83bcd3099a10853d5">
|
||||
<source>Filter tags</source>
|
||||
<target>Tags filtern</target>
|
||||
@@ -1163,9 +1235,9 @@
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1b29a8153575e5ad26cc7dd8bd75c4f45f6bfe7e">
|
||||
<source>Created: <x equiv-text="{{document.created | date}}" id="INTERPOLATION"/></source>
|
||||
<target>Erstellt: <x equiv-text="{{document.created | date}}" id="INTERPOLATION"/></target>
|
||||
<trans-unit datatype="html" id="849b42384616374df49bd8b3711ec159cb10b845">
|
||||
<source>Created: <x equiv-text="{{document.created | customDate}}" id="INTERPOLATION"/></source>
|
||||
<target>Erstellt: <x equiv-text="{{document.created | customDate}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
@@ -1598,6 +1670,38 @@
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1206520795340730278">
|
||||
<source>English (US)</source>
|
||||
<target>Englisch (US)</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1858110241312746425">
|
||||
<source>German</source>
|
||||
<target>Deutsch</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="3071065188816255493">
|
||||
<source>Dutch</source>
|
||||
<target>Niederländisch</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7633754075223722162">
|
||||
<source>French</source>
|
||||
<target>Französisch</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">77</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1519954996184640001">
|
||||
<source>Error</source>
|
||||
<target>Fehler</target>
|
||||
@@ -1779,7 +1883,7 @@
|
||||
<target>Neues Element erstellen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5324147361912094446">
|
||||
@@ -1787,7 +1891,7 @@
|
||||
<target>Element bearbeiten</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1699589597032579396">
|
||||
@@ -1795,7 +1899,7 @@
|
||||
<target>Konnte Element nicht speichern: <x equiv-text="error" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
<context context-type="linenumber">58</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="810888510148304696">
|
||||
@@ -1803,7 +1907,7 @@
|
||||
<target>Automatisch</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5044611416737085530">
|
||||
@@ -1811,7 +1915,7 @@
|
||||
<target>Möchten Sie dieses Element wirklich löschen?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8371896857609524947">
|
||||
@@ -1819,7 +1923,7 @@
|
||||
<target>Assoziierte Dokumente werden nicht gelöscht.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
<context context-type="linenumber">104</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7022070615528435141">
|
||||
@@ -1827,7 +1931,7 @@
|
||||
<target>Löschen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">85</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5467489005440577210">
|
||||
@@ -1835,7 +1939,7 @@
|
||||
<target>Fehler beim Löschen des Elements: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">93</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5851669019930456395">
|
||||
@@ -1912,7 +2016,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8419167206585286450">
|
||||
<source>Fuzzy: Document contains a word similar to this word</source>
|
||||
<target>Ungenau: Dokument enthält ein zum folgdendem Wort ähnliches Wort</target>
|
||||
<target>Ungenau: Dokument enthält ein zum folgendem Wort ähnliches Wort</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/data/matching-model.ts</context>
|
||||
<context context-type="linenumber">16</context>
|
||||
|
||||
+233
-105
@@ -38,7 +38,7 @@
|
||||
<target>Sélectionner aucun</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2ed8a0c2ce4968f8614151eefed20a0aa3daeeb9">
|
||||
@@ -46,7 +46,7 @@
|
||||
<target>Sélectionner la page</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="b463541a7e303aa4d0b1102eaff8afbaf34e7a74">
|
||||
@@ -54,15 +54,15 @@
|
||||
<target>Sélectionner tout</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ec97f67072a83aaa972536b18d83179f6e4bbec9">
|
||||
<source>Sort by</source>
|
||||
<target>Trier par</target>
|
||||
<trans-unit datatype="html" id="5d43539fc358c3a548b9d487be821db73e2702ff">
|
||||
<source>Sort</source>
|
||||
<target>Trier</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">41</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="b7641aed03492978b4ec6843b1e53f30464294d9">
|
||||
@@ -94,7 +94,7 @@
|
||||
<target>{VAR_PLURAL, plural, =1 {<x id="INTERPOLATION"/> document sélectionné sur 1} other {<x id="INTERPOLATION"/> documents sélectionnés sur <x id="INTERPOLATION_1"/>}}</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
<context context-type="linenumber">85</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="bb773fdeaad5e7fb8e6cd77e1cc558e1b194a0c9">
|
||||
@@ -102,7 +102,7 @@
|
||||
<target>{VAR_PLURAL, plural, =1 {Un document} other {<x id="INTERPOLATION"/> documents}}</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">88</context>
|
||||
<context context-type="linenumber">86</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="bb91083b44e3f77dd68de773ceab467ca3d57507">
|
||||
@@ -110,7 +110,7 @@
|
||||
<target>(filtré)</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">88</context>
|
||||
<context context-type="linenumber">86</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ca0b795795658155d44ddca02e95f1feeeb4a88f">
|
||||
@@ -118,7 +118,7 @@
|
||||
<target>NSA</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">107</context>
|
||||
<context context-type="linenumber">105</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7b5c6286aaded63fb279d6deb8aa8c704e085ced">
|
||||
@@ -126,7 +126,7 @@
|
||||
<target>Correspondant</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">113</context>
|
||||
<context context-type="linenumber">111</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="fdf7cbdc140d0aab0f0b6c06065a0fd448ed6a2e">
|
||||
@@ -134,7 +134,7 @@
|
||||
<target>Titre</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">119</context>
|
||||
<context context-type="linenumber">117</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2bd5919e8098513664a89d5b7b52d61e3063950f">
|
||||
@@ -142,7 +142,7 @@
|
||||
<target>Type de document</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">125</context>
|
||||
<context context-type="linenumber">123</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1b051734b0ee9021991c91b3ed4e81c244322462">
|
||||
@@ -150,7 +150,7 @@
|
||||
<target>Date de création</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">131</context>
|
||||
<context context-type="linenumber">129</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="80e3b490720757978c99a7b5af3885faf202b955">
|
||||
@@ -158,7 +158,7 @@
|
||||
<target>Date d'ajout</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">137</context>
|
||||
<context context-type="linenumber">135</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9021887951960049161">
|
||||
@@ -166,7 +166,7 @@
|
||||
<target>Confirmer la suppression</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">186</context>
|
||||
<context context-type="linenumber">192</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5382975254277698192">
|
||||
@@ -174,7 +174,7 @@
|
||||
<target>Voulez-vous vraiment supprimer le document "<x equiv-text="this.document.title" id="PH"/>" ?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">187</context>
|
||||
<context context-type="linenumber">193</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6691075929777935948">
|
||||
@@ -182,7 +182,7 @@
|
||||
<target>Les fichiers liés à ce document seront supprimés définitivement. Cette action est irréversible.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">188</context>
|
||||
<context context-type="linenumber">194</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="719892092227206532">
|
||||
@@ -190,7 +190,7 @@
|
||||
<target>Supprimer le document</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">190</context>
|
||||
<context context-type="linenumber">196</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1844801255494293730">
|
||||
@@ -198,7 +198,7 @@
|
||||
<target>Une erreur s'est produite lors de la suppression du document : <x equiv-text="JSON.stringify(error)" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">197</context>
|
||||
<context context-type="linenumber">203</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="826b25211922a1b46436589233cb6f1a163d89b7">
|
||||
@@ -449,12 +449,20 @@
|
||||
<context context-type="linenumber">2</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ddb40946e790522301687ecddb9ce1cb8ad40dd1">
|
||||
<source>Filter by:</source>
|
||||
<target>Filtrer par : </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">8</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="cff1428d10d59d14e45edec3c735a27b5482db59">
|
||||
<source>Name</source>
|
||||
<target>Nom</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8fa4d523f7b91df4390120b85ed0406138273e1a">
|
||||
@@ -462,7 +470,7 @@
|
||||
<target>Couleur</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">14</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="d0c4488f742efeba0915e90e285a022da813deff">
|
||||
@@ -470,7 +478,7 @@
|
||||
<target>Rapprochement</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9bcf8d20d23c111eca1431abd2d2ce0de324499c">
|
||||
@@ -478,7 +486,7 @@
|
||||
<target>Nombre de documents</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">16</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="030b4423b92167200e39519599f9b863b4f7c62c">
|
||||
@@ -486,7 +494,7 @@
|
||||
<target>Actions</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
<context context-type="linenumber">23</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9d51b3c90afda70700229d1b8a55371c13cb3bce">
|
||||
@@ -494,7 +502,7 @@
|
||||
<target>Documents</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">32</context>
|
||||
<context context-type="linenumber">38</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="28f86ffd419b869711aa13f5e5ff54be6d70731c">
|
||||
@@ -502,7 +510,7 @@
|
||||
<target>Éditer</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="4990731724078522539">
|
||||
@@ -542,7 +550,7 @@
|
||||
<target>Vue "<x equiv-text="savedView.name" id="PH"/>" supprimée.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
<context context-type="linenumber">63</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5647210819299459618">
|
||||
@@ -550,7 +558,23 @@
|
||||
<target>Paramètres enregistrés avec succès.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
<context context-type="linenumber">79</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6839066544204061364">
|
||||
<source>Use system language</source>
|
||||
<target>Utiliser la langue du système</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7729897675462249787">
|
||||
<source>Use date format of display language</source>
|
||||
<target>Utiliser le format de date de la langue d'affichage</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8488620293789898901">
|
||||
@@ -558,7 +582,7 @@
|
||||
<target>Une erreur s'est produite lors de l'enregistrement des paramètres sur le serveur : <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">86</context>
|
||||
<context context-type="linenumber">103</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
|
||||
@@ -582,7 +606,7 @@
|
||||
<target>Vues enregistrées</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">56</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13">
|
||||
@@ -593,12 +617,92 @@
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="75f8908d266f7cc9b9e68e0be906fd080a223606">
|
||||
<source>Display language</source>
|
||||
<target>Langue d'affichage</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1284a077dc18a2d1ff1b744f16f1797eea28ae37">
|
||||
<source>You need to reload the page after applying a new language.</source>
|
||||
<target>Vous devez recharger la page après avoir sélectionné une nouvelle langue.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="0cd55822928764cc82a62ee3e6f3adbc1c630479">
|
||||
<source>Date display</source>
|
||||
<target>Affichage de la date</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">32</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7df4de0d0704a06a302d853e31f2580eba98f127">
|
||||
<source>Date format</source>
|
||||
<target>Format de date</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">45</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="69852399a18b8ca4fca8c5bfddd3f00a6d137593">
|
||||
<source>Short: <x equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}" id="INTERPOLATION"/></source>
|
||||
<target>Court : <x equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="d01a59285e711252b98c4f193394e4b854615c78">
|
||||
<source>Medium: <x equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}" id="INTERPOLATION"/></source>
|
||||
<target>Moyen : <x equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="00481629776699b1caebd12b3b1176e2e23740a8">
|
||||
<source>Long: <x equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}" id="INTERPOLATION"/></source>
|
||||
<target>Long : <x equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">59</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2045151788cbdda7512752e408da59a6b54a8ef0">
|
||||
<source>Items per page</source>
|
||||
<target>Éléments par page</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="c4435e56bf0289e78fedc462f1d21fb30b9de55d">
|
||||
<source>Document editor</source>
|
||||
<target>Visionneuse de documents</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="4903e521c9bfd11ce88e7a5575106ef638912e0d">
|
||||
<source>Use PDF viewer provided by the browser</source>
|
||||
<target>Utiliser la visionneuse PDF fournie par le navigateur</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="d7db07023e53f8396d18d375c2b78c25fc81c197">
|
||||
<source>This is usually faster for displaying large PDF documents, but it might not work on some browsers.</source>
|
||||
<target>Cette option est généralement plus rapide pour l'affichage de grands documents PDF, mais elle peut ne pas fonctionner sur certains navigateurs.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9ee5d1cbfd6ee168dae37aaba2b59b50bcabb2ff">
|
||||
@@ -606,7 +710,7 @@
|
||||
<target>Mode sombre</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">94</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="f8cb5506e70fd71fddc9bb71cee18bfff7b29637">
|
||||
@@ -614,39 +718,7 @@
|
||||
<target>Utiliser les paramètres du système</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">36</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82">
|
||||
<source>Bulk editing</source>
|
||||
<target>Edition en masse</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">44</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586">
|
||||
<source>Show confirmation dialogs</source>
|
||||
<target>Afficher les messages de confirmation</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">48</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a">
|
||||
<source>Deleting documents will always ask for confirmation.</source>
|
||||
<target>La suppression de documents requiert toujours une confirmation.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">48</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e">
|
||||
<source>Apply on close</source>
|
||||
<target>Appliquer lors de la fermeture</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">49</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8ee474504043fa89821d626e4f3413240fa91b53">
|
||||
@@ -654,7 +726,39 @@
|
||||
<target>Activer le mode sombre</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
<context context-type="linenumber">98</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82">
|
||||
<source>Bulk editing</source>
|
||||
<target>Edition en masse</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">102</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586">
|
||||
<source>Show confirmation dialogs</source>
|
||||
<target>Afficher les messages de confirmation</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a">
|
||||
<source>Deleting documents will always ask for confirmation.</source>
|
||||
<target>La suppression de documents requiert toujours une confirmation.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e">
|
||||
<source>Apply on close</source>
|
||||
<target>Appliquer lors de la fermeture</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">107</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8cb90334f5dfd7fc67205085f59381e2a334ccfc">
|
||||
@@ -662,7 +766,7 @@
|
||||
<target>Apparaît sur</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">68</context>
|
||||
<context context-type="linenumber">126</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5">
|
||||
@@ -670,7 +774,7 @@
|
||||
<target>Montrer sur le tableau de bord</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">71</context>
|
||||
<context context-type="linenumber">129</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="541bfc5b123b3f8867fd681eaceefb663a811973">
|
||||
@@ -678,7 +782,7 @@
|
||||
<target>Montrer dans la barre latérale</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
<context context-type="linenumber">133</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="abba764a7a595d04dc8c3b26e04b3780d4fdb540">
|
||||
@@ -686,7 +790,7 @@
|
||||
<target>Aucune vue sauvegardée n'est définie.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">85</context>
|
||||
<context context-type="linenumber">143</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ef60a738a565f498b858e903e42bc5ffc3cc1299">
|
||||
@@ -718,7 +822,7 @@
|
||||
<target>Dernière correspondance</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1234709746630139322">
|
||||
@@ -766,7 +870,7 @@
|
||||
<target>Algorithme de rapprochement</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="eab7fc7cf2d663e54de934b779fce4275a303f0f">
|
||||
@@ -774,7 +878,7 @@
|
||||
<target>Modèle de rapprochement</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="632e529f49cf3d367dfbd15bd055e9b53aef30fb">
|
||||
@@ -782,7 +886,7 @@
|
||||
<target>Insensible à la casse</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9153094873118985366">
|
||||
@@ -1018,14 +1122,6 @@
|
||||
<context context-type="linenumber">46</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ddb40946e790522301687ecddb9ce1cb8ad40dd1">
|
||||
<source>Filter by:</source>
|
||||
<target>Filtrer par : </target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">4</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="02d184c288f567825a1fcbf83bcd3099a10853d5">
|
||||
<source>Filter tags</source>
|
||||
<target>Filtrer les étiquettes</target>
|
||||
@@ -1039,7 +1135,7 @@
|
||||
<target>Filtrer les correspondants</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">19</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="0ad509732aaf702b7ea8c771c7809fa84bc85908">
|
||||
@@ -1047,7 +1143,7 @@
|
||||
<target>Filtrer les types de documents</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
<context context-type="linenumber">27</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2d9d55f1b70142ff4597ba32179d16888fd9c6b2">
|
||||
@@ -1055,7 +1151,7 @@
|
||||
<target>Réinitialiser les filtres</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7593728289020204896">
|
||||
@@ -1063,7 +1159,7 @@
|
||||
<target>Non affecté</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
|
||||
<context context-type="linenumber">161</context>
|
||||
<context context-type="linenumber">166</context>
|
||||
</context-group>
|
||||
<note from="description" priority="1">Filter drop down element to filter for documents with no correspondent/type/tag assigned</note>
|
||||
</trans-unit>
|
||||
@@ -1072,7 +1168,7 @@
|
||||
<target>Appliquer</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
|
||||
<context context-type="linenumber">28</context>
|
||||
<context context-type="linenumber">26</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="4873149362496451858">
|
||||
@@ -1139,12 +1235,12 @@
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1b29a8153575e5ad26cc7dd8bd75c4f45f6bfe7e">
|
||||
<source>Created: <x equiv-text="{{document.created | date}}" id="INTERPOLATION"/></source>
|
||||
<target>Créé le : <x equiv-text="{{document.created | date}}" id="INTERPOLATION"/></target>
|
||||
<trans-unit datatype="html" id="849b42384616374df49bd8b3711ec159cb10b845">
|
||||
<source>Created: <x equiv-text="{{document.created | customDate}}" id="INTERPOLATION"/></source>
|
||||
<target>Créé le : <x equiv-text="{{document.created | customDate}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
||||
<context context-type="linenumber">65</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="cd6f3fd48957e1fea6545c2b2defc7b2435ebfa8">
|
||||
@@ -1168,7 +1264,7 @@
|
||||
<target>Score :</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
||||
<context context-type="linenumber">61</context>
|
||||
<context context-type="linenumber">62</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2840db547019ce8c76b2cdbe3a1653c5b68b06af">
|
||||
@@ -1355,7 +1451,7 @@
|
||||
<target>Sélectionner :</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="dfc3c34e182ea73c5d784ff7c8135f087992dac1">
|
||||
@@ -1363,7 +1459,7 @@
|
||||
<target>Tout</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="907df6a2b68daecc3c399cf40a764b358bd9fd84">
|
||||
@@ -1371,7 +1467,7 @@
|
||||
<target>Éditer :</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
|
||||
<context context-type="linenumber">28</context>
|
||||
<context context-type="linenumber">27</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="27d158b47717ff9305d19866960418c603f19d55">
|
||||
@@ -1574,6 +1670,38 @@
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1206520795340730278">
|
||||
<source>English (US)</source>
|
||||
<target>Anglais (US)</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1858110241312746425">
|
||||
<source>German</source>
|
||||
<target>Allemand</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="3071065188816255493">
|
||||
<source>Dutch</source>
|
||||
<target>Néerlandais</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7633754075223722162">
|
||||
<source>French</source>
|
||||
<target>Français</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">77</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1519954996184640001">
|
||||
<source>Error</source>
|
||||
<target>Erreur</target>
|
||||
@@ -1755,7 +1883,7 @@
|
||||
<target>Créer un nouvel élément</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5324147361912094446">
|
||||
@@ -1763,7 +1891,7 @@
|
||||
<target>Éditer l'élément</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1699589597032579396">
|
||||
@@ -1771,7 +1899,7 @@
|
||||
<target>Impossible d'enregistrer l'élément : <x equiv-text="error" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
<context context-type="linenumber">58</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="810888510148304696">
|
||||
@@ -1779,7 +1907,7 @@
|
||||
<target>Automatique</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5044611416737085530">
|
||||
@@ -1787,7 +1915,7 @@
|
||||
<target>Voulez-vous vraiment supprimer cet élément ?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8371896857609524947">
|
||||
@@ -1795,7 +1923,7 @@
|
||||
<target>Les documents associés ne seront pas supprimés.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
<context context-type="linenumber">104</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7022070615528435141">
|
||||
@@ -1803,7 +1931,7 @@
|
||||
<target>Supprimer</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">85</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5467489005440577210">
|
||||
@@ -1811,7 +1939,7 @@
|
||||
<target>Une erreur s'est produite lors de la suppression de l'élément : <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">93</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5851669019930456395">
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2155249406916744630">
|
||||
<source>View "<x equiv-text="this.list.savedView.name" id="PH"/>" saved successfully.</source>
|
||||
<target>View &quot;<x equiv-text="this.list.savedView.name" id="PH"/>&quot; met succes opgeslagen.</target>
|
||||
<target>View "<x equiv-text="this.list.savedView.name" id="PH"/>" met succes opgeslagen.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">94</context>
|
||||
@@ -19,7 +19,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6837554170707123455">
|
||||
<source>View "<x equiv-text="savedView.name" id="PH"/>" created successfully.</source>
|
||||
<target>View &quot;<x equiv-text="savedView.name" id="PH"/>&quot; met succes gemaakt.</target>
|
||||
<target>View "<x equiv-text="savedView.name" id="PH"/>" met succes gemaakt.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.ts</context>
|
||||
<context context-type="linenumber">115</context>
|
||||
@@ -38,7 +38,7 @@
|
||||
<target>Niets selecteren</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2ed8a0c2ce4968f8614151eefed20a0aa3daeeb9">
|
||||
@@ -46,7 +46,7 @@
|
||||
<target>Selecteer pagina</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="b463541a7e303aa4d0b1102eaff8afbaf34e7a74">
|
||||
@@ -54,15 +54,15 @@
|
||||
<target>Alles selecteren</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ec97f67072a83aaa972536b18d83179f6e4bbec9">
|
||||
<source>Sort by</source>
|
||||
<target>Sorteer op</target>
|
||||
<trans-unit datatype="html" id="5d43539fc358c3a548b9d487be821db73e2702ff">
|
||||
<source>Sort</source>
|
||||
<target>Sorteren</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">41</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="b7641aed03492978b4ec6843b1e53f30464294d9">
|
||||
@@ -83,7 +83,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="72e7d343f9165602cce1ca7faffbc565fd31ef92">
|
||||
<source>Save "<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>"</source>
|
||||
<target>Opslaan &quot;<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>&quot;</target>
|
||||
<target>Opslaan "<x equiv-text="{{list.savedViewTitle}}" id="INTERPOLATION"/>"</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">71</context>
|
||||
@@ -94,7 +94,7 @@
|
||||
<target>{VAR_PLURAL, plural, =1 {<x id="INTERPOLATION"/> van één document geselecteerd} other {<x id="INTERPOLATION"/> van <x id="INTERPOLATION_1"/> documenten geselecteerd}}</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
<context context-type="linenumber">85</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="bb773fdeaad5e7fb8e6cd77e1cc558e1b194a0c9">
|
||||
@@ -102,7 +102,7 @@
|
||||
<target>{VAR_PLURAL, plural, =1 {Eén document} other {<x id="INTERPOLATION"/> documenten}}</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">88</context>
|
||||
<context context-type="linenumber">86</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="bb91083b44e3f77dd68de773ceab467ca3d57507">
|
||||
@@ -110,7 +110,7 @@
|
||||
<target>(gefilterd)</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">88</context>
|
||||
<context context-type="linenumber">86</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ca0b795795658155d44ddca02e95f1feeeb4a88f">
|
||||
@@ -118,7 +118,7 @@
|
||||
<target>ASN</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">107</context>
|
||||
<context context-type="linenumber">105</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7b5c6286aaded63fb279d6deb8aa8c704e085ced">
|
||||
@@ -126,7 +126,7 @@
|
||||
<target>Correspondent</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">113</context>
|
||||
<context context-type="linenumber">111</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="fdf7cbdc140d0aab0f0b6c06065a0fd448ed6a2e">
|
||||
@@ -134,7 +134,7 @@
|
||||
<target>Titel</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">119</context>
|
||||
<context context-type="linenumber">117</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2bd5919e8098513664a89d5b7b52d61e3063950f">
|
||||
@@ -142,7 +142,7 @@
|
||||
<target>Documenttype</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">125</context>
|
||||
<context context-type="linenumber">123</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1b051734b0ee9021991c91b3ed4e81c244322462">
|
||||
@@ -150,7 +150,7 @@
|
||||
<target>Aangemaakt</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">131</context>
|
||||
<context context-type="linenumber">129</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="80e3b490720757978c99a7b5af3885faf202b955">
|
||||
@@ -158,7 +158,7 @@
|
||||
<target>Toegevoegd</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
|
||||
<context context-type="linenumber">137</context>
|
||||
<context context-type="linenumber">135</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9021887951960049161">
|
||||
@@ -166,15 +166,15 @@
|
||||
<target>Bevestig het verwijderen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">186</context>
|
||||
<context context-type="linenumber">192</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5382975254277698192">
|
||||
<source>Do you really want to delete document "<x equiv-text="this.document.title" id="PH"/>"?</source>
|
||||
<target>Wilt u het document echt verwijderen &quot;<x equiv-text="this.document.title" id="PH"/>&quot;?</target>
|
||||
<target>Wilt u het document echt verwijderen "<x equiv-text="this.document.title" id="PH"/>"?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">187</context>
|
||||
<context context-type="linenumber">193</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6691075929777935948">
|
||||
@@ -182,7 +182,7 @@
|
||||
<target>De bestanden voor dit document worden definitief verwijderd. Deze bewerking kan niet ongedaan worden gemaakt.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">188</context>
|
||||
<context context-type="linenumber">194</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="719892092227206532">
|
||||
@@ -190,7 +190,7 @@
|
||||
<target>Verwijder document</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">190</context>
|
||||
<context context-type="linenumber">196</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1844801255494293730">
|
||||
@@ -198,7 +198,7 @@
|
||||
<target>Fout bij het verwijderen van het document: <x equiv-text="JSON.stringify(error)" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
|
||||
<context context-type="linenumber">197</context>
|
||||
<context context-type="linenumber">203</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="826b25211922a1b46436589233cb6f1a163d89b7">
|
||||
@@ -427,7 +427,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="93754014749412887">
|
||||
<source>Do you really want to delete the tag "<x equiv-text="object.name" id="PH"/>"?</source>
|
||||
<target>Wil je het etiket echt verwijderen &quot;<x equiv-text="object.name" id="PH"/>&quot;?</target>
|
||||
<target>Wil je het etiket echt verwijderen "<x equiv-text="object.name" id="PH"/>"?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.ts</context>
|
||||
<context context-type="linenumber">30</context>
|
||||
@@ -449,12 +449,20 @@
|
||||
<context context-type="linenumber">2</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ddb40946e790522301687ecddb9ce1cb8ad40dd1">
|
||||
<source>Filter by:</source>
|
||||
<target>Filter op:</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">8</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="cff1428d10d59d14e45edec3c735a27b5482db59">
|
||||
<source>Name</source>
|
||||
<target>Naam</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8fa4d523f7b91df4390120b85ed0406138273e1a">
|
||||
@@ -462,7 +470,7 @@
|
||||
<target>Kleur</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">14</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="d0c4488f742efeba0915e90e285a022da813deff">
|
||||
@@ -470,7 +478,7 @@
|
||||
<target>Overeenkomend</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9bcf8d20d23c111eca1431abd2d2ce0de324499c">
|
||||
@@ -478,7 +486,7 @@
|
||||
<target>Aantal documenten</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">16</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="030b4423b92167200e39519599f9b863b4f7c62c">
|
||||
@@ -486,7 +494,7 @@
|
||||
<target>Acties</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
<context context-type="linenumber">23</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9d51b3c90afda70700229d1b8a55371c13cb3bce">
|
||||
@@ -494,7 +502,7 @@
|
||||
<target>Documenten</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">32</context>
|
||||
<context context-type="linenumber">38</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="28f86ffd419b869711aa13f5e5ff54be6d70731c">
|
||||
@@ -502,12 +510,12 @@
|
||||
<target>Bewerk</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/tag-list/tag-list.component.html</context>
|
||||
<context context-type="linenumber">37</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="4990731724078522539">
|
||||
<source>Do you really want to delete the document type "<x equiv-text="object.name" id="PH"/>"?</source>
|
||||
<target>Wilt u het documenttype echt verwijderen &quot;<x equiv-text="object.name" id="PH"/>&quot;?</target>
|
||||
<target>Wilt u het documenttype echt verwijderen "<x equiv-text="object.name" id="PH"/>"?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/document-type-list/document-type-list.component.ts</context>
|
||||
<context context-type="linenumber">26</context>
|
||||
@@ -539,10 +547,10 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5610279464668232148">
|
||||
<source>Saved view "<x equiv-text="savedView.name" id="PH"/>" deleted.</source>
|
||||
<target>Opgeslagen view &quot;<x equiv-text="savedView.name" id="PH"/>&quot; verwijderd.</target>
|
||||
<target>Opgeslagen view "<x equiv-text="savedView.name" id="PH"/>" verwijderd.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
<context context-type="linenumber">63</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5647210819299459618">
|
||||
@@ -550,7 +558,23 @@
|
||||
<target>Instellingen succesvol opgeslagen.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
<context context-type="linenumber">79</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6839066544204061364">
|
||||
<source>Use system language</source>
|
||||
<target>Gebruik de systeemtaal</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7729897675462249787">
|
||||
<source>Use date format of display language</source>
|
||||
<target>Datumopmaak van weergavetaal gebruiken</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8488620293789898901">
|
||||
@@ -558,7 +582,7 @@
|
||||
<target>Fout bij het opslaan van de instellingen: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.ts</context>
|
||||
<context context-type="linenumber">86</context>
|
||||
<context context-type="linenumber">103</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="121cc5391cd2a5115bc2b3160379ee5b36cd7716">
|
||||
@@ -582,7 +606,7 @@
|
||||
<target>Opgeslagen views</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">56</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="bbe41ac2ea4a6c00ea941a41b33105048f8e9f13">
|
||||
@@ -593,12 +617,92 @@
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="75f8908d266f7cc9b9e68e0be906fd080a223606">
|
||||
<source>Display language</source>
|
||||
<target>Taal</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1284a077dc18a2d1ff1b744f16f1797eea28ae37">
|
||||
<source>You need to reload the page after applying a new language.</source>
|
||||
<target>De pagina moet herladen worden nadat een andere taal werd gekozen.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="0cd55822928764cc82a62ee3e6f3adbc1c630479">
|
||||
<source>Date display</source>
|
||||
<target>Datumweergave</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">32</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7df4de0d0704a06a302d853e31f2580eba98f127">
|
||||
<source>Date format</source>
|
||||
<target>Datumopmaak</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">45</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="69852399a18b8ca4fca8c5bfddd3f00a6d137593">
|
||||
<source>Short: <x equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}" id="INTERPOLATION"/></source>
|
||||
<target>Kort: <x equiv-text="{{today | customDate:'shortDate':null:computedDateLocale}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="d01a59285e711252b98c4f193394e4b854615c78">
|
||||
<source>Medium: <x equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}" id="INTERPOLATION"/></source>
|
||||
<target>Medium: <x equiv-text="{{today | customDate:'mediumDate':null:computedDateLocale}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="00481629776699b1caebd12b3b1176e2e23740a8">
|
||||
<source>Long: <x equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}" id="INTERPOLATION"/></source>
|
||||
<target>Lang: <x equiv-text="{{today | customDate:'longDate':null:computedDateLocale}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">59</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2045151788cbdda7512752e408da59a6b54a8ef0">
|
||||
<source>Items per page</source>
|
||||
<target>Resultaten per pagina</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">17</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="c4435e56bf0289e78fedc462f1d21fb30b9de55d">
|
||||
<source>Document editor</source>
|
||||
<target>Documentbewerker</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="4903e521c9bfd11ce88e7a5575106ef638912e0d">
|
||||
<source>Use PDF viewer provided by the browser</source>
|
||||
<target>Gebruik de PDF lezer ingebouwd in uw browser</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="d7db07023e53f8396d18d375c2b78c25fc81c197">
|
||||
<source>This is usually faster for displaying large PDF documents, but it might not work on some browsers.</source>
|
||||
<target>Dit is gewoonlijk sneller om grote PDF's te tonen, maar werkt niet bij elke browser.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">87</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9ee5d1cbfd6ee168dae37aaba2b59b50bcabb2ff">
|
||||
@@ -606,7 +710,7 @@
|
||||
<target>Nachtmodus</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">94</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="f8cb5506e70fd71fddc9bb71cee18bfff7b29637">
|
||||
@@ -614,39 +718,7 @@
|
||||
<target>Gebruik systeeminstellingen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">36</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82">
|
||||
<source>Bulk editing</source>
|
||||
<target>Massabewerking</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">44</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586">
|
||||
<source>Show confirmation dialogs</source>
|
||||
<target>Toon dialoogvenster ter bevestiging</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">48</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a">
|
||||
<source>Deleting documents will always ask for confirmation.</source>
|
||||
<target>Er zal altijd om bevestiging gevraagd worden voor het verwijderen van documenten.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">48</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e">
|
||||
<source>Apply on close</source>
|
||||
<target>Toepassen bij het sluiten</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">49</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8ee474504043fa89821d626e4f3413240fa91b53">
|
||||
@@ -654,7 +726,39 @@
|
||||
<target>Gebruik nachtmodus</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
<context context-type="linenumber">98</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="3863a86cd9e69a61d143d3daf51df44203df4a82">
|
||||
<source>Bulk editing</source>
|
||||
<target>Massabewerking</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">102</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="c0ac61661c6c326d6e0e00c231b95cf2ac0c6586">
|
||||
<source>Show confirmation dialogs</source>
|
||||
<target>Toon dialoogvenster ter bevestiging</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="291bbe56ecbe945dcf05580a57d679fa7bd1e06a">
|
||||
<source>Deleting documents will always ask for confirmation.</source>
|
||||
<target>Er zal altijd om bevestiging gevraagd worden voor het verwijderen van documenten.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8cfddc13e04f5545ac63f419ef363505d6f78c2e">
|
||||
<source>Apply on close</source>
|
||||
<target>Toepassen bij het sluiten</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">107</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8cb90334f5dfd7fc67205085f59381e2a334ccfc">
|
||||
@@ -662,7 +766,7 @@
|
||||
<target>Komt voor bij</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">68</context>
|
||||
<context context-type="linenumber">126</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6717cf1acf04728fc2b7c39f6d3297f8ff15fde5">
|
||||
@@ -670,7 +774,7 @@
|
||||
<target>Toon op het dashboard</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">71</context>
|
||||
<context context-type="linenumber">129</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="541bfc5b123b3f8867fd681eaceefb663a811973">
|
||||
@@ -678,7 +782,7 @@
|
||||
<target>Toon in de zijbalk</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
<context context-type="linenumber">133</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="abba764a7a595d04dc8c3b26e04b3780d4fdb540">
|
||||
@@ -686,7 +790,7 @@
|
||||
<target>Geen opgeslagen views gedefinieerd.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/settings/settings.component.html</context>
|
||||
<context context-type="linenumber">85</context>
|
||||
<context context-type="linenumber">143</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ef60a738a565f498b858e903e42bc5ffc3cc1299">
|
||||
@@ -699,7 +803,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7427874343955308724">
|
||||
<source>Do you really want to delete the correspondent "<x equiv-text="object.name" id="PH"/>"?</source>
|
||||
<target>Bent u zeker dat u correspondent &quot;<x equiv-text="object.name" id="PH"/>&quot; wilt verwijderen?</target>
|
||||
<target>Bent u zeker dat u correspondent "<x equiv-text="object.name" id="PH"/>" wilt verwijderen?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.ts</context>
|
||||
<context context-type="linenumber">26</context>
|
||||
@@ -718,7 +822,7 @@
|
||||
<target>Laatste correspondentie</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-list.component.html</context>
|
||||
<context context-type="linenumber">15</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1234709746630139322">
|
||||
@@ -766,7 +870,7 @@
|
||||
<target>Algoritme voor het bepalen van de overeenkomst</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="eab7fc7cf2d663e54de934b779fce4275a303f0f">
|
||||
@@ -774,7 +878,7 @@
|
||||
<target>Patroon voor overeenkomst</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="632e529f49cf3d367dfbd15bd055e9b53aef30fb">
|
||||
@@ -782,7 +886,7 @@
|
||||
<target>Niet hoofdlettergevoelig</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="9153094873118985366">
|
||||
@@ -867,7 +971,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="afa760e48c97d64d19c1455d18b7834a2256e23f">
|
||||
<source>Did you mean "<x equiv-text="<a [routerLink]="" (click)="searchCorrectedQuery()">{{correctedQuery}}" id="START_LINK"/><x equiv-text="{{correctedQuery}}</a>" id="INTERPOLATION"/><x equiv-text="</a>" id="CLOSE_LINK"/>"?</source>
|
||||
<target>Bedoelde u &quot;<x equiv-text="<a [routerLink]="" (click)="searchCorrectedQuery()">{{correctedQuery}}" id="START_LINK"/><x equiv-text="{{correctedQuery}}</a>" id="INTERPOLATION"/><x equiv-text="</a>" id="CLOSE_LINK"/>&quot;?</target>
|
||||
<target>Bedoelde u "<x equiv-text="<a [routerLink]="" (click)="searchCorrectedQuery()">{{correctedQuery}}" id="START_LINK"/><x equiv-text="{{correctedQuery}}</a>" id="INTERPOLATION"/><x equiv-text="</a>" id="CLOSE_LINK"/>"?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/search/search.component.html</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
@@ -1018,14 +1122,6 @@
|
||||
<context context-type="linenumber">46</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="ddb40946e790522301687ecddb9ce1cb8ad40dd1">
|
||||
<source>Filter by:</source>
|
||||
<target>Filter op:</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">4</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="02d184c288f567825a1fcbf83bcd3099a10853d5">
|
||||
<source>Filter tags</source>
|
||||
<target>Etiketten filteren</target>
|
||||
@@ -1039,7 +1135,7 @@
|
||||
<target>Correspondenten filteren</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">19</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="0ad509732aaf702b7ea8c771c7809fa84bc85908">
|
||||
@@ -1047,7 +1143,7 @@
|
||||
<target>Documenttypes filteren</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
<context context-type="linenumber">27</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2d9d55f1b70142ff4597ba32179d16888fd9c6b2">
|
||||
@@ -1055,7 +1151,7 @@
|
||||
<target>Filters terug zetten</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7593728289020204896">
|
||||
@@ -1063,7 +1159,7 @@
|
||||
<target>Niet toegekend</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
|
||||
<context context-type="linenumber">161</context>
|
||||
<context context-type="linenumber">166</context>
|
||||
</context-group>
|
||||
<note from="description" priority="1">Filter drop down element to filter for documents with no correspondent/type/tag assigned</note>
|
||||
</trans-unit>
|
||||
@@ -1072,7 +1168,7 @@
|
||||
<target>Toepassen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.html</context>
|
||||
<context context-type="linenumber">28</context>
|
||||
<context context-type="linenumber">26</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="4873149362496451858">
|
||||
@@ -1139,12 +1235,12 @@
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1b29a8153575e5ad26cc7dd8bd75c4f45f6bfe7e">
|
||||
<source>Created: <x equiv-text="{{document.created | date}}" id="INTERPOLATION"/></source>
|
||||
<target>Aangemaakt op: <x equiv-text="{{document.created | date}}" id="INTERPOLATION"/></target>
|
||||
<trans-unit datatype="html" id="849b42384616374df49bd8b3711ec159cb10b845">
|
||||
<source>Created: <x equiv-text="{{document.created | customDate}}" id="INTERPOLATION"/></source>
|
||||
<target>Aangemaakt op: <x equiv-text="{{document.created | customDate}}" id="INTERPOLATION"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
||||
<context context-type="linenumber">65</context>
|
||||
<context context-type="linenumber">67</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="cd6f3fd48957e1fea6545c2b2defc7b2435ebfa8">
|
||||
@@ -1168,7 +1264,7 @@
|
||||
<target>Score:</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/document-card-large/document-card-large.component.html</context>
|
||||
<context context-type="linenumber">61</context>
|
||||
<context context-type="linenumber">62</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="2840db547019ce8c76b2cdbe3a1653c5b68b06af">
|
||||
@@ -1189,7 +1285,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8639884465898458690">
|
||||
<source>"<x equiv-text="items[0].name" id="PH"/>" and "<x equiv-text="items[1].name" id="PH_1"/>"</source>
|
||||
<target>&quot;<x equiv-text="items[0].name" id="PH"/>&quot; en &quot;<x equiv-text="items[1].name" id="PH_1"/>&quot;</target>
|
||||
<target>"<x equiv-text="items[0].name" id="PH"/>" en "<x equiv-text="items[1].name" id="PH_1"/>"</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
@@ -1198,7 +1294,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7894972847287473517">
|
||||
<source>"<x equiv-text="i.name" id="PH"/>"</source>
|
||||
<target>&quot;<x equiv-text="i.name" id="PH"/>&quot;</target>
|
||||
<target>"<x equiv-text="i.name" id="PH"/>"</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
<context context-type="linenumber">116</context>
|
||||
@@ -1215,7 +1311,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1822679894391095557">
|
||||
<source><x equiv-text="list" id="PH"/> and "<x equiv-text="items[items.length - 1].name" id="PH_1"/>"</source>
|
||||
<target><x equiv-text="list" id="PH"/> en &quot;<x equiv-text="items[items.length - 1].name" id="PH_1"/>&quot;</target>
|
||||
<target><x equiv-text="list" id="PH"/> en "<x equiv-text="items[items.length - 1].name" id="PH_1"/>"</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
<context context-type="linenumber">117</context>
|
||||
@@ -1232,7 +1328,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6619516195038467207">
|
||||
<source>This operation will add the tag "<x equiv-text="tag.name" id="PH"/>" to <x equiv-text="this.list.selected.size" id="PH_1"/> selected document(s).</source>
|
||||
<target>Het etiket &quot;<x equiv-text="tag.name" id="PH"/>&quot; zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
|
||||
<target>Het etiket "<x equiv-text="tag.name" id="PH"/>" zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
<context context-type="linenumber">129</context>
|
||||
@@ -1248,7 +1344,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7181166515756808573">
|
||||
<source>This operation will remove the tag "<x equiv-text="tag.name" id="PH"/>" from <x equiv-text="this.list.selected.size" id="PH_1"/> selected document(s).</source>
|
||||
<target>Het etiket &quot;<x equiv-text="tag.name" id="PH"/>&quot; zal verwijderd worden van <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en).</target>
|
||||
<target>Het etiket "<x equiv-text="tag.name" id="PH"/>" zal verwijderd worden van <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en).</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
<context context-type="linenumber">134</context>
|
||||
@@ -1280,7 +1376,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="6900893559485781849">
|
||||
<source>This operation will assign the correspondent "<x equiv-text="correspondent.name" id="PH"/>" to <x equiv-text="this.list.selected.size" id="PH_1"/> selected document(s).</source>
|
||||
<target>De correspondent &quot;<x equiv-text="correspondent.name" id="PH"/>&quot; zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
|
||||
<target>De correspondent "<x equiv-text="correspondent.name" id="PH"/>" zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
<context context-type="linenumber">160</context>
|
||||
@@ -1304,7 +1400,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="332180123895325027">
|
||||
<source>This operation will assign the document type "<x equiv-text="documentType.name" id="PH"/>" to <x equiv-text="this.list.selected.size" id="PH_1"/> selected document(s).</source>
|
||||
<target>Het documenttype &quot;<x equiv-text="documentType.name" id="PH"/>&quot; zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
|
||||
<target>Het documenttype "<x equiv-text="documentType.name" id="PH"/>" zal aan <x equiv-text="this.list.selected.size" id="PH_1"/> geselecteerd(e) document(en) worden toegewezen.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.ts</context>
|
||||
<context context-type="linenumber">183</context>
|
||||
@@ -1355,7 +1451,7 @@
|
||||
<target>Selecteer:</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="dfc3c34e182ea73c5d784ff7c8135f087992dac1">
|
||||
@@ -1363,7 +1459,7 @@
|
||||
<target>Alles</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="907df6a2b68daecc3c399cf40a764b358bd9fd84">
|
||||
@@ -1371,7 +1467,7 @@
|
||||
<target>Bewerk:</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
|
||||
<context context-type="linenumber">28</context>
|
||||
<context context-type="linenumber">27</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="27d158b47717ff9305d19866960418c603f19d55">
|
||||
@@ -1574,6 +1670,38 @@
|
||||
<context context-type="linenumber">12</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1206520795340730278">
|
||||
<source>English (US)</source>
|
||||
<target>Engels (US)</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1858110241312746425">
|
||||
<source>German</source>
|
||||
<target>Duits</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">75</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="3071065188816255493">
|
||||
<source>Dutch</source>
|
||||
<target>Nederlands</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7633754075223722162">
|
||||
<source>French</source>
|
||||
<target>Frans</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
|
||||
<context context-type="linenumber">77</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1519954996184640001">
|
||||
<source>Error</source>
|
||||
<target>Fout</target>
|
||||
@@ -1755,7 +1883,7 @@
|
||||
<target>Maak nieuw item</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">43</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5324147361912094446">
|
||||
@@ -1763,7 +1891,7 @@
|
||||
<target>Item bewerken</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">47</context>
|
||||
<context context-type="linenumber">54</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="1699589597032579396">
|
||||
@@ -1771,7 +1899,7 @@
|
||||
<target>Kon het element niet opslaan: <x equiv-text="error" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/common/edit-dialog/edit-dialog.component.ts</context>
|
||||
<context context-type="linenumber">51</context>
|
||||
<context context-type="linenumber">58</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="810888510148304696">
|
||||
@@ -1779,7 +1907,7 @@
|
||||
<target>Automatisch</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5044611416737085530">
|
||||
@@ -1787,7 +1915,7 @@
|
||||
<target>Wil je dit element echt verwijderen?</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">76</context>
|
||||
<context context-type="linenumber">97</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="8371896857609524947">
|
||||
@@ -1795,7 +1923,7 @@
|
||||
<target>Geassocieerde documenten zullen niet verwijderd worden.</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">83</context>
|
||||
<context context-type="linenumber">104</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="7022070615528435141">
|
||||
@@ -1803,7 +1931,7 @@
|
||||
<target>Verwijderen</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">85</context>
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5467489005440577210">
|
||||
@@ -1811,7 +1939,7 @@
|
||||
<target>Fout bij het verwijderen van het element: <x equiv-text="JSON.stringify(error.error)" id="PH"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/manage/generic-list/generic-list.component.ts</context>
|
||||
<context context-type="linenumber">93</context>
|
||||
<context context-type="linenumber">114</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="html" id="5851669019930456395">
|
||||
|
||||
@@ -91,7 +91,7 @@ def generate_unique_filename(doc, root):
|
||||
return new_filename
|
||||
|
||||
|
||||
def generate_filename(doc, counter=0):
|
||||
def generate_filename(doc, counter=0, append_gpg=True):
|
||||
path = ""
|
||||
|
||||
try:
|
||||
@@ -151,7 +151,7 @@ def generate_filename(doc, counter=0):
|
||||
filename = f"{doc.pk:07}{counter_str}{doc.file_type}"
|
||||
|
||||
# Append .gpg for encrypted files
|
||||
if doc.storage_type == doc.STORAGE_TYPE_GPG:
|
||||
if append_gpg and doc.storage_type == doc.STORAGE_TYPE_GPG:
|
||||
filename += ".gpg"
|
||||
|
||||
return filename
|
||||
|
||||
@@ -11,6 +11,7 @@ from django import db
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import transaction
|
||||
from filelock import FileLock
|
||||
from whoosh.writing import AsyncWriter
|
||||
|
||||
from documents.models import Document
|
||||
@@ -47,8 +48,10 @@ def handle_document(document_id):
|
||||
archive_checksum=checksum,
|
||||
content=parser.get_text()
|
||||
)
|
||||
create_source_path_directory(document.archive_path)
|
||||
shutil.move(parser.get_archive_path(), document.archive_path)
|
||||
with FileLock(settings.MEDIA_LOCK):
|
||||
create_source_path_directory(document.archive_path)
|
||||
shutil.move(parser.get_archive_path(),
|
||||
document.archive_path)
|
||||
|
||||
with AsyncWriter(index.open_index()) as writer:
|
||||
index.update_document(writer, document)
|
||||
|
||||
@@ -5,7 +5,6 @@ from time import sleep
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.utils.text import slugify
|
||||
from django_q.tasks import async_task
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
from watchdog.observers.polling import PollingObserver
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
|
||||
import tqdm
|
||||
from django.conf import settings
|
||||
from django.core import serializers
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import transaction
|
||||
from filelock import FileLock
|
||||
|
||||
from documents.models import Document, Correspondent, Tag, DocumentType
|
||||
from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \
|
||||
EXPORTER_ARCHIVE_NAME
|
||||
from paperless.db import GnuPG
|
||||
from ...file_handling import generate_filename, delete_empty_directories
|
||||
from ...mixins import Renderable
|
||||
|
||||
|
||||
@@ -24,13 +30,47 @@ class Command(Renderable, BaseCommand):
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument("target")
|
||||
|
||||
parser.add_argument(
|
||||
"-c", "--compare-checksums",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Compare file checksums when determining whether to export "
|
||||
"a file or not. If not specified, file size and time "
|
||||
"modified is used instead."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-f", "--use-filename-format",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Use PAPERLESS_FILENAME_FORMAT for storing files in the "
|
||||
"export directory, if configured."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-d", "--delete",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="After exporting, delete files in the export directory that "
|
||||
"do not belong to the current export, such as files from "
|
||||
"deleted documents."
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
BaseCommand.__init__(self, *args, **kwargs)
|
||||
self.target = None
|
||||
self.files_in_export_dir = []
|
||||
self.exported_files = []
|
||||
self.compare_checksums = False
|
||||
self.use_filename_format = False
|
||||
self.delete = False
|
||||
|
||||
def handle(self, *args, **options):
|
||||
|
||||
self.target = options["target"]
|
||||
self.compare_checksums = options['compare_checksums']
|
||||
self.use_filename_format = options['use_filename_format']
|
||||
self.delete = options['delete']
|
||||
|
||||
if not os.path.exists(self.target):
|
||||
raise CommandError("That path doesn't exist")
|
||||
@@ -38,83 +78,148 @@ class Command(Renderable, BaseCommand):
|
||||
if not os.access(self.target, os.W_OK):
|
||||
raise CommandError("That path doesn't appear to be writable")
|
||||
|
||||
if os.listdir(self.target):
|
||||
raise CommandError("That directory is not empty.")
|
||||
|
||||
self.dump()
|
||||
with FileLock(settings.MEDIA_LOCK):
|
||||
self.dump()
|
||||
|
||||
def dump(self):
|
||||
# 1. Take a snapshot of what files exist in the current export folder
|
||||
for root, dirs, files in os.walk(self.target):
|
||||
self.files_in_export_dir.extend(
|
||||
map(lambda f: os.path.abspath(os.path.join(root, f)), files)
|
||||
)
|
||||
|
||||
documents = Document.objects.all()
|
||||
document_map = {d.pk: d for d in documents}
|
||||
manifest = json.loads(serializers.serialize("json", documents))
|
||||
# 2. Create manifest, containing all correspondents, types, tags and
|
||||
# documents
|
||||
with transaction.atomic():
|
||||
manifest = json.loads(
|
||||
serializers.serialize("json", Correspondent.objects.all()))
|
||||
|
||||
for index, document_dict in enumerate(manifest):
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", Tag.objects.all()))
|
||||
|
||||
# Force output to unencrypted as that will be the current state.
|
||||
# The importer will make the decision to encrypt or not.
|
||||
manifest[index]["fields"]["storage_type"] = Document.STORAGE_TYPE_UNENCRYPTED # NOQA: E501
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", DocumentType.objects.all()))
|
||||
|
||||
documents = Document.objects.order_by("id")
|
||||
document_map = {d.pk: d for d in documents}
|
||||
document_manifest = json.loads(
|
||||
serializers.serialize("json", documents))
|
||||
manifest += document_manifest
|
||||
|
||||
# 3. Export files from each document
|
||||
for index, document_dict in tqdm.tqdm(enumerate(document_manifest),
|
||||
total=len(document_manifest)):
|
||||
# 3.1. store files unencrypted
|
||||
document_dict["fields"]["storage_type"] = Document.STORAGE_TYPE_UNENCRYPTED # NOQA: E501
|
||||
|
||||
document = document_map[document_dict["pk"]]
|
||||
|
||||
print(f"Exporting: {document}")
|
||||
|
||||
# 3.2. generate a unique filename
|
||||
filename_counter = 0
|
||||
while True:
|
||||
original_name = document.get_public_filename(
|
||||
counter=filename_counter)
|
||||
original_target = os.path.join(self.target, original_name)
|
||||
if self.use_filename_format:
|
||||
base_name = generate_filename(
|
||||
document, counter=filename_counter,
|
||||
append_gpg=False)
|
||||
else:
|
||||
base_name = document.get_public_filename(
|
||||
counter=filename_counter)
|
||||
|
||||
if not os.path.exists(original_target):
|
||||
if base_name not in self.exported_files:
|
||||
self.exported_files.append(base_name)
|
||||
break
|
||||
else:
|
||||
filename_counter += 1
|
||||
|
||||
thumbnail_name = original_name + "-thumbnail.png"
|
||||
thumbnail_target = os.path.join(self.target, thumbnail_name)
|
||||
|
||||
# 3.3. write filenames into manifest
|
||||
original_name = base_name
|
||||
original_target = os.path.join(self.target, original_name)
|
||||
document_dict[EXPORTER_FILE_NAME] = original_name
|
||||
|
||||
thumbnail_name = base_name + "-thumbnail.png"
|
||||
thumbnail_target = os.path.join(self.target, thumbnail_name)
|
||||
document_dict[EXPORTER_THUMBNAIL_NAME] = thumbnail_name
|
||||
|
||||
if os.path.exists(document.archive_path):
|
||||
archive_name = document.get_public_filename(
|
||||
archive=True, counter=filename_counter, suffix="_archive")
|
||||
archive_name = base_name + "-archive.pdf"
|
||||
archive_target = os.path.join(self.target, archive_name)
|
||||
document_dict[EXPORTER_ARCHIVE_NAME] = archive_name
|
||||
else:
|
||||
archive_target = None
|
||||
|
||||
# 3.4. write files to target folder
|
||||
t = int(time.mktime(document.created.timetuple()))
|
||||
if document.storage_type == Document.STORAGE_TYPE_GPG:
|
||||
|
||||
os.makedirs(os.path.dirname(original_target), exist_ok=True)
|
||||
with open(original_target, "wb") as f:
|
||||
f.write(GnuPG.decrypted(document.source_file))
|
||||
os.utime(original_target, times=(t, t))
|
||||
|
||||
os.makedirs(os.path.dirname(thumbnail_target), exist_ok=True)
|
||||
with open(thumbnail_target, "wb") as f:
|
||||
f.write(GnuPG.decrypted(document.thumbnail_file))
|
||||
os.utime(thumbnail_target, times=(t, t))
|
||||
|
||||
if archive_target:
|
||||
os.makedirs(os.path.dirname(archive_target), exist_ok=True)
|
||||
with open(archive_target, "wb") as f:
|
||||
f.write(GnuPG.decrypted(document.archive_path))
|
||||
os.utime(archive_target, times=(t, t))
|
||||
else:
|
||||
self.check_and_copy(document.source_path,
|
||||
document.checksum,
|
||||
original_target)
|
||||
|
||||
shutil.copy(document.source_path, original_target)
|
||||
shutil.copy(document.thumbnail_path, thumbnail_target)
|
||||
self.check_and_copy(document.thumbnail_path,
|
||||
None,
|
||||
thumbnail_target)
|
||||
|
||||
if archive_target:
|
||||
shutil.copy(document.archive_path, archive_target)
|
||||
self.check_and_copy(document.archive_path,
|
||||
document.archive_checksum,
|
||||
archive_target)
|
||||
|
||||
manifest += json.loads(
|
||||
serializers.serialize("json", Correspondent.objects.all()))
|
||||
# 4. write manifest to target forlder
|
||||
manifest_path = os.path.abspath(
|
||||
os.path.join(self.target, "manifest.json"))
|
||||
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", Tag.objects.all()))
|
||||
|
||||
manifest += json.loads(serializers.serialize(
|
||||
"json", DocumentType.objects.all()))
|
||||
|
||||
with open(os.path.join(self.target, "manifest.json"), "w") as f:
|
||||
with open(manifest_path, "w") as f:
|
||||
json.dump(manifest, f, indent=2)
|
||||
|
||||
if self.delete:
|
||||
# 5. Remove files which we did not explicitly export in this run
|
||||
|
||||
if manifest_path in self.files_in_export_dir:
|
||||
self.files_in_export_dir.remove(manifest_path)
|
||||
|
||||
for f in self.files_in_export_dir:
|
||||
os.remove(f)
|
||||
|
||||
delete_empty_directories(os.path.abspath(os.path.dirname(f)),
|
||||
os.path.abspath(self.target))
|
||||
|
||||
def check_and_copy(self, source, source_checksum, target):
|
||||
if os.path.abspath(target) in self.files_in_export_dir:
|
||||
self.files_in_export_dir.remove(os.path.abspath(target))
|
||||
|
||||
perform_copy = False
|
||||
|
||||
if os.path.exists(target):
|
||||
source_stat = os.stat(source)
|
||||
target_stat = os.stat(target)
|
||||
if self.compare_checksums and source_checksum:
|
||||
with open(target, "rb") as f:
|
||||
target_checksum = hashlib.md5(f.read()).hexdigest()
|
||||
perform_copy = target_checksum != source_checksum
|
||||
elif source_stat.st_mtime != target_stat.st_mtime:
|
||||
perform_copy = True
|
||||
elif source_stat.st_size != target_stat.st_size:
|
||||
perform_copy = True
|
||||
else:
|
||||
# Copy if it does not exist
|
||||
perform_copy = True
|
||||
|
||||
if perform_copy:
|
||||
os.makedirs(os.path.dirname(target), exist_ok=True)
|
||||
shutil.copy2(source, target)
|
||||
@@ -148,10 +148,10 @@ class Command(Renderable, BaseCommand):
|
||||
|
||||
create_source_path_directory(document.source_path)
|
||||
|
||||
shutil.copy(document_path, document.source_path)
|
||||
shutil.copy(thumbnail_path, document.thumbnail_path)
|
||||
shutil.copy2(document_path, document.source_path)
|
||||
shutil.copy2(thumbnail_path, document.thumbnail_path)
|
||||
if archive_path:
|
||||
create_source_path_directory(document.archive_path)
|
||||
shutil.copy(archive_path, document.archive_path)
|
||||
shutil.copy2(archive_path, document.archive_path)
|
||||
|
||||
document.save()
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
# Generated by Django 3.1.5 on 2021-01-28 15:14
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('documents', '1011_auto_20210101_2340'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CustomMetaDataField',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('type_string', models.CharField(max_length=128)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomMetaDataFieldValue',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='custom_metadata', to='documents.document')),
|
||||
('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='documents.custommetadatafield')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomMetaDataFieldBooleanValue',
|
||||
fields=[
|
||||
('value', models.BooleanField()),
|
||||
('parent', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='boolean', serialize=False, to='documents.custommetadatafieldvalue')),
|
||||
],
|
||||
bases=('documents.custommetadatafieldvalue',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomMetaDataFieldShortTextValue',
|
||||
fields=[
|
||||
('value', models.CharField(max_length=128)),
|
||||
('parent', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='shorttext', serialize=False, to='documents.custommetadatafieldvalue')),
|
||||
],
|
||||
bases=('documents.custommetadatafieldvalue',),
|
||||
),
|
||||
]
|
||||
+39
-6
@@ -63,12 +63,6 @@ class MatchingModel(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
self.match = self.match.lower()
|
||||
|
||||
models.Model.save(self, *args, **kwargs)
|
||||
|
||||
|
||||
class Correspondent(MatchingModel):
|
||||
|
||||
@@ -313,6 +307,45 @@ class Document(models.Model):
|
||||
return open(self.thumbnail_path, "rb")
|
||||
|
||||
|
||||
class CustomMetaDataField(models.Model):
|
||||
type_string = models.CharField(max_length=128)
|
||||
|
||||
|
||||
class CustomMetaDataFieldValue(models.Model):
|
||||
field = models.ForeignKey(
|
||||
CustomMetaDataField,
|
||||
blank=False,
|
||||
null=False,
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
document = models.ForeignKey(
|
||||
Document,
|
||||
blank=False,
|
||||
null=False,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="custom_metadata"
|
||||
)
|
||||
|
||||
|
||||
class CustomMetaDataFieldShortTextValue(CustomMetaDataFieldValue):
|
||||
value = models.CharField(max_length=128)
|
||||
parent = models.OneToOneField(
|
||||
CustomMetaDataFieldValue,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="shorttext",
|
||||
parent_link=True)
|
||||
|
||||
|
||||
class CustomMetaDataFieldBooleanValue(CustomMetaDataFieldValue):
|
||||
value = models.BooleanField()
|
||||
parent = models.OneToOneField(
|
||||
CustomMetaDataFieldValue,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="boolean",
|
||||
parent_link=True)
|
||||
|
||||
|
||||
class Log(models.Model):
|
||||
|
||||
LEVELS = (
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
File renamed without changes.
@@ -228,6 +228,12 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
|
||||
self.assertEqual(len(results), 2)
|
||||
self.assertCountEqual([results[0]['id'], results[1]['id']], [doc1.id, doc3.id])
|
||||
|
||||
response = self.client.get("/api/documents/?tags__id__in={},{}".format(tag_2.id, tag_3.id))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
results = response.data['results']
|
||||
self.assertEqual(len(results), 2)
|
||||
self.assertCountEqual([results[0]['id'], results[1]['id']], [doc2.id, doc3.id])
|
||||
|
||||
response = self.client.get("/api/documents/?tags__id__all={},{}".format(tag_2.id, tag_3.id))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
results = response.data['results']
|
||||
@@ -923,6 +929,14 @@ class TestBulkEdit(DirectoriesMixin, APITestCase):
|
||||
doc2 = Document.objects.get(id=self.doc2.id)
|
||||
self.assertEqual(doc2.correspondent, self.c1)
|
||||
|
||||
def test_api_no_correspondent(self):
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
"documents": [self.doc2.id],
|
||||
"method": "set_correspondent",
|
||||
"parameters": {}
|
||||
}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_api_invalid_document_type(self):
|
||||
self.assertEqual(self.doc2.document_type, self.dt1)
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
@@ -935,6 +949,14 @@ class TestBulkEdit(DirectoriesMixin, APITestCase):
|
||||
doc2 = Document.objects.get(id=self.doc2.id)
|
||||
self.assertEqual(doc2.document_type, self.dt1)
|
||||
|
||||
def test_api_no_document_type(self):
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
"documents": [self.doc2.id],
|
||||
"method": "set_document_type",
|
||||
"parameters": {}
|
||||
}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_api_add_invalid_tag(self):
|
||||
self.assertEqual(list(self.doc2.tags.all()), [self.t1])
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
@@ -946,6 +968,14 @@ class TestBulkEdit(DirectoriesMixin, APITestCase):
|
||||
|
||||
self.assertEqual(list(self.doc2.tags.all()), [self.t1])
|
||||
|
||||
def test_api_add_tag_no_tag(self):
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
"documents": [self.doc2.id],
|
||||
"method": "add_tag",
|
||||
"parameters": {}
|
||||
}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_api_delete_invalid_tag(self):
|
||||
self.assertEqual(list(self.doc2.tags.all()), [self.t1])
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
@@ -957,6 +987,14 @@ class TestBulkEdit(DirectoriesMixin, APITestCase):
|
||||
|
||||
self.assertEqual(list(self.doc2.tags.all()), [self.t1])
|
||||
|
||||
def test_api_delete_tag_no_tag(self):
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
"documents": [self.doc2.id],
|
||||
"method": "remove_tag",
|
||||
"parameters": {}
|
||||
}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_api_modify_invalid_tags(self):
|
||||
self.assertEqual(list(self.doc2.tags.all()), [self.t1])
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
@@ -966,6 +1004,21 @@ class TestBulkEdit(DirectoriesMixin, APITestCase):
|
||||
}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_api_modify_tags_no_tags(self):
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
"documents": [self.doc2.id],
|
||||
"method": "modify_tags",
|
||||
"parameters": {"remove_tags": [1123123]}
|
||||
}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
response = self.client.post("/api/documents/bulk_edit/", json.dumps({
|
||||
"documents": [self.doc2.id],
|
||||
"method": "modify_tags",
|
||||
"parameters": {'add_tags': [self.t2.id, 1657]}
|
||||
}), content_type='application/json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_api_selection_data_empty(self):
|
||||
response = self.client.post("/api/documents/selection_data/", json.dumps({
|
||||
"documents": []
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from documents.models import Document, CustomMetaDataField, CustomMetaDataFieldBooleanValue, \
|
||||
CustomMetaDataFieldShortTextValue
|
||||
|
||||
|
||||
class CustomMetadataTest(TestCase):
|
||||
|
||||
def test_typing(self):
|
||||
|
||||
doc = Document.objects.create(title="test", checksum="asd")
|
||||
|
||||
field1 = CustomMetaDataField.objects.create(type_string="boolean")
|
||||
field2 = CustomMetaDataField.objects.create(type_string="short_text")
|
||||
|
||||
CustomMetaDataFieldBooleanValue.objects.create(value=True, field=field1, document=doc)
|
||||
CustomMetaDataFieldShortTextValue.objects.create(value="test", field=field2, document=doc)
|
||||
|
||||
self.assertEqual(doc.custom_metadata.all().count(), 2)
|
||||
self.assertEqual(doc.custom_metadata.all()[0].boolean.value, True)
|
||||
self.assertEqual(doc.custom_metadata.all()[1].shorttext.value, "test")
|
||||
|
||||
def test_sorting(self):
|
||||
|
||||
doc1 = Document.objects.create(title="test", checksum="A")
|
||||
doc2 = Document.objects.create(title="test", checksum="B")
|
||||
|
||||
field = CustomMetaDataField.objects.create(type_string="short_text")
|
||||
|
||||
CustomMetaDataFieldShortTextValue.objects.create(value="testA", field=field, document=doc1)
|
||||
CustomMetaDataFieldShortTextValue.objects.create(value="testB", field=field, document=doc2)
|
||||
|
||||
docs1 = list(Document.objects.order_by("custom_metadata__shorttext__value"))
|
||||
docs2 = list(Document.objects.order_by("-custom_metadata__shorttext__value"))
|
||||
|
||||
self.assertListEqual([d.id for d in docs1], [doc1.id, doc2.id])
|
||||
self.assertListEqual([d.id for d in docs2], [doc2.id, doc1.id])
|
||||
|
||||
def test_filtering(self):
|
||||
|
||||
doc1 = Document.objects.create(title="test", checksum="A")
|
||||
doc2 = Document.objects.create(title="test", checksum="B")
|
||||
|
||||
field = CustomMetaDataField.objects.create(type_string="short_text")
|
||||
|
||||
CustomMetaDataFieldShortTextValue.objects.create(value="testA", field=field, document=doc1)
|
||||
CustomMetaDataFieldShortTextValue.objects.create(value="testB", field=field, document=doc2)
|
||||
|
||||
docs1 = list(Document.objects.filter(custom_metadata__shorttext__value__iexact="testa"))
|
||||
docs2 = list(Document.objects.filter(custom_metadata__shorttext__value__exact="testb"))
|
||||
docs3 = list(Document.objects.filter(custom_metadata__shorttext__value__icontains="TEST"))
|
||||
|
||||
self.assertListEqual([d.id for d in docs1], [doc1.id])
|
||||
self.assertListEqual([d.id for d in docs2], [])
|
||||
self.assertCountEqual([d.id for d in docs3], [doc1.id, doc2.id])
|
||||
@@ -70,18 +70,18 @@ class TestDecryptDocuments(TestCase):
|
||||
PASSPHRASE="test"
|
||||
).enable()
|
||||
|
||||
doc = Document.objects.create(checksum="9c9691e51741c1f4f41a20896af31770", title="wow", filename="0000002.pdf.gpg", mime_type="application/pdf", storage_type=Document.STORAGE_TYPE_GPG)
|
||||
doc = Document.objects.create(checksum="82186aaa94f0b98697d704b90fd1c072", title="wow", filename="0000004.pdf.gpg", mime_type="application/pdf", storage_type=Document.STORAGE_TYPE_GPG)
|
||||
|
||||
shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "originals", "0000002.pdf.gpg"), os.path.join(originals_dir, "0000002.pdf.gpg"))
|
||||
shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "thumbnails", f"0000002.png.gpg"), os.path.join(thumb_dir, f"{doc.id:07}.png.gpg"))
|
||||
shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "originals", "0000004.pdf.gpg"), os.path.join(originals_dir, "0000004.pdf.gpg"))
|
||||
shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "thumbnails", f"0000004.png.gpg"), os.path.join(thumb_dir, f"{doc.id:07}.png.gpg"))
|
||||
|
||||
call_command('decrypt_documents')
|
||||
|
||||
doc.refresh_from_db()
|
||||
|
||||
self.assertEqual(doc.storage_type, Document.STORAGE_TYPE_UNENCRYPTED)
|
||||
self.assertEqual(doc.filename, "0000002.pdf")
|
||||
self.assertTrue(os.path.isfile(os.path.join(originals_dir, "0000002.pdf")))
|
||||
self.assertEqual(doc.filename, "0000004.pdf")
|
||||
self.assertTrue(os.path.isfile(os.path.join(originals_dir, "0000004.pdf")))
|
||||
self.assertTrue(os.path.isfile(doc.source_path))
|
||||
self.assertTrue(os.path.isfile(os.path.join(thumb_dir, f"{doc.id:07}.png")))
|
||||
self.assertTrue(os.path.isfile(doc.thumbnail_path))
|
||||
|
||||
@@ -3,6 +3,8 @@ import json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from django.core.management import call_command
|
||||
from django.test import TestCase, override_settings
|
||||
@@ -10,54 +12,87 @@ from django.test import TestCase, override_settings
|
||||
from documents.management.commands import document_exporter
|
||||
from documents.models import Document, Tag, DocumentType, Correspondent
|
||||
from documents.sanity_checker import check_sanity
|
||||
from documents.settings import EXPORTER_FILE_NAME
|
||||
from documents.tests.utils import DirectoriesMixin, paperless_environment
|
||||
|
||||
|
||||
class TestExportImport(DirectoriesMixin, TestCase):
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.target = tempfile.mkdtemp()
|
||||
self.addCleanup(shutil.rmtree, self.target)
|
||||
|
||||
self.d1 = Document.objects.create(content="Content", checksum="42995833e01aea9b3edee44bbfdd7ce1", archive_checksum="62acb0bcbfbcaa62ca6ad3668e4e404b", title="wow1", filename="0000001.pdf", mime_type="application/pdf")
|
||||
self.d2 = Document.objects.create(content="Content", checksum="9c9691e51741c1f4f41a20896af31770", title="wow2", filename="0000002.pdf", mime_type="application/pdf")
|
||||
self.d3 = Document.objects.create(content="Content", checksum="d38d7ed02e988e072caf924e0f3fcb76", title="wow2", filename="0000003.pdf", mime_type="application/pdf")
|
||||
self.d4 = Document.objects.create(content="Content", checksum="82186aaa94f0b98697d704b90fd1c072", title="wow_dec", filename="0000004.pdf.gpg", mime_type="application/pdf", storage_type=Document.STORAGE_TYPE_GPG)
|
||||
|
||||
self.t1 = Tag.objects.create(name="t")
|
||||
self.dt1 = DocumentType.objects.create(name="dt")
|
||||
self.c1 = Correspondent.objects.create(name="c")
|
||||
|
||||
self.d1.tags.add(self.t1)
|
||||
self.d1.correspondent = self.c1
|
||||
self.d1.document_type = self.dt1
|
||||
self.d1.save()
|
||||
super(TestExportImport, self).setUp()
|
||||
|
||||
def _get_document_from_manifest(self, manifest, id):
|
||||
f = list(filter(lambda d: d['model'] == "documents.document" and d['pk'] == id, manifest))
|
||||
if len(f) == 1:
|
||||
return f[0]
|
||||
else:
|
||||
raise ValueError(f"document with id {id} does not exist in manifest")
|
||||
|
||||
@override_settings(
|
||||
PASSPHRASE="test"
|
||||
)
|
||||
def test_exporter(self):
|
||||
def _do_export(self, use_filename_format=False, compare_checksums=False, delete=False):
|
||||
args = ['document_exporter', self.target]
|
||||
if use_filename_format:
|
||||
args += ["--use-filename-format"]
|
||||
if compare_checksums:
|
||||
args += ["--compare-checksums"]
|
||||
if delete:
|
||||
args += ["--delete"]
|
||||
|
||||
call_command(*args)
|
||||
|
||||
with open(os.path.join(self.target, "manifest.json")) as f:
|
||||
manifest = json.load(f)
|
||||
|
||||
return manifest
|
||||
|
||||
def test_exporter(self, use_filename_format=False):
|
||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
||||
shutil.copytree(os.path.join(os.path.dirname(__file__), "samples", "documents"), os.path.join(self.dirs.media_dir, "documents"))
|
||||
|
||||
file = os.path.join(self.dirs.originals_dir, "0000001.pdf")
|
||||
manifest = self._do_export(use_filename_format=use_filename_format)
|
||||
|
||||
d1 = Document.objects.create(content="Content", checksum="42995833e01aea9b3edee44bbfdd7ce1", archive_checksum="62acb0bcbfbcaa62ca6ad3668e4e404b", title="wow", filename="0000001.pdf", mime_type="application/pdf")
|
||||
d2 = Document.objects.create(content="Content", checksum="9c9691e51741c1f4f41a20896af31770", title="wow", filename="0000002.pdf.gpg", mime_type="application/pdf", storage_type=Document.STORAGE_TYPE_GPG)
|
||||
t1 = Tag.objects.create(name="t")
|
||||
dt1 = DocumentType.objects.create(name="dt")
|
||||
c1 = Correspondent.objects.create(name="c")
|
||||
self.assertEqual(len(manifest), 7)
|
||||
self.assertEqual(len(list(filter(lambda e: e['model'] == 'documents.document', manifest))), 4)
|
||||
|
||||
d1.tags.add(t1)
|
||||
d1.correspondents = c1
|
||||
d1.document_type = dt1
|
||||
d1.save()
|
||||
d2.save()
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
|
||||
|
||||
target = tempfile.mkdtemp()
|
||||
self.addCleanup(shutil.rmtree, target)
|
||||
|
||||
call_command('document_exporter', target)
|
||||
|
||||
with open(os.path.join(target, "manifest.json")) as f:
|
||||
manifest = json.load(f)
|
||||
|
||||
self.assertEqual(len(manifest), 5)
|
||||
self.assertEqual(self._get_document_from_manifest(manifest, self.d1.id)['fields']['title'], "wow1")
|
||||
self.assertEqual(self._get_document_from_manifest(manifest, self.d2.id)['fields']['title'], "wow2")
|
||||
self.assertEqual(self._get_document_from_manifest(manifest, self.d3.id)['fields']['title'], "wow2")
|
||||
self.assertEqual(self._get_document_from_manifest(manifest, self.d4.id)['fields']['title'], "wow_dec")
|
||||
|
||||
for element in manifest:
|
||||
if element['model'] == 'documents.document':
|
||||
fname = os.path.join(target, element[document_exporter.EXPORTER_FILE_NAME])
|
||||
fname = os.path.join(self.target, element[document_exporter.EXPORTER_FILE_NAME])
|
||||
self.assertTrue(os.path.exists(fname))
|
||||
self.assertTrue(os.path.exists(os.path.join(target, element[document_exporter.EXPORTER_THUMBNAIL_NAME])))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, element[document_exporter.EXPORTER_THUMBNAIL_NAME])))
|
||||
|
||||
with open(fname, "rb") as f:
|
||||
checksum = hashlib.md5(f.read()).hexdigest()
|
||||
self.assertEqual(checksum, element['fields']['checksum'])
|
||||
|
||||
self.assertEqual(element['fields']['storage_type'], Document.STORAGE_TYPE_UNENCRYPTED)
|
||||
|
||||
if document_exporter.EXPORTER_ARCHIVE_NAME in element:
|
||||
fname = os.path.join(target, element[document_exporter.EXPORTER_ARCHIVE_NAME])
|
||||
fname = os.path.join(self.target, element[document_exporter.EXPORTER_ARCHIVE_NAME])
|
||||
self.assertTrue(os.path.exists(fname))
|
||||
|
||||
with open(fname, "rb") as f:
|
||||
@@ -65,24 +100,123 @@ class TestExportImport(DirectoriesMixin, TestCase):
|
||||
self.assertEqual(checksum, element['fields']['archive_checksum'])
|
||||
|
||||
with paperless_environment() as dirs:
|
||||
self.assertEqual(Document.objects.count(), 2)
|
||||
self.assertEqual(Document.objects.count(), 4)
|
||||
Document.objects.all().delete()
|
||||
Correspondent.objects.all().delete()
|
||||
DocumentType.objects.all().delete()
|
||||
Tag.objects.all().delete()
|
||||
self.assertEqual(Document.objects.count(), 0)
|
||||
|
||||
call_command('document_importer', target)
|
||||
self.assertEqual(Document.objects.count(), 2)
|
||||
call_command('document_importer', self.target)
|
||||
self.assertEqual(Document.objects.count(), 4)
|
||||
self.assertEqual(Tag.objects.count(), 1)
|
||||
self.assertEqual(Correspondent.objects.count(), 1)
|
||||
self.assertEqual(DocumentType.objects.count(), 1)
|
||||
self.assertEqual(Document.objects.get(id=self.d1.id).title, "wow1")
|
||||
self.assertEqual(Document.objects.get(id=self.d2.id).title, "wow2")
|
||||
self.assertEqual(Document.objects.get(id=self.d3.id).title, "wow2")
|
||||
self.assertEqual(Document.objects.get(id=self.d4.id).title, "wow_dec")
|
||||
messages = check_sanity()
|
||||
# everything is alright after the test
|
||||
self.assertEqual(len(messages), 0, str([str(m) for m in messages]))
|
||||
|
||||
@override_settings(
|
||||
PAPERLESS_FILENAME_FORMAT="{title}"
|
||||
)
|
||||
def test_exporter_with_filename_format(self):
|
||||
self.test_exporter()
|
||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
||||
shutil.copytree(os.path.join(os.path.dirname(__file__), "samples", "documents"), os.path.join(self.dirs.media_dir, "documents"))
|
||||
|
||||
with override_settings(PAPERLESS_FILENAME_FORMAT="{created_year}/{correspondent}/{title}"):
|
||||
self.test_exporter(use_filename_format=True)
|
||||
|
||||
def test_update_export_changed_time(self):
|
||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
||||
shutil.copytree(os.path.join(os.path.dirname(__file__), "samples", "documents"), os.path.join(self.dirs.media_dir, "documents"))
|
||||
|
||||
self._do_export()
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
|
||||
|
||||
st_mtime_1 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
|
||||
|
||||
with mock.patch("documents.management.commands.document_exporter.shutil.copy2") as m:
|
||||
self._do_export()
|
||||
m.assert_not_called()
|
||||
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
|
||||
st_mtime_2 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
|
||||
|
||||
Path(self.d1.source_path).touch()
|
||||
|
||||
with mock.patch("documents.management.commands.document_exporter.shutil.copy2") as m:
|
||||
self._do_export()
|
||||
self.assertEqual(m.call_count, 1)
|
||||
|
||||
st_mtime_3 = os.stat(os.path.join(self.target, "manifest.json")).st_mtime
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
|
||||
|
||||
self.assertNotEqual(st_mtime_1, st_mtime_2)
|
||||
self.assertNotEqual(st_mtime_2, st_mtime_3)
|
||||
|
||||
def test_update_export_changed_checksum(self):
|
||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
||||
shutil.copytree(os.path.join(os.path.dirname(__file__), "samples", "documents"), os.path.join(self.dirs.media_dir, "documents"))
|
||||
|
||||
self._do_export()
|
||||
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
|
||||
|
||||
with mock.patch("documents.management.commands.document_exporter.shutil.copy2") as m:
|
||||
self._do_export()
|
||||
m.assert_not_called()
|
||||
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
|
||||
|
||||
self.d2.checksum = "asdfasdgf3"
|
||||
self.d2.save()
|
||||
|
||||
with mock.patch("documents.management.commands.document_exporter.shutil.copy2") as m:
|
||||
self._do_export(compare_checksums=True)
|
||||
self.assertEqual(m.call_count, 1)
|
||||
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
|
||||
|
||||
def test_update_export_deleted_document(self):
|
||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
||||
shutil.copytree(os.path.join(os.path.dirname(__file__), "samples", "documents"), os.path.join(self.dirs.media_dir, "documents"))
|
||||
|
||||
manifest = self._do_export()
|
||||
|
||||
self.assertTrue(len(manifest), 7)
|
||||
doc_from_manifest = self._get_document_from_manifest(manifest, self.d3.id)
|
||||
self.assertTrue(os.path.isfile(os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME])))
|
||||
self.d3.delete()
|
||||
|
||||
manifest = self._do_export()
|
||||
self.assertRaises(ValueError, self._get_document_from_manifest, manifest, self.d3.id)
|
||||
self.assertTrue(os.path.isfile(os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME])))
|
||||
|
||||
manifest = self._do_export(delete=True)
|
||||
self.assertFalse(os.path.isfile(os.path.join(self.target, doc_from_manifest[EXPORTER_FILE_NAME])))
|
||||
|
||||
self.assertTrue(len(manifest), 6)
|
||||
|
||||
@override_settings(PAPERLESS_FILENAME_FORMAT="{title}/{correspondent}")
|
||||
def test_update_export_changed_location(self):
|
||||
shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
|
||||
shutil.copytree(os.path.join(os.path.dirname(__file__), "samples", "documents"), os.path.join(self.dirs.media_dir, "documents"))
|
||||
|
||||
m = self._do_export(use_filename_format=True)
|
||||
self.assertTrue(os.path.isfile(os.path.join(self.target, "wow1", "c.pdf")))
|
||||
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
|
||||
|
||||
self.d1.title = "new_title"
|
||||
self.d1.save()
|
||||
self._do_export(use_filename_format=True, delete=True)
|
||||
self.assertFalse(os.path.isfile(os.path.join(self.target, "wow1", "c.pdf")))
|
||||
self.assertFalse(os.path.isdir(os.path.join(self.target, "wow1")))
|
||||
self.assertTrue(os.path.isfile(os.path.join(self.target, "new_title", "c.pdf")))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.target, "manifest.json")))
|
||||
self.assertTrue(os.path.isfile(os.path.join(self.target, "wow2", "none.pdf")))
|
||||
self.assertTrue(os.path.isfile(os.path.join(self.target, "wow2", "none_01.pdf")))
|
||||
|
||||
def test_export_missing_files(self):
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ class TestMigrateMimeType(DirectoriesMixin, TestMigrations):
|
||||
|
||||
doc2 = Document.objects.create(checksum="B", file_type="pdf", storage_type=STORAGE_TYPE_GPG)
|
||||
self.doc2_id = doc2.id
|
||||
shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "originals", "0000002.pdf.gpg"), source_path_before(doc2))
|
||||
shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "originals", "0000004.pdf.gpg"), source_path_before(doc2))
|
||||
|
||||
def testMimeTypesMigrated(self):
|
||||
Document = self.apps.get_model('documents', 'Document')
|
||||
|
||||
@@ -120,3 +120,4 @@ class TestParserAvailability(TestCase):
|
||||
|
||||
self.assertTrue(is_file_ext_supported('.pdf'))
|
||||
self.assertFalse(is_file_ext_supported('.hsdfh'))
|
||||
self.assertFalse(is_file_ext_supported(''))
|
||||
@@ -20,7 +20,7 @@ class TestSettings(TestCase):
|
||||
self.assertEqual(default_threads, 1)
|
||||
|
||||
def test_workers_threads(self):
|
||||
for i in range(2, 64):
|
||||
for i in range(1, 64):
|
||||
with mock.patch("paperless.settings.multiprocessing.cpu_count") as cpu_count:
|
||||
cpu_count.return_value = i
|
||||
|
||||
@@ -31,4 +31,4 @@ class TestSettings(TestCase):
|
||||
self.assertTrue(default_workers >= 1)
|
||||
self.assertTrue(default_threads >= 1)
|
||||
|
||||
self.assertTrue(default_workers * default_threads < i, f"{i}")
|
||||
self.assertTrue(default_workers * default_threads <= i, f"{i}")
|
||||
@@ -159,6 +159,9 @@ class DocumentViewSet(RetrieveModelMixin,
|
||||
"added",
|
||||
"archive_serial_number")
|
||||
|
||||
def get_queryset(self):
|
||||
return Document.objects.distinct()
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
fields_param = self.request.query_params.get('fields', None)
|
||||
if fields_param:
|
||||
|
||||
@@ -354,8 +354,10 @@ LOGGING = {
|
||||
|
||||
def default_task_workers():
|
||||
# always leave one core open
|
||||
available_cores = max(multiprocessing.cpu_count() - 1, 1)
|
||||
available_cores = max(multiprocessing.cpu_count(), 1)
|
||||
try:
|
||||
if available_cores < 4:
|
||||
return available_cores
|
||||
return max(
|
||||
math.floor(math.sqrt(available_cores)),
|
||||
1
|
||||
@@ -376,7 +378,7 @@ Q_CLUSTER = {
|
||||
|
||||
def default_threads_per_worker(task_workers):
|
||||
# always leave one core open
|
||||
available_cores = max(multiprocessing.cpu_count() - 1, 1)
|
||||
available_cores = max(multiprocessing.cpu_count(), 1)
|
||||
try:
|
||||
return max(
|
||||
math.floor(available_cores / task_workers),
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = (0, 9, 14)
|
||||
__version__ = (1, 0, 0)
|
||||
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
import uuid
|
||||
from collections import namedtuple
|
||||
from typing import ContextManager
|
||||
@@ -9,6 +10,7 @@ from django.test import TestCase
|
||||
from imap_tools import MailMessageFlags, MailboxFolderSelectError
|
||||
|
||||
from documents.models import Correspondent
|
||||
from documents.tests.utils import DirectoriesMixin
|
||||
from paperless_mail import tasks
|
||||
from paperless_mail.mail import MailError, MailAccountHandler
|
||||
from paperless_mail.models import MailRule, MailAccount
|
||||
@@ -130,7 +132,7 @@ def fake_magic_from_buffer(buffer, mime=False):
|
||||
|
||||
|
||||
@mock.patch('paperless_mail.mail.magic.from_buffer', fake_magic_from_buffer)
|
||||
class TestMail(TestCase):
|
||||
class TestMail(DirectoriesMixin, TestCase):
|
||||
|
||||
def setUp(self):
|
||||
patcher = mock.patch('paperless_mail.mail.MailBox')
|
||||
@@ -146,6 +148,7 @@ class TestMail(TestCase):
|
||||
self.reset_bogus_mailbox()
|
||||
|
||||
self.mail_account_handler = MailAccountHandler()
|
||||
super(TestMail, self).setUp()
|
||||
|
||||
def reset_bogus_mailbox(self):
|
||||
self.bogus_mailbox.messages = []
|
||||
@@ -220,9 +223,13 @@ class TestMail(TestCase):
|
||||
args1, kwargs1 = self.async_task.call_args_list[0]
|
||||
args2, kwargs2 = self.async_task.call_args_list[1]
|
||||
|
||||
self.assertTrue(os.path.isfile(kwargs1['path']), kwargs1['path'])
|
||||
|
||||
self.assertEqual(kwargs1['override_title'], "file_0")
|
||||
self.assertEqual(kwargs1['override_filename'], "file_0.pdf")
|
||||
|
||||
self.assertTrue(os.path.isfile(kwargs2['path']), kwargs1['path'])
|
||||
|
||||
self.assertEqual(kwargs2['override_title'], "file_1")
|
||||
self.assertEqual(kwargs2['override_filename'], "file_1.pdf")
|
||||
|
||||
@@ -253,6 +260,7 @@ class TestMail(TestCase):
|
||||
self.assertEqual(self.async_task.call_count, 1)
|
||||
|
||||
args, kwargs = self.async_task.call_args
|
||||
self.assertTrue(os.path.isfile(kwargs['path']), kwargs['path'])
|
||||
self.assertEqual(kwargs['override_filename'], "f1.pdf")
|
||||
|
||||
def test_handle_disposition(self):
|
||||
|
||||
Reference in new issue
Block a user