Enhancment: Formatted filename for single document downloads (#12095)

---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
Jan Kleine
2026-02-26 19:06:47 +01:00
committed by GitHub
parent 5e1202a416
commit c86ebc0260
7 changed files with 107 additions and 18 deletions
@@ -44,14 +44,21 @@
<span class="d-none d-lg-inline ps-1" i18n>Download</span>
</button>
@if (metadata?.has_archive_version) {
<div class="btn-group" ngbDropdown role="group">
<button class="btn btn-sm btn-outline-primary dropdown-toggle" [disabled]="downloading" ngbDropdownToggle></button>
<div class="dropdown-menu shadow" ngbDropdownMenu>
<div class="btn-group" ngbDropdown role="group">
<button class="btn btn-sm btn-outline-primary dropdown-toggle" [disabled]="downloading" ngbDropdownToggle></button>
<div class="dropdown-menu shadow" ngbDropdownMenu>
@if (metadata?.has_archive_version) {
<button ngbDropdownItem (click)="download(true)" [disabled]="downloading" i18n>Download original</button>
</div>
<div class="dropdown-divider"></div>
}
<form class="px-3 py-1">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="downloadUseFormatting" [(ngModel)]="useFormattedFilename" [ngModelOptions]="{standalone: true}" />
<label class="form-check-label" for="downloadUseFormatting" i18n>Use formatted filename</label>
</div>
</form>
</div>
}
</div>
</div>
<div class="ms-auto" ngbDropdown>
@@ -1813,7 +1813,13 @@ describe('DocumentDetailComponent', () => {
component.selectedVersionId = 10
component.download()
expect(getDownloadUrlSpy).toHaveBeenNthCalledWith(1, doc.id, false, null)
expect(getDownloadUrlSpy).toHaveBeenNthCalledWith(
1,
doc.id,
false,
null,
false
)
httpTestingController
.expectOne('download-latest')
.error(new ProgressEvent('failed'))
@@ -1826,7 +1832,13 @@ describe('DocumentDetailComponent', () => {
component.selectedVersionId = doc.id
component.download()
expect(getDownloadUrlSpy).toHaveBeenNthCalledWith(3, doc.id, false, doc.id)
expect(getDownloadUrlSpy).toHaveBeenNthCalledWith(
3,
doc.id,
false,
doc.id,
false
)
httpTestingController
.expectOne('download-non-latest')
.error(new ProgressEvent('failed'))
@@ -1849,7 +1861,13 @@ describe('DocumentDetailComponent', () => {
.mockReturnValueOnce('print-no-version')
component.download()
expect(getDownloadUrlSpy).toHaveBeenNthCalledWith(1, doc.id, false, null)
expect(getDownloadUrlSpy).toHaveBeenNthCalledWith(
1,
doc.id,
false,
null,
false
)
httpTestingController
.expectOne('download-no-version')
.error(new ProgressEvent('failed'))
@@ -276,6 +276,7 @@ export class DocumentDetailComponent
customFields: CustomField[]
public downloading: boolean = false
public useFormattedFilename: boolean = false
public readonly CustomFieldDataType = CustomFieldDataType
@@ -1289,7 +1290,8 @@ export class DocumentDetailComponent
const downloadUrl = this.documentsService.getDownloadUrl(
this.documentId,
original,
selectedVersionId
selectedVersionId,
this.useFormattedFilename
)
this.http
.get(downloadUrl, { observe: 'response', responseType: 'blob' })
@@ -272,10 +272,10 @@ describe(`DocumentService`, () => {
expect(url).toEqual(
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/download/?version=123`
)
url = service.getDownloadUrl(documents[0].id, true, 123)
expect(url).toEqual(
`${environment.apiBaseUrl}${endpoint}/${documents[0].id}/download/?original=true&version=123`
)
url = service.getDownloadUrl(documents[0].id, true, 123, true)
expect(url).toContain('original=true')
expect(url).toContain('version=123')
expect(url).toContain('follow_formatting=true')
})
it('should pass optional get params for version and fields', () => {
@@ -202,7 +202,8 @@ export class DocumentService extends AbstractPaperlessService<Document> {
getDownloadUrl(
id: number,
original: boolean = false,
versionID: number = null
versionID: number = null,
followFormatting: boolean = false
): string {
let url = new URL(this.getResourceUrl(id, 'download'))
if (original) {
@@ -211,6 +212,9 @@ export class DocumentService extends AbstractPaperlessService<Document> {
if (versionID) {
url.searchParams.append('version', versionID.toString())
}
if (followFormatting) {
url.searchParams.append('follow_formatting', 'true')
}
return url.toString()
}