Compare commits

..
Author SHA1 Message Date
shamoon 9c8b9d7a20 Dont need em any more 2026-10-18 23:45:10 -07:00
shamoon 0c4aa7f9f7 Fix: dont include doc content on global search results 2026-10-18 23:32:52 -07:00
GitHub Actions c21deaa452 Auto translate strings 2026-10-19 06:20:40 +00:00
shamoon c47766121d Fix: remove some redundant pagers (#14487) 2026-10-18 23:19:24 -07:00
GitHub Actions 979bcd3896 Auto translate strings 2026-10-19 06:05:02 +00:00
shamoon d450f03d5f Fix: prevent weird wrapping of dashboard widget column titles (#14486) 2026-10-18 23:03:43 -07:00
GitHub Actions 730c2d8688 Auto translate strings 2026-10-19 03:23:47 +00:00
shamoon b1738c85d7 Fix: dont paginate 'all' tasks view across sections (#14484) 2026-10-18 20:22:18 -07:00
Trenton H a8b835fcd0 Fix: use float parsing for importer manifest records with a passphrase (#14483) 2026-10-18 15:16:13 -07:00
GitHub Actions 41498113d3 Auto translate strings 2026-10-18 14:29:34 +00:00
309a4b004d Fix: respect subpath in logout, share link and auto-login redirects (#14476)
---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2026-10-18 14:28:15 +00:00
GitHub Actions 961f7fd672 Auto translate strings 2026-10-18 00:30:41 +00:00
shamoon de94657e17 Fix: dont tie suggestions button to ai enabled any more (#14478) 2026-10-17 17:29:29 -07:00
Trenton H 0cb2cdbfdd Fix: handle single-class classifier in threshold prediction (#14471) (#14473)
scikit-learn treats a single-class fit as binary, so predict_proba returns
[1 - p, p] for one class and argmax could index past classes_. Use only
column 0, the real class, so the confidence threshold still applies.
2026-10-16 10:47:58 -07:00
GitHub Actions 1f3d451a3a Auto translate strings 2026-10-16 17:12:50 +00:00
Trenton H 183cfd2da4 Fix: keep counter and extension when shortening long public filenames (#14472)
get_public_filename relied on sanitize_filename to truncate, which cuts
from the end and so dropped the counter and extension of long titles.
Multi-byte titles could also exceed the 255 byte filesystem limit.

Shorten the title by UTF-8 bytes before appending the counter and
extension. The exporter reserves room for its longer derived thumbnail
and archive suffixes.
2026-10-16 10:11:34 -07:00
shamoon 30a24ee02e Chore: disable deprecated ts-jest isolation override 2026-10-15 22:51:55 -07:00
github-actions[bot]andCrowdin Bot 1943c99b27 New Crowdin translations by GitHub Action (#14449)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-10-15 22:51:00 -07:00
shamoon 8b26ecafb6 Fix: prevent ownerless docs leaking others in to LLM prompts 2026-10-15 22:42:34 -07:00
Trenton H d4a57204eb Fix: copy only direct user grants in DocumentMetadataOverrides.from_document (#14452)
get_users_with_perms includes group members by default, so PDF actions such
as merge and split gave them direct grants on the new documents, which
outlived their group membership.
2026-10-15 15:11:28 -07:00
dependabot[bot] c50c6f5517 Chore(deps): Bump filelock from 3.32.6 to 4.0.12 (#14336)
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.32.6 to 4.0.12.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.32.6...4.0.12)

---
updated-dependencies:
- dependency-name: filelock
  dependency-version: 4.0.3
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-14 16:11:12 -07:00
GitHub Actions 34a6c718fa Auto translate strings 2026-10-14 15:18:54 +00:00
shamoon 14aaa42777 Fix: support combined text/query searches (#14443) 2026-10-14 08:17:22 -07:00
shamoon c86c822bc7 Fix ai auto-close message 2026-10-13 12:15:28 -07:00
dependabot[bot] b23b118a49 Chore(deps): Bump the uv group across 1 directory with 3 updates (#14418)
Bumps the uv group with 3 updates in the / directory: [banks](https://github.com/masci/banks), [fsspec](https://github.com/fsspec/filesystem_spec) and [multidict](https://github.com/aio-libs/multidict).


Updates `banks` from 2.4.5 to 2.5.1
- [Release notes](https://github.com/masci/banks/releases)
- [Commits](https://github.com/masci/banks/compare/v2.4.5...v2.5.1)

Updates `fsspec` from 2026.1.0 to 2026.6.0
- [Commits](https://github.com/fsspec/filesystem_spec/compare/2026.1.0...2026.6.0)

Updates `multidict` from 6.7.1 to 6.9.1
- [Release notes](https://github.com/aio-libs/multidict/releases)
- [Changelog](https://github.com/aio-libs/multidict/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/multidict/compare/v6.7.1...v6.9.1)

---
updated-dependencies:
- dependency-name: banks
  dependency-version: 2.5.1
  dependency-type: indirect
- dependency-name: fsspec
  dependency-version: 2026.6.0
  dependency-type: indirect
- dependency-name: multidict
  dependency-version: 6.9.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 17:25:40 +00:00
135 changed files with 3442 additions and 2644 deletions

No files matched your search

+5 -4
View File
@@ -76,11 +76,12 @@ jobs:
return;
}
// Reaching here with the honeypot means AI usage was not disclosed in the form, so that
// message can name the cause.
// A missing template label only tells us the issue did not come from the form, which
// is reason enough to close it, but not proof of how it was written.
const body = honeypot
// is reason enough to close it, but not proof of how it was written. It takes priority:
// a hand-built body can carry the honeypot *and* a disclosure, and the form message is
// the only one that is accurate for both. Reaching the honeypot message means the issue
// came from the form without disclosing AI usage, so that message can name the cause.
const body = honeypot && !bypassedTemplate
? 'This issue was automatically closed because it contains a marker that is only visible to ' +
'automated tools, which indicates it was generated by an AI agent, but the "AI usage" field does not ' +
'disclose this.\n\n' +
+3 -3
View File
@@ -43,7 +43,7 @@ dependencies = [
"drf-spectacular~=0.30",
"drf-spectacular-sidecar>=2026.7.1,<2026.10",
"drf-writable-nested~=0.7.1",
"filelock~=3.32.0",
"filelock>=3.32,<4.1",
"flower>=2.0.1,<2.3",
"gotenberg-client[httpx]~=1.0",
"httpx-oauth~=0.17",
@@ -256,8 +256,8 @@ isort.force-single-line = true
[tool.codespell]
ignore-words-list = "criterias,afterall,valeu,ureue,equest,ure,assertIn,Oktober,commitish,NIN,nin,reprot"
skip = """\
src-ui/src/locale/*,src-ui/pnpm-lock.yaml,src-ui/e2e/*,src/paperless/tests/samples/mail/*,src/documents/tests/samples\
/*,src/paperless_testing/sample_files/*,*.po,*.json\
src-ui/src/locale/*,src-ui/pnpm-lock.yaml,src-ui/e2e/*,src/paperless_mail/tests/samples/*,src/paperless/tests/samples\
/mail/*,src/documents/tests/samples/*,*.po,*.json\
"""
write-changes = true
+1 -3
View File
@@ -134,9 +134,7 @@ def seed_database() -> None:
originals.mkdir(parents=True)
thumbnails = Path(os.environ["PAPERLESS_MEDIA_ROOT"]) / "documents" / "thumbnails"
thumbnails.mkdir(parents=True)
sample_pdf = (
SOURCE_ROOT / "paperless_testing" / "sample_files" / "simple-digital.pdf"
)
sample_pdf = SOURCE_ROOT / "documents" / "tests" / "samples" / "simple.pdf"
for document in documents:
shutil.copyfile(sample_pdf, originals / document.filename)
shutil.copyfile(
+2
View File
@@ -15,6 +15,8 @@ module.exports = {
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
useESM: true,
// Disable the builder's deprecated override; tsconfig.spec.json enables isolation.
isolatedModules: false,
},
],
},
+178 -167
View File
File diff suppressed because it is too large. Load diff
@@ -102,25 +102,34 @@
</button>
}
<ngb-pagination
[pageSize]="pageSize"
[collectionSize]="totalTasks()"
[page]="page"
[maxSize]="5"
[rotate]="true"
size="sm"
aria-label="Tasks pagination"
(pageChange)="setPage($event)">
</ngb-pagination>
@if (selectedSection !== TaskSection.All && totalTasks() > pageSize) {
<ngb-pagination
[pageSize]="pageSize"
[collectionSize]="totalTasks()"
[page]="page"
[maxSize]="5"
[rotate]="true"
size="sm"
aria-label="Tasks pagination"
(pageChange)="setPage($event)">
</ngb-pagination>
}
</div>
<ng-template let-tasks="tasks" let-section="section" #tasksTemplate>
<div class="section-header d-flex align-items-center justify-content-between mb-2">
<div>
<h5 class="mb-0">{{ sectionLabel(section) }}</h5>
<div class="small text-muted">
<ng-container i18n>{tasks.length, plural, =1 {1 task} other {{{tasks.length}} tasks}}</ng-container>
</div>
<div class="section-header mb-2">
<h5 class="mb-0">{{ sectionLabel(section) }}</h5>
@let count = sectionCount(section);
@let hasMore = selectedSection === TaskSection.All && count > tasks.length;
<div class="small text-muted">
@if (hasMore) {
<ng-container i18n>{{tasks.length}} of {{count}} tasks</ng-container>
<button class="btn btn-link show-all p-0 ms-2 align-baseline" type="button" (click)="setSection(section)">
<ng-container i18n>Show all</ng-container><i-bs class="ms-1" width="1em" height="1em" name="arrow-right"></i-bs>
</button>
} @else {
<ng-container i18n>{count, plural, =1 {1 task} other {{{count}} tasks}}</ng-container>
}
</div>
</div>
@@ -169,10 +178,9 @@
<div>{{ taskDisplayName(task) }}</div>
<div class="small text-muted">
@if (taskShowsSeparateTypeLabel(task)) {
<span>{{ task.task_type_display }}</span>
<span class="mx-1">&bull;</span>
<span class="me-2">{{ task.task_type_display }}</span>
}
<span>{{ task.trigger_source_display }}</span>
<span class="text-nowrap"><i-bs class="me-1" width="1em" height="1em" name="box-arrow-in-right"></i-bs>{{ task.trigger_source_display }}</span>
</div>
</td>
<td class="d-none d-lg-table-cell created-col">{{ task.date_created | customDate:'short' }}</td>
@@ -72,3 +72,7 @@ pre {
tbody tr:nth-last-child(2) td {
border-bottom: none !important;
}
.section-header .show-all {
font-size: inherit;
}
@@ -223,16 +223,6 @@ describe('TasksComponent', () => {
)
.flush(paginatedTasks)
httpTestingController
.expectOne(
(req) =>
req.url === `${environment.apiBaseUrl}tasks/` &&
req.params.get('acknowledged') === 'false' &&
req.params.get('page_size') === '25' &&
req.params.get('page') === '1'
)
.flush(paginatedTasks)
httpTestingController
.expectOne(
(req) =>
@@ -241,8 +231,30 @@ describe('TasksComponent', () => {
!req.params.has('status')
)
.flush(sectionCountResponse)
flushTaskLists(10)
})
// answers task list requests with the fixture tasks matching their status filter
function flushTaskLists(pageSize: number, count?: number) {
httpTestingController
.match(
(req) =>
req.url === `${environment.apiBaseUrl}tasks/` &&
req.params.get('page_size') === `${pageSize}`
)
.forEach((req) => {
const statuses = req.request.params.getAll('status')
const results = tasks.filter((task) => statuses.includes(task.status))
req.flush({ count: count ?? results.length, results })
})
}
function selectSection(section: TaskSection, count: number) {
component.setSection(section)
flushTaskLists(25, count)
}
it('should display task sections with counts', () => {
expect(component.selectedSection).toBe(TaskSection.All)
expect(component.selectedTaskType).toBeNull()
@@ -338,6 +350,7 @@ describe('TasksComponent', () => {
})
it('should render pagination controls next to the task filter', () => {
selectSection(TaskSection.Completed, 30)
fixture.detectChanges()
const controls = fixture.debugElement.query(By.css('.task-controls'))
@@ -348,6 +361,51 @@ describe('TasksComponent', () => {
expect(pagination).not.toBeNull()
})
it('should only paginate a section with more than one page of tasks', () => {
const pagination = () =>
fixture.debugElement.query(By.css('ngb-pagination'))
fixture.detectChanges()
expect(pagination()).toBeNull()
selectSection(TaskSection.Completed, 2)
fixture.detectChanges()
expect(pagination()).toBeNull()
})
it('should load the all tasks overview one section at a time', () => {
expect(component.tasksForSection(TaskSection.NeedsAttention)).toHaveLength(
2
)
expect(component.tasksForSection(TaskSection.InProgress)).toHaveLength(3)
expect(component.tasksForSection(TaskSection.Completed)).toHaveLength(2)
expect(component.totalTasks()).toBe(7)
})
it('should show section totals and link truncated sections to their full list', () => {
component.sectionCounts.update((counts) => ({
...counts,
[TaskSection.Completed]: 27,
}))
fixture.detectChanges()
const header = (label: string) =>
fixture.debugElement
.queryAll(By.css('.section-header'))
.find((h) => h.nativeElement.textContent.includes(label))
expect(header('Needs attention').nativeElement.textContent).toContain(
'2 tasks'
)
expect(header('Needs attention').query(By.css('button'))).toBeNull()
expect(header('Recently completed').nativeElement.textContent).toContain(
'2 of 27 tasks'
)
header('Recently completed').query(By.css('button')).nativeElement.click()
expect(component.selectedSection).toBe(TaskSection.Completed)
})
it('should apply the selected section to the server-side task query', () => {
component.setSection(TaskSection.NeedsAttention)
@@ -368,55 +426,56 @@ describe('TasksComponent', () => {
it('should apply task type and trigger source filters to the server-side task query', () => {
component.setTaskType(PaperlessTaskType.SanityCheck)
httpTestingController
.expectOne(
expect(
httpTestingController.match(
(request) =>
request.url === `${environment.apiBaseUrl}tasks/` &&
request.params.get('page_size') === '25' &&
request.params.get('page_size') === '10' &&
request.params.get('task_type') === PaperlessTaskType.SanityCheck
)
.flush({ count: 1, results: [tasks[6]] })
).toHaveLength(3)
component.setTriggerSource(PaperlessTaskTriggerSource.System)
httpTestingController
.expectOne(
expect(
httpTestingController.match(
(request) =>
request.url === `${environment.apiBaseUrl}tasks/` &&
request.params.get('page_size') === '25' &&
request.params.get('page_size') === '10' &&
request.params.get('task_type') === PaperlessTaskType.SanityCheck &&
request.params.get('trigger_source') ===
PaperlessTaskTriggerSource.System
)
.flush({ count: 1, results: [tasks[6]] })
).toHaveLength(3)
})
it('should apply text filters to the server-side task query', () => {
component.filterText = 'invoice'
jest.advanceTimersByTime(150)
httpTestingController
.expectOne(
expect(
httpTestingController.match(
(request) =>
request.url === `${environment.apiBaseUrl}tasks/` &&
request.params.get('page_size') === '25' &&
request.params.get('page_size') === '10' &&
request.params.get('name') === 'invoice'
)
.flush({ count: 1, results: [tasks[0]] })
).toHaveLength(3)
component.setFilterTarget(TaskFilterTargetID.Result)
httpTestingController
.expectOne(
expect(
httpTestingController.match(
(request) =>
request.url === `${environment.apiBaseUrl}tasks/` &&
request.params.get('page_size') === '25' &&
request.params.get('page_size') === '10' &&
request.params.get('result') === 'invoice'
)
.flush({ count: 0, results: [] })
).toHaveLength(3)
})
it('should load a different task page when pagination changes', () => {
selectSection(TaskSection.Completed, 30)
component.setPage(2)
const pageTwoTasks = {
@@ -440,6 +499,7 @@ describe('TasksComponent', () => {
})
it('should not replace section counts with current-page counts', () => {
selectSection(TaskSection.Completed, 30)
component.setPage(2)
httpTestingController
@@ -455,11 +515,21 @@ describe('TasksComponent', () => {
results: [tasks[0]],
})
expect(component.sectionCount(TaskSection.All)).toBe(7)
expect(component.sectionCount(TaskSection.NeedsAttention)).toBe(2)
expect(component.sectionCount(TaskSection.InProgress)).toBe(3)
expect(component.sectionCount(TaskSection.Completed)).toBe(2)
})
it('should stop loading when the overview fails to load', () => {
component.setSection(TaskSection.All)
httpTestingController
.match((req) => req.params.get('page_size') === '10')[0]
.flush('', { status: 500, statusText: 'Error' })
expect(component.loading()).toBe(false)
})
it('should expose stable task type options and disable empty ones', () => {
expect(component.taskTypeOptions.map((option) => option.value)).toContain(
PaperlessTaskType.TrainClassifier
@@ -15,6 +15,9 @@ import {
distinctUntilChanged,
filter,
first,
forkJoin,
map,
Observable,
Subject,
takeUntil,
timer,
@@ -25,6 +28,7 @@ import {
PaperlessTaskTriggerSource,
PaperlessTaskType,
} from 'src/app/data/paperless-task'
import { Results } from 'src/app/data/results'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { TasksService } from 'src/app/services/tasks.service'
@@ -169,6 +173,7 @@ export class TasksComponent
public expandedTask: number
public autoRefreshEnabled: boolean = true
public readonly pageSize = 25
public readonly overviewSize = 10
public page: number = 1
readonly totalTasks = signal(0)
readonly sectionCounts = signal<Record<TaskSection, number>>({
@@ -484,13 +489,6 @@ export class TasksComponent
return this.sectionCounts()[section]
}
private setSectionCount(section: TaskSection, count: number) {
this.sectionCounts.update((counts) => ({
...counts,
[section]: count,
}))
}
sectionShowsResults(section: TaskSection): boolean {
return section !== TaskSection.InProgress
}
@@ -737,21 +735,12 @@ export class TasksComponent
this.reloadSectionCounts()
this.loading.set(true)
this.tasksService
.list(
this.page,
this.pageSize,
this.getParamsForSection(this.selectedSection)
)
this.listTasks()
.pipe(first(), takeUntil(this.unsubscribeNotifier))
.subscribe({
next: (result) => {
this.pagedTasks.set(result.results)
this.totalTasks.set(result.count)
this.setSectionCount(TaskSection.All, result.count)
if (this.selectedSection !== TaskSection.All) {
this.setSectionCount(this.selectedSection, result.count)
}
this.loading.set(false)
if (
this.page > 1 &&
@@ -767,4 +756,30 @@ export class TasksComponent
},
})
}
private listTasks(): Observable<Results<PaperlessTask>> {
if (this.selectedSection !== TaskSection.All) {
return this.tasksService.list(
this.page,
this.pageSize,
this.getParamsForSection(this.selectedSection)
)
}
// All is an overview of each section to avoid splitting sections across pages
return forkJoin(
this.sections.map((section) =>
this.tasksService.list(
1,
this.overviewSize,
this.getParamsForSection(section)
)
)
).pipe(
map((results) => ({
count: results.reduce((sum, result) => sum + result.count, 0),
results: results.flatMap((result) => result.results),
}))
)
}
}
@@ -154,6 +154,31 @@ describe('UsersAndGroupsComponent', () => {
jest.useRealTimers()
})
it('should logout current user under base href if password changed', () => {
jest.useFakeTimers()
const base = document.createElement('base')
base.href = '/paperless/'
document.head.appendChild(base)
completeSetup()
let modal: NgbModalRef
modalService.activeInstances.subscribe((refs) => (modal = refs[0]))
component.editUser(users[0])
const navSpy = jest
.spyOn(navUtils, 'setLocationHref')
.mockImplementation(() => {})
const editDialog = modal.componentInstance as UserEditDialogComponent
editDialog.passwordIsSet = true
settingsService.currentUser.set(users[0]) // simulate logged in as same user
editDialog.succeeded.emit(users[0])
fixture.detectChanges()
jest.advanceTimersByTime(2600)
expect(navSpy).toHaveBeenCalledWith(
`${window.location.origin}/paperless/accounts/logout/?next=/paperless/accounts/login/?next=/paperless/`
)
base.remove()
jest.useRealTimers()
})
it('should support edit / create group, show error if needed', () => {
completeSetup()
let modal: NgbModalRef
@@ -116,8 +116,9 @@ export class UsersAndGroupsComponent
$localize`Password has been changed, you will be logged out momentarily.`
)
setTimeout(() => {
const basePath = new URL(document.baseURI).pathname
setLocationHref(
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
`${window.location.origin}${basePath}accounts/logout/?next=${basePath}accounts/login/?next=${basePath}`
)
}, 2500)
} else {
@@ -218,6 +218,38 @@ describe('ProfileEditDialogComponent', () => {
jest.useRealTimers()
})
it('should logout under base href on save if password changed', () => {
jest.useFakeTimers()
const base = document.createElement('base')
base.href = '/paperless/'
document.head.appendChild(base)
const getSpy = jest.spyOn(profileService, 'get')
getSpy.mockReturnValue(of(profile))
const getProvidersSpy = jest.spyOn(
profileService,
'getSocialAccountProviders'
)
getProvidersSpy.mockReturnValue(of(socialAccountProviders))
component.ngOnInit()
component['newPassword'] = 'new*pass'
component.form.get('password').patchValue('new*pass')
component.form.get('password_confirm').patchValue('new*pass')
const updateSpy = jest.spyOn(profileService, 'update')
updateSpy.mockReturnValue(of(null))
const navSpy = jest
.spyOn(navUtils, 'setLocationHref')
.mockImplementation(() => {})
component.save()
expect(updateSpy).toHaveBeenCalled()
jest.advanceTimersByTime(2600)
expect(navSpy).toHaveBeenCalledWith(
`${window.location.origin}/paperless/accounts/logout/?next=/paperless/accounts/login/?next=/paperless/`
)
base.remove()
jest.useRealTimers()
})
it('should support auth token copy', () => {
jest.useFakeTimers()
const getSpy = jest.spyOn(profileService, 'get')
@@ -210,8 +210,9 @@ export class ProfileEditDialogComponent
$localize`Password has been changed, you will be logged out momentarily.`
)
setTimeout(() => {
const basePath = new URL(document.baseURI).pathname
setLocationHref(
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
`${window.location.origin}${basePath}accounts/logout/?next=${basePath}accounts/login/?next=${basePath}`
)
}, 2500)
}
@@ -1,6 +1,6 @@
<div class="d-flex align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-primary" (click)="clickSuggest()" [disabled]="disabled() || loading() || (suggestions() && !aiEnabled())" [aria-label]="noSuggestions ? 'No suggestions' : 'Suggest'" i18n-aria-label>
<button type="button" class="btn btn-sm btn-outline-primary" (click)="clickSuggest()" [disabled]="disabled() || loading()" [aria-label]="noSuggestions ? 'No suggestions' : 'Suggest'" i18n-aria-label>
@if (loading()) {
<div class="spinner-border spinner-border-sm" role="status"></div>
} @else if (noSuggestions) {
@@ -18,46 +18,44 @@
}
</button>
@if (aiEnabled()) {
<div class="btn-group" ngbDropdown #dropdown="ngbDropdown" [popperOptions]="popperOptions">
<button type="button" class="btn btn-sm btn-outline-primary" ngbDropdownToggle [disabled]="disabled() || loading() || !suggestions()" aria-expanded="false" aria-controls="suggestionsDropdown" aria-label="Suggestions dropdown">
<span class="visually-hidden" i18n>Show suggestions</span>
</button>
<div class="btn-group" ngbDropdown #dropdown="ngbDropdown" [popperOptions]="popperOptions">
<button type="button" class="btn btn-sm btn-outline-primary" ngbDropdownToggle [disabled]="disabled() || loading() || !suggestions()" aria-expanded="false" aria-controls="suggestionsDropdown" aria-label="Suggestions dropdown">
<span class="visually-hidden" i18n>Show suggestions</span>
</button>
<div ngbDropdownMenu aria-labelledby="suggestionsDropdown" class="shadow suggestions-dropdown">
<div class="list-group list-group-flush small pb-0">
@if (novelSuggestions === 0 && fieldSuggestions === 0) {
<div class="list-group-item text-muted fst-italic">
<small class="text-muted small fst-italic" i18n>No novel suggestions</small>
</div>
<div ngbDropdownMenu aria-labelledby="suggestionsDropdown" class="shadow suggestions-dropdown">
<div class="list-group list-group-flush small pb-0">
@if (novelSuggestions === 0 && fieldSuggestions === 0) {
<div class="list-group-item text-muted fst-italic">
<small class="text-muted small fst-italic" i18n>No novel suggestions</small>
</div>
}
@if (suggestions()?.suggested_tags?.length > 0) {
<small class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="tags"></i-bs><ng-container i18n>Tags</ng-container></small>
@for (tag of suggestions().suggested_tags; track tag) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addTag.emit(tag)">{{ tag }}</button>
}
@if (suggestions()?.suggested_tags?.length > 0) {
<small class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="tags"></i-bs><ng-container i18n>Tags</ng-container></small>
@for (tag of suggestions().suggested_tags; track tag) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addTag.emit(tag)">{{ tag }}</button>
}
}
@if (suggestions()?.suggested_document_types?.length > 0) {
<div class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="hash"></i-bs><ng-container i18n>Document Types</ng-container></div>
@for (type of suggestions().suggested_document_types; track type) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addDocumentType.emit(type)">{{ type }}</button>
}
@if (suggestions()?.suggested_document_types?.length > 0) {
<div class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="hash"></i-bs><ng-container i18n>Document Types</ng-container></div>
@for (type of suggestions().suggested_document_types; track type) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addDocumentType.emit(type)">{{ type }}</button>
}
}
@if (suggestions()?.suggested_correspondents?.length > 0) {
<div class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="person"></i-bs><ng-container i18n>Correspondents</ng-container></div>
@for (correspondent of suggestions().suggested_correspondents; track correspondent) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addCorrespondent.emit(correspondent)">{{ correspondent }}</button>
}
@if (suggestions()?.suggested_correspondents?.length > 0) {
<div class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="person"></i-bs><ng-container i18n>Correspondents</ng-container></div>
@for (correspondent of suggestions().suggested_correspondents; track correspondent) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addCorrespondent.emit(correspondent)">{{ correspondent }}</button>
}
}
@if (fieldSuggestions > 0) {
<div class="list-group-item text-muted fst-italic">
<small class="text-muted small fst-italic" i18n>{fieldSuggestions, plural, =1 {1 suggestion available below} other {{{fieldSuggestions}} suggestions available below}}</small>
</div>
}
</div>
}
@if (fieldSuggestions > 0) {
<div class="list-group-item text-muted fst-italic">
<small class="text-muted small fst-italic" i18n>{fieldSuggestions, plural, =1 {1 suggestion available below} other {{{fieldSuggestions}} suggestions available below}}</small>
</div>
}
</div>
</div>
}
</div>
</div>
@if (aiEnabled()) {
@@ -251,6 +251,37 @@ describe('SuggestionsDropdownComponent', () => {
expect(fixture.nativeElement.textContent).toContain('No novel suggestions')
})
it('should open ML date suggestions with AI disabled', () => {
const getSuggestions = jest.spyOn(component.getSuggestions, 'emit')
fixture.componentRef.setInput('aiEnabled', false)
fixture.componentRef.setInput('fetchedSources', [SuggestionSource.ML])
fixture.componentRef.setInput('suggestions', {
dates: ['2026-01-04', '2026-02-01'],
})
fixture.componentRef.setInput('appliedCreated', '2026-01-04')
fixture.detectChanges()
const button = fixture.nativeElement.querySelector('button')
expect(button.disabled).toBeFalsy()
expect(button.querySelector('.badge').textContent.trim()).toBe('1')
button.click()
fixture.detectChanges()
expect(component.dropdown.isOpen()).toBeTruthy()
expect(fixture.nativeElement.textContent).toContain(
'1 suggestion available below'
)
expect(getSuggestions).not.toHaveBeenCalled()
expect(
fixture.nativeElement.querySelector('button[title="Suggestion options"]')
).toBeNull()
button.click()
fixture.detectChanges()
expect(component.dropdown.isOpen()).toBeFalsy()
})
it('should fetch unfetched sources and show existing suggestions', () => {
jest.spyOn(component.getSuggestions, 'emit')
fixture.componentRef.setInput('aiEnabled', true)
@@ -53,11 +53,7 @@ export class SuggestionsDropdownComponent {
addCorrespondent: EventEmitter<string> = new EventEmitter()
public clickSuggest(): void {
if (
this.disabled() ||
this.loading() ||
(this.suggestions() && !this.aiEnabled())
) {
if (this.disabled() || this.loading()) {
return
}
@@ -25,6 +25,8 @@
@if (displayFields().includes(field)) {
<th
scope="col"
class="text-truncate"
[title]="getColumnTitle(field)"
[ngClass]="{
'd-none d-md-table-cell': i > 1,
'w-25': field === DisplayField.CREATED || field === DisplayField.ADDED
@@ -484,6 +484,22 @@ describe('FilterEditorComponent', () => {
expect(component.textFilter).toBeNull()
})
it('should ingest title filter rules alongside a fulltext query with date created', () => {
component.filterRules = [
{
rule_type: FILTER_SIMPLE_TITLE,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'created:[-1 week to now]',
},
]
expect(component.dateCreatedRelativeDate).toEqual(1) // RELATIVE_DATE_QUERYSTRINGS['-1 week to now']
expect(component.textFilter).toEqual('foo')
expect(component.textFilterTarget).toEqual('title') // TEXT_FILTER_TARGET_TITLE
})
it('should ingest text filter rules for fulltext query that include date added', () => {
expect(component.dateAddedRelativeDate).toBeNull()
component.filterRules = [
@@ -1845,7 +1861,7 @@ describe('FilterEditorComponent', () => {
])
})
it('should carry over text filtering on date created with relative date', () => {
it('should keep text filtering separate from date created relative date', () => {
component.textFilter = 'foo'
const dateCreatedDropdown = fixture.debugElement.queryAll(
By.directive(DatesDropdownComponent)
@@ -1855,14 +1871,18 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TEXT,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'foo,created:[-1 week to now]',
value: 'created:[-1 week to now]',
},
])
})
it('should carry over text filtering once with created and added relative dates', () => {
it('should keep text filtering separate from created and added relative dates', () => {
component.textFilter = 'foo'
const datesDropdown = fixture.debugElement.query(
By.directive(DatesDropdownComponent)
@@ -1873,14 +1893,18 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TEXT,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'foo,created:[-1 week to now],added:[-1 month to now]',
value: 'created:[-1 week to now],added:[-1 month to now]',
},
])
})
it('should convert legacy title filters into full text query when adding a created relative date', () => {
it('should keep legacy title filters as a title search when adding a created relative date', () => {
component.filterRules = [
{
rule_type: FILTER_TITLE,
@@ -1895,14 +1919,18 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TITLE,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'foo,created:[-1 week to now]',
value: 'created:[-1 week to now]',
},
])
})
it('should convert simple title filters into full text query when adding a created relative date', () => {
it('should keep simple title filters as a title search when adding a created relative date', () => {
component.filterRules = [
{
rule_type: FILTER_SIMPLE_TITLE,
@@ -1917,9 +1945,13 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TITLE,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'foo,created:[-1 week to now]',
value: 'created:[-1 week to now]',
},
])
})
@@ -2005,7 +2037,7 @@ describe('FilterEditorComponent', () => {
])
})
it('should carry over text filtering on date added with relative date', () => {
it('should keep text filtering separate from date added relative date', () => {
component.textFilter = 'foo'
const datesDropdown = fixture.debugElement.query(
By.directive(DatesDropdownComponent)
@@ -2015,9 +2047,13 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TEXT,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'foo,added:[-1 week to now]',
value: 'added:[-1 week to now]',
},
])
})
@@ -1083,23 +1083,6 @@ export class FilterEditorComponent
(fr) => fr.rule_type == FILTER_FULLTEXT_QUERY
)
// if had a title / content search and added a relative date we need to carry it over...
if (
!existingRule &&
this._textFilter?.length > 0 &&
(this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT ||
this.textFilterTarget == TEXT_FILTER_TARGET_TITLE)
) {
existingRule = filterRules.find(
(fr) =>
fr.rule_type == FILTER_TITLE_CONTENT ||
fr.rule_type == FILTER_SIMPLE_TEXT ||
fr.rule_type == FILTER_TITLE ||
fr.rule_type == FILTER_SIMPLE_TITLE
)
existingRule.rule_type = FILTER_FULLTEXT_QUERY
}
let queryArgs = existingRule?.value.split(',') ?? []
if (this.dateCreatedRelativeDate !== null) {
const rd = RELATIVE_DATE_QUERYSTRINGS.find(
@@ -11,7 +11,7 @@
<div class="input-group input-group-sm w-auto d-none d-md-flex">
<label class="input-group-text bg-transparent border-0" for="managementPageSize" i18n>Show:</label>
</div>
<div class="input-group input-group-sm w-auto me-3">
<div class="input-group input-group-sm w-auto">
<select id="managementPageSize" class="form-select form-select-sm small" [(ngModel)]="pageSize">
<option [ngValue]="25">25</option>
<option [ngValue]="50">50</option>
@@ -19,7 +19,9 @@
</select>
<span class="input-group-text text-muted d-none d-md-flex" i18n>per page</span>
</div>
<ngb-pagination [pageSize]="pageSize" [collectionSize]="collectionSize()" [page]="page()" [maxSize]="5" (pageChange)="page.set($event); reloadData()" size="sm" aria-label="Pagination"></ngb-pagination>
@if (collectionSize() > pageSize) {
<ngb-pagination [pageSize]="pageSize" [collectionSize]="collectionSize()" [page]="page()" [maxSize]="5" (pageChange)="page.set($event); reloadData()" class="ms-3" size="sm" aria-label="Pagination"></ngb-pagination>
}
</div>
</div>
@@ -70,7 +72,7 @@
}
</div>
}
@if (collectionSize() > 20) {
@if (collectionSize() > pageSize) {
<ngb-pagination class="ms-auto" [pageSize]="pageSize" [collectionSize]="collectionSize()" [page]="page()" [maxSize]="5" (pageChange)="page.set($event); reloadData()" size="sm" aria-label="Pagination"></ngb-pagination>
}
</div>
@@ -93,15 +93,17 @@
[disabled]="selectedMailIds().size === 0"
(confirm)="deleteSelected()">
</pngx-confirm-button>
<div class="ms-auto">
<ngb-pagination
[collectionSize]="processedMails().length"
[(page)]="page"
[pageSize]="50"
[maxSize]="5"
(pageChange)="loadProcessedMails()">
</ngb-pagination>
</div>
@if (processedMails().length > 50) {
<div class="ms-auto">
<ngb-pagination
[collectionSize]="processedMails().length"
[(page)]="page"
[pageSize]="50"
[maxSize]="5"
(pageChange)="loadProcessedMails()">
</ngb-pagination>
</div>
}
</div>
}
</div>
+46 -46
View File
@@ -17,7 +17,7 @@
<context context-type="linenumber">386,393</context>
</context-group>
<note priority="1" from="description">Currently selected slide number read by screen reader</note>
<target state="needs-translation"> Slide <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> of <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/> </target>
<target state="translated"> スライド <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> / <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/> </target>
</trans-unit>
<trans-unit id="ngb.carousel.previous" datatype="html">
<source>Previous</source>
@@ -479,7 +479,7 @@
<context context-type="sourcefile">src/app/app.component.ts</context>
<context context-type="linenumber">201</context>
</context-group>
<target state="needs-translation">Attributes like tags, correspondents, document types, storage paths and custom fields can all be managed here. They can also be created from the document edit view.</target>
<target state="translated">タグ、取引先、ドキュメントの種類、保存先パス、カスタムフィールドなどの属性をここで管理できます。これらはドキュメントの編集画面から作成することもできます。</target>
</trans-unit>
<trans-unit id="7851939076947092983" datatype="html">
<source>Manage e-mail accounts and rules for automatically importing documents.</source>
@@ -507,7 +507,7 @@
<context context-type="sourcefile">src/app/app.component.ts</context>
<context context-type="linenumber">225</context>
</context-group>
<target state="needs-translation">Tasks helps you track background work, what needs attention, and what recently completed.</target>
<target state="translated">タスクでは、バックグラウンド処理の進行状況、対応が必要な項目、最近完了した処理を確認できます。</target>
</trans-unit>
<trans-unit id="2755033621682206902" datatype="html">
<source>Check out the settings for various tweaks to the web app.</source>
@@ -571,7 +571,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">35</context>
</context-group>
<target state="needs-translation">This value overrides <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>, which is set outside Paperless.</target>
<target state="translated">この値は、Paperless の外で設定されている <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>を上書きします。</target>
</trans-unit>
<trans-unit id="7221396516204435584" datatype="html">
<source><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</source>
@@ -579,7 +579,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
<target state="needs-translation"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</target>
<target state="translated"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> はPaperlessの外部で設定されています。ここに値を入力すると、その設定を上書きできます。</target>
</trans-unit>
<trans-unit id="8318849619178340389" datatype="html">
<source>Use the externally configured value</source>
@@ -587,7 +587,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">42</context>
</context-group>
<target state="needs-translation">Use the externally configured value</target>
<target state="translated">外部で設定された値を使用</target>
</trans-unit>
<trans-unit id="6032629623003430385" datatype="html">
<source>Reset to external</source>
@@ -595,7 +595,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">43</context>
</context-group>
<target state="needs-translation">Reset to external</target>
<target state="translated">外部設定に戻す</target>
</trans-unit>
<trans-unit id="7808756054397155068" datatype="html">
<source>Reset</source>
@@ -875,7 +875,7 @@
<context context-type="sourcefile">src/app/components/admin/logs/logs.component.html</context>
<context context-type="linenumber">18,19</context>
</context-group>
<target state="needs-translation">lines</target>
<target state="translated">行</target>
</trans-unit>
<trans-unit id="8838884664569764142" datatype="html">
<source>Auto refresh</source>
@@ -1003,7 +1003,7 @@
<context context-type="sourcefile">src/app/components/admin/logs/logs.component.html</context>
<context context-type="linenumber">63,64</context>
</context-group>
<target state="needs-translation">Jump to bottom</target>
<target state="translated">一番下へ移動</target>
</trans-unit>
<trans-unit id="1255048712725285892" datatype="html">
<source>Options to customize appearance, notifications and more. Settings apply to the &lt;strong&gt;current user only&lt;/strong&gt;.</source>
@@ -1135,7 +1135,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">115,116</context>
</context-group>
<target state="needs-translation">Sidebar items to show:</target>
<target state="translated">サイドバーに表示するアイテム:</target>
</trans-unit>
<trans-unit id="1356890996281769972" datatype="html">
<source>Dark mode</source>
@@ -1323,7 +1323,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">201</context>
</context-group>
<target state="needs-translation">Show document counts in sidebar saved views</target>
<target state="translated">サイドバーの保存済みビューにドキュメント数を表示</target>
</trans-unit>
<trans-unit id="8939587804990976924" datatype="html">
<source>Items per page</source>
@@ -1403,7 +1403,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">259,260</context>
</context-group>
<target state="needs-translation">Suggestions default to</target>
<target state="translated">提案機能のデフォルト設定</target>
</trans-unit>
<trans-unit id="6251586038459969294" datatype="html">
<source>Both</source>
@@ -1411,7 +1411,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">264</context>
</context-group>
<target state="needs-translation">Both</target>
<target state="translated">両方</target>
</trans-unit>
<trans-unit id="8967224283122923634" datatype="html">
<source>ML only</source>
@@ -1427,7 +1427,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">268</context>
</context-group>
<target state="needs-translation">AI only</target>
<target state="translated">AIのみ</target>
</trans-unit>
<trans-unit id="1043449647727456069" datatype="html">
<source>Automatically request suggestions for inbox documents</source>
@@ -1435,7 +1435,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">276</context>
</context-group>
<target state="needs-translation">Automatically request suggestions for inbox documents</target>
<target state="translated">受信トレイ内のドキュメントに対して自動的に提案をリクエストする</target>
</trans-unit>
<trans-unit id="460700039378563125" datatype="html">
<source>If un-checked, suggestions must be requested via the Suggest button.</source>
@@ -1443,7 +1443,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">276</context>
</context-group>
<target state="needs-translation">If un-checked, suggestions must be requested via the Suggest button.</target>
<target state="translated">チェックを外した場合、提案を取得するには「提案」ボタンを押す必要があります。</target>
</trans-unit>
<trans-unit id="8793267604636304297" datatype="html">
<source>Show document thumbnail during loading</source>
@@ -1467,7 +1467,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">300,301</context>
</context-group>
<target state="needs-translation">Uncheck fields to hide them on the document details page.</target>
<target state="translated">ドキュメントの詳細ページで非表示にするフィールドは、チェックを外してください。</target>
</trans-unit>
<trans-unit id="8508424367627989968" datatype="html">
<source>Bulk editing</source>
@@ -1507,7 +1507,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1907</context>
</context-group>
<target state="needs-translation">PDF Editor</target>
<target state="translated">PDF エディター</target>
</trans-unit>
<trans-unit id="1577733187050997705" datatype="html">
<source>Default editing mode</source>
@@ -1515,7 +1515,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">317,318</context>
</context-group>
<target state="needs-translation">Default editing mode</target>
<target state="translated">デフォルトの編集モード</target>
</trans-unit>
<trans-unit id="7273640930165035289" datatype="html">
<source>Create new document(s)</source>
@@ -1527,7 +1527,7 @@
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
<context context-type="linenumber">82,83</context>
</context-group>
<target state="needs-translation">Create new document(s)</target>
<target state="translated">新しいドキュメントを作成</target>
</trans-unit>
<trans-unit id="7860582931776068318" datatype="html">
<source>Add document version</source>
@@ -1539,7 +1539,7 @@
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
<context context-type="linenumber">87,88</context>
</context-group>
<target state="needs-translation">Add document version</target>
<target state="translated">ドキュメントのバージョンを追加</target>
</trans-unit>
<trans-unit id="8104421162933956065" datatype="html">
<source>Notes</source>
@@ -1639,7 +1639,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">347</context>
</context-group>
<target state="needs-translation"> Settings apply to this user account for objects (Tags, Mail Rules, etc.) created via the web UI. These settings do not apply to documents. </target>
<target state="translated">これらの設定は、Web UIから作成されたオブジェクト(タグ、メールルールなど)に対して、このユーザーアカウントに適用されます。ドキュメントには適用されません。</target>
</trans-unit>
<trans-unit id="4292903881380648974" datatype="html">
<source>Default Owner</source>
@@ -2203,7 +2203,7 @@
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">331</context>
</context-group>
<target state="needs-translation">Tasks</target>
<target state="translated">タスク</target>
</trans-unit>
<trans-unit id="8492095365580052820" datatype="html">
<source>Tasks shows detailed information about document consumption and system tasks.</source>
@@ -2211,7 +2211,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">4,5</context>
</context-group>
<target state="needs-translation">Tasks shows detailed information about document consumption and system tasks.</target>
<target state="translated">タスクでは、ドキュメントの取り込み処理やシステムタスクに関する詳細情報を確認できます。</target>
</trans-unit>
<trans-unit id="103921551219467537" datatype="html">
<source>Clear selection</source>
@@ -2235,7 +2235,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">16</context>
</context-group>
<target state="needs-translation">Dismiss all</target>
<target state="translated">すべて閉じる</target>
</trans-unit>
<trans-unit id="1616102757855967475" datatype="html">
<source>All</source>
@@ -2295,7 +2295,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">219</context>
</context-group>
<target state="needs-translation">All types</target>
<target state="translated">すべての種類</target>
</trans-unit>
<trans-unit id="131016739441837046" datatype="html">
<source>All sources</source>
@@ -2307,7 +2307,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">231</context>
</context-group>
<target state="needs-translation">All sources</target>
<target state="translated">すべてのソース</target>
</trans-unit>
<trans-unit id="3643866539136239218" datatype="html">
<source>Clear search</source>
@@ -2327,7 +2327,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">92,93</context>
</context-group>
<target state="needs-translation">Search tasks</target>
<target state="translated">タスクを検索</target>
</trans-unit>
<trans-unit id="6849725902312323996" datatype="html">
<source>Reset filters</source>
@@ -2351,7 +2351,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">122</context>
</context-group>
<target state="needs-translation">{VAR_PLURAL, plural, =1 {1 task} other {<x id="INTERPOLATION"/> tasks}}</target>
<target state="translated">{VAR_PLURAL, plural, =1 {1件のタスク} other {<x id="INTERPOLATION"/>件のタスク}}</target>
</trans-unit>
<trans-unit id="8953033926734869941" datatype="html">
<source>Name</source>
@@ -2651,7 +2651,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">207</context>
</context-group>
<target state="needs-translation">View task details</target>
<target state="translated">タスクの詳細を表示</target>
</trans-unit>
<trans-unit id="1536087519743707362" datatype="html">
<source>Dismiss</source>
@@ -2691,7 +2691,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">232,233</context>
</context-group>
<target state="needs-translation">Result message</target>
<target state="translated">結果メッセージ</target>
</trans-unit>
<trans-unit id="6621329748219109148" datatype="html">
<source>Duplicate</source>
@@ -2699,7 +2699,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">239,240</context>
</context-group>
<target state="needs-translation">Duplicate</target>
<target state="translated">重複</target>
</trans-unit>
<trans-unit id="7593555694782789615" datatype="html">
<source>Open</source>
@@ -2739,7 +2739,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">257,258</context>
</context-group>
<target state="needs-translation">Input data</target>
<target state="translated">入力データ</target>
</trans-unit>
<trans-unit id="1585185618099050920" datatype="html">
<source>Result data</source>
@@ -2747,7 +2747,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">263,264</context>
</context-group>
<target state="needs-translation">Result data</target>
<target state="translated">結果データ</target>
</trans-unit>
<trans-unit id="7976920528153858271" datatype="html">
<source>No tasks match the current filters.</source>
@@ -2755,7 +2755,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">285,287</context>
</context-group>
<target state="needs-translation">No tasks match the current filters.</target>
<target state="translated">現在のフィルター条件に一致するタスクはありません。</target>
</trans-unit>
<trans-unit id="2525230676386818985" datatype="html">
<source>Result</source>
@@ -2771,7 +2771,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">55</context>
</context-group>
<target state="needs-translation">Needs attention</target>
<target state="translated">注意が必要</target>
</trans-unit>
<trans-unit id="3307214558906712688" datatype="html">
<source>In progress</source>
@@ -2779,7 +2779,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">56</context>
</context-group>
<target state="needs-translation">In progress</target>
<target state="translated">進行中</target>
</trans-unit>
<trans-unit id="1371178633976505412" datatype="html">
<source>Recently completed</source>
@@ -2787,7 +2787,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">57</context>
</context-group>
<target state="needs-translation">Recently completed</target>
<target state="translated">最近完了したタスク</target>
</trans-unit>
<trans-unit id="2936452645673957909" datatype="html">
<source>Consume File</source>
@@ -2795,7 +2795,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">66</context>
</context-group>
<target state="needs-translation">Consume File</target>
<target state="translated">ファイルの取り込み</target>
</trans-unit>
<trans-unit id="5622386857946132209" datatype="html">
<source>Train Classifier</source>
@@ -2811,7 +2811,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">74</context>
</context-group>
<target state="needs-translation">Sanity Check</target>
<target state="translated">整合性チェック</target>
</trans-unit>
<trans-unit id="7502272564743467653" datatype="html">
<source>Mail Fetch</source>
@@ -2839,7 +2839,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">80</context>
</context-group>
<target state="needs-translation">Empty Trash</target>
<target state="translated">ゴミ箱を空にする</target>
</trans-unit>
<trans-unit id="4173754487295857311" datatype="html">
<source>Check Workflows</source>
@@ -2847,7 +2847,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">84</context>
</context-group>
<target state="needs-translation">Check Workflows</target>
<target state="translated">ワークフローの確認</target>
</trans-unit>
<trans-unit id="2065831805515896240" datatype="html">
<source>Bulk Update</source>
@@ -2855,7 +2855,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">88</context>
</context-group>
<target state="needs-translation">Bulk Update</target>
<target state="translated">一括更新</target>
</trans-unit>
<trans-unit id="5062348912423871240" datatype="html">
<source>Reprocess Document</source>
@@ -2863,7 +2863,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">92</context>
</context-group>
<target state="needs-translation">Reprocess Document</target>
<target state="translated">ドキュメントの再処理</target>
</trans-unit>
<trans-unit id="6903696804811148799" datatype="html">
<source>Build Share Link</source>
@@ -2871,7 +2871,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">96</context>
</context-group>
<target state="needs-translation">Build Share Link</target>
<target state="translated">共有リンクの生成</target>
</trans-unit>
<trans-unit id="6581973658756561124" datatype="html">
<source>Bulk Delete</source>
@@ -2879,7 +2879,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">100</context>
</context-group>
<target state="needs-translation">Bulk Delete</target>
<target state="translated">一括削除</target>
</trans-unit>
<trans-unit id="5672013191898828327" datatype="html">
<source>Apply AI Suggestions</source>
+20 -20
View File
@@ -17,7 +17,7 @@
<context context-type="linenumber">386,393</context>
</context-group>
<note priority="1" from="description">Currently selected slide number read by screen reader</note>
<target state="needs-translation"> Slide <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> of <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/> </target>
<target state="translated"> Shlid <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> i <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/></target>
</trans-unit>
<trans-unit id="ngb.carousel.previous" datatype="html">
<source>Previous</source>
@@ -571,7 +571,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">35</context>
</context-group>
<target state="needs-translation">This value overrides <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>, which is set outside Paperless.</target>
<target state="translated">Ky vlerë mbizotëron <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>, i cili është vendosur jashtë Paperless.</target>
</trans-unit>
<trans-unit id="7221396516204435584" datatype="html">
<source><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</source>
@@ -579,7 +579,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
<target state="needs-translation"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</target>
<target state="translated"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> është vendosur jashtë Paperless. Shkruani një vlerë këtu për ta mbingarkuar atë.</target>
</trans-unit>
<trans-unit id="8318849619178340389" datatype="html">
<source>Use the externally configured value</source>
@@ -587,7 +587,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">42</context>
</context-group>
<target state="needs-translation">Use the externally configured value</target>
<target state="translated">Përdorni vlerën e konfiguruar nga jashtë</target>
</trans-unit>
<trans-unit id="6032629623003430385" datatype="html">
<source>Reset to external</source>
@@ -595,7 +595,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">43</context>
</context-group>
<target state="needs-translation">Reset to external</target>
<target state="translated">Rinisni në të jashtëm</target>
</trans-unit>
<trans-unit id="7808756054397155068" datatype="html">
<source>Reset</source>
@@ -1135,7 +1135,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">115,116</context>
</context-group>
<target state="needs-translation">Sidebar items to show:</target>
<target state="translated">Elementët e shiritit anësor për t'u shfaqur:</target>
</trans-unit>
<trans-unit id="1356890996281769972" datatype="html">
<source>Dark mode</source>
@@ -1403,7 +1403,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">259,260</context>
</context-group>
<target state="needs-translation">Suggestions default to</target>
<target state="translated">Sugjerimet parazgjedhore janë</target>
</trans-unit>
<trans-unit id="6251586038459969294" datatype="html">
<source>Both</source>
@@ -1411,7 +1411,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">264</context>
</context-group>
<target state="needs-translation">Both</target>
<target state="translated">Të dyja</target>
</trans-unit>
<trans-unit id="8967224283122923634" datatype="html">
<source>ML only</source>
@@ -1419,7 +1419,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">266</context>
</context-group>
<target state="needs-translation">ML only</target>
<target state="translated">Vetëm ML</target>
</trans-unit>
<trans-unit id="6418969597665857901" datatype="html">
<source>AI only</source>
@@ -1427,7 +1427,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">268</context>
</context-group>
<target state="needs-translation">AI only</target>
<target state="translated">Vetëm AI</target>
</trans-unit>
<trans-unit id="1043449647727456069" datatype="html">
<source>Automatically request suggestions for inbox documents</source>
@@ -1435,7 +1435,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">276</context>
</context-group>
<target state="needs-translation">Automatically request suggestions for inbox documents</target>
<target state="translated">Kërkoni automatikisht sugjerime për dokumentet në kutinë e postës elektronike</target>
</trans-unit>
<trans-unit id="460700039378563125" datatype="html">
<source>If un-checked, suggestions must be requested via the Suggest button.</source>
@@ -1443,7 +1443,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">276</context>
</context-group>
<target state="needs-translation">If un-checked, suggestions must be requested via the Suggest button.</target>
<target state="translated">Nëse nuk është shënuar, sugjerimet duhet të kërkohen përmes butonit Sugjero.</target>
</trans-unit>
<trans-unit id="8793267604636304297" datatype="html">
<source>Show document thumbnail during loading</source>
@@ -2319,7 +2319,7 @@
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
<context context-type="linenumber">21</context>
</context-group>
<target state="needs-translation">Clear search</target>
<target state="translated">Pastro kërkimin</target>
</trans-unit>
<trans-unit id="3570013039254960445" datatype="html">
<source>Search tasks</source>
@@ -2651,7 +2651,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">207</context>
</context-group>
<target state="needs-translation">View task details</target>
<target state="translated">Shiko detajet e detyrës</target>
</trans-unit>
<trans-unit id="1536087519743707362" datatype="html">
<source>Dismiss</source>
@@ -2887,7 +2887,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">104</context>
</context-group>
<target state="needs-translation">Apply AI Suggestions</target>
<target state="translated">Apliko sugjerimet e AI</target>
</trans-unit>
<trans-unit id="9172233176401579786" datatype="html">
<source>Scheduled</source>
@@ -3783,7 +3783,7 @@
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">42</context>
</context-group>
<target state="needs-translation">User menu</target>
<target state="translated">Menuja e përdoruesit</target>
</trans-unit>
<trans-unit id="2448391510242468907" datatype="html">
<source>Logged in as <x id="INTERPOLATION" equiv-text="{{this.settingsService.displayName}}"/></source>
@@ -3939,7 +3939,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-links.component.html</context>
<context context-type="linenumber">2,3</context>
</context-group>
<target state="needs-translation">Share links</target>
<target state="translated">Ndani lidhjet</target>
</trans-unit>
<trans-unit id="7844706011418789951" datatype="html">
<source>Administration</source>
@@ -4015,7 +4015,7 @@
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">403</context>
</context-group>
<target state="needs-translation">Configure update checking</target>
<target state="translated">Konfiguro kontrollin e përditësimeve</target>
</trans-unit>
<trans-unit id="1542489069631984294" datatype="html">
<source>Sidebar views updated</source>
@@ -4259,7 +4259,7 @@
<context context-type="sourcefile">src/app/data/saved-view-icons.ts</context>
<context context-type="linenumber">21</context>
</context-group>
<target state="needs-translation">Chat</target>
<target state="translated">Bisedë</target>
</trans-unit>
<trans-unit id="8066608938393600549" datatype="html">
<source>Message</source>
@@ -4379,7 +4379,7 @@
<context context-type="sourcefile">src/app/components/common/confirm-dialog/merge-as-versions-confirm-dialog/merge-as-versions-confirm-dialog.component.html</context>
<context context-type="linenumber">8,9</context>
</context-group>
<target state="needs-translation">Root document:</target>
<target state="translated">Dokumenti rrënjësor:</target>
</trans-unit>
<trans-unit id="2121026240713807342" datatype="html">
<source>Versions (oldest first):</source>
+2
View File
@@ -41,6 +41,7 @@ import {
bell,
bodyText,
bookmark,
boxArrowInRight,
boxArrowUp,
boxArrowUpRight,
boxes,
@@ -293,6 +294,7 @@ const icons = {
braces,
bodyText,
bookmark,
boxArrowInRight,
boxArrowUp,
boxArrowUpRight,
boxes,
-38
View File
@@ -11,8 +11,6 @@ from typing import TYPE_CHECKING
import pytest
from paperless_testing import samples
if TYPE_CHECKING:
from collections.abc import Generator
from pathlib import Path
@@ -151,39 +149,3 @@ def fake_progress_manager(
monkeypatch.setattr("documents.tasks.ProgressManager", FakeProgressManager)
return FakeProgressManager
@pytest.fixture(scope="session")
def shared_samples_dir() -> Path:
"""Directory of sample files used by more than one app's tests."""
return samples.SHARED_SAMPLES_DIR
@pytest.fixture(scope="session")
def simple_digital_pdf_file() -> Path:
"""One-page PDF with a text layer."""
return samples.SIMPLE_DIGITAL_PDF
@pytest.fixture(scope="session")
def multi_page_digital_pdf_file() -> Path:
"""Three-page PDF with a text layer."""
return samples.MULTI_PAGE_DIGITAL_PDF
@pytest.fixture(scope="session")
def with_form_pdf_file() -> Path:
"""PDF containing a fillable form."""
return samples.WITH_FORM_PDF
@pytest.fixture(scope="session")
def multi_page_images_pdf_file() -> Path:
"""Multi-page PDF of scanned images, no text layer."""
return samples.MULTI_PAGE_IMAGES_PDF
@pytest.fixture(scope="session")
def thumbnail_webp_file() -> Path:
"""Small WebP thumbnail."""
return samples.THUMBNAIL_WEBP
+4
View File
@@ -54,6 +54,10 @@ def _predict_with_threshold(classifier, X, threshold: float) -> int | None:
cutoff so that uncertain predictions are discarded rather than assigned.
"""
probas = classifier.predict_proba(X)[0]
if len(classifier.classes_) == 1:
# scikit-learn treats a single-class fit as binary, so predict_proba
# returns [1 - p, p] for one class. Column 0 is the real class.
probas = probas[:1]
best_idx = int(probas.argmax())
best_class = int(classifier.classes_[best_idx])
+2
View File
@@ -130,12 +130,14 @@ class DocumentMetadataOverrides:
get_users_with_perms(
doc,
only_with_perms_in=["view_document"],
with_group_users=False,
).values_list("id", flat=True),
)
overrides.change_users = list(
get_users_with_perms(
doc,
only_with_perms_in=["change_document"],
with_group_users=False,
).values_list("id", flat=True),
)
overrides.custom_fields = {
@@ -3,6 +3,7 @@ from itertools import islice
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
from typing import Final
from allauth.mfa.models import Authenticator
from allauth.socialaccount.models import SocialAccount
@@ -70,6 +71,15 @@ from paperless.models import ApplicationConfiguration
from paperless_mail.models import MailAccount
from paperless_mail.models import MailRule
# Derived names replace the original's extension with these suffixes
THUMBNAIL_SUFFIX: Final[str] = "-thumbnail.webp"
ARCHIVE_SUFFIX: Final[str] = "-archive.pdf"
# Leave room for the longest derived suffix, which replaces the original's extension
MAX_BASE_NAME_BYTES: Final[int] = Document.MAX_PUBLIC_FILENAME_BYTES - max(
len(THUMBNAIL_SUFFIX),
len(ARCHIVE_SUFFIX),
)
def serialize_queryset_batched(
queryset: "QuerySet[Any]",
@@ -512,7 +522,10 @@ class Command(CryptMixin, PaperlessCommand):
counter=filename_counter,
)
else:
base_name = document.get_public_filename(counter=filename_counter)
base_name = document.get_public_filename(
counter=filename_counter,
max_bytes=MAX_BASE_NAME_BYTES,
)
# The thumbnail and archive names are derived from the stem, so
# uniqueness must hold without the original's extension
@@ -541,7 +554,7 @@ class Command(CryptMixin, PaperlessCommand):
document_dict[EXPORTER_FILE_NAME] = original_arc
if not self.no_thumbnail:
thumbnail_name = base_name.parent / (base_name.stem + "-thumbnail.webp")
thumbnail_name = base_name.parent / (base_name.stem + THUMBNAIL_SUFFIX)
if self.use_folder_prefix:
thumbnail_name = Path("thumbnails") / thumbnail_name
thumbnail_arc = thumbnail_name.as_posix()
@@ -550,7 +563,7 @@ class Command(CryptMixin, PaperlessCommand):
thumbnail_arc = None
if not self.no_archive and document.has_archive_version:
archive_name = base_name.parent / (base_name.stem + "-archive.pdf")
archive_name = base_name.parent / (base_name.stem + ARCHIVE_SUFFIX)
if self.use_folder_prefix:
archive_name = Path("archive") / archive_name
archive_arc = archive_name.as_posix()
@@ -66,7 +66,7 @@ def iter_manifest_records(path: Path) -> Generator[dict, None, None]:
"""Yield records one at a time from a manifest JSON array via ijson."""
try:
with path.open("rb") as f:
yield from ijson.items(f, "item")
yield from ijson.items(f, "item", use_float=True)
except ijson.JSONError as e:
raise CommandError(f"Failed to parse manifest file {path}: {e}") from e
+23 -12
View File
@@ -157,6 +157,8 @@ class StoragePath(MatchingModel):
class Document(SoftDeleteModel, ModelWithOwner): # type: ignore[django-manager-missing]
MAX_STORED_FILENAME_LENGTH: Final[int] = 1024
# Per-component limit of common filesystems (ext4, XFS, btrfs, ZFS, APFS)
MAX_PUBLIC_FILENAME_BYTES: Final[int] = 255
correspondent = models.ForeignKey(
Correspondent,
@@ -484,28 +486,37 @@ class Document(SoftDeleteModel, ModelWithOwner): # type: ignore[django-manager-
def archive_file(self):
return Path(self.archive_path).open("rb")
def get_public_filename(self, *, archive=False, counter=0, suffix=None) -> str:
def get_public_filename(
self,
*,
archive: bool = False,
counter: int = 0,
max_bytes: int = MAX_PUBLIC_FILENAME_BYTES,
) -> str:
"""
Returns a sanitized filename for the document, not including any paths.
The name fits in max_bytes (UTF-8). A long title is shortened before
the counter and extension are appended, because sanitize_filename
truncates from the end. The tail is cut only when max_bytes is
smaller than the tail itself.
"""
# Root owns metadata for all versions
context_document = (
self.root_document if self.root_document_id is not None else self
)
result = str(context_document)
if counter:
result += f"_{counter:02}"
extension = ".pdf" if archive else self.file_type
tail = f"_{counter:02}{extension}" if counter else extension
if suffix:
result += suffix
head_bytes = max(0, max_bytes - len(tail.encode()))
head = str(context_document).encode()[:head_bytes].decode(errors="ignore")
if archive:
result += ".pdf"
else:
result += self.file_type
return pathvalidate.sanitize_filename(result, replacement_text="-")
return pathvalidate.sanitize_filename(
head + tail,
replacement_text="-",
max_len=max_bytes,
)
@property
def file_type(self):
+22
View File
@@ -511,6 +511,28 @@ def restrict_queryset_to_visible(
return queryset.filter(pk__in=permitted_object_ids(user, queryset.model, perm))
def user_is_active_superuser(user: User | None) -> bool:
"""
True only for an active, authenticated superuser.
"""
return user is not None and user_is_unrestricted(user)
def restrict_queryset_to_owner_visible(
queryset: QuerySet[ModelT],
user: User | None,
perm: str,
) -> QuerySet[ModelT]:
"""
Like ``restrict_queryset_to_visible``, but ``user=None`` sees only unowned
rows (``permitted_object_ids(None, ...)`` semantics) instead of every row.
See ``user_is_active_superuser``.
"""
if user_is_active_superuser(user):
return queryset
return queryset.filter(pk__in=permitted_object_ids(user, queryset.model, perm))
def permitted_document_ids(
user: User | None,
*,
+9
View File
@@ -926,6 +926,7 @@ class TantivyBackend:
sort_field: str | None = None,
sort_reverse: bool = False,
search_mode: SearchMode = SearchMode.QUERY,
filter_query: str | None = None,
limit: int | None = None,
) -> list[int]:
"""
@@ -941,6 +942,7 @@ class TantivyBackend:
sort_field: Field to sort by (None for relevance ranking)
sort_reverse: Whether to reverse the sort order
search_mode: Query parsing mode (QUERY, TEXT, or TITLE)
filter_query: Optional advanced query results must also match
limit: Maximum number of IDs to return (None = all matching docs)
Returns:
@@ -948,6 +950,13 @@ class TantivyBackend:
"""
self._ensure_open()
user_query = self._parse_query(query, search_mode)
if filter_query is not None:
user_query = user_query.and_must_match(
tantivy.Query.const_score_query(
self._parse_query(filter_query, SearchMode.QUERY),
0.0, # zero so doesn't affect relevance
),
)
final_query = self._apply_permission_filter(user_query, user)
searcher = self._index.searcher()
+10 -6
View File
@@ -12,25 +12,29 @@ if TYPE_CHECKING:
from paperless_testing.dirs import PaperlessDirs
@pytest.fixture(scope="session")
def document_samples_dir() -> Path:
"""Path to the shared test sample documents."""
return Path(__file__).parent / "samples" / "documents"
@pytest.fixture()
def sample_doc(
paperless_dirs: "PaperlessDirs",
simple_digital_pdf_file: Path,
multi_page_images_pdf_file: Path,
thumbnail_webp_file: Path,
document_samples_dir: Path,
) -> "Document":
"""Create a document with valid files and matching checksums."""
with filelock.FileLock(paperless_dirs.media_lock):
shutil.copy(
simple_digital_pdf_file,
document_samples_dir / "originals" / "0000001.pdf",
paperless_dirs.originals_dir / "0000001.pdf",
)
shutil.copy(
multi_page_images_pdf_file,
document_samples_dir / "archive" / "0000001.pdf",
paperless_dirs.archive_dir / "0000001.pdf",
)
shutil.copy(
thumbnail_webp_file,
document_samples_dir / "thumbnails" / "0000001.webp",
paperless_dirs.thumbnail_dir / "0000001.webp",
)
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.
+1
View File
@@ -0,0 +1 @@
This is a test file.
@@ -53,7 +53,7 @@ class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase):
archive_checksum="D",
)
shutil.copy(self.SIMPLE_PDF, self.doc2.source_path)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", self.doc2.source_path)
shutil.copy(self.SAMPLE_DIR / "simple.png", self.doc2b.source_path)
shutil.copy(self.SAMPLE_DIR / "simple.jpg", self.doc3.source_path)
shutil.copy(self.SAMPLE_DIR / "test_with_bom.pdf", self.doc3.archive_path)
+2 -1
View File
@@ -835,6 +835,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
for filters in (
{"text": "new doc 2017-03-16"},
{"title_search": "apple"},
{"title_search": "apple", "query": "created:[-1 year to now]"},
):
with self.subTest(filters=filters):
get_backend.return_value.search_ids.return_value = [self.doc2.id]
@@ -886,7 +887,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
"all": True,
"filters": {
"text": "B",
"query": "c1",
"title_search": "c1",
},
"method": "set_storage_path",
"parameters": {"storage_path": self.sp1.id},
+33 -49
View File
@@ -56,8 +56,6 @@ from paperless_testing.http import read_streaming_response
from paperless_testing.permissions import grant_all_global
from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
from paperless_testing.samples import THUMBNAIL_WEBP
class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
@@ -1854,11 +1852,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f},
@@ -1886,7 +1880,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
payload = SimpleUploadedFile(
"../../outside.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
(Path(__file__).parent / "samples" / "simple.pdf").read_bytes(),
content_type="application/pdf",
)
@@ -1915,7 +1909,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
pdf_bytes = SIMPLE_DIGITAL_PDF.read_bytes()
pdf_bytes = (Path(__file__).parent / "samples" / "simple.pdf").read_bytes()
boundary = "paperless-boundary"
payload = (
(
@@ -1991,7 +1985,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
def test_upload_insufficient_permissions(self) -> None:
self.client.force_authenticate(user=UserFactory(username="testuser2"))
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f},
@@ -2004,11 +1998,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{
@@ -2041,7 +2031,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"documenst": f},
@@ -2067,7 +2057,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "title": "my custom title"},
@@ -2087,7 +2077,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
c = Correspondent.objects.create(name="test-corres")
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "correspondent": c.id},
@@ -2106,7 +2096,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "correspondent": 3456},
@@ -2121,7 +2111,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
dt = DocumentType.objects.create(name="invoice")
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "document_type": dt.id},
@@ -2140,7 +2130,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "document_type": 34578},
@@ -2155,7 +2145,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
sp = StoragePath.objects.create(name="invoices")
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "storage_path": sp.id},
@@ -2174,7 +2164,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "storage_path": 34578},
@@ -2190,7 +2180,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
t1 = Tag.objects.create(name="tag1")
t2 = Tag.objects.create(name="tag2")
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "tags": [t2.id, t1.id]},
@@ -2211,7 +2201,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
t1 = Tag.objects.create(name="tag1")
t2 = Tag.objects.create(name="tag2")
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "tags": [t2.id, t1.id, 734563]},
@@ -2235,7 +2225,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
0,
tzinfo=zoneinfo.ZoneInfo("America/Los_Angeles"),
)
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "created": created},
@@ -2251,11 +2241,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "archive_serial_number": 500},
@@ -2282,11 +2268,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
data_type=CustomField.FieldDataType.STRING,
)
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{
@@ -2341,7 +2323,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
w1.actions.add(action1)
w1.save()
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{
@@ -2382,11 +2364,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
data_type=CustomField.FieldDataType.INT,
)
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{
@@ -2435,7 +2413,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
]
for payload in error_payloads:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
data = {"document": f, **payload}
response = self.client.post(
"/api/documents/post_document/",
@@ -2493,7 +2471,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with SIMPLE_DIGITAL_PDF.open("rb") as f:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "from_webui": True},
@@ -2532,8 +2510,14 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
archive_filename="archive.pdf",
)
source_file: Path = THUMBNAIL_WEBP
archive_file: Path = SIMPLE_DIGITAL_PDF
source_file: Path = (
Path(__file__).parent
/ "samples"
/ "documents"
/ "thumbnails"
/ "0000001.webp"
)
archive_file: Path = Path(__file__).parent / "samples" / "simple.pdf"
shutil.copy(source_file, doc.source_path)
shutil.copy(archive_file, doc.archive_path)
@@ -2566,7 +2550,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
mime_type="application/pdf",
)
shutil.copy(SIMPLE_DIGITAL_PDF, doc.source_path)
shutil.copy(Path(__file__).parent / "samples" / "simple.pdf", doc.source_path)
response = self.client.get(f"/api/documents/{doc.pk}/metadata/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -4304,8 +4288,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
filename="test2.pdf",
)
archive_file = SIMPLE_DIGITAL_PDF
source_file = SIMPLE_DIGITAL_PDF
archive_file = Path(__file__).parent / "samples" / "simple.pdf"
source_file = Path(__file__).parent / "samples" / "simple.pdf"
shutil.copy(archive_file, doc.archive_path)
shutil.copy(source_file, doc2.source_path)
+5 -8
View File
@@ -12,9 +12,6 @@ from documents.tests.utils import SampleDirMixin
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global
from paperless_testing.samples import MULTI_PAGE_DIGITAL_PDF
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
from paperless_testing.samples import WITH_FORM_PDF
class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
@@ -45,15 +42,15 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
# Copy sample files to document paths (using different files to distinguish versions)
shutil.copy(
SIMPLE_DIGITAL_PDF,
self.SAMPLE_DIR / "documents" / "originals" / "0000001.pdf",
self.doc1.archive_path,
)
shutil.copy(
MULTI_PAGE_DIGITAL_PDF,
self.SAMPLE_DIR / "documents" / "originals" / "0000002.pdf",
self.doc1.source_path,
)
shutil.copy(
WITH_FORM_PDF,
self.SAMPLE_DIR / "documents" / "originals" / "0000003.pdf",
self.doc2.source_path,
)
@@ -380,7 +377,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
checksum="3",
filename="test3.pdf",
)
shutil.copy(self.SIMPLE_PDF, doc3.source_path)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", doc3.source_path)
doc4 = Document.objects.create(
title="test1",
@@ -389,7 +386,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
checksum="4",
filename="test4.pdf",
)
shutil.copy(self.SIMPLE_PDF, doc4.source_path)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", doc4.source_path)
response = self.client.post(
self.ENDPOINT,
+61 -24
View File
@@ -245,8 +245,68 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
self.assertEqual(response.data["count"], 1)
self.assertEqual(response.data["results"][0]["id"], title_match.id)
def test_simple_search_combined_with_query_filter(self) -> None:
"""
GIVEN:
- A document with the term in its title, created this year
- A document with the term only in its content, created this year
- A document with the term in its title, created years ago
WHEN:
- A title or text search is combined with a relative created
date query, as the filter editor sends it
THEN:
- The simple search keeps its own field scope and the query only
narrows the results (issue #14436)
"""
this_year = timezone.localdate().replace(month=1, day=1)
title_match = Document.objects.create(
title="foo report",
content="nothing relevant",
checksum="F1",
created=this_year,
pk=21,
)
content_match = Document.objects.create(
title="something else",
content="foo appears here",
checksum="F2",
created=this_year,
pk=22,
)
old_title_match = Document.objects.create(
title="foo archive",
content="nothing relevant",
checksum="F3",
created=datetime.date(2001, 1, 1),
pk=23,
)
backend = get_backend()
for doc in (title_match, content_match, old_title_match):
backend.add_or_update(doc)
query = "created:[-1 year to now]"
response = self.client.get(
"/api/documents/",
{"title_search": "foo", "query": query},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(
[r["id"] for r in response.data["results"]],
[title_match.id],
)
response = self.client.get(
"/api/documents/",
{"text": "foo", "query": query},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertCountEqual(
[r["id"] for r in response.data["results"]],
[title_match.id, content_match.id],
)
def test_search_rejects_multiple_search_modes(self) -> None:
response = self.client.get("/api/documents/?text=bank&query=bank")
response = self.client.get("/api/documents/?text=bank&title_search=bank")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data["detail"],
@@ -2077,29 +2137,6 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
self.assertEqual(len(response.data["documents"]), 1)
self.assertEqual(response.data["documents"][0]["id"], title_match.id)
def test_global_search_returns_latest_version_content(self) -> None:
root = Document.objects.create(
title="bank statement",
content="superseded content",
checksum="GSV1",
pk=23,
)
Document.objects.create(
title="bank statement v2",
content="latest content",
checksum="GSV2",
pk=24,
root_document=root,
version_index=1,
)
self.client.force_authenticate(self.user)
response = self.client.get("/api/search/?query=bank&db_only=true")
self.assertEqual(response.status_code, status.HTTP_200_OK)
returned = {doc["id"]: doc["content"] for doc in response.data["documents"]}
self.assertEqual(returned.get(root.id), "latest content")
def test_global_search_filters_owned_mail_objects(self) -> None:
user1 = UserFactory(username="mail-search-user")
user2 = UserFactory(username="other-mail-search-user")
+2 -2
View File
@@ -167,7 +167,7 @@ class TestBarcode(
- No barcodes detected
- No pages to split on
"""
test_file = self.SIMPLE_PDF
test_file = self.SAMPLE_DIR / "simple.pdf"
with self.get_reader(test_file) as reader:
reader.detect()
separator_page_numbers = reader.get_separation_pages()
@@ -459,7 +459,7 @@ class TestBarcode(
THEN:
- Nothing happens
"""
test_file = self.SIMPLE_PDF
test_file = self.SAMPLE_DIR / "simple.pdf"
with self.get_reader(test_file) as reader:
try:
+30 -9
View File
@@ -27,9 +27,6 @@ from documents.permissions import set_permissions_for_objects
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.permissions import grant_object
from paperless_testing.samples import MULTI_PAGE_DIGITAL_PDF
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
from paperless_testing.samples import WITH_FORM_PDF
class TestBulkEdit(DirectoriesMixin, TestCase):
@@ -707,27 +704,47 @@ class TestPDFActions(DirectoriesMixin, TestCase):
super().setUp()
sample1 = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(
SIMPLE_DIGITAL_PDF,
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
sample1,
)
sample1_archive = self.dirs.archive_dir / "sample_archive.pdf"
shutil.copy(
SIMPLE_DIGITAL_PDF,
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
sample1_archive,
)
sample2 = self.dirs.scratch_dir / "sample2.pdf"
shutil.copy(
MULTI_PAGE_DIGITAL_PDF,
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000002.pdf",
sample2,
)
sample2_archive = self.dirs.archive_dir / "sample2_archive.pdf"
shutil.copy(
MULTI_PAGE_DIGITAL_PDF,
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000002.pdf",
sample2_archive,
)
sample3 = self.dirs.scratch_dir / "sample3.pdf"
shutil.copy(
WITH_FORM_PDF,
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000003.pdf",
sample3,
)
self.doc1 = Document.objects.create(
@@ -761,7 +778,11 @@ class TestPDFActions(DirectoriesMixin, TestCase):
)
img_doc_archive = self.dirs.archive_dir / "sample_image.pdf"
shutil.copy(
SIMPLE_DIGITAL_PDF,
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
img_doc_archive,
)
self.img_doc = Document.objects.create(
+44
View File
@@ -14,6 +14,7 @@ from django.test import override_settings
from django.test.utils import CaptureQueriesContext
from pytest_django.fixtures import Settings
from pytest_mock import MockerFixture
from sklearn.neural_network import MLPClassifier
from documents.classifier import ClassifierModelCorruptError
from documents.classifier import DocumentClassifier
@@ -1036,6 +1037,49 @@ def test_classifier_match_threshold_default() -> None:
assert settings.CLASSIFIER_MATCH_THRESHOLD == 0.3
class TestPredictWithThresholdSingleClass:
@pytest.fixture
def single_class_model(self) -> tuple[MLPClassifier, np.ndarray]:
X = np.random.default_rng(0).random((2, 5))
return MLPClassifier(tol=0.01, random_state=0).fit(X, [3, 3]), X[:1]
def test_returns_only_class(
self,
single_class_model: tuple[MLPClassifier, np.ndarray],
) -> None:
"""
GIVEN:
- A classifier fitted on a single class (every document shares one label)
WHEN:
- A prediction is requested with the threshold disabled
THEN:
- No error is raised and the only class is returned
"""
classifier, X = single_class_model
assert _predict_with_threshold(classifier, X, threshold=0.0) == 3
def test_applies_threshold(
self,
single_class_model: tuple[MLPClassifier, np.ndarray],
) -> None:
"""
GIVEN:
- A classifier fitted on a single class
WHEN:
- A prediction is requested
THEN:
- The class's confidence is still compared against the threshold
"""
classifier, X = single_class_model
confidence = classifier.predict_proba(X)[0][0]
assert _predict_with_threshold(classifier, X, threshold=confidence - 0.01) == 3
assert (
_predict_with_threshold(classifier, X, threshold=confidence + 0.01) is None
)
class TestPreprocessContent:
@pytest.fixture
def samples(self) -> Path:
+56 -9
View File
@@ -34,11 +34,10 @@ from documents.tests.utils import GetConsumerMixin
from paperless_mail.models import MailRule
from paperless_testing.assertions import FileSystemAssertsMixin
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import UserFactory
from paperless_testing.fakes.progress import FakeProgressManager
from paperless_testing.samples import MULTI_PAGE_DIGITAL_PDF
from paperless_testing.samples import MULTI_PAGE_IMAGES_PDF
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
from paperless_testing.permissions import grant_object
class _BaseNewStyleParser:
@@ -115,7 +114,9 @@ class _BaseNewStyleParser:
class DummyParser(_BaseNewStyleParser):
_ARCHIVE_SRC = MULTI_PAGE_IMAGES_PDF
_ARCHIVE_SRC = (
Path(__file__).parent / "samples" / "documents" / "archive" / "0000001.pdf"
)
def parse(self, document_path, mime_type, *, produce_archive: bool = True) -> None:
self._text = "The Text"
@@ -201,19 +202,33 @@ class TestConsumer(
self.addCleanup(patcher.stop)
def get_test_file(self):
src = SIMPLE_DIGITAL_PDF
src = (
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf"
)
dst = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(src, dst)
return dst
def get_test_file2(self):
src = MULTI_PAGE_DIGITAL_PDF
src = (
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000002.pdf"
)
dst = self.dirs.scratch_dir / "sample2.pdf"
shutil.copy(src, dst)
return dst
def get_test_archive_file(self):
src = MULTI_PAGE_IMAGES_PDF
src = (
Path(__file__).parent / "samples" / "documents" / "archive" / "0000001.pdf"
)
dst = self.dirs.scratch_dir / "sample_archive.pdf"
shutil.copy(src, dst)
return dst
@@ -1049,7 +1064,7 @@ class TestConsumer(
@mock.patch("documents.consumer.get_parser_registry")
def test_similar_filenames(self, m) -> None:
shutil.copy(
SIMPLE_DIGITAL_PDF,
Path(__file__).parent / "samples" / "simple.pdf",
settings.CONSUMPTION_DIR / "simple.pdf",
)
shutil.copy(
@@ -1590,7 +1605,13 @@ class TestConsumerRemoteOCR(
self.addCleanup(patcher.stop)
def _consume(self, *, overrides: DocumentMetadataOverrides | None = None) -> bool:
src = SIMPLE_DIGITAL_PDF
src = (
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf"
)
dst = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(src, dst)
@@ -1664,6 +1685,32 @@ class TestMetadataOverrides(TestCase):
self.assertEqual(base.actor_id, 2)
self.assertEqual(base.version_label, "v2")
def test_from_document_copies_user_and_group_grants_separately(self) -> None:
"""
GIVEN:
- A document with view and change granted directly to one user and
to a group with another user as a member
WHEN:
- Overrides are built from the document
THEN:
- The user lists hold only the directly granted user
- The group lists hold the group
"""
direct_user = UserFactory()
group_member = UserFactory()
group = Group.objects.create(name="group")
group_member.groups.add(group)
document = DocumentFactory()
grant_object(direct_user, document, "view_document", "change_document")
grant_object(group, document, "view_document", "change_document")
overrides = DocumentMetadataOverrides.from_document(document)
self.assertEqual(overrides.view_users, [direct_user.pk])
self.assertEqual(overrides.change_users, [direct_user.pk])
self.assertEqual(overrides.view_groups, [group.pk])
self.assertEqual(overrides.change_groups, [group.pk])
class TestBarcodeApplyDetectedASN(TestCase):
"""
@@ -423,35 +423,3 @@ class TestTrashAndGlobalSearchEffectiveContentIsNeverPerInstance:
[result] = [r for r in response.data["results"] if r["id"] == root.id]
assert result["content"] == "own-content"
assert _get_effective_content_fallback_queries(ctx) == []
def test_global_search_db_only_shows_latest_version_content_with_no_extra_query(
self,
admin_client: APIClient,
) -> None:
"""
GIVEN:
- A root document, findable by title, whose own content differs
from its latest version's
WHEN:
- Using the global search endpoint's db_only mode
THEN:
- The response shows the latest version's content, resolved by
GlobalSearchView's own effective_content annotation
- There is no per-instance fallback query
"""
root = DocumentFactory(title="findme", content="own-content")
DocumentFactory(
root_document=root,
version_index=1,
content="version-content",
)
with CaptureQueriesContext(connection) as ctx:
response = admin_client.get(
"/api/search/?query=findme&db_only=true",
)
assert response.status_code == status.HTTP_200_OK
[result] = [d for d in response.data["documents"] if d["id"] == root.id]
assert result["content"] == "version-content"
assert _get_effective_content_fallback_queries(ctx) == []
@@ -4,13 +4,16 @@ from datetime import date
from pathlib import Path
from unittest import mock
import pytest
from django.test import TestCase
from django.test import override_settings
from faker import Faker
from pytest_mock import MockerFixture
from documents.models import Correspondent
from documents.models import Document
from documents.tasks import empty_trash
from paperless_testing.factories import DocumentFactory
class TestDocument(TestCase):
@@ -257,3 +260,72 @@ def test_suggestion_content() -> None:
)
doc.content = content_over_limit
assert doc.suggestion_content == expected_cropped_content
class TestPublicFilenameLength:
@pytest.fixture(autouse=True)
def _no_parser_registry(self, mocker: MockerFixture) -> None:
# The parser registry reads ApplicationConfiguration from the database
extensions = {"application/pdf": ".pdf", "image/jpeg": ".jpg"}
mocker.patch(
"documents.models.get_default_file_extension",
side_effect=extensions.__getitem__,
)
LIMIT = Document.MAX_PUBLIC_FILENAME_BYTES
@pytest.mark.parametrize(
("title", "archive", "counter", "max_bytes", "tail"),
[
pytest.param("a" * 300, False, 0, LIMIT, ".jpg", id="ascii"),
pytest.param("a" * 300, True, 0, LIMIT, ".pdf", id="archive"),
pytest.param("ä" * 200, False, 0, LIMIT, ".jpg", id="umlaut"),
pytest.param("ä" * 200, False, 1, LIMIT, "_01.jpg", id="count"),
pytest.param("a" * 300, False, 1, 100, "_01.jpg", id="max"),
],
)
def test_long_title_keeps_tail_within_limit(
self,
title: str,
*,
archive: bool,
counter: int,
max_bytes: int,
tail: str,
) -> None:
"""
GIVEN:
- A document whose title alone is longer than the byte limit
WHEN:
- The public file name is generated
THEN:
- The name ends with the counter and extension
- The name fits in the limit
"""
name = DocumentFactory.build(
title=title,
mime_type="image/jpeg",
).get_public_filename(
archive=archive,
counter=counter,
max_bytes=max_bytes,
)
assert name.endswith(tail)
assert len(name.encode()) <= max_bytes
def test_long_title_counters_give_distinct_names(self) -> None:
"""
GIVEN:
- A document whose title alone is longer than the byte limit
WHEN:
- Public file names are generated with increasing counters
THEN:
- Each counter produces a distinct name
"""
doc = DocumentFactory.build(title="ä" * 200, mime_type="application/pdf")
names = [doc.get_public_filename(counter=counter) for counter in range(3)]
assert len(set(names)) == 3
assert all(len(n.encode()) <= self.LIMIT for n in names)
+2 -5
View File
@@ -37,15 +37,12 @@ class TestDoubleSided(
self.double_sided_dir.mkdir()
self.staging_file = self.dirs.scratch_dir / STAGING_FILE_NAME
def _sample(self, name: str) -> Path:
return self.SIMPLE_PDF if name == "simple.pdf" else self.SAMPLE_DIR / name
def consume_file(self, srcname, dstname: str | Path = "foo.pdf"):
"""
Starts the consume process and also ensures the
destination file does not exist afterwards
"""
src = self._sample(srcname)
src = self.SAMPLE_DIR / srcname
dst = self.double_sided_dir / dstname
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(src, dst)
@@ -60,7 +57,7 @@ class TestDoubleSided(
return msg
def create_staging_file(self, src="double-sided-odd.pdf", datetime=None) -> None:
shutil.copy(self._sample(src), self.staging_file)
shutil.copy(self.SAMPLE_DIR / src, self.staging_file)
if datetime is None:
datetime = dt.datetime.now()
os.utime(str(self.staging_file), (datetime.timestamp(),) * 2)
+1 -2
View File
@@ -23,9 +23,8 @@ from documents.tasks import update_document_content_maybe_archive_file
from paperless_testing.assertions import FileSystemAssertsMixin
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
sample_file: Path = SIMPLE_DIGITAL_PDF
sample_file: Path = Path(__file__).parent / "samples" / "simple.pdf"
@pytest.mark.management
+187 -21
View File
@@ -50,6 +50,7 @@ from documents.settings import EXPORTER_FILE_NAME
from documents.settings import EXPORTER_SHARE_LINK_BUNDLE_NAME
from documents.settings import EXPORTER_THUMBNAIL_NAME
from documents.tests.utils import SampleDirMixin
from paperless.models import ApplicationConfiguration
from paperless_mail.models import MailAccount
from paperless_testing.assertions import FileSystemAssertsMixin
from paperless_testing.dirs import DirectoriesMixin
@@ -58,7 +59,6 @@ from paperless_testing.dirs import paperless_environment
from paperless_testing.factories import DocumentBarcodeFactory
from paperless_testing.factories import DocumentFactory
from paperless_testing.permissions import grant_object
from paperless_testing.samples import install_document_samples
@pytest.mark.management
@@ -204,7 +204,10 @@ class TestExportImport(
def test_exporter(self, *, use_filename_format=False) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
num_permission_objects = Permission.objects.count()
@@ -308,7 +311,10 @@ class TestExportImport(
def test_exporter_with_filename_format(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
with override_settings(
FILENAME_FORMAT="{created_year}/{correspondent}/{title}",
@@ -317,7 +323,10 @@ class TestExportImport(
def test_exporter_includes_share_links_and_bundles(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
share_link = ShareLink.objects.create(
slug="share-link-slug",
@@ -416,7 +425,10 @@ class TestExportImport(
def test_update_export_changed_time(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
self._do_export()
self.assertIsFile(self.target / "manifest.json")
@@ -452,7 +464,10 @@ class TestExportImport(
def test_update_export_changed_checksum(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
self._do_export()
@@ -479,7 +494,10 @@ class TestExportImport(
def test_update_export_deleted_document(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
manifest = self._do_export()
@@ -511,7 +529,10 @@ class TestExportImport(
@override_settings(FILENAME_FORMAT="{title}/{correspondent}")
def test_update_export_changed_location(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
self._do_export(use_filename_format=True)
self.assertIsFile(self.target / "wow1" / "c.pdf")
@@ -553,7 +574,10 @@ class TestExportImport(
- Zipfile contains exported files
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
args = ["document_exporter", self.target, "--zip"]
@@ -582,7 +606,10 @@ class TestExportImport(
- Zipfile contains exported files
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
args = ["document_exporter", self.target, "--zip", "--use-filename-format"]
@@ -618,7 +645,10 @@ class TestExportImport(
- The existing file and directory in target are removed
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
# Create stuff in target directory
existing_file = self.target / "test.txt"
@@ -714,7 +744,10 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
manifest = self._do_export()
has_archive = False
@@ -757,7 +790,10 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
manifest = self._do_export()
has_thumbnail = False
@@ -802,7 +838,10 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
manifest = self._do_export(split_manifest=True)
has_document = False
@@ -827,7 +866,10 @@ class TestExportImport(
def _export_import_barcodes(self, *, split_manifest: bool) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
DocumentBarcodeFactory(document=self.d1, value="https://example.com")
DocumentBarcodeFactory(document=self.d2, page=2, value="DE8937")
@@ -863,7 +905,10 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
self._do_export(use_folder_prefix=True)
@@ -890,7 +935,10 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
self._do_export(use_folder_prefix=True, split_manifest=True)
@@ -917,7 +965,10 @@ class TestExportImport(
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
num_content_type_objects = ContentType.objects.count()
num_permission_objects = Permission.objects.count()
@@ -954,7 +1005,10 @@ class TestExportImport(
def test_exporter_with_auditlog_disabled(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
with override_settings(
AUDIT_LOG_ENABLED=False,
@@ -974,7 +1028,10 @@ class TestExportImport(
survive the round-trip with deleted_at preserved
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
install_document_samples(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
# d1 has self.note and self.cfi1 attached via setUp
self.d1.delete()
@@ -1018,7 +1075,10 @@ class TestExportImport(
"""
shutil.rmtree(self.dirs.media_dir / "documents")
install_document_samples(self.dirs.media_dir / "documents")
shutil.copytree(
self.SAMPLE_DIR / "documents",
self.dirs.media_dir / "documents",
)
_ = self._do_export(data_only=True)
@@ -1343,6 +1403,45 @@ class TestCryptExportImport(
social_token = SocialToken.objects.first()
self.assertIsNotNone(social_token)
def test_import_passphrase_with_float_field(self) -> None:
"""
GIVEN:
- An application configuration with a float field set
WHEN:
- Export and import are run with a passphrase
THEN:
- The import succeeds and the float value is preserved
"""
ApplicationConfiguration.objects.update_or_create(
pk=1,
defaults={"rotate_pages_threshold": 12.5},
)
call_command(
"document_exporter",
"--no-progress-bar",
"--passphrase",
"securepassword",
self.target,
skip_checks=True,
)
ApplicationConfiguration.objects.all().delete()
call_command(
"document_importer",
"--no-progress-bar",
"--passphrase",
"securepassword",
self.target,
skip_checks=True,
)
self.assertEqual(
ApplicationConfiguration.objects.get().rotate_pages_threshold,
12.5,
)
def test_import_crypt_no_passphrase(self) -> None:
"""
GIVEN:
@@ -1478,3 +1577,70 @@ class TestExportDerivedFileNameCollisions:
assert (
target / record[EXPORTER_ARCHIVE_NAME]
).read_text() == f"archive {pk}"
def test_long_titles_export_within_file_name_limit(
self,
paperless_dirs: PaperlessDirs,
tmp_path: Path,
) -> None:
"""
GIVEN:
- Two documents with the same created date and the same title
- The title alone is longer than 255 bytes and contains umlauts
WHEN:
- The exporter runs
THEN:
- The export finishes with distinct original, thumbnail and archive names
- Every exported file name fits in the byte limit
- Each exported file holds its own document's content
"""
for pk in (1, 2):
(paperless_dirs.originals_dir / f"{pk:07}.pdf").write_bytes(
f"original {pk}".encode(),
)
(paperless_dirs.archive_dir / f"{pk:07}.pdf").write_bytes(
f"archive {pk}".encode(),
)
(paperless_dirs.thumbnail_dir / f"{pk:07}.webp").write_bytes(
f"thumbnail {pk}".encode(),
)
DocumentFactory(
pk=pk,
title="Bestellbestätigung " * 15,
created=date(2026, 10, 15),
filename=f"{pk:07}.pdf",
archive_filename=f"{pk:07}.pdf",
mime_type="application/pdf",
)
target = tmp_path / "export"
target.mkdir()
call_command(
"document_exporter",
str(target),
"--no-progress-bar",
skip_checks=True,
)
manifest = json.loads((target / "manifest.json").read_text())
records = {r["pk"]: r for r in manifest if r["model"] == "documents.document"}
for key in (
EXPORTER_FILE_NAME,
EXPORTER_THUMBNAIL_NAME,
EXPORTER_ARCHIVE_NAME,
):
assert records[1][key] != records[2][key]
for pk in (1, 2):
record = records[pk]
for key, content in (
(EXPORTER_FILE_NAME, f"original {pk}"),
(EXPORTER_THUMBNAIL_NAME, f"thumbnail {pk}"),
(EXPORTER_ARCHIVE_NAME, f"archive {pk}"),
):
assert (
len(Path(record[key]).name.encode())
<= Document.MAX_PUBLIC_FILENAME_BYTES
)
assert (target / record[key]).read_text() == content
@@ -11,7 +11,6 @@ from documents.models import Document
from documents.parsers import get_default_thumbnail
from paperless_testing.assertions import FileSystemAssertsMixin
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
@pytest.mark.management
@@ -25,7 +24,7 @@ class TestMakeThumbnails(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
filename="test.pdf",
)
shutil.copy(
SIMPLE_DIGITAL_PDF,
Path(__file__).parent / "samples" / "simple.pdf",
self.d1.source_path,
)
@@ -37,7 +36,7 @@ class TestMakeThumbnails(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
filename="test2.pdf",
)
shutil.copy(
SIMPLE_DIGITAL_PDF,
Path(__file__).parent / "samples" / "simple.pdf",
self.d2.source_path,
)
@@ -7,6 +7,7 @@ from unittest import mock
from django.conf import settings
from django.contrib.auth.models import User
from django.test import override_settings
from django.utils import timezone
from rest_framework import serializers
from rest_framework import status
@@ -225,6 +226,25 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_302_FOUND)
self.assertIn("sharelink_notfound=1", response["Location"])
@override_settings(LOGIN_URL="/paperless/accounts/login/")
def test_unknown_share_link_redirects_under_subpath(self) -> None:
"""
GIVEN:
- Paperless is served under a subpath
WHEN:
- An unknown public share link is requested
THEN:
- The redirect to the login page keeps the subpath
"""
self.client.logout()
response = self.client.get("/share/unknownsharelink/")
self.assertEqual(response.status_code, status.HTTP_302_FOUND)
self.assertEqual(
response["Location"],
"/paperless/accounts/login/?sharelink_notfound=1",
)
def test_share_link_missing_file_redirects(self) -> None:
"""
GIVEN:
+23 -5
View File
@@ -21,7 +21,6 @@ from documents.tests.helpers import dummy_preprocess
from paperless_testing.assertions import FileSystemAssertsMixin
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
@pytest.mark.django_db
@@ -230,12 +229,20 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
"""
sample1 = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(
SIMPLE_DIGITAL_PDF,
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
sample1,
)
sample1_archive = self.dirs.archive_dir / "sample_archive.pdf"
shutil.copy(
SIMPLE_DIGITAL_PDF,
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
sample1_archive,
)
doc = Document.objects.create(
@@ -263,7 +270,11 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
"""
sample1 = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(
SIMPLE_DIGITAL_PDF,
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
sample1,
)
doc = Document.objects.create(
@@ -279,7 +290,14 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
def _create_root_with_version(self) -> tuple[Document, Document]:
sample1 = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(SIMPLE_DIGITAL_PDF, sample1)
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
sample1,
)
root = DocumentFactory(content="root content", mime_type="application/pdf")
version = DocumentFactory(
content="my document",
+71 -27
View File
@@ -178,7 +178,7 @@ class TestWorkflows(
self.assertEqual(action.__str__(), "WorkflowAction 1")
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -290,7 +290,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -409,7 +409,7 @@ class TestWorkflows(
w2.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -478,7 +478,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -531,7 +531,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -608,7 +608,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -687,7 +687,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -765,7 +765,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -876,7 +876,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -928,7 +928,7 @@ class TestWorkflows(
generated = generate_unique_filename(doc)
destination = (settings.ORIGINALS_DIR / generated).resolve()
create_source_path_directory(destination)
shutil.copy(self.SIMPLE_PDF, destination)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", destination)
Document.objects.filter(pk=doc.pk).update(filename=generated.as_posix())
doc.refresh_from_db()
@@ -2023,7 +2023,7 @@ class TestWorkflows(
superuser = UserFactory(username="superuser", superuser=True)
self.client.force_authenticate(user=superuser)
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
tasks.consume_file(
@@ -3009,7 +3009,7 @@ class TestWorkflows(
generated = generate_unique_filename(doc)
destination = (settings.ORIGINALS_DIR / generated).resolve()
create_source_path_directory(destination)
shutil.copy(self.SIMPLE_PDF, destination)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", destination)
Document.objects.filter(pk=doc.pk).update(filename=generated.as_posix())
doc.refresh_from_db()
doc.tags.set([self.t1, self.t2])
@@ -3073,7 +3073,7 @@ class TestWorkflows(
generated = generate_unique_filename(doc)
destination = (settings.ORIGINALS_DIR / generated).resolve()
create_source_path_directory(destination)
shutil.copy(self.SIMPLE_PDF, destination)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", destination)
Document.objects.filter(pk=doc.pk).update(filename=generated.as_posix())
doc.refresh_from_db()
doc.tags.set([self.t1])
@@ -3222,7 +3222,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -3346,7 +3346,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -3496,7 +3496,7 @@ class TestWorkflows(
workflow.actions.set([assignment_action, email_action])
temp_working_copy = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "working-copy.pdf",
)
@@ -3596,7 +3596,7 @@ class TestWorkflows(
# move the file
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -3704,7 +3704,7 @@ class TestWorkflows(
generated = generate_unique_filename(doc)
destination = (settings.ORIGINALS_DIR / generated).resolve()
create_source_path_directory(destination)
shutil.copy(self.SIMPLE_PDF, destination)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", destination)
Document.objects.filter(pk=doc.pk).update(filename=generated.as_posix())
run_workflows(WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, doc)
@@ -3915,7 +3915,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -4036,7 +4036,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -4387,7 +4387,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -4835,7 +4835,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -5027,7 +5027,7 @@ class TestWorkflows(
# Create a test file to be consumed
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
test_file_path = Path(test_file)
@@ -5091,7 +5091,7 @@ class TestWorkflows(
# Create a test file to be consumed
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple2.pdf",
)
test_file_path = Path(test_file)
@@ -5529,7 +5529,7 @@ class TestDateWorkflowLocalization(
)
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
tmp_path / "simple.pdf",
)
@@ -5596,7 +5596,7 @@ class TestRemoteOCRWorkflowAction(DirectoriesMixin, SampleDirMixin, APITestCase)
self._make_workflow(WorkflowTrigger.WorkflowTriggerType.CONSUMPTION)
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
overrides = DocumentMetadataOverrides()
@@ -5814,7 +5814,7 @@ class TestApplyAISuggestionsWorkflowAction(
)
test_file = shutil.copy(
self.SIMPLE_PDF,
self.SAMPLE_DIR / "simple.pdf",
self.dirs.scratch_dir / "simple.pdf",
)
@@ -6176,6 +6176,50 @@ class TestApplyAISuggestionsWorkflowAction(
self.assertEqual(self.doc.tags.count(), 0)
def test_unowned_document_does_not_get_private_objects_by_id(self) -> None:
"""
GIVEN:
- An unowned document
- AI suggestions selecting another user's private tag and
correspondent by ID, plus an unowned tag
WHEN:
- The suggestions are applied
THEN:
- Only the unowned tag is assigned; an unowned document must not
be treated as unrestricted
"""
self.doc.owner = None
self.doc.save(update_fields=["owner"])
other = User.objects.create(username="someone-else")
private_tag = Tag.objects.create(name="Private Tag", owner=other)
public_tag = Tag.objects.create(name="Public Tag", owner=None)
private_correspondent = Correspondent.objects.create(
name="Private Correspondent",
owner=other,
)
action = self.make_action(
ai_suggestion_fields=[
WorkflowAction.AISuggestionField.TAGS,
WorkflowAction.AISuggestionField.CORRESPONDENT,
],
)
suggestions: ClassificationSuggestions = {
**SUGGESTIONS,
"tags": {
"existing_ids": [private_tag.pk, public_tag.pk],
"new_names": [],
},
"correspondents": {
"existing_ids": [private_correspondent.pk],
"new_names": [],
},
}
self.apply(action, suggestions)
self.assertEqual(list(self.doc.tags.all()), [public_tag])
self.assertIsNone(self.doc.correspondent)
def test_unparsable_dates_are_skipped(self) -> None:
"""
GIVEN:
-3
View File
@@ -11,7 +11,6 @@ from documents.data_models import ConsumableDocument
from documents.data_models import DocumentMetadataOverrides
from documents.data_models import DocumentSource
from paperless_testing.fakes.progress import FakeProgressManager
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
class ConsumeTaskMixin:
@@ -54,8 +53,6 @@ class SampleDirMixin:
BARCODE_SAMPLE_DIR = SAMPLE_DIR / "barcodes"
SIMPLE_PDF = SIMPLE_DIGITAL_PDF
class GetConsumerMixin:
@contextmanager
+46 -27
View File
@@ -289,6 +289,7 @@ logger = logging.getLogger("paperless.api")
# clauses efficiently, so this threshold mainly protects SQLite users.
_TANTIVY_INTERSECT_THRESHOLD = 5_000
_TANTIVY_SEARCH_PARAM_NAMES = ("text", "title_search", "query", "more_like_id")
_TANTIVY_COMBINABLE_SEARCH_PARAMS = ({"text", "query"}, {"title_search", "query"})
# whoosh-compat's fieldname tagger (used only for SearchMode.QUERY, via the
# whoosh grammar in parse_user_query) is O(n^2) in plain word characters:
@@ -308,6 +309,18 @@ _TANTIVY_SEARCH_PARAM_NAMES = ("text", "title_search", "query", "more_like_id")
_MAX_QUERY_LENGTH: Final[int] = 4096
def _validate_tantivy_search_params(active: list[str]) -> None:
# may also have a filter query alongside a simple search e.g. relative dates
if len(active) > 1 and set(active) not in _TANTIVY_COMBINABLE_SEARCH_PARAMS:
raise ValidationError(
{
"detail": _(
"Specify only one of text, title_search, query, or more_like_id.",
),
},
)
def _get_tantivy_query_and_mode(params):
from documents.search import QueryTooLongError
from documents.search import SearchMode
@@ -321,9 +334,15 @@ def _get_tantivy_query_and_mode(params):
else:
return None # pragma: no cover
if len(raw) > _MAX_QUERY_LENGTH:
raise QueryTooLongError(len(raw), _MAX_QUERY_LENGTH)
return raw, mode
filter_query = (
str(params["query"])
if mode is not SearchMode.QUERY and "query" in params
else None
)
for value in (raw, filter_query):
if value is not None and len(value) > _MAX_QUERY_LENGTH:
raise QueryTooLongError(len(value), _MAX_QUERY_LENGTH)
return raw, mode, filter_query
def _get_more_like_id(query_params: dict[str, Any], user: User | None) -> int:
@@ -2566,15 +2585,9 @@ class UnifiedSearchViewSet(DocumentViewSet):
def parse_search_params() -> SearchParams:
"""Extract query string, search mode, and ordering from request."""
active = self._get_active_search_params(request)
if len(active) > 1:
raise ValidationError(
{
"detail": _(
"Specify only one of text, title_search, query, or more_like_id.",
),
},
)
_validate_tantivy_search_params(
self._get_active_search_params(request),
)
ordering_param = request.query_params.get("ordering", "")
sort_reverse = ordering_param.startswith("-")
@@ -2636,7 +2649,9 @@ class UnifiedSearchViewSet(DocumentViewSet):
filtered_qs: QuerySet[Document],
) -> SearchResultPage:
"""Handle text/title/query search: IDs, ORM intersection, page highlights."""
query_str, search_mode = _get_tantivy_query_and_mode(request.query_params)
query_str, search_mode, filter_query = _get_tantivy_query_and_mode(
request.query_params,
)
# "score" is not a real Tantivy sort field — it means relevance order,
# which is Tantivy's default when no sort field is specified.
@@ -2649,6 +2664,7 @@ class UnifiedSearchViewSet(DocumentViewSet):
),
sort_reverse=sort_reverse,
search_mode=search_mode,
filter_query=filter_query,
)
ordered_ids = intersect_and_order(
all_ids,
@@ -2883,14 +2899,7 @@ class DocumentSelectionMixin:
]
if not search_filters:
return None
if len(search_filters) > 1:
raise ValidationError(
{
"detail": _(
"Specify only one of text, title_search, query, or more_like_id.",
),
},
)
_validate_tantivy_search_params(search_filters)
from documents.search import SearchQueryError
from documents.search import get_backend
@@ -2909,11 +2918,14 @@ class DocumentSelectionMixin:
user=search_user,
)
else:
query_str, search_mode = _get_tantivy_query_and_mode(filters)
query_str, search_mode, filter_query = _get_tantivy_query_and_mode(
filters,
)
search_ids = backend.search_ids(
query_str,
user=search_user,
search_mode=search_mode,
filter_query=filter_query,
)
except SearchQueryError as e:
# Same user-fixable-query mapping as the search list endpoint:
@@ -3920,7 +3932,12 @@ class GlobalSearchView(PassUserMixin):
"request": request,
}
docs_serializer = DocumentSerializer(docs, many=True, context=context)
docs_serializer = DocumentSerializer(
docs,
many=True,
context=context,
fields=("id", "title", "created"),
)
saved_views_serializer = SavedViewSerializer(
saved_views,
many=True,
@@ -4899,7 +4916,7 @@ class SharedLinkView(View):
share_link.expiration is not None
and share_link.expiration < timezone.now()
):
return HttpResponseRedirect("/accounts/login/?sharelink_expired=1")
return HttpResponseRedirect(f"{settings.LOGIN_URL}?sharelink_expired=1")
try:
return serve_file(
doc=share_link.document,
@@ -4908,14 +4925,16 @@ class SharedLinkView(View):
disposition="inline",
)
except FileNotFoundError:
return HttpResponseRedirect("/accounts/login/?sharelink_notfound=1")
return HttpResponseRedirect(
f"{settings.LOGIN_URL}?sharelink_notfound=1",
)
bundle = ShareLinkBundle.objects.filter(slug=slug).first()
if bundle is None:
return HttpResponseRedirect("/accounts/login/?sharelink_notfound=1")
return HttpResponseRedirect(f"{settings.LOGIN_URL}?sharelink_notfound=1")
if bundle.expiration is not None and bundle.expiration < timezone.now():
return HttpResponseRedirect("/accounts/login/?sharelink_expired=1")
return HttpResponseRedirect(f"{settings.LOGIN_URL}?sharelink_expired=1")
if bundle.status in {
ShareLinkBundle.Status.PENDING,
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Afrikaans\n"
"Language: af_ZA\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Ongeldige veranderlike bespeur."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Amharic\n"
"Language: am_ET\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Arabic\n"
"Language: ar_SA\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "اكتشاف متغير خاطئ."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "كخطوة أخيرة، يرجى ملء النموذج التالي:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Belarusian\n"
"Language: be_BY\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Выяўлена няправільная зменная."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Bulgarian\n"
"Language: bg_BG\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "стартиране на работните процеси"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Засечена е невалидна променлива."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "Като последна стъпка, моля, попълнете с
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Catalan\n"
"Language: ca_ES\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "flux corrents"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Permisos insuficients."
@@ -1676,7 +1676,7 @@ msgstr "Variable detectada invàlida."
msgid "Duplicate document identifiers are not allowed."
msgstr "Duplicat d'identificadors de documents no permès."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Documents no trobats: %(ids)s"
@@ -1917,40 +1917,40 @@ msgstr "Com a pas final, completa el següent formulari:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Invalid more_like_id"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configuració AI invàlida."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Especifica només un dels següents valors: text, title_search, query o more_like_id."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "Invalid more_like_id"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Configuració AI invàlida."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Permisos insuficients per compartir document %(id)s."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Paquet ja s'està processant."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "El paquet de link encarà s'està preparant. Prova de nou més tard."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "El paquet d'enllaç no està disponible."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Czech\n"
"Language: cs_CZ\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "spuštění pracovních postupů"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Nedostatečná oprávnění."
@@ -1676,7 +1676,7 @@ msgstr "Zjištěna neplatná proměnná."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "Jako poslední krok vyplňte následující formulář:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Nedostatečná oprávnění ke sdílení dokumentu %(id)s."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Danish\n"
"Language: da_DK\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "workflow-kørsler"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Ugyldig variabel fundet."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "Som sidste trin, udfyld venligst flg. formular:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: German, Switzerland\n"
"Language: de_CH\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "Arbeitsablauf wird ausgeführt"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Unzureichende Berechtigungen."
@@ -1676,7 +1676,7 @@ msgstr "Ungültige Variable erkannt."
msgid "Duplicate document identifiers are not allowed."
msgstr "Doppelte Dokumentbezeichner sind nicht erlaubt."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Dokumente nicht gefunden: %(ids)s"
@@ -1917,40 +1917,40 @@ msgstr "Bitte füllen Sie zum Abschluss folgendes Formular aus:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Ungültige more_like_id"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Ungültige KI-Konfiguration."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Geben Sie nur einen von text, title_search, query, oder more_like_id an."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "Ungültige more_like_id"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Ungültige KI-Konfiguration."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Unzureichende Berechtigungen, um Dokument %(id)s zu teilen."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Paket wird bereits verarbeitet."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Das Freigabelink-Paket wird noch vorbereitet. Bitte versuchen Sie es später erneut."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "Das Freigabelink-Paket ist nicht verfügbar."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "Arbeitsablauf wird ausgeführt"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Unzureichende Berechtigungen."
@@ -1676,7 +1676,7 @@ msgstr "Ungültige Variable erkannt."
msgid "Duplicate document identifiers are not allowed."
msgstr "Doppelte Dokumentbezeichner sind nicht erlaubt."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Dokumente nicht gefunden: %(ids)s"
@@ -1917,40 +1917,40 @@ msgstr "Bitte füllen Sie zum Abschluss folgendes Formular aus:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Ungültige more_like_id"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Ungültige KI-Konfiguration."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "Zeitüberschreitung bei der KI-Backendanfrage."
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr "Das KI-Backend hat die Anfrage abgelehnt. Details in den Protokollen."
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Geben Sie nur einen von text, title_search, query, oder more_like_id an."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "Ungültige more_like_id"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Ungültige KI-Konfiguration."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr "Zeitüberschreitung bei der KI-Backendanfrage."
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr "Das KI-Backend hat die Anfrage abgelehnt. Details in den Protokollen."
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Unzureichende Berechtigungen, um Dokument %(id)s zu teilen."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Paket wird bereits verarbeitet."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Das Freigabelink-Paket wird noch vorbereitet. Bitte versuchen Sie es später erneut."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "Das Freigabelink-Paket ist nicht verfügbar."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Greek\n"
"Language: el_GR\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "εκτελέσεις ροής εργασίας"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Εντοπίστηκε μη έγκυρη μεταβλητή."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "Ως τελικό βήμα, παρακαλούμε να συμπληρώ
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
File diff suppressed because it is too large. Load diff
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "ejecuciones de flujo de trabajo"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Permisos insuficientes."
@@ -1676,7 +1676,7 @@ msgstr "Variable inválida."
msgid "Duplicate document identifiers are not allowed."
msgstr "No se permiten identificadores de documento duplicados."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Documentos no encontrados: %(ids)s"
@@ -1917,40 +1917,40 @@ msgstr "Como último paso, por favor complete el siguiente formulario:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configuración de IA inválida."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Especifique solo uno entre text, title_search, query, o more_like_id."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Configuración de IA inválida."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Permisos insuficientes para compartir el documento %(id)s."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "El paquete ya está siendo procesado."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "El paquete de enlace compartido aún está siendo preparado. Por favor, inténtalo de nuevo más tarde."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "El paquete de enlace compartido no está disponible."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Estonian\n"
"Language: et_EE\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Persian\n"
"Language: fa_IR\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "گردش کار اجرا می شود"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "متغیر نامعتبر شناسایی شده است."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr "به عنوان مرحله آخر ، لطفاً فرم زیر را تک
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Finnish\n"
"Language: fi_FI\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Virheellinen muuttuja havaittu."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Language: fr_FR\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "le flux de travail s'exécute"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Droits insuffisants."
@@ -1676,7 +1676,7 @@ msgstr "Variable invalide détectée."
msgid "Duplicate document identifiers are not allowed."
msgstr "Les identificateurs de document en double ne sont pas autorisés."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Documents introuvables : %(ids)s"
@@ -1916,40 +1916,40 @@ msgstr "Pour finir, veuillez remplir le formulaire suivant :"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "More_like_id invalide"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configuration IA invalide."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "La requête d'arrière-plan IA a expiré."
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Spécifiez seulement un texte, titre, recherche ou more_like_id."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "More_like_id invalide"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Configuration IA invalide."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr "La requête d'arrière-plan IA a expiré."
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Droits d'accès insuffisant pour partager %(id)s document."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Le paquet est déjà en cours de traitement."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Le lot de liens de partage est en cours de préparation. Veuillez réessayer plus tard."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "Le lot de liens de partage n'est pas disponible."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Hebrew\n"
"Language: he_IL\n"
@@ -1637,7 +1637,7 @@ msgid "workflow runs"
msgstr "הרצות זרימת עבודה"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "הרשאות אינן מספיקות."
@@ -1677,7 +1677,7 @@ msgstr "משתנה לא חוקי זוהה."
msgid "Duplicate document identifiers are not allowed."
msgstr "מזהי מסמכים כפולים אינם מורשים."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "מסמכים לא נמצאו: %(ids)s"
@@ -1918,40 +1918,40 @@ msgstr ": כצעד אחרון, אנא מלא את הטופס הבא:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "מזהה more_like_id אינו תקין"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "הגדרות בינה מלאכותית שגויות."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "יש לציין רק אחד מהבאים: text‏, title_search‏, query או more_like_id."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "מזהה more_like_id אינו תקין"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "הגדרות בינה מלאכותית שגויות."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "הרשאות לא מספיקות לשיתוף מסמך %(id)s."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "החבילה (Bundle) כבר נמצאת בתהליך עיבוד."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "חבילת קישור השיתוף עדיין בהכנה. נא לנסות שוב מאוחר יותר."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "חבילת קישור השיתוף אינה זמינה."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Hindi\n"
"Language: hi_IN\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Croatian\n"
"Language: hr_HR\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "tijek rada pokrenut"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Nedovoljne ovlasti."
@@ -1676,7 +1676,7 @@ msgstr "Otkrivena je nevaljana vrsta datoteke."
msgid "Duplicate document identifiers are not allowed."
msgstr "Duplicirani identifikatori dokumenata nisu dopušteni."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Dokumenti nisu pronađeni: %(ids)s"
@@ -1917,40 +1917,40 @@ msgstr "Kao posljednji korak ispunite sljedeći obrazac:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Nevažeći more_like_id"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Nevažeća AI konfiguracija."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "Zahtjev prema AI pozadinskom sustavu je istekao."
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr "AI pozadinski sustav je odbio zahtjev. Provjerite zapise (logs) za više detalja."
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Navedite samo jedno od: text, title_search, query ili more_like_id."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "Nevažeći more_like_id"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Nevažeća AI konfiguracija."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr "Zahtjev prema AI pozadinskom sustavu je istekao."
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr "AI pozadinski sustav je odbio zahtjev. Provjerite zapise (logs) za više detalja."
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Nedovoljne ovlasti za dijeljenje dokumenta %(id)s."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Paket se već obrađuje."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Paket linka za dijeljenje se još priprema. Pokušajte ponovo kasnije."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "Paket linka za dijeljenje nije dostupan."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Hungarian\n"
"Language: hu_HU\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "munkafolyamat futtatások"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Nincs jogosúltsága."
@@ -1676,7 +1676,7 @@ msgstr "Érvénytelen változó észlelve."
msgid "Duplicate document identifiers are not allowed."
msgstr "A dokumentumazonosítók duplikálása nem megengedett."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Dokumentumok nem találhatók: %(ids)s"
@@ -1917,40 +1917,40 @@ msgstr "Utolsó lépésként kérjük válaszoljon az alábbi kérdésekre:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Érvénytelen more_like_id"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Érvénytelen MI konfiguráció."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "A text, title_search, query, vagy more_like_id közül csak egyet adjon meg."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "Érvénytelen more_like_id"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Érvénytelen MI konfiguráció."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Nincs megfelelő jogosultság a %(id)s dokumentum megosztásához."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "A csomag feldolgozása már folyamatban van."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "A megosztási linkcsomag készítése folyamatban. Kérjük, próbálja meg később."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "A megosztási linkcsomag nem elérhető."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Indonesian\n"
"Language: id_ID\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "daftar jalankan alur kerja"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Izin tidak mencukupi"
@@ -1676,7 +1676,7 @@ msgstr "Variabel ilegal terdeteksi."
msgid "Duplicate document identifiers are not allowed."
msgstr "Penggunaan pengenal dokumen ganda tidak diperbolehkan."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Dokumen tidak ditemukan: %(ids)s"
@@ -1917,40 +1917,40 @@ msgstr "Sebagai langkah terakhir, mohon formulir berikut:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Izin tidak mencukupi untuk berbagi dokumen %(id)s"
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Paket sedang diproses."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Bundel tautan berbagi masih dalam proses persiapan. Silakan coba lagi nanti."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "Bundel tautan berbagi tidak tersedia."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"Language: it_IT\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "esecuzioni del flusso di lavoro"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Autorizzazioni insufficienti."
@@ -1676,7 +1676,7 @@ msgstr "Variabile non valida rilevata."
msgid "Duplicate document identifiers are not allowed."
msgstr "Non sono consentiti identificatori di documenti duplicati."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Documenti non trovati: %(ids)s"
@@ -1917,40 +1917,40 @@ msgstr "Come ultimo passo, compila il seguente modulo:"
msgid ", "
msgstr ", "
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "more_like_id non valido"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configurazione AI non valida."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "Richiesta di backend AI scaduta."
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr "Il backend IA ha rifiutato la richiesta. Controlla i log per i dettagli."
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Specificare solo uno tra text, title_search, query o more_like_id."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "more_like_id non valido"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Configurazione AI non valida."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr "Richiesta di backend AI scaduta."
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr "Il backend IA ha rifiutato la richiesta. Controlla i log per i dettagli."
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Autorizzazioni insufficienti per condividere il documento %(id)s."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Il pacchetto è già in fase di elaborazione."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Il pacchetto di link di condivisione è ancora in fase di preparazione. Riprova più tardi."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "Il pacchetto di link di condivisione non è disponibile."
+71 -71
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-15 12:29\n"
"Last-Translator: \n"
"Language-Team: Japanese\n"
"Language: ja_JP\n"
@@ -136,7 +136,7 @@ msgstr "タグ"
#: documents/models.py:125
msgid "Cannot set itself as parent."
msgstr ""
msgstr "親として設定することはできません。"
#: documents/models.py:127
msgid "Cannot set parent to a descendant."
@@ -249,23 +249,23 @@ msgstr "物理的なドキュメントアーカイブ内での、このドキュ
#: documents/models.py:321
msgid "root document for this version"
msgstr ""
msgstr "このバージョンのルートドキュメント"
#: documents/models.py:325
msgid "version index"
msgstr ""
msgstr "バージョンのインデックス"
#: documents/models.py:329
msgid "Index of this version within the root document."
msgstr ""
msgstr "ルートドキュメント内における、このバージョンのインデックス"
#: documents/models.py:333
msgid "version label"
msgstr ""
msgstr "バージョンラベル"
#: documents/models.py:337
msgid "Optional short label for a document version."
msgstr ""
msgstr "ドキュメントバージョンの任意の短いラベル。"
#: documents/models.py:342 documents/models.py:961 documents/models.py:992
#: documents/models.py:1096 documents/models.py:2126
@@ -282,119 +282,119 @@ msgstr "アーカイブ"
#: documents/models.py:563
msgid "Bank"
msgstr ""
msgstr "銀行"
#: documents/models.py:564
msgid "Basket"
msgstr ""
msgstr "買い物かご"
#: documents/models.py:565
msgid "Bell"
msgstr ""
msgstr "ベル"
#: documents/models.py:566
msgid "Bookmark"
msgstr ""
msgstr "ブックマーク"
#: documents/models.py:567
msgid "Boxes"
msgstr ""
msgstr "箱"
#: documents/models.py:568
msgid "Briefcase"
msgstr ""
msgstr "ブリーフケース"
#: documents/models.py:569
msgid "Building"
msgstr ""
msgstr "建物"
#: documents/models.py:570
msgid "Calculator"
msgstr ""
msgstr "電卓"
#: documents/models.py:571
msgid "Calendar"
msgstr ""
msgstr "カレンダー"
#: documents/models.py:572
msgid "Camera"
msgstr ""
msgstr "カメラ"
#: documents/models.py:573
msgid "Checklist"
msgstr ""
msgstr "チェックリスト"
#: documents/models.py:574
msgid "Cash"
msgstr ""
msgstr "現金"
#: documents/models.py:575
msgid "Chat"
msgstr ""
msgstr "チャット"
#: documents/models.py:576
msgid "Check"
msgstr ""
msgstr "確認"
#: documents/models.py:577
msgid "Clipboard"
msgstr ""
msgstr "クリップボード"
#: documents/models.py:578
msgid "Clock"
msgstr ""
msgstr "時計"
#: documents/models.py:579
msgid "Credit card"
msgstr ""
msgstr "クレジットカード"
#: documents/models.py:580
msgid "Download"
msgstr ""
msgstr "ダウンロード"
#: documents/models.py:581
msgid "Envelope"
msgstr ""
msgstr "封筒"
#: documents/models.py:582
msgid "Warning"
msgstr ""
msgstr "警告"
#: documents/models.py:583
msgid "File"
msgstr ""
msgstr "ファイル"
#: documents/models.py:584
msgid "Checked file"
msgstr ""
msgstr "チェック済みファイル"
#: documents/models.py:585
msgid "Locked file"
msgstr ""
msgstr "ロックされたファイル"
#: documents/models.py:586
msgid "Medical file"
msgstr ""
msgstr "医療ファイル"
#: documents/models.py:587
msgid "Person file"
msgstr ""
msgstr "個人ファイル"
#: documents/models.py:590
msgid "Spreadsheet"
msgstr ""
msgstr "スプレッドシート"
#: documents/models.py:592
msgid "Text file"
msgstr ""
msgstr "テキストファイル"
#: documents/models.py:593
msgid "Files"
msgstr ""
msgstr "ファイル"
#: documents/models.py:594
msgid "Folder"
msgstr ""
msgstr "フォルダー"
#: documents/models.py:595 paperless_mail/admin.py:58
msgid "Filter"
@@ -406,11 +406,11 @@ msgstr ""
#: documents/models.py:597
msgid "Globe"
msgstr ""
msgstr "地球儀"
#: documents/models.py:598
msgid "Hash"
msgstr ""
msgstr "ハッシュ"
#: documents/models.py:599
msgid "Heart"
@@ -418,27 +418,27 @@ msgstr ""
#: documents/models.py:600
msgid "House"
msgstr ""
msgstr "家"
#: documents/models.py:601
msgid "Inbox"
msgstr ""
msgstr "受信トレイ"
#: documents/models.py:602
msgid "Journals"
msgstr ""
msgstr "日記"
#: documents/models.py:603
msgid "Task list"
msgstr ""
msgstr "タスクリスト"
#: documents/models.py:604
msgid "Newspaper"
msgstr ""
msgstr "新聞"
#: documents/models.py:605
msgid "Attachment"
msgstr ""
msgstr "添付ファイル"
#: documents/models.py:606
msgid "People"
@@ -446,15 +446,15 @@ msgstr ""
#: documents/models.py:607
msgid "Person"
msgstr ""
msgstr "人物"
#: documents/models.py:608
msgid "Printer"
msgstr ""
msgstr "プリンター"
#: documents/models.py:609
msgid "Receipt"
msgstr ""
msgstr "領収書"
#: documents/models.py:610
msgid "Safe"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "ワークフローの実行"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "無効な変数を検出しました"
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "最後のステップとして、以下のフォームに記入してく
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Korean\n"
"Language: ko_KR\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "워크플로우 실행"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "잘못된 변수가 감지되었습니다."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "마지막 단계로 다음 양식을 작성해 주세요:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Luxembourgish\n"
"Language: lb_LU\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Ongëlteg Zeechen detektéiert."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Lithuanian\n"
"Language: lt_LT\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr "Galiausiai užpildykite šią formą:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Nepakanka leidimų bendrinti dokumentą %(id)s."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Rinkinys jau apdorojamas."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Bendrinimo nuorodų rinkinys vis dar ruošiamas. Bandykite dar kartą vėliau."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "Dalinimos nuorodų rinkinys yra nepasiekiamas."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Latvian\n"
"Language: lv_LV\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Macedonian\n"
"Language: mk_MK\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Malay\n"
"Language: ms_MY\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Ongeldige variabele ontdekt."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "Vul als laatste stap het volgende formulier in:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Norwegian\n"
"Language: no_NO\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "workflow runs (NO)"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Utilstrekkelige tillatelser."
@@ -1676,7 +1676,7 @@ msgstr "Ugyldig variabel oppdaget."
msgid "Duplicate document identifiers are not allowed."
msgstr "Duplicate document identifiers are not allowed. (NO)"
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Documents not found: %(ids)s (NO)"
@@ -1917,40 +1917,40 @@ msgstr "As a final step, please complete the following form: (NO)"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Invalid more_like_id (NO)"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Invalid AI configuration. (NO)"
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "Forespørselen til KI-tjenesten fikk tidsavbrudd."
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Specify only one of text, title_search, query, or more_like_id. (NO)"
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "Invalid more_like_id (NO)"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Invalid AI configuration. (NO)"
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr "Forespørselen til KI-tjenesten fikk tidsavbrudd."
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Insufficient permissions to share document %(id)s. (NO)"
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Bundle is already being processed. (NO)"
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "The share link bundle is still being prepared. Please try again later. (NO)"
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "The share link bundle is unavailable. (NO)"
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Polish\n"
"Language: pl_PL\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "uruchomienia przepływu pracy"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Niewystarczające uprawnienia."
@@ -1676,7 +1676,7 @@ msgstr "Wykryto nieprawidłową zmienną."
msgid "Duplicate document identifiers are not allowed."
msgstr "Nie dopuszcza się powielania identyfikatorów dokumentów."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Nie znaleziono dokumentów: %(ids)s"
@@ -1917,40 +1917,40 @@ msgstr "Na koniec należy wypełnić poniższy formularz:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Nieprawidłowy more_like_id"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "Nieprawidłowy more_like_id"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Brak uprawnień do udostępnienia dokumentu %(id)s."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Pakiet jest już przetwarzany."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Pakiet do udostępnienia jest nadal przygotowywany. Spróbuj ponownie później."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "Pakiet do udostępnienia jest niedostępny."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Portuguese, Brazilian\n"
"Language: pt_BR\n"
@@ -1637,7 +1637,7 @@ msgid "workflow runs"
msgstr "execução de fluxo de trabalho"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Permissões insuficientes."
@@ -1677,7 +1677,7 @@ msgstr "Variável inválida detectada."
msgid "Duplicate document identifiers are not allowed."
msgstr "Não são permitidos identificadores de documento duplicados."
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Documentos não encontrados: %(ids)s"
@@ -1918,40 +1918,40 @@ msgstr "Como etapa final, por favor preencha o seguinte formulário:"
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "more_like_id inválido"
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configuração de IA inválida."
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "A requisição backend da IA expirou."
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Especifique apenas um text, title_search, consulta ou mais_like_id."
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr "more_like_id inválido"
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr "Configuração de IA inválida."
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr "A requisição backend da IA expirou."
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Permissões insuficientes para compartilhar o documento %(id)s."
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr "Pacote já está sendo processado."
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "O pacote de link de compartilhamento ainda está sendo preparado. Tente novamente mais tarde."
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr "O pacote de link de compartilhamento não está disponível."
+25 -25
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"Last-Translator: \n"
"Language-Team: Portuguese\n"
"Language: pt_PT\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Variável inválida detetada."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4799
#: documents/serialisers.py:2988 documents/views.py:4811
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2574 documents/views.py:2890
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#: documents/views.py:356 documents/views.py:2765
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4862
#: documents/views.py:4874
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4926
#: documents/views.py:4938
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4940
#: documents/views.py:4952
msgid "The share link bundle is unavailable."
msgstr ""
Loaded 100 of 135 files, more files were not shown because too many files have changed in this diff. Show more