Compare commits

..
Author SHA1 Message Date
stumpylog 475e006edb Fixes the E2E test that depended on the old location 2026-10-13 09:43:36 -07:00
Trenton HandClaude Sonnet 5.5 24002e259d Chore: Drop the sample duplicate guard test
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 07:24:59 -07:00
Trenton HandClaude Sonnet 5.5 c61b322632 Chore: Use shared samples in tests that arrived from dev
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 07:24:59 -07:00
Trenton HandClaude Sonnet 5.5 bdef6009c3 Chore: Tidy leftover parentheses and the samples module docstring
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 07:24:59 -07:00
Trenton HandClaude Sonnet 5.5 b2d1bcf221 Chore: Remove duplicate and unreferenced test sample files
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 07:24:59 -07:00
Trenton HandClaude Sonnet 5.5 20e7fd99f1 Chore: Materialise duplicated media-tree samples from the shared copies
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 07:24:59 -07:00
Trenton HandClaude Sonnet 5.5 9ce52a3957 Chore: Use the shared simple-digital.pdf in documents tests and drop the unreferenced simple.txt
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 07:24:58 -07:00
Trenton HandClaude Sonnet 5.5 1de0da8491 Chore: Move the four duplicated parser sample PDFs into a shared paperless_testing location
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 07:24:53 -07:00
135 changed files with 2221 additions and 3019 deletions

No files matched your search

