diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss
index 6f2260e47..9dd7ed5cf 100644
--- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss
+++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss
@@ -22,10 +22,14 @@
}
}
+.doc-img-container {
+ position: relative;
+ height: 180px;
+}
+
.doc-img {
object-fit: cover;
object-position: top left;
- height: 180px;
}
.document-card-check {
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts
index 45b812462..c6127494b 100644
--- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts
+++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts
@@ -61,6 +61,12 @@ describe('DocumentCardSmallComponent', () => {
expect(fixture.nativeElement.textContent).toContain('12 pages')
})
+ it('should lazy load the thumbnail', () => {
+ const thumbnail: HTMLImageElement =
+ fixture.nativeElement.querySelector('img.doc-img')
+ expect(thumbnail.getAttribute('loading')).toEqual('lazy')
+ })
+
it('should display a document, limit tags to 5', () => {
expect(fixture.nativeElement.textContent).toContain('Document 10')
expect(
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts
index 1e45b982a..633ad3e2e 100644
--- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts
+++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts
@@ -1,4 +1,4 @@
-import { AsyncPipe } from '@angular/common'
+import { AsyncPipe, NgOptimizedImage } from '@angular/common'
import {
AfterViewInit,
Component,
@@ -46,6 +46,7 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
TagComponent,
CustomFieldDisplayComponent,
AsyncPipe,
+ NgOptimizedImage,
UsernamePipe,
CorrespondentNamePipe,
DocumentTypeNamePipe,
diff --git a/src/documents/tests/test_views.py b/src/documents/tests/test_views.py
index 376cb8e93..343f2cc13 100644
--- a/src/documents/tests/test_views.py
+++ b/src/documents/tests/test_views.py
@@ -177,6 +177,49 @@ class TestViews(DirectoriesMixin, TestCase):
self.assertEqual(response.request["PATH_INFO"], "/accounts/login/")
self.assertContains(response, b"Share link has expired")
+ def test_share_link_archive_falls_back_to_original(self) -> None:
+ """
+ GIVEN:
+ - A document without an archive version
+ - A share link using the default archive file version
+ WHEN:
+ - An unauthenticated request for the share link is made
+ THEN:
+ - The original document is returned
+ """
+ _, filename = tempfile.mkstemp(dir=self.dirs.originals_dir)
+ content = b"This document has no archive"
+
+ with Path(filename).open("wb") as f:
+ f.write(content)
+
+ doc = Document.objects.create(
+ title="no archive",
+ filename=Path(filename).name,
+ mime_type="text/plain",
+ )
+
+ sharelink_permissions = Permission.objects.filter(
+ codename__contains="sharelink",
+ )
+ self.user.user_permissions.add(*sharelink_permissions)
+ self.client.force_login(self.user)
+
+ create_response = self.client.post(
+ "/api/share_links/",
+ {"document": doc.pk},
+ )
+ self.assertEqual(create_response.status_code, status.HTTP_201_CREATED)
+ share_link = ShareLink.objects.get(document=doc)
+ self.assertEqual(share_link.file_version, ShareLink.FileVersion.ARCHIVE)
+
+ self.client.logout()
+
+ response = self.client.get(f"/share/{share_link.slug}")
+
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertEqual(read_streaming_response(response), content)
+
def test_list_with_full_permissions(self) -> None:
"""
GIVEN:
diff --git a/src/documents/views.py b/src/documents/views.py
index 0a9045126..1591d0b06 100644
--- a/src/documents/views.py
+++ b/src/documents/views.py
@@ -4534,7 +4534,8 @@ class SharedLinkView(View):
return HttpResponseRedirect("/accounts/login/?sharelink_expired=1")
return serve_file(
doc=share_link.document,
- use_archive=share_link.file_version == "archive",
+ use_archive=share_link.file_version == ShareLink.FileVersion.ARCHIVE
+ and share_link.document.has_archive_version,
disposition="inline",
)
diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po
index dff2d995a..598552327 100644
--- a/src/locale/en_US/LC_MESSAGES/django.po
+++ b/src/locale/en_US/LC_MESSAGES/django.po
@@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-07-15 20:44+0000\n"
+"POT-Creation-Date: 2026-07-19 20:55+0000\n"
"PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n"
"Language-Team: English\n"
@@ -53,7 +53,7 @@ msgstr ""
msgid "Maximum nesting depth exceeded."
msgstr ""
-#: documents/filters.py:1052
+#: documents/filters.py:1059
msgid "Custom field not found"
msgstr ""
@@ -1686,11 +1686,11 @@ msgstr ""
msgid "Bundle is already being processed."
msgstr ""
-#: documents/views.py:4554
+#: documents/views.py:4555
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
-#: documents/views.py:4564
+#: documents/views.py:4565
msgid "The share link bundle is unavailable."
msgstr ""