Compare commits

...

11 Commits

Author SHA1 Message Date
shamoon
dc58a5673b Document index accent folding 2025-06-15 10:11:21 -07:00
Michael Hobl
cbd9823ad6 Documentation: correct Gotenberg API timeout value (#10186) 2025-06-15 07:36:49 -07:00
shamoon
ce76303a32 Feature: add Persian translation (#10183) 2025-06-14 19:14:51 -07:00
Kilian
246f17c6c8 Enhancement: support import of zipped export (#10073) 2025-06-13 10:06:37 -07:00
shamoon
4313635b01 Revert "Development: fix dependabot by specifying pnpm version (#10103)"
This reverts commit 9c9a0e4496.
2025-06-10 09:19:00 -07:00
shamoon
7ca2bd0666 Fix: fix mail account test api schema (#10164) 2025-06-10 07:09:24 -07:00
shamoon
4c6075e962 Fix: correct api schema for mail_account process (#10157) 2025-06-09 14:51:58 -07:00
shamoon
8d48e99487 Fix: correct api schema for next_asn (#10151) 2025-06-09 09:32:34 -07:00
shamoon
454a2d9e9e Fix: fix email and notes endpoints api spec (#10148) 2025-06-09 07:09:28 -07:00
shamoon
b8c713d4b9 Chore: get CI running on crowdin PRs 2025-06-06 00:29:06 -07:00
github-actions[bot]
2a8cb87232 Changelog v2.16.3 - GHA (#10129)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2025-06-05 14:41:33 -07:00
19 changed files with 247 additions and 84 deletions

View File

@@ -162,7 +162,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
package_json_file: 'src-ui/package.json' version: 10
- name: Use Node.js 20 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
@@ -195,7 +195,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
package_json_file: 'src-ui/package.json' version: 10
- name: Use Node.js 20 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
@@ -245,7 +245,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
package_json_file: 'src-ui/package.json' version: 10
- name: Use Node.js 20 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
@@ -288,7 +288,7 @@ jobs:
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v4 uses: pnpm/action-setup@v4
with: with:
package_json_file: 'src-ui/package.json' version: 10
- name: Use Node.js 20 - name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:

View File

@@ -14,6 +14,8 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
token: ${{ secrets.PNGX_BOT_PAT }}
- name: crowdin action - name: crowdin action
uses: crowdin/github-action@v2 uses: crowdin/github-action@v2
with: with:

View File

@@ -333,7 +333,7 @@ must be provided to import. If this value is lost, the export cannot be imported
The document importer takes the export produced by the [Document The document importer takes the export produced by the [Document
exporter](#exporter) and imports it into paperless. exporter](#exporter) and imports it into paperless.
The importer works just like the exporter. You point it at a directory, The importer works just like the exporter. You point it at a directory or the generated .zip file,
and the script does the rest of the work: and the script does the rest of the work:
```shell ```shell
@@ -351,9 +351,6 @@ When you use the provided docker compose script, put the export inside
the `export` folder in your paperless source directory. Specify the `export` folder in your paperless source directory. Specify
`../export` as the `source`. `../export` as the `source`.
Note that .zip files (as can be generated from the exporter) are not supported. You must unzip them into
the target directory first.
!!! note !!! note
Importing from a previous version of Paperless may work, but for best Importing from a previous version of Paperless may work, but for best

View File

@@ -1,5 +1,61 @@
# Changelog # Changelog
## paperless-ngx 2.16.3
### Features / Enhancements
- Performance: pre-filter document list in scheduled workflow checks [@shamoon](https://github.com/shamoon) ([#10031](https://github.com/paperless-ngx/paperless-ngx/pull/10031))
- Chore: refactor consumer plugin checks to a pre-flight plugin [@shamoon](https://github.com/shamoon) ([#9994](https://github.com/paperless-ngx/paperless-ngx/pull/9994))
- Enhancement: include DOCUMENT_TYPE to post consume scripts [@matthesrieke](https://github.com/matthesrieke) ([#9977](https://github.com/paperless-ngx/paperless-ngx/pull/9977))
### Bug Fixes
- Fix: handle whoosh query correction errors [@shamoon](https://github.com/shamoon) ([#10121](https://github.com/paperless-ngx/paperless-ngx/pull/10121))
- Fix: handle favicon with non-default static dir [@shamoon](https://github.com/shamoon) ([#10107](https://github.com/paperless-ngx/paperless-ngx/pull/10107))
- Fixhancement: cleanup user or group references in settings upon deletion [@shamoon](https://github.com/shamoon) ([#10049](https://github.com/paperless-ngx/paperless-ngx/pull/10049))
- Fix: Add exception to in [@Freilichtbuehne](https://github.com/Freilichtbuehne) ([#10070](https://github.com/paperless-ngx/paperless-ngx/pull/10070))
- Fix: include base href when opening global search result in new window [@shamoon](https://github.com/shamoon) ([#10066](https://github.com/paperless-ngx/paperless-ngx/pull/10066))
### Dependencies
<details>
<summary>10 changes</summary>
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10085](https://github.com/paperless-ngx/paperless-ngx/pull/10085))
- Chore(deps): Update granian[uvloop] requirement from ~=2.2.0 to ~=2.3.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10055](https://github.com/paperless-ngx/paperless-ngx/pull/10055))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 18 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10099](https://github.com/paperless-ngx/paperless-ngx/pull/10099))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10100](https://github.com/paperless-ngx/paperless-ngx/pull/10100))
- Chore(deps): Bump bootstrap from 5.3.3 to 5.3.6 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10091](https://github.com/paperless-ngx/paperless-ngx/pull/10091))
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.9.0 to 1.9.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10090](https://github.com/paperless-ngx/paperless-ngx/pull/10090))
- Chore(deps-dev): Bump @types/node from 22.15.3 to 22.15.29 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10089](https://github.com/paperless-ngx/paperless-ngx/pull/10089))
- Chore(deps): Bump zone.js from 0.15.0 to 0.15.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10088](https://github.com/paperless-ngx/paperless-ngx/pull/10088))
- docker(deps): bump astral-sh/uv from 0.7.8-python3.12-bookworm-slim to 0.7.9-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10084](https://github.com/paperless-ngx/paperless-ngx/pull/10084))
- docker(deps): Bump astral-sh/uv from 0.6.16-python3.12-bookworm-slim to 0.7.8-python3.12-bookworm-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#10056](https://github.com/paperless-ngx/paperless-ngx/pull/10056))
</details>
### All App Changes
<details>
<summary>16 changes</summary>
- Fix: handle whoosh query correction errors [@shamoon](https://github.com/shamoon) ([#10121](https://github.com/paperless-ngx/paperless-ngx/pull/10121))
- Performance: pre-filter document list in scheduled workflow checks [@shamoon](https://github.com/shamoon) ([#10031](https://github.com/paperless-ngx/paperless-ngx/pull/10031))
- Chore(deps): Bump the small-changes group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10085](https://github.com/paperless-ngx/paperless-ngx/pull/10085))
- Chore: refactor consumer plugin checks to a pre-flight plugin [@shamoon](https://github.com/shamoon) ([#9994](https://github.com/paperless-ngx/paperless-ngx/pull/9994))
- Chore(deps): Update granian[uvloop] requirement from ~=2.2.0 to ~=2.3.2 @[dependabot[bot]](https://github.com/apps/dependabot) ([#10055](https://github.com/paperless-ngx/paperless-ngx/pull/10055))
- Fix: handle favicon with non-default static dir [@shamoon](https://github.com/shamoon) ([#10107](https://github.com/paperless-ngx/paperless-ngx/pull/10107))
- Chore(deps): Bump the frontend-angular-dependencies group in /src-ui with 18 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10099](https://github.com/paperless-ngx/paperless-ngx/pull/10099))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group in /src-ui with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#10100](https://github.com/paperless-ngx/paperless-ngx/pull/10100))
- Chore(deps): Bump bootstrap from 5.3.3 to 5.3.6 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10091](https://github.com/paperless-ngx/paperless-ngx/pull/10091))
- Chore(deps-dev): Bump @codecov/webpack-plugin from 1.9.0 to 1.9.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10090](https://github.com/paperless-ngx/paperless-ngx/pull/10090))
- Chore(deps-dev): Bump @types/node from 22.15.3 to 22.15.29 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10089](https://github.com/paperless-ngx/paperless-ngx/pull/10089))
- Chore(deps): Bump zone.js from 0.15.0 to 0.15.1 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#10088](https://github.com/paperless-ngx/paperless-ngx/pull/10088))
- Fixhancement: cleanup user or group references in settings upon deletion [@shamoon](https://github.com/shamoon) ([#10049](https://github.com/paperless-ngx/paperless-ngx/pull/10049))
- Enhancement: include DOCUMENT_TYPE to post consume scripts [@matthesrieke](https://github.com/matthesrieke) ([#9977](https://github.com/paperless-ngx/paperless-ngx/pull/9977))
- Fix: Add exception to in [@Freilichtbuehne](https://github.com/Freilichtbuehne) ([#10070](https://github.com/paperless-ngx/paperless-ngx/pull/10070))
- Fix: include base href when opening global search result in new window [@shamoon](https://github.com/shamoon) ([#10066](https://github.com/paperless-ngx/paperless-ngx/pull/10066))
</details>
## paperless-ngx 2.16.2 ## paperless-ngx 2.16.2
### Bug Fixes ### Bug Fixes

View File

@@ -130,7 +130,7 @@ command:
- 'gotenberg' - 'gotenberg'
- '--chromium-disable-javascript=true' - '--chromium-disable-javascript=true'
- '--chromium-allow-list=file:///tmp/.*' - '--chromium-allow-list=file:///tmp/.*'
- '--api-timeout=60' - '--api-timeout=60s'
``` ```
## Permission denied errors in the consumption directory ## Permission denied errors in the consumption directory

View File

@@ -27,6 +27,7 @@
"el-GR": "src/locale/messages.el_GR.xlf", "el-GR": "src/locale/messages.el_GR.xlf",
"en-GB": "src/locale/messages.en_GB.xlf", "en-GB": "src/locale/messages.en_GB.xlf",
"es-ES": "src/locale/messages.es_ES.xlf", "es-ES": "src/locale/messages.es_ES.xlf",
"fa-IR": "src/locale/messages.fa_IR.xlf",
"fi-FI": "src/locale/messages.fi_FI.xlf", "fi-FI": "src/locale/messages.fi_FI.xlf",
"fr-FR": "src/locale/messages.fr_FR.xlf", "fr-FR": "src/locale/messages.fr_FR.xlf",
"hu-HU": "src/locale/messages.hu_HU.xlf", "hu-HU": "src/locale/messages.hu_HU.xlf",

View File

@@ -9805,123 +9805,130 @@
<context context-type="linenumber">171</context> <context context-type="linenumber">171</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4977087909184008115" datatype="html">
<source>Persian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">177</context>
</context-group>
</trans-unit>
<trans-unit id="792060551707690640" datatype="html"> <trans-unit id="792060551707690640" datatype="html">
<source>Polish</source> <source>Polish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">177</context> <context context-type="linenumber">183</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="9184513005098760425" datatype="html"> <trans-unit id="9184513005098760425" datatype="html">
<source>Portuguese (Brazil)</source> <source>Portuguese (Brazil)</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">183</context> <context context-type="linenumber">189</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="153799456510623899" datatype="html"> <trans-unit id="153799456510623899" datatype="html">
<source>Portuguese</source> <source>Portuguese</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">189</context> <context context-type="linenumber">195</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8118856427047826368" datatype="html"> <trans-unit id="8118856427047826368" datatype="html">
<source>Romanian</source> <source>Romanian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">201</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="7137419789978325708" datatype="html"> <trans-unit id="7137419789978325708" datatype="html">
<source>Russian</source> <source>Russian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">207</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="9102963095355753902" datatype="html"> <trans-unit id="9102963095355753902" datatype="html">
<source>Slovak</source> <source>Slovak</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">207</context> <context context-type="linenumber">213</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4287008301409320881" datatype="html"> <trans-unit id="4287008301409320881" datatype="html">
<source>Slovenian</source> <source>Slovenian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">213</context> <context context-type="linenumber">219</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8608389829607915090" datatype="html"> <trans-unit id="8608389829607915090" datatype="html">
<source>Serbian</source> <source>Serbian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">219</context> <context context-type="linenumber">225</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="499386805970351976" datatype="html"> <trans-unit id="499386805970351976" datatype="html">
<source>Swedish</source> <source>Swedish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">225</context> <context context-type="linenumber">231</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5682359291233237791" datatype="html"> <trans-unit id="5682359291233237791" datatype="html">
<source>Turkish</source> <source>Turkish</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">231</context> <context context-type="linenumber">237</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3578644052206125685" datatype="html"> <trans-unit id="3578644052206125685" datatype="html">
<source>Ukrainian</source> <source>Ukrainian</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">237</context> <context context-type="linenumber">243</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4689443708886954687" datatype="html"> <trans-unit id="4689443708886954687" datatype="html">
<source>Chinese Simplified</source> <source>Chinese Simplified</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">243</context> <context context-type="linenumber">249</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8082606363137705994" datatype="html"> <trans-unit id="8082606363137705994" datatype="html">
<source>Chinese Traditional</source> <source>Chinese Traditional</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">249</context> <context context-type="linenumber">255</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4912706592792948707" datatype="html"> <trans-unit id="4912706592792948707" datatype="html">
<source>ISO 8601</source> <source>ISO 8601</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">257</context> <context context-type="linenumber">263</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="313643372755303297" datatype="html"> <trans-unit id="313643372755303297" datatype="html">
<source>Successfully completed one-time migratration of settings to the database!</source> <source>Successfully completed one-time migratration of settings to the database!</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">590</context> <context context-type="linenumber">596</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5558341108007064934" datatype="html"> <trans-unit id="5558341108007064934" datatype="html">
<source>Unable to migrate settings to the database, please try saving manually.</source> <source>Unable to migrate settings to the database, please try saving manually.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">591</context> <context context-type="linenumber">597</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="1168781785897678748" datatype="html"> <trans-unit id="1168781785897678748" datatype="html">
<source>You can restart the tour from the settings page.</source> <source>You can restart the tour from the settings page.</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context> <context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">664</context> <context context-type="linenumber">670</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3852289441366561594" datatype="html"> <trans-unit id="3852289441366561594" datatype="html">

View File

@@ -69,13 +69,6 @@
"ts-node": "~10.9.1", "ts-node": "~10.9.1",
"typescript": "^5.5.4" "typescript": "^5.5.4"
}, },
"packageManager": "pnpm@10.11.1",
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": "10.11.1"
}
},
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
"@parcel/watcher", "@parcel/watcher",

View File

@@ -20,6 +20,7 @@ import localeDe from '@angular/common/locales/de'
import localeEl from '@angular/common/locales/el' import localeEl from '@angular/common/locales/el'
import localeEnGb from '@angular/common/locales/en-GB' import localeEnGb from '@angular/common/locales/en-GB'
import localeEs from '@angular/common/locales/es' import localeEs from '@angular/common/locales/es'
import localeFa from '@angular/common/locales/fa'
import localeFi from '@angular/common/locales/fi' import localeFi from '@angular/common/locales/fi'
import localeFr from '@angular/common/locales/fr' import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu' import localeHu from '@angular/common/locales/hu'
@@ -53,6 +54,7 @@ registerLocaleData(localeDe)
registerLocaleData(localeEl) registerLocaleData(localeEl)
registerLocaleData(localeEnGb) registerLocaleData(localeEnGb)
registerLocaleData(localeEs) registerLocaleData(localeEs)
registerLocaleData(localeFa)
registerLocaleData(localeFi) registerLocaleData(localeFi)
registerLocaleData(localeFr) registerLocaleData(localeFr)
registerLocaleData(localeHu) registerLocaleData(localeHu)

View File

@@ -172,6 +172,12 @@ const LANGUAGE_OPTIONS = [
englishName: 'Norwegian', englishName: 'Norwegian',
dateInputFormat: 'dd.mm.yyyy', dateInputFormat: 'dd.mm.yyyy',
}, },
{
code: 'fa-ir',
name: $localize`Persian`,
englishName: 'Persian',
dateInputFormat: 'yyyy-mm-dd',
},
{ {
code: 'pl-pl', code: 'pl-pl',
name: $localize`Polish`, name: $localize`Polish`,

View File

@@ -162,6 +162,7 @@ import localeDe from '@angular/common/locales/de'
import localeEl from '@angular/common/locales/el' import localeEl from '@angular/common/locales/el'
import localeEnGb from '@angular/common/locales/en-GB' import localeEnGb from '@angular/common/locales/en-GB'
import localeEs from '@angular/common/locales/es' import localeEs from '@angular/common/locales/es'
import localeFa from '@angular/common/locales/fa'
import localeFi from '@angular/common/locales/fi' import localeFi from '@angular/common/locales/fi'
import localeFr from '@angular/common/locales/fr' import localeFr from '@angular/common/locales/fr'
import localeHu from '@angular/common/locales/hu' import localeHu from '@angular/common/locales/hu'
@@ -198,6 +199,7 @@ registerLocaleData(localeDe)
registerLocaleData(localeEl) registerLocaleData(localeEl)
registerLocaleData(localeEnGb) registerLocaleData(localeEnGb)
registerLocaleData(localeEs) registerLocaleData(localeEs)
registerLocaleData(localeFa)
registerLocaleData(localeFi) registerLocaleData(localeFi)
registerLocaleData(localeFr) registerLocaleData(localeFr)
registerLocaleData(localeHu) registerLocaleData(localeHu)

View File

@@ -17,6 +17,8 @@ from guardian.shortcuts import get_users_with_perms
from whoosh import classify from whoosh import classify
from whoosh import highlight from whoosh import highlight
from whoosh import query from whoosh import query
from whoosh.analysis import CharsetFilter
from whoosh.analysis import StemmingAnalyzer
from whoosh.fields import BOOLEAN from whoosh.fields import BOOLEAN
from whoosh.fields import DATETIME from whoosh.fields import DATETIME
from whoosh.fields import KEYWORD from whoosh.fields import KEYWORD
@@ -36,6 +38,7 @@ from whoosh.qparser.dateparse import DateParserPlugin
from whoosh.qparser.dateparse import English from whoosh.qparser.dateparse import English
from whoosh.qparser.plugins import FieldsPlugin from whoosh.qparser.plugins import FieldsPlugin
from whoosh.scoring import TF_IDF from whoosh.scoring import TF_IDF
from whoosh.support.charset import accent_map
from whoosh.util.times import timespan from whoosh.util.times import timespan
from whoosh.writing import AsyncWriter from whoosh.writing import AsyncWriter
@@ -54,10 +57,13 @@ logger = logging.getLogger("paperless.index")
def get_schema() -> Schema: def get_schema() -> Schema:
# add accent-folding filter to a stemming analyzer:
af_analyzer = StemmingAnalyzer() | CharsetFilter(accent_map)
return Schema( return Schema(
id=NUMERIC(stored=True, unique=True), id=NUMERIC(stored=True, unique=True),
title=TEXT(sortable=True), title=TEXT(sortable=True, analyzer=af_analyzer),
content=TEXT(), content=TEXT(analyzer=af_analyzer),
asn=NUMERIC(sortable=True, signed=False), asn=NUMERIC(sortable=True, signed=False),
correspondent=TEXT(sortable=True), correspondent=TEXT(sortable=True),
correspondent_id=NUMERIC(), correspondent_id=NUMERIC(),

View File

@@ -1,9 +1,12 @@
import json import json
import logging import logging
import os import os
import tempfile
from collections.abc import Generator from collections.abc import Generator
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from zipfile import ZipFile
from zipfile import is_zipfile
import tqdm import tqdm
from django.conf import settings from django.conf import settings
@@ -234,14 +237,19 @@ class Command(CryptMixin, BaseCommand):
self.manifest_paths = [] self.manifest_paths = []
self.manifest = [] self.manifest = []
# Create a temporary directory for extracting a zip file into it, even if supplied source is no zip file to keep code cleaner.
with tempfile.TemporaryDirectory() as tmp_dir:
if is_zipfile(self.source):
with ZipFile(self.source) as zf:
zf.extractall(tmp_dir)
self.source = Path(tmp_dir)
self._run_import()
def _run_import(self):
self.pre_check() self.pre_check()
self.load_metadata() self.load_metadata()
self.load_manifest_files() self.load_manifest_files()
self.check_manifest_validity() self.check_manifest_validity()
self.decrypt_secret_fields() self.decrypt_secret_fields()
# see /src/documents/signals/handlers.py # see /src/documents/signals/handlers.py

View File

@@ -557,7 +557,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
response = self.client.get("/api/search/autocomplete/?term=app") response = self.client.get("/api/search/autocomplete/?term=app")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, [b"apples", b"applebaum", b"appletini"]) self.assertEqual(response.data, [b"appl", b"applebaum", b"appletini"])
d3.owner = u2 d3.owner = u2
@@ -566,7 +566,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
response = self.client.get("/api/search/autocomplete/?term=app") response = self.client.get("/api/search/autocomplete/?term=app")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, [b"apples", b"applebaum"]) self.assertEqual(response.data, [b"appl", b"applebaum"])
assign_perm("view_document", u1, d3) assign_perm("view_document", u1, d3)
@@ -575,7 +575,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
response = self.client.get("/api/search/autocomplete/?term=app") response = self.client.get("/api/search/autocomplete/?term=app")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, [b"apples", b"applebaum", b"appletini"]) self.assertEqual(response.data, [b"appl", b"applebaum", b"appletini"])
def test_search_autocomplete_field_name_match(self): def test_search_autocomplete_field_name_match(self):
""" """

View File

@@ -2,6 +2,7 @@ import json
import tempfile import tempfile
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
from zipfile import ZipFile
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management import call_command from django.core.management import call_command
@@ -335,3 +336,42 @@ class TestCommandImport(
self.assertIn("Version mismatch:", stdout_str) self.assertIn("Version mismatch:", stdout_str)
self.assertIn("importing 2.8.1", stdout_str) self.assertIn("importing 2.8.1", stdout_str)
def test_import_zipped_export(self):
"""
GIVEN:
- A zip file with correct content (manifest.json and version.json inside)
WHEN:
- An import is attempted using the zip file as the source
THEN:
- The command reads from the zip without warnings or errors
"""
stdout = StringIO()
zip_path = self.dirs.scratch_dir / "export.zip"
# Create manifest.json and version.json in a temp dir
with tempfile.TemporaryDirectory() as temp_dir:
temp_dir_path = Path(temp_dir)
(temp_dir_path / "manifest.json").touch()
(temp_dir_path / "version.json").touch()
# Create the zip file
with ZipFile(zip_path, "w") as zf:
zf.write(temp_dir_path / "manifest.json", arcname="manifest.json")
zf.write(temp_dir_path / "version.json", arcname="version.json")
# Try to import from the zip file
with self.assertRaises(json.decoder.JSONDecodeError):
call_command(
"document_importer",
"--no-progress-bar",
str(zip_path),
stdout=stdout,
)
stdout.seek(0)
stdout_str = str(stdout.read())
# There should be no error or warnings. Therefore the output should be empty.
self.assertEqual(stdout_str, "")

View File

@@ -145,6 +145,7 @@ from documents.serialisers import CustomFieldSerializer
from documents.serialisers import DocumentListSerializer from documents.serialisers import DocumentListSerializer
from documents.serialisers import DocumentSerializer from documents.serialisers import DocumentSerializer
from documents.serialisers import DocumentTypeSerializer from documents.serialisers import DocumentTypeSerializer
from documents.serialisers import NotesSerializer
from documents.serialisers import PostDocumentSerializer from documents.serialisers import PostDocumentSerializer
from documents.serialisers import RunTaskViewSerializer from documents.serialisers import RunTaskViewSerializer
from documents.serialisers import SavedViewSerializer from documents.serialisers import SavedViewSerializer
@@ -433,27 +434,24 @@ class DocumentTypeViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin):
), ),
notes=extend_schema( notes=extend_schema(
description="View, add, or delete notes for the document", description="View, add, or delete notes for the document",
responses={ methods=["GET", "POST", "DELETE"],
200: { request=inline_serializer(
"type": "array", name="NoteCreateRequest",
"items": { fields={
"type": "object", "note": serializers.CharField(),
"properties": {
"id": {"type": "integer"},
"note": {"type": "string"},
"created": {"type": "string", "format": "date-time"},
"user": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"username": {"type": "string"},
"first_name": {"type": "string"},
"last_name": {"type": "string"},
},
},
},
},
}, },
),
parameters=[
OpenApiParameter(
name="id",
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
required=False,
description="Note ID to delete (used only for DELETE requests)",
),
],
responses={
200: NotesSerializer(many=True),
400: None, 400: None,
403: None, 403: None,
404: None, 404: None,
@@ -519,6 +517,28 @@ class DocumentTypeViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin):
404: None, 404: None,
}, },
), ),
email=extend_schema(
description="Email the document to one or more recipients as an attachment.",
request=inline_serializer(
name="EmailRequest",
fields={
"addresses": serializers.CharField(),
"subject": serializers.CharField(),
"message": serializers.CharField(),
"use_archive_version": serializers.BooleanField(default=True),
},
),
responses={
200: inline_serializer(
name="EmailResponse",
fields={"message": serializers.CharField()},
),
400: None,
403: None,
404: None,
500: None,
},
),
) )
class DocumentViewSet( class DocumentViewSet(
PassUserMixin, PassUserMixin,
@@ -1079,6 +1099,12 @@ class DocumentViewSet(
200: DocumentSerializer(many=True, all_fields=True), 200: DocumentSerializer(many=True, all_fields=True),
}, },
), ),
next_asn=extend_schema(
description="Get the next available Archive Serial Number (ASN) for a new document",
responses={
200: OpenApiTypes.INT,
},
),
) )
class UnifiedSearchViewSet(DocumentViewSet): class UnifiedSearchViewSet(DocumentViewSet):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-05-24 17:14+0000\n" "POT-Creation-Date: 2025-06-14 19:08-0700\n"
"PO-Revision-Date: 2022-02-17 04:17\n" "PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: English\n" "Language-Team: English\n"
@@ -1694,90 +1694,94 @@ msgid "Spanish"
msgstr "" msgstr ""
#: paperless/settings.py:766 #: paperless/settings.py:766
msgid "Finnish" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings.py:767 #: paperless/settings.py:767
msgid "French" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings.py:768 #: paperless/settings.py:768
msgid "Hungarian" msgid "French"
msgstr "" msgstr ""
#: paperless/settings.py:769 #: paperless/settings.py:769
msgid "Italian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings.py:770 #: paperless/settings.py:770
msgid "Japanese" msgid "Italian"
msgstr "" msgstr ""
#: paperless/settings.py:771 #: paperless/settings.py:771
msgid "Korean" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings.py:772 #: paperless/settings.py:772
msgid "Luxembourgish" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings.py:773 #: paperless/settings.py:773
msgid "Norwegian" msgid "Luxembourgish"
msgstr "" msgstr ""
#: paperless/settings.py:774 #: paperless/settings.py:774
msgid "Dutch" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings.py:775 #: paperless/settings.py:775
msgid "Polish" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings.py:776 #: paperless/settings.py:776
msgid "Portuguese (Brazil)" msgid "Polish"
msgstr "" msgstr ""
#: paperless/settings.py:777 #: paperless/settings.py:777
msgid "Portuguese" msgid "Portuguese (Brazil)"
msgstr "" msgstr ""
#: paperless/settings.py:778 #: paperless/settings.py:778
msgid "Romanian" msgid "Portuguese"
msgstr "" msgstr ""
#: paperless/settings.py:779 #: paperless/settings.py:779
msgid "Russian" msgid "Romanian"
msgstr "" msgstr ""
#: paperless/settings.py:780 #: paperless/settings.py:780
msgid "Slovak" msgid "Russian"
msgstr "" msgstr ""
#: paperless/settings.py:781 #: paperless/settings.py:781
msgid "Slovenian" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings.py:782 #: paperless/settings.py:782
msgid "Serbian" msgid "Slovenian"
msgstr "" msgstr ""
#: paperless/settings.py:783 #: paperless/settings.py:783
msgid "Swedish" msgid "Serbian"
msgstr "" msgstr ""
#: paperless/settings.py:784 #: paperless/settings.py:784
msgid "Turkish" msgid "Swedish"
msgstr "" msgstr ""
#: paperless/settings.py:785 #: paperless/settings.py:785
msgid "Ukrainian" msgid "Turkish"
msgstr "" msgstr ""
#: paperless/settings.py:786 #: paperless/settings.py:786
msgid "Chinese Simplified" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings.py:787 #: paperless/settings.py:787
msgid "Chinese Simplified"
msgstr ""
#: paperless/settings.py:788
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""

View File

@@ -763,6 +763,7 @@ LANGUAGES = [
("el-gr", _("Greek")), ("el-gr", _("Greek")),
("en-gb", _("English (GB)")), ("en-gb", _("English (GB)")),
("es-es", _("Spanish")), ("es-es", _("Spanish")),
("fa-ir", _("Persian")),
("fi-fi", _("Finnish")), ("fi-fi", _("Finnish")),
("fr-fr", _("French")), ("fr-fr", _("French")),
("hu-hu", _("Hungarian")), ("hu-hu", _("Hungarian")),

View File

@@ -35,6 +35,7 @@ from paperless_mail.tasks import process_mail_accounts
@extend_schema_view( @extend_schema_view(
test=extend_schema( test=extend_schema(
operation_id="mail_account_test", operation_id="mail_account_test",
request=MailAccountSerializer,
description="Test a mail account", description="Test a mail account",
responses={ responses={
200: inline_serializer( 200: inline_serializer(
@@ -44,6 +45,17 @@ from paperless_mail.tasks import process_mail_accounts
400: OpenApiTypes.STR, 400: OpenApiTypes.STR,
}, },
), ),
process=extend_schema(
operation_id="mail_account_process",
description="Manually process the selected mail account for new messages.",
responses={
200: inline_serializer(
name="MailAccountProcessResponse",
fields={"result": serializers.CharField(default="OK")},
),
404: None,
},
),
) )
class MailAccountViewSet(ModelViewSet, PassUserMixin): class MailAccountViewSet(ModelViewSet, PassUserMixin):
model = MailAccount model = MailAccount