+4 -5
View File
@@ -76,12 +76,11 @@ 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. 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
// is reason enough to close it, but not proof of how it was written.
const body = honeypot
? '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,<4.1",
"filelock~=3.32.0",
"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_mail/tests/samples/*,src/paperless/tests/samples\
/mail/*,src/documents/tests/samples/*,*.po,*.json\
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\
"""
write-changes = true
+3 -1
View File
@@ -134,7 +134,9 @@ 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 / "documents" / "tests" / "samples" / "simple.pdf"
sample_pdf = (
SOURCE_ROOT / "paperless_testing" / "sample_files" / "simple-digital.pdf"
)
for document in documents:
shutil.copyfile(sample_pdf, originals / document.filename)
shutil.copyfile(
-2
View File
@@ -15,8 +15,6 @@ 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,
},
],
},
+167 -178
View File
File diff suppressed because it is too large. Load diff
@@ -102,34 +102,25 @@
</button>
}
@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>
}
<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 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 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>
</div>
@@ -178,9 +169,10 @@
<div>{{ taskDisplayName(task) }}</div>
<div class="small text-muted">
@if (taskShowsSeparateTypeLabel(task)) {
<span class="me-2">{{ task.task_type_display }}</span>
<span>{{ task.task_type_display }}</span>
<span class="mx-1">&bull;</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>
<span>{{ task.trigger_source_display }}</span>
</div>
</td>
<td class="d-none d-lg-table-cell created-col">{{ task.date_created | customDate:'short' }}</td>
@@ -72,7 +72,3 @@ pre {
tbody tr:nth-last-child(2) td {
border-bottom: none !important;
}
.section-header .show-all {
font-size: inherit;
}
@@ -223,6 +223,16 @@ 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) =>
@@ -231,30 +241,8 @@ 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()
@@ -350,7 +338,6 @@ 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'))
@@ -361,51 +348,6 @@ 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)
@@ -426,56 +368,55 @@ describe('TasksComponent', () => {
it('should apply task type and trigger source filters to the server-side task query', () => {
component.setTaskType(PaperlessTaskType.SanityCheck)
expect(
httpTestingController.match(
httpTestingController
.expectOne(
(request) =>
request.url === `${environment.apiBaseUrl}tasks/` &&
request.params.get('page_size') === '10' &&
request.params.get('page_size') === '25' &&
request.params.get('task_type') === PaperlessTaskType.SanityCheck
)
).toHaveLength(3)
.flush({ count: 1, results: [tasks[6]] })
component.setTriggerSource(PaperlessTaskTriggerSource.System)
expect(
httpTestingController.match(
httpTestingController
.expectOne(
(request) =>
request.url === `${environment.apiBaseUrl}tasks/` &&
request.params.get('page_size') === '10' &&
request.params.get('page_size') === '25' &&
request.params.get('task_type') === PaperlessTaskType.SanityCheck &&
request.params.get('trigger_source') ===
PaperlessTaskTriggerSource.System
)
).toHaveLength(3)
.flush({ count: 1, results: [tasks[6]] })
})
it('should apply text filters to the server-side task query', () => {
component.filterText = 'invoice'
jest.advanceTimersByTime(150)
expect(
httpTestingController.match(
httpTestingController
.expectOne(
(request) =>
request.url === `${environment.apiBaseUrl}tasks/` &&
request.params.get('page_size') === '10' &&
request.params.get('page_size') === '25' &&
request.params.get('name') === 'invoice'
)
).toHaveLength(3)
.flush({ count: 1, results: [tasks[0]] })
component.setFilterTarget(TaskFilterTargetID.Result)
expect(
httpTestingController.match(
httpTestingController
.expectOne(
(request) =>
request.url === `${environment.apiBaseUrl}tasks/` &&
request.params.get('page_size') === '10' &&
request.params.get('page_size') === '25' &&
request.params.get('result') === 'invoice'
)
).toHaveLength(3)
.flush({ count: 0, results: [] })
})
it('should load a different task page when pagination changes', () => {
selectSection(TaskSection.Completed, 30)
component.setPage(2)
const pageTwoTasks = {
@@ -499,7 +440,6 @@ describe('TasksComponent', () => {
})
it('should not replace section counts with current-page counts', () => {
selectSection(TaskSection.Completed, 30)
component.setPage(2)
httpTestingController
@@ -515,21 +455,11 @@ 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,9 +15,6 @@ import {
distinctUntilChanged,
filter,
first,
forkJoin,
map,
Observable,
Subject,
takeUntil,
timer,
@@ -28,7 +25,6 @@ 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'
@@ -173,7 +169,6 @@ 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>>({
@@ -489,6 +484,13 @@ 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
}
@@ -735,12 +737,21 @@ export class TasksComponent
this.reloadSectionCounts()
this.loading.set(true)
this.listTasks()
this.tasksService
.list(
this.page,
this.pageSize,
this.getParamsForSection(this.selectedSection)
)
.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 &&
@@ -756,30 +767,4 @@ 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,31 +154,6 @@ 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,9 +116,8 @@ 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}${basePath}accounts/logout/?next=${basePath}accounts/login/?next=${basePath}`
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
)
}, 2500)
} else {
@@ -218,38 +218,6 @@ 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,9 +210,8 @@ 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}${basePath}accounts/logout/?next=${basePath}accounts/login/?next=${basePath}`
`${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/`
)
}, 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()" [aria-label]="noSuggestions ? 'No suggestions' : 'Suggest'" i18n-aria-label>
<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>
@if (loading()) {
<div class="spinner-border spinner-border-sm" role="status"></div>
} @else if (noSuggestions) {
@@ -18,44 +18,46 @@
}
</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>
@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 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>
<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_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_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_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_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 (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>
}
@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>
</div>
</div>
</div>
}
</div>
@if (aiEnabled()) {
@@ -251,37 +251,6 @@ 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,7 +53,11 @@ export class SuggestionsDropdownComponent {
addCorrespondent: EventEmitter<string> = new EventEmitter()
public clickSuggest(): void {
if (this.disabled() || this.loading()) {
if (
this.disabled() ||
this.loading() ||
(this.suggestions() && !this.aiEnabled())
) {
return
}
@@ -25,8 +25,6 @@
@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,22 +484,6 @@ 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 = [
@@ -1861,7 +1845,7 @@ describe('FilterEditorComponent', () => {
])
})
it('should keep text filtering separate from date created relative date', () => {
it('should carry over text filtering on date created with relative date', () => {
component.textFilter = 'foo'
const dateCreatedDropdown = fixture.debugElement.queryAll(
By.directive(DatesDropdownComponent)
@@ -1871,18 +1855,14 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TEXT,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'created:[-1 week to now]',
value: 'foo,created:[-1 week to now]',
},
])
})
it('should keep text filtering separate from created and added relative dates', () => {
it('should carry over text filtering once with created and added relative dates', () => {
component.textFilter = 'foo'
const datesDropdown = fixture.debugElement.query(
By.directive(DatesDropdownComponent)
@@ -1893,18 +1873,14 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TEXT,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'created:[-1 week to now],added:[-1 month to now]',
value: 'foo,created:[-1 week to now],added:[-1 month to now]',
},
])
})
it('should keep legacy title filters as a title search when adding a created relative date', () => {
it('should convert legacy title filters into full text query when adding a created relative date', () => {
component.filterRules = [
{
rule_type: FILTER_TITLE,
@@ -1919,18 +1895,14 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TITLE,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'created:[-1 week to now]',
value: 'foo,created:[-1 week to now]',
},
])
})
it('should keep simple title filters as a title search when adding a created relative date', () => {
it('should convert simple title filters into full text query when adding a created relative date', () => {
component.filterRules = [
{
rule_type: FILTER_SIMPLE_TITLE,
@@ -1945,13 +1917,9 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TITLE,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'created:[-1 week to now]',
value: 'foo,created:[-1 week to now]',
},
])
})
@@ -2037,7 +2005,7 @@ describe('FilterEditorComponent', () => {
])
})
it('should keep text filtering separate from date added relative date', () => {
it('should carry over text filtering on date added with relative date', () => {
component.textFilter = 'foo'
const datesDropdown = fixture.debugElement.query(
By.directive(DatesDropdownComponent)
@@ -2047,13 +2015,9 @@ describe('FilterEditorComponent', () => {
fixture.detectChanges()
tick(400)
expect(component.filterRules).toEqual([
{
rule_type: FILTER_SIMPLE_TEXT,
value: 'foo',
},
{
rule_type: FILTER_FULLTEXT_QUERY,
value: 'added:[-1 week to now]',
value: 'foo,added:[-1 week to now]',
},
])
})
@@ -1083,6 +1083,23 @@ 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">
<div class="input-group input-group-sm w-auto me-3">
<select id="managementPageSize" class="form-select form-select-sm small" [(ngModel)]="pageSize">
<option [ngValue]="25">25</option>
<option [ngValue]="50">50</option>
@@ -19,9 +19,7 @@
</select>
<span class="input-group-text text-muted d-none d-md-flex" i18n>per page</span>
</div>
@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>
}
<ngb-pagination [pageSize]="pageSize" [collectionSize]="collectionSize()" [page]="page()" [maxSize]="5" (pageChange)="page.set($event); reloadData()" size="sm" aria-label="Pagination"></ngb-pagination>
</div>
</div>
@@ -72,7 +70,7 @@
}
</div>
}
@if (collectionSize() > pageSize) {
@if (collectionSize() > 20) {
<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,17 +93,15 @@
[disabled]="selectedMailIds().size === 0"
(confirm)="deleteSelected()">
</pngx-confirm-button>
@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 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="translated"> スライド <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> / <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/> </target>
<target state="needs-translation"> Slide <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> of <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="translated">タグ、取引先、ドキュメントの種類、保存先パス、カスタムフィールドなどの属性をここで管理できます。これらはドキュメントの編集画面から作成することもできます。</target>
<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>
</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="translated">タスクでは、バックグラウンド処理の進行状況、対応が必要な項目、最近完了した処理を確認できます。</target>
<target state="needs-translation">Tasks helps you track background work, what needs attention, and what recently completed.</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="translated">この値は、Paperless の外で設定されている <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>を上書きします。</target>
<target state="needs-translation">This value overrides <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>, which is set outside 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="translated"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> はPaperlessの外部で設定されています。ここに値を入力すると、その設定を上書きできます。</target>
<target state="needs-translation"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</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="translated">外部で設定された値を使用</target>
<target state="needs-translation">Use the externally configured value</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="translated">外部設定に戻す</target>
<target state="needs-translation">Reset to external</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="translated">行</target>
<target state="needs-translation">lines</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="translated">一番下へ移動</target>
<target state="needs-translation">Jump to bottom</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="translated">サイドバーに表示するアイテム:</target>
<target state="needs-translation">Sidebar items to show:</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="translated">サイドバーの保存済みビューにドキュメント数を表示</target>
<target state="needs-translation">Show document counts in sidebar saved views</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="translated">提案機能のデフォルト設定</target>
<target state="needs-translation">Suggestions default to</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="translated">両方</target>
<target state="needs-translation">Both</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="translated">AIのみ</target>
<target state="needs-translation">AI only</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="translated">受信トレイ内のドキュメントに対して自動的に提案をリクエストする</target>
<target state="needs-translation">Automatically request suggestions for inbox documents</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="translated">チェックを外した場合、提案を取得するには「提案」ボタンを押す必要があります。</target>
<target state="needs-translation">If un-checked, suggestions must be requested via the Suggest button.</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="translated">ドキュメントの詳細ページで非表示にするフィールドは、チェックを外してください。</target>
<target state="needs-translation">Uncheck fields to hide them on the document details page.</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="translated">PDF エディター</target>
<target state="needs-translation">PDF Editor</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="translated">デフォルトの編集モード</target>
<target state="needs-translation">Default editing mode</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="translated">新しいドキュメントを作成</target>
<target state="needs-translation">Create new document(s)</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="translated">ドキュメントのバージョンを追加</target>
<target state="needs-translation">Add document version</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="translated">これらの設定は、Web UIから作成されたオブジェクト(タグ、メールルールなど)に対して、このユーザーアカウントに適用されます。ドキュメントには適用されません。</target>
<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>
</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="translated">タスク</target>
<target state="needs-translation">Tasks</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="translated">タスクでは、ドキュメントの取り込み処理やシステムタスクに関する詳細情報を確認できます。</target>
<target state="needs-translation">Tasks shows detailed information about document consumption and system tasks.</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="translated">すべて閉じる</target>
<target state="needs-translation">Dismiss all</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="translated">すべての種類</target>
<target state="needs-translation">All types</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="translated">すべてのソース</target>
<target state="needs-translation">All sources</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="translated">タスクを検索</target>
<target state="needs-translation">Search tasks</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="translated">{VAR_PLURAL, plural, =1 {1件のタスク} other {<x id="INTERPOLATION"/>件のタスク}}</target>
<target state="needs-translation">{VAR_PLURAL, plural, =1 {1 task} other {<x id="INTERPOLATION"/> tasks}}</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="translated">タスクの詳細を表示</target>
<target state="needs-translation">View task details</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="translated">結果メッセージ</target>
<target state="needs-translation">Result message</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="translated">重複</target>
<target state="needs-translation">Duplicate</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="translated">入力データ</target>
<target state="needs-translation">Input data</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="translated">結果データ</target>
<target state="needs-translation">Result data</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="translated">現在のフィルター条件に一致するタスクはありません。</target>
<target state="needs-translation">No tasks match the current filters.</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="translated">注意が必要</target>
<target state="needs-translation">Needs attention</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="translated">進行中</target>
<target state="needs-translation">In progress</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="translated">最近完了したタスク</target>
<target state="needs-translation">Recently completed</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="translated">ファイルの取り込み</target>
<target state="needs-translation">Consume File</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="translated">整合性チェック</target>
<target state="needs-translation">Sanity Check</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="translated">ゴミ箱を空にする</target>
<target state="needs-translation">Empty Trash</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="translated">ワークフローの確認</target>
<target state="needs-translation">Check Workflows</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="translated">一括更新</target>
<target state="needs-translation">Bulk Update</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="translated">ドキュメントの再処理</target>
<target state="needs-translation">Reprocess Document</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="translated">共有リンクの生成</target>
<target state="needs-translation">Build Share Link</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="translated">一括削除</target>
<target state="needs-translation">Bulk Delete</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="translated"> Shlid <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> i <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/></target>
<target state="needs-translation"> Slide <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> of <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="translated">Ky vlerë mbizotëron <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>, i cili është vendosur jashtë Paperless.</target>
<target state="needs-translation">This value overrides <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>, which is set outside 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="translated"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> është vendosur jashtë Paperless. Shkruani një vlerë këtu për ta mbingarkuar atë.</target>
<target state="needs-translation"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</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="translated">Përdorni vlerën e konfiguruar nga jashtë</target>
<target state="needs-translation">Use the externally configured value</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="translated">Rinisni në të jashtëm</target>
<target state="needs-translation">Reset to external</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="translated">Elementët e shiritit anësor për t'u shfaqur:</target>
<target state="needs-translation">Sidebar items to show:</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="translated">Sugjerimet parazgjedhore janë</target>
<target state="needs-translation">Suggestions default to</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="translated">Të dyja</target>
<target state="needs-translation">Both</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="translated">Vetëm ML</target>
<target state="needs-translation">ML only</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="translated">Vetëm AI</target>
<target state="needs-translation">AI only</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="translated">Kërkoni automatikisht sugjerime për dokumentet në kutinë e postës elektronike</target>
<target state="needs-translation">Automatically request suggestions for inbox documents</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="translated">Nëse nuk është shënuar, sugjerimet duhet të kërkohen përmes butonit Sugjero.</target>
<target state="needs-translation">If un-checked, suggestions must be requested via the Suggest button.</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="translated">Pastro kërkimin</target>
<target state="needs-translation">Clear search</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="translated">Shiko detajet e detyrës</target>
<target state="needs-translation">View task details</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="translated">Apliko sugjerimet e AI</target>
<target state="needs-translation">Apply AI Suggestions</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="translated">Menuja e përdoruesit</target>
<target state="needs-translation">User menu</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="translated">Ndani lidhjet</target>
<target state="needs-translation">Share links</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="translated">Konfiguro kontrollin e përditësimeve</target>
<target state="needs-translation">Configure update checking</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="translated">Bisedë</target>
<target state="needs-translation">Chat</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="translated">Dokumenti rrënjësor:</target>
<target state="needs-translation">Root document:</target>
</trans-unit>
<trans-unit id="2121026240713807342" datatype="html">
<source>Versions (oldest first):</source>
-2
View File
@@ -41,7 +41,6 @@ import {
bell,
bodyText,
bookmark,
boxArrowInRight,
boxArrowUp,
boxArrowUpRight,
boxes,
@@ -294,7 +293,6 @@ const icons = {
braces,
bodyText,
bookmark,
boxArrowInRight,
boxArrowUp,
boxArrowUpRight,
boxes,
+38
View File
@@ -11,6 +11,8 @@ from typing import TYPE_CHECKING
import pytest
from paperless_testing import samples
if TYPE_CHECKING:
from collections.abc import Generator
from pathlib import Path
@@ -149,3 +151,39 @@ 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,10 +54,6 @@ 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,14 +130,12 @@ 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,7 +3,6 @@ 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
@@ -71,15 +70,6 @@ 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]",
@@ -522,10 +512,7 @@ class Command(CryptMixin, PaperlessCommand):
counter=filename_counter,
)
else:
base_name = document.get_public_filename(
counter=filename_counter,
max_bytes=MAX_BASE_NAME_BYTES,
)
base_name = document.get_public_filename(counter=filename_counter)
# The thumbnail and archive names are derived from the stem, so
# uniqueness must hold without the original's extension
@@ -554,7 +541,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_SUFFIX)
thumbnail_name = base_name.parent / (base_name.stem + "-thumbnail.webp")
if self.use_folder_prefix:
thumbnail_name = Path("thumbnails") / thumbnail_name
thumbnail_arc = thumbnail_name.as_posix()
@@ -563,7 +550,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_SUFFIX)
archive_name = base_name.parent / (base_name.stem + "-archive.pdf")
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", use_float=True)
yield from ijson.items(f, "item")
except ijson.JSONError as e:
raise CommandError(f"Failed to parse manifest file {path}: {e}") from e
+12 -23
View File
@@ -157,8 +157,6 @@ 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,
@@ -486,37 +484,28 @@ 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: bool = False,
counter: int = 0,
max_bytes: int = MAX_PUBLIC_FILENAME_BYTES,
) -> str:
def get_public_filename(self, *, archive=False, counter=0, suffix=None) -> 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)
extension = ".pdf" if archive else self.file_type
tail = f"_{counter:02}{extension}" if counter else extension
if counter:
result += f"_{counter:02}"
head_bytes = max(0, max_bytes - len(tail.encode()))
head = str(context_document).encode()[:head_bytes].decode(errors="ignore")
if suffix:
result += suffix
return pathvalidate.sanitize_filename(
head + tail,
replacement_text="-",
max_len=max_bytes,
)
if archive:
result += ".pdf"
else:
result += self.file_type
return pathvalidate.sanitize_filename(result, replacement_text="-")
@property
def file_type(self):
-22
View File
@@ -511,28 +511,6 @@ 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,7 +926,6 @@ 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]:
"""
@@ -942,7 +941,6 @@ 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:
@@ -950,13 +948,6 @@ 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()
+6 -10
View File
@@ -12,29 +12,25 @@ 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",
document_samples_dir: Path,
simple_digital_pdf_file: Path,
multi_page_images_pdf_file: Path,
thumbnail_webp_file: Path,
) -> "Document":
"""Create a document with valid files and matching checksums."""
with filelock.FileLock(paperless_dirs.media_lock):
shutil.copy(
document_samples_dir / "originals" / "0000001.pdf",
simple_digital_pdf_file,
paperless_dirs.originals_dir / "0000001.pdf",
)
shutil.copy(
document_samples_dir / "archive" / "0000001.pdf",
multi_page_images_pdf_file,
paperless_dirs.archive_dir / "0000001.pdf",
)
shutil.copy(
document_samples_dir / "thumbnails" / "0000001.webp",
thumbnail_webp_file,
paperless_dirs.thumbnail_dir / "0000001.webp",
)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.
-1
View File
@@ -1 +0,0 @@
This is a test file.
@@ -53,7 +53,7 @@ class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase):
archive_checksum="D",
)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", self.doc2.source_path)
shutil.copy(self.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)
+1 -2
View File
@@ -835,7 +835,6 @@ 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]
@@ -887,7 +886,7 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
"all": True,
"filters": {
"text": "B",
"title_search": "c1",
"query": "c1",
},
"method": "set_storage_path",
"parameters": {"storage_path": self.sp1.id},
+49 -33
View File
@@ -56,6 +56,8 @@ 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):
@@ -1852,7 +1854,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f},
@@ -1880,7 +1886,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
payload = SimpleUploadedFile(
"../../outside.pdf",
(Path(__file__).parent / "samples" / "simple.pdf").read_bytes(),
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
)
@@ -1909,7 +1915,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
pdf_bytes = (Path(__file__).parent / "samples" / "simple.pdf").read_bytes()
pdf_bytes = SIMPLE_DIGITAL_PDF.read_bytes()
boundary = "paperless-boundary"
payload = (
(
@@ -1985,7 +1991,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
def test_upload_insufficient_permissions(self) -> None:
self.client.force_authenticate(user=UserFactory(username="testuser2"))
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f},
@@ -1998,7 +2004,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post(
"/api/documents/post_document/",
{
@@ -2031,7 +2041,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"documenst": f},
@@ -2057,7 +2067,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "title": "my custom title"},
@@ -2077,7 +2087,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
c = Correspondent.objects.create(name="test-corres")
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "correspondent": c.id},
@@ -2096,7 +2106,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "correspondent": 3456},
@@ -2111,7 +2121,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
dt = DocumentType.objects.create(name="invoice")
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "document_type": dt.id},
@@ -2130,7 +2140,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "document_type": 34578},
@@ -2145,7 +2155,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
)
sp = StoragePath.objects.create(name="invoices")
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "storage_path": sp.id},
@@ -2164,7 +2174,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "storage_path": 34578},
@@ -2180,7 +2190,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
t1 = Tag.objects.create(name="tag1")
t2 = Tag.objects.create(name="tag2")
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "tags": [t2.id, t1.id]},
@@ -2201,7 +2211,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
t1 = Tag.objects.create(name="tag1")
t2 = Tag.objects.create(name="tag2")
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "tags": [t2.id, t1.id, 734563]},
@@ -2225,7 +2235,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
0,
tzinfo=zoneinfo.ZoneInfo("America/Los_Angeles"),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "created": created},
@@ -2241,7 +2251,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "archive_serial_number": 500},
@@ -2268,7 +2282,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
data_type=CustomField.FieldDataType.STRING,
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post(
"/api/documents/post_document/",
{
@@ -2323,7 +2341,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
w1.actions.add(action1)
w1.save()
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{
@@ -2364,7 +2382,11 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
data_type=CustomField.FieldDataType.INT,
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SimpleUploadedFile(
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post(
"/api/documents/post_document/",
{
@@ -2413,7 +2435,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
]
for payload in error_payloads:
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
data = {"document": f, **payload}
response = self.client.post(
"/api/documents/post_document/",
@@ -2471,7 +2493,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()),
)
with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
with SIMPLE_DIGITAL_PDF.open("rb") as f:
response = self.client.post(
"/api/documents/post_document/",
{"document": f, "from_webui": True},
@@ -2510,14 +2532,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
archive_filename="archive.pdf",
)
source_file: Path = (
Path(__file__).parent
/ "samples"
/ "documents"
/ "thumbnails"
/ "0000001.webp"
)
archive_file: Path = Path(__file__).parent / "samples" / "simple.pdf"
source_file: Path = THUMBNAIL_WEBP
archive_file: Path = SIMPLE_DIGITAL_PDF
shutil.copy(source_file, doc.source_path)
shutil.copy(archive_file, doc.archive_path)
@@ -2550,7 +2566,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
mime_type="application/pdf",
)
shutil.copy(Path(__file__).parent / "samples" / "simple.pdf", doc.source_path)
shutil.copy(SIMPLE_DIGITAL_PDF, doc.source_path)
response = self.client.get(f"/api/documents/{doc.pk}/metadata/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -4288,8 +4304,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
filename="test2.pdf",
)
archive_file = Path(__file__).parent / "samples" / "simple.pdf"
source_file = Path(__file__).parent / "samples" / "simple.pdf"
archive_file = SIMPLE_DIGITAL_PDF
source_file = SIMPLE_DIGITAL_PDF
shutil.copy(archive_file, doc.archive_path)
shutil.copy(source_file, doc2.source_path)
+8 -5
View File
@@ -12,6 +12,9 @@ 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):
@@ -42,15 +45,15 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
# Copy sample files to document paths (using different files to distinguish versions)
shutil.copy(
self.SAMPLE_DIR / "documents" / "originals" / "0000001.pdf",
SIMPLE_DIGITAL_PDF,
self.doc1.archive_path,
)
shutil.copy(
self.SAMPLE_DIR / "documents" / "originals" / "0000002.pdf",
MULTI_PAGE_DIGITAL_PDF,
self.doc1.source_path,
)
shutil.copy(
self.SAMPLE_DIR / "documents" / "originals" / "0000003.pdf",
WITH_FORM_PDF,
self.doc2.source_path,
)
@@ -377,7 +380,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
checksum="3",
filename="test3.pdf",
)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", doc3.source_path)
shutil.copy(self.SIMPLE_PDF, doc3.source_path)
doc4 = Document.objects.create(
title="test1",
@@ -386,7 +389,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
checksum="4",
filename="test4.pdf",
)
shutil.copy(self.SAMPLE_DIR / "simple.pdf", doc4.source_path)
shutil.copy(self.SIMPLE_PDF, doc4.source_path)
response = self.client.post(
self.ENDPOINT,
+24 -61
View File
@@ -245,68 +245,8 @@ 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&title_search=bank")
response = self.client.get("/api/documents/?text=bank&query=bank")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data["detail"],
@@ -2137,6 +2077,29 @@ 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.SAMPLE_DIR / "simple.pdf"
test_file = self.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.SAMPLE_DIR / "simple.pdf"
test_file = self.SIMPLE_PDF
with self.get_reader(test_file) as reader:
try:
+9 -30
View File
@@ -27,6 +27,9 @@ 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):
@@ -704,47 +707,27 @@ class TestPDFActions(DirectoriesMixin, TestCase):
super().setUp()
sample1 = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
SIMPLE_DIGITAL_PDF,
sample1,
)
sample1_archive = self.dirs.archive_dir / "sample_archive.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
SIMPLE_DIGITAL_PDF,
sample1_archive,
)
sample2 = self.dirs.scratch_dir / "sample2.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000002.pdf",
MULTI_PAGE_DIGITAL_PDF,
sample2,
)
sample2_archive = self.dirs.archive_dir / "sample2_archive.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000002.pdf",
MULTI_PAGE_DIGITAL_PDF,
sample2_archive,
)
sample3 = self.dirs.scratch_dir / "sample3.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000003.pdf",
WITH_FORM_PDF,
sample3,
)
self.doc1 = Document.objects.create(
@@ -778,11 +761,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
)
img_doc_archive = self.dirs.archive_dir / "sample_image.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
SIMPLE_DIGITAL_PDF,
img_doc_archive,
)
self.img_doc = Document.objects.create(
-44
View File
@@ -14,7 +14,6 @@ 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
@@ -1037,49 +1036,6 @@ 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:
+9 -56
View File
@@ -34,10 +34,11 @@ 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.permissions import grant_object
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
class _BaseNewStyleParser:
@@ -114,9 +115,7 @@ class _BaseNewStyleParser:
class DummyParser(_BaseNewStyleParser):
_ARCHIVE_SRC = (
Path(__file__).parent / "samples" / "documents" / "archive" / "0000001.pdf"
)
_ARCHIVE_SRC = MULTI_PAGE_IMAGES_PDF
def parse(self, document_path, mime_type, *, produce_archive: bool = True) -> None:
self._text = "The Text"
@@ -202,33 +201,19 @@ class TestConsumer(
self.addCleanup(patcher.stop)
def get_test_file(self):
src = (
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf"
)
src = SIMPLE_DIGITAL_PDF
dst = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(src, dst)
return dst
def get_test_file2(self):
src = (
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000002.pdf"
)
src = MULTI_PAGE_DIGITAL_PDF
dst = self.dirs.scratch_dir / "sample2.pdf"
shutil.copy(src, dst)
return dst
def get_test_archive_file(self):
src = (
Path(__file__).parent / "samples" / "documents" / "archive" / "0000001.pdf"
)
src = MULTI_PAGE_IMAGES_PDF
dst = self.dirs.scratch_dir / "sample_archive.pdf"
shutil.copy(src, dst)
return dst
@@ -1064,7 +1049,7 @@ class TestConsumer(
@mock.patch("documents.consumer.get_parser_registry")
def test_similar_filenames(self, m) -> None:
shutil.copy(
Path(__file__).parent / "samples" / "simple.pdf",
SIMPLE_DIGITAL_PDF,
settings.CONSUMPTION_DIR / "simple.pdf",
)
shutil.copy(
@@ -1605,13 +1590,7 @@ class TestConsumerRemoteOCR(
self.addCleanup(patcher.stop)
def _consume(self, *, overrides: DocumentMetadataOverrides | None = None) -> bool:
src = (
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf"
)
src = SIMPLE_DIGITAL_PDF
dst = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(src, dst)
@@ -1685,32 +1664,6 @@ 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,3 +423,35 @@ 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,16 +4,13 @@ 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):
@@ -260,72 +257,3 @@ 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)
+5 -2
View File
@@ -37,12 +37,15 @@ 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_DIR / srcname
src = self._sample(srcname)
dst = self.double_sided_dir / dstname
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy(src, dst)
@@ -57,7 +60,7 @@ class TestDoubleSided(
return msg
def create_staging_file(self, src="double-sided-odd.pdf", datetime=None) -> None:
shutil.copy(self.SAMPLE_DIR / src, self.staging_file)
shutil.copy(self._sample(src), self.staging_file)
if datetime is None:
datetime = dt.datetime.now()
os.utime(str(self.staging_file), (datetime.timestamp(),) * 2)
+2 -1
View File
@@ -23,8 +23,9 @@ 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 = Path(__file__).parent / "samples" / "simple.pdf"
sample_file: Path = SIMPLE_DIGITAL_PDF
@pytest.mark.management
+21 -187
View File
@@ -50,7 +50,6 @@ 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
@@ -59,6 +58,7 @@ 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,10 +204,7 @@ class TestExportImport(
def test_exporter(self, *, use_filename_format=False) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
num_permission_objects = Permission.objects.count()
@@ -311,10 +308,7 @@ class TestExportImport(
def test_exporter_with_filename_format(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
with override_settings(
FILENAME_FORMAT="{created_year}/{correspondent}/{title}",
@@ -323,10 +317,7 @@ class TestExportImport(
def test_exporter_includes_share_links_and_bundles(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
share_link = ShareLink.objects.create(
slug="share-link-slug",
@@ -425,10 +416,7 @@ class TestExportImport(
def test_update_export_changed_time(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
self._do_export()
self.assertIsFile(self.target / "manifest.json")
@@ -464,10 +452,7 @@ class TestExportImport(
def test_update_export_changed_checksum(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
self._do_export()
@@ -494,10 +479,7 @@ class TestExportImport(
def test_update_export_deleted_document(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
manifest = self._do_export()
@@ -529,10 +511,7 @@ class TestExportImport(
@override_settings(FILENAME_FORMAT="{title}/{correspondent}")
def test_update_export_changed_location(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
self._do_export(use_filename_format=True)
self.assertIsFile(self.target / "wow1" / "c.pdf")
@@ -574,10 +553,7 @@ class TestExportImport(
- Zipfile contains exported files
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
args = ["document_exporter", self.target, "--zip"]
@@ -606,10 +582,7 @@ class TestExportImport(
- Zipfile contains exported files
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
args = ["document_exporter", self.target, "--zip", "--use-filename-format"]
@@ -645,10 +618,7 @@ class TestExportImport(
- The existing file and directory in target are removed
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
# Create stuff in target directory
existing_file = self.target / "test.txt"
@@ -744,10 +714,7 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
manifest = self._do_export()
has_archive = False
@@ -790,10 +757,7 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
manifest = self._do_export()
has_thumbnail = False
@@ -838,10 +802,7 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
manifest = self._do_export(split_manifest=True)
has_document = False
@@ -866,10 +827,7 @@ class TestExportImport(
def _export_import_barcodes(self, *, split_manifest: bool) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
DocumentBarcodeFactory(document=self.d1, value="https://example.com")
DocumentBarcodeFactory(document=self.d2, page=2, value="DE8937")
@@ -905,10 +863,7 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
self._do_export(use_folder_prefix=True)
@@ -935,10 +890,7 @@ class TestExportImport(
- Documents can be imported again
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
self._do_export(use_folder_prefix=True, split_manifest=True)
@@ -965,10 +917,7 @@ class TestExportImport(
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
num_content_type_objects = ContentType.objects.count()
num_permission_objects = Permission.objects.count()
@@ -1005,10 +954,7 @@ class TestExportImport(
def test_exporter_with_auditlog_disabled(self) -> None:
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
with override_settings(
AUDIT_LOG_ENABLED=False,
@@ -1028,10 +974,7 @@ class TestExportImport(
survive the round-trip with deleted_at preserved
"""
shutil.rmtree(Path(self.dirs.media_dir) / "documents")
shutil.copytree(
Path(__file__).parent / "samples" / "documents",
Path(self.dirs.media_dir) / "documents",
)
install_document_samples(Path(self.dirs.media_dir) / "documents")
# d1 has self.note and self.cfi1 attached via setUp
self.d1.delete()
@@ -1075,10 +1018,7 @@ class TestExportImport(
"""
shutil.rmtree(self.dirs.media_dir / "documents")
shutil.copytree(
self.SAMPLE_DIR / "documents",
self.dirs.media_dir / "documents",
)
install_document_samples(self.dirs.media_dir / "documents")
_ = self._do_export(data_only=True)
@@ -1403,45 +1343,6 @@ 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:
@@ -1577,70 +1478,3 @@ 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,6 +11,7 @@ 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
@@ -24,7 +25,7 @@ class TestMakeThumbnails(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
filename="test.pdf",
)
shutil.copy(
Path(__file__).parent / "samples" / "simple.pdf",
SIMPLE_DIGITAL_PDF,
self.d1.source_path,
)
@@ -36,7 +37,7 @@ class TestMakeThumbnails(DirectoriesMixin, FileSystemAssertsMixin, TestCase):
filename="test2.pdf",
)
shutil.copy(
Path(__file__).parent / "samples" / "simple.pdf",
SIMPLE_DIGITAL_PDF,
self.d2.source_path,
)
@@ -7,7 +7,6 @@ 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
@@ -226,25 +225,6 @@ 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:
+5 -23
View File
@@ -21,6 +21,7 @@ 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
@@ -229,20 +230,12 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
"""
sample1 = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
SIMPLE_DIGITAL_PDF,
sample1,
)
sample1_archive = self.dirs.archive_dir / "sample_archive.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
SIMPLE_DIGITAL_PDF,
sample1_archive,
)
doc = Document.objects.create(
@@ -270,11 +263,7 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
"""
sample1 = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
SIMPLE_DIGITAL_PDF,
sample1,
)
doc = Document.objects.create(
@@ -290,14 +279,7 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
def _create_root_with_version(self) -> tuple[Document, Document]:
sample1 = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
sample1,
)
shutil.copy(SIMPLE_DIGITAL_PDF, sample1)
root = DocumentFactory(content="root content", mime_type="application/pdf")
version = DocumentFactory(
content="my document",
+27 -71
View File
@@ -178,7 +178,7 @@ class TestWorkflows(
self.assertEqual(action.__str__(), "WorkflowAction 1")
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -290,7 +290,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -409,7 +409,7 @@ class TestWorkflows(
w2.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -478,7 +478,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -531,7 +531,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -608,7 +608,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -687,7 +687,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -765,7 +765,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -876,7 +876,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.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.SAMPLE_DIR / "simple.pdf", destination)
shutil.copy(self.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.SAMPLE_DIR / "simple.pdf",
self.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.SAMPLE_DIR / "simple.pdf", destination)
shutil.copy(self.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.SAMPLE_DIR / "simple.pdf", destination)
shutil.copy(self.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.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -3346,7 +3346,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.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.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "working-copy.pdf",
)
@@ -3596,7 +3596,7 @@ class TestWorkflows(
# move the file
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.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.SAMPLE_DIR / "simple.pdf", destination)
shutil.copy(self.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.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -4036,7 +4036,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -4387,7 +4387,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -4835,7 +4835,7 @@ class TestWorkflows(
w.save()
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.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.SAMPLE_DIR / "simple.pdf",
self.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.SAMPLE_DIR / "simple.pdf",
self.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.SAMPLE_DIR / "simple.pdf",
self.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.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
overrides = DocumentMetadataOverrides()
@@ -5814,7 +5814,7 @@ class TestApplyAISuggestionsWorkflowAction(
)
test_file = shutil.copy(
self.SAMPLE_DIR / "simple.pdf",
self.SIMPLE_PDF,
self.dirs.scratch_dir / "simple.pdf",
)
@@ -6176,50 +6176,6 @@ 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,6 +11,7 @@ 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:
@@ -53,6 +54,8 @@ class SampleDirMixin:
BARCODE_SAMPLE_DIR = SAMPLE_DIR / "barcodes"
SIMPLE_PDF = SIMPLE_DIGITAL_PDF
class GetConsumerMixin:
@contextmanager
+27 -46
View File
@@ -289,7 +289,6 @@ 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:
@@ -309,18 +308,6 @@ _TANTIVY_COMBINABLE_SEARCH_PARAMS = ({"text", "query"}, {"title_search", "query"
_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
@@ -334,15 +321,9 @@ def _get_tantivy_query_and_mode(params):
else:
return None # pragma: no cover
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
if len(raw) > _MAX_QUERY_LENGTH:
raise QueryTooLongError(len(raw), _MAX_QUERY_LENGTH)
return raw, mode
def _get_more_like_id(query_params: dict[str, Any], user: User | None) -> int:
@@ -2585,9 +2566,15 @@ class UnifiedSearchViewSet(DocumentViewSet):
def parse_search_params() -> SearchParams:
"""Extract query string, search mode, and ordering from request."""
_validate_tantivy_search_params(
self._get_active_search_params(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.",
),
},
)
ordering_param = request.query_params.get("ordering", "")
sort_reverse = ordering_param.startswith("-")
@@ -2649,9 +2636,7 @@ class UnifiedSearchViewSet(DocumentViewSet):
filtered_qs: QuerySet[Document],
) -> SearchResultPage:
"""Handle text/title/query search: IDs, ORM intersection, page highlights."""
query_str, search_mode, filter_query = _get_tantivy_query_and_mode(
request.query_params,
)
query_str, search_mode = _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.
@@ -2664,7 +2649,6 @@ class UnifiedSearchViewSet(DocumentViewSet):
),
sort_reverse=sort_reverse,
search_mode=search_mode,
filter_query=filter_query,
)
ordered_ids = intersect_and_order(
all_ids,
@@ -2899,7 +2883,14 @@ class DocumentSelectionMixin:
]
if not search_filters:
return None
_validate_tantivy_search_params(search_filters)
if len(search_filters) > 1:
raise ValidationError(
{
"detail": _(
"Specify only one of text, title_search, query, or more_like_id.",
),
},
)
from documents.search import SearchQueryError
from documents.search import get_backend
@@ -2918,14 +2909,11 @@ class DocumentSelectionMixin:
user=search_user,
)
else:
query_str, search_mode, filter_query = _get_tantivy_query_and_mode(
filters,
)
query_str, search_mode = _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:
@@ -3932,12 +3920,7 @@ class GlobalSearchView(PassUserMixin):
"request": request,
}
docs_serializer = DocumentSerializer(
docs,
many=True,
context=context,
fields=("id", "title", "created"),
)
docs_serializer = DocumentSerializer(docs, many=True, context=context)
saved_views_serializer = SavedViewSerializer(
saved_views,
many=True,
@@ -4916,7 +4899,7 @@ class SharedLinkView(View):
share_link.expiration is not None
and share_link.expiration < timezone.now()
):
return HttpResponseRedirect(f"{settings.LOGIN_URL}?sharelink_expired=1")
return HttpResponseRedirect("/accounts/login/?sharelink_expired=1")
try:
return serve_file(
doc=share_link.document,
@@ -4925,16 +4908,14 @@ class SharedLinkView(View):
disposition="inline",
)
except FileNotFoundError:
return HttpResponseRedirect(
f"{settings.LOGIN_URL}?sharelink_notfound=1",
)
return HttpResponseRedirect("/accounts/login/?sharelink_notfound=1")
bundle = ShareLinkBundle.objects.filter(slug=slug).first()
if bundle is None:
return HttpResponseRedirect(f"{settings.LOGIN_URL}?sharelink_notfound=1")
return HttpResponseRedirect("/accounts/login/?sharelink_notfound=1")
if bundle.expiration is not None and bundle.expiration < timezone.now():
return HttpResponseRedirect(f"{settings.LOGIN_URL}?sharelink_expired=1")
return HttpResponseRedirect("/accounts/login/?sharelink_expired=1")
if bundle.status in {
ShareLinkBundle.Status.PENDING,
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "كخطوة أخيرة، يرجى ملء النموذج التالي:"
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "Като последна стъпка, моля, попълнете с
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Invalid more_like_id"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configuració AI invàlida."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Permisos insuficients per compartir document %(id)s."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Paquet ja s'està processant."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "El paquet d'enllaç no està disponible."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, 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:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Ungültige more_like_id"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Ungültige KI-Konfiguration."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Unzureichende Berechtigungen, um Dokument %(id)s zu teilen."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Paket wird bereits verarbeitet."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "Das Freigabelink-Paket ist nicht verfügbar."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Ungültige more_like_id"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Ungültige KI-Konfiguration."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "Zeitüberschreitung bei der KI-Backendanfrage."
#: documents/views.py:1726
#: 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:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Unzureichende Berechtigungen, um Dokument %(id)s zu teilen."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Paket wird bereits verarbeitet."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "Das Freigabelink-Paket ist nicht verfügbar."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "Ως τελικό βήμα, παρακαλούμε να συμπληρώ
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
File diff suppressed because it is too large. Load diff
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configuración de IA inválida."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Permisos insuficientes para compartir el documento %(id)s."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "El paquete ya está siendo procesado."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "El paquete de enlace compartido no está disponible."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr "به عنوان مرحله آخر ، لطفاً فرم زیر را تک
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "More_like_id invalide"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configuration IA invalide."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "La requête d'arrière-plan IA a expiré."
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Droits d'accès insuffisant pour partager %(id)s document."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Le paquet est déjà en cours de traitement."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "Le lot de liens de partage n'est pas disponible."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "מסמכים לא נמצאו: %(ids)s"
@@ -1918,40 +1918,40 @@ msgstr ": כצעד אחרון, אנא מלא את הטופס הבא:"
msgid ", "
msgstr ""
#: 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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "מזהה more_like_id אינו תקין"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "הגדרות בינה מלאכותית שגויות."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "הרשאות לא מספיקות לשיתוף מסמך %(id)s."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "החבילה (Bundle) כבר נמצאת בתהליך עיבוד."
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "חבילת קישור השיתוף עדיין בהכנה. נא לנסות שוב מאוחר יותר."
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "חבילת קישור השיתוף אינה זמינה."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Nevažeći more_like_id"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Nevažeća AI konfiguracija."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "Zahtjev prema AI pozadinskom sustavu je istekao."
#: documents/views.py:1726
#: 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:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Nedovoljne ovlasti za dijeljenje dokumenta %(id)s."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Paket se već obrađuje."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "Paket linka za dijeljenje nije dostupan."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Érvénytelen more_like_id"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Érvénytelen MI konfiguráció."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, 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:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "A csomag feldolgozása már folyamatban van."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "A megosztási linkcsomag nem elérhető."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Izin tidak mencukupi untuk berbagi dokumen %(id)s"
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Paket sedang diproses."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "Bundel tautan berbagi tidak tersedia."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "more_like_id non valido"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configurazione AI non valida."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "Richiesta di backend AI scaduta."
#: documents/views.py:1726
#: 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:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Autorizzazioni insufficienti per condividere il documento %(id)s."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Il pacchetto è già in fase di elaborazione."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "Il pacchetto di link di condivisione non è disponibile."
+62 -62
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-15 12:29\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "最後のステップとして、以下のフォームに記入してく
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "마지막 단계로 다음 양식을 작성해 주세요:"
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr "Galiausiai užpildykite šią formą:"
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Nepakanka leidimų bendrinti dokumentą %(id)s."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Rinkinys jau apdorojamas."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "Dalinimos nuorodų rinkinys yra nepasiekiamas."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1916,40 +1916,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Invalid more_like_id (NO)"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Invalid AI configuration. (NO)"
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "Forespørselen til KI-tjenesten fikk tidsavbrudd."
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Insufficient permissions to share document %(id)s. (NO)"
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Bundle is already being processed. (NO)"
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "The share link bundle is unavailable. (NO)"
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "Nieprawidłowy more_like_id"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Brak uprawnień do udostępnienia dokumentu %(id)s."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Pakiet jest już przetwarzany."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "Pakiet do udostępnienia jest niedostępny."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, 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: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:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr "more_like_id inválido"
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr "Configuração de IA inválida."
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr "A requisição backend da IA expirou."
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
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
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Permissões insuficientes para compartilhar o documento %(id)s."
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr "Pacote já está sendo processado."
#: documents/views.py:4938
#: documents/views.py:4926
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:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr "O pacote de link de compartilhamento não está disponível."
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\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:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: 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:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr ""
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
msgid "The share link bundle is unavailable."
msgstr ""
+16 -16
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-14 15:18+0000\n"
"PO-Revision-Date: 2026-10-14 15:19\n"
"POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n"
"Language-Team: Romanian\n"
"Language: ro_RO\n"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "rulări flux de lucru"
#: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:363 documents/views.py:2768
#: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Variabilă nevalidă detectată."
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4811
#: documents/serialisers.py:2988 documents/views.py:4799
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1917,40 +1917,40 @@ msgstr "În etapa finală, vă rugăm să completați următorul formular:"
msgid ", "
msgstr ""
#: documents/views.py:318
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:356 documents/views.py:2765
#: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1703
#: documents/views.py:1684
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1714
#: documents/views.py:1695
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1726
#: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:4827
#: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4815
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4874
#: documents/views.py:4862
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4938
#: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4952
#: documents/views.py:4940
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