From 1ccdd1f4dbf0fa8689f094a46c1345a7673dda80 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 5 Nov 2025 17:24:23 -0800 Subject: [PATCH] Consistent naming to Share Link Bundle --- .../share-bundle-dialog.component.spec.ts | 7 -- ...are-bundle-manage-dialog.component.spec.ts | 7 -- .../share-link-bundle-dialog.component.html} | 8 +- .../share-link-bundle-dialog.component.scss} | 0 ...share-link-bundle-dialog.component.spec.ts | 7 ++ .../share-link-bundle-dialog.component.ts} | 40 ++++----- ...-link-bundle-manage-dialog.component.html} | 8 +- ...ink-bundle-manage-dialog.component.spec.ts | 7 ++ ...re-link-bundle-manage-dialog.component.ts} | 62 +++++++------- .../bulk-editor/bulk-editor.component.html | 8 +- .../bulk-editor/bulk-editor.component.ts | 26 +++--- src-ui/src/app/data/share-bundle.ts | 40 --------- src-ui/src/app/data/share-link-bundle.ts | 46 ++++++++++ .../app/services/rest/share-bundle.service.ts | 38 --------- .../rest/share-link-bundle.service.ts | 41 +++++++++ src/documents/admin.py | 6 +- src/documents/filters.py | 6 +- ...sharebundle.py => 1075_sharelinkbundle.py} | 36 ++++---- src/documents/models.py | 12 +-- src/documents/serialisers.py | 16 ++-- src/documents/tasks.py | 36 ++++---- src/documents/views.py | 85 +++++++++---------- src/paperless/settings.py | 8 +- src/paperless/urls.py | 4 +- 24 files changed, 283 insertions(+), 271 deletions(-) delete mode 100644 src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.spec.ts delete mode 100644 src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.spec.ts rename src-ui/src/app/components/common/{share-bundle-dialog/share-bundle-dialog.component.html => share-link-bundle-dialog/share-link-bundle-dialog.component.html} (91%) rename src-ui/src/app/components/common/{share-bundle-dialog/share-bundle-dialog.component.scss => share-link-bundle-dialog/share-link-bundle-dialog.component.scss} (100%) create mode 100644 src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.spec.ts rename src-ui/src/app/components/common/{share-bundle-dialog/share-bundle-dialog.component.ts => share-link-bundle-dialog/share-link-bundle-dialog.component.ts} (73%) rename src-ui/src/app/components/common/{share-bundle-manage-dialog/share-bundle-manage-dialog.component.html => share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html} (95%) create mode 100644 src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.spec.ts rename src-ui/src/app/components/common/{share-bundle-manage-dialog/share-bundle-manage-dialog.component.ts => share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts} (68%) delete mode 100644 src-ui/src/app/data/share-bundle.ts create mode 100644 src-ui/src/app/data/share-link-bundle.ts delete mode 100644 src-ui/src/app/services/rest/share-bundle.service.ts create mode 100644 src-ui/src/app/services/rest/share-link-bundle.service.ts rename src/documents/migrations/{1075_sharebundle.py => 1075_sharelinkbundle.py} (84%) diff --git a/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.spec.ts b/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.spec.ts deleted file mode 100644 index baae428ec..000000000 --- a/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -describe('ShareBundleDialogComponent', () => { - it('is pending implementation', () => { - pending( - 'ShareBundleDialogComponent tests will be implemented once the dialog logic is finalized.' - ) - }) -}) diff --git a/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.spec.ts b/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.spec.ts deleted file mode 100644 index 3f39da1b5..000000000 --- a/src-ui/src/app/components/common/share-bundle-manage-dialog/share-bundle-manage-dialog.component.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -describe('ShareBundleManageDialogComponent', () => { - it('is pending implementation', () => { - pending( - 'ShareBundleManageDialogComponent tests will be implemented once the dialog logic is finalized.' - ) - }) -}) diff --git a/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.html b/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.html similarity index 91% rename from src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.html rename to src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.html index 0c9c95bf0..19a6e5d93 100644 --- a/src-ui/src/app/components/common/share-bundle-dialog/share-bundle-dialog.component.html +++ b/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.html @@ -50,9 +50,9 @@ } @else {
-
Share link requested
+
Share link bundle requested

- You can copy the link below or open the management screen to monitor its progress. The link will start working once it is ready. + You can copy the share link below or open the manager to monitor progress. The link will start working once the bundle is ready.

@@ -105,11 +105,11 @@
- - @if (emailEnabled) { diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index 1e82d612e..69bba8141 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -33,7 +33,7 @@ import { SelectionDataItem, } from 'src/app/services/rest/document.service' import { SavedViewService } from 'src/app/services/rest/saved-view.service' -import { ShareBundleService } from 'src/app/services/rest/share-bundle.service' +import { ShareLinkBundleService } from 'src/app/services/rest/share-link-bundle.service' import { StoragePathService } from 'src/app/services/rest/storage-path.service' import { TagService } from 'src/app/services/rest/tag.service' import { SettingsService } from 'src/app/services/settings.service' @@ -55,8 +55,8 @@ import { } from '../../common/filterable-dropdown/filterable-dropdown.component' import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component' import { PermissionsDialogComponent } from '../../common/permissions-dialog/permissions-dialog.component' -import { ShareBundleDialogComponent } from '../../common/share-bundle-dialog/share-bundle-dialog.component' -import { ShareBundleManageDialogComponent } from '../../common/share-bundle-manage-dialog/share-bundle-manage-dialog.component' +import { ShareLinkBundleDialogComponent } from '../../common/share-link-bundle-dialog/share-link-bundle-dialog.component' +import { ShareLinkBundleManageDialogComponent } from '../../common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component' import { ComponentWithPermissions } from '../../with-permissions/with-permissions.component' import { CustomFieldsBulkEditDialogComponent } from './custom-fields-bulk-edit-dialog/custom-fields-bulk-edit-dialog.component' @@ -90,7 +90,7 @@ export class BulkEditorComponent private customFieldService = inject(CustomFieldsService) private permissionService = inject(PermissionsService) private savedViewService = inject(SavedViewService) - private shareBundleService = inject(ShareBundleService) + private shareLinkBundleService = inject(ShareLinkBundleService) tagSelectionModel = new FilterableDropdownSelectionModel(true) correspondentSelectionModel = new FilterableDropdownSelectionModel() @@ -912,12 +912,12 @@ export class BulkEditorComponent return this.settings.get(SETTINGS_KEYS.EMAIL_ENABLED) } - shareSelected() { - const modal = this.modalService.open(ShareBundleDialogComponent, { + createShareLinkBundle() { + const modal = this.modalService.open(ShareLinkBundleDialogComponent, { backdrop: 'static', size: 'lg', }) - const dialog = modal.componentInstance as ShareBundleDialogComponent + const dialog = modal.componentInstance as ShareLinkBundleDialogComponent const selectedDocuments = this.list.documents.filter((d) => this.list.selected.has(d.id) ) @@ -931,7 +931,7 @@ export class BulkEditorComponent } dialog.loading = true dialog.buttonsEnabled = false - this.shareBundleService + this.shareLinkBundleService .createBundle(payload) .pipe(first()) .subscribe({ @@ -943,17 +943,17 @@ export class BulkEditorComponent dialog.payload = null dialog.onOpenManage = () => { modal.close() - this.manageShareLinks() + this.manageShareLinkBundles() } this.toastService.showInfo( - $localize`Bulk share link creation requested.` + $localize`Share link bundle creation requested.` ) }, error: (error) => { dialog.loading = false dialog.buttonsEnabled = true this.toastService.showError( - $localize`Bulk share link creation is not available yet.`, + $localize`Share link bundle creation is not available yet.`, error ) }, @@ -961,8 +961,8 @@ export class BulkEditorComponent }) } - manageShareLinks() { - const modal = this.modalService.open(ShareBundleManageDialogComponent, { + manageShareLinkBundles() { + const modal = this.modalService.open(ShareLinkBundleManageDialogComponent, { backdrop: 'static', size: 'lg', }) diff --git a/src-ui/src/app/data/share-bundle.ts b/src-ui/src/app/data/share-bundle.ts deleted file mode 100644 index 0bd06e53b..000000000 --- a/src-ui/src/app/data/share-bundle.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { FileVersion } from './share-link' - -export enum ShareBundleStatus { - Pending = 'pending', - Processing = 'processing', - Ready = 'ready', - Failed = 'failed', -} - -export interface ShareBundleSummary { - id: number - slug: string - created: string // Date - expiration?: string // Date - documents: number[] - document_count: number - file_version: FileVersion - status: ShareBundleStatus - built_at?: string - size_bytes?: number - last_error?: string -} - -export interface ShareBundleCreatePayload { - document_ids: number[] - file_version: FileVersion - expiration_days: number | null -} - -export const SHARE_BUNDLE_STATUS_LABELS: Record = { - [ShareBundleStatus.Pending]: $localize`Pending`, - [ShareBundleStatus.Processing]: $localize`Processing`, - [ShareBundleStatus.Ready]: $localize`Ready`, - [ShareBundleStatus.Failed]: $localize`Failed`, -} - -export const SHARE_BUNDLE_FILE_VERSION_LABELS: Record = { - [FileVersion.Archive]: $localize`Archive`, - [FileVersion.Original]: $localize`Original`, -} diff --git a/src-ui/src/app/data/share-link-bundle.ts b/src-ui/src/app/data/share-link-bundle.ts new file mode 100644 index 000000000..cdabd1d15 --- /dev/null +++ b/src-ui/src/app/data/share-link-bundle.ts @@ -0,0 +1,46 @@ +import { FileVersion } from './share-link' + +export enum ShareLinkBundleStatus { + Pending = 'pending', + Processing = 'processing', + Ready = 'ready', + Failed = 'failed', +} + +export interface ShareLinkBundleSummary { + id: number + slug: string + created: string // Date + expiration?: string // Date + documents: number[] + document_count: number + file_version: FileVersion + status: ShareLinkBundleStatus + built_at?: string + size_bytes?: number + last_error?: string +} + +export interface ShareLinkBundleCreatePayload { + document_ids: number[] + file_version: FileVersion + expiration_days: number | null +} + +export const SHARE_LINK_BUNDLE_STATUS_LABELS: Record< + ShareLinkBundleStatus, + string +> = { + [ShareLinkBundleStatus.Pending]: $localize`Pending`, + [ShareLinkBundleStatus.Processing]: $localize`Processing`, + [ShareLinkBundleStatus.Ready]: $localize`Ready`, + [ShareLinkBundleStatus.Failed]: $localize`Failed`, +} + +export const SHARE_LINK_BUNDLE_FILE_VERSION_LABELS: Record< + FileVersion, + string +> = { + [FileVersion.Archive]: $localize`Archive`, + [FileVersion.Original]: $localize`Original`, +} diff --git a/src-ui/src/app/services/rest/share-bundle.service.ts b/src-ui/src/app/services/rest/share-bundle.service.ts deleted file mode 100644 index 049952199..000000000 --- a/src-ui/src/app/services/rest/share-bundle.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable } from '@angular/core' -import { Observable } from 'rxjs' -import { map } from 'rxjs/operators' -import { - ShareBundleCreatePayload, - ShareBundleSummary, -} from 'src/app/data/share-bundle' -import { AbstractNameFilterService } from './abstract-name-filter-service' - -@Injectable({ - providedIn: 'root', -}) -export class ShareBundleService extends AbstractNameFilterService { - constructor() { - super() - this.resourceName = 'share_bundles' - } - - createBundle( - payload: ShareBundleCreatePayload - ): Observable { - this.clearCache() - return this.http.post(this.getResourceUrl(), payload) - } - rebuildBundle(bundleId: number): Observable { - this.clearCache() - return this.http.post( - this.getResourceUrl(bundleId, 'rebuild'), - {} - ) - } - - listAllBundles(): Observable { - return this.list(1, 1000, 'created', true).pipe( - map((response) => response.results) - ) - } -} diff --git a/src-ui/src/app/services/rest/share-link-bundle.service.ts b/src-ui/src/app/services/rest/share-link-bundle.service.ts new file mode 100644 index 000000000..2aa719974 --- /dev/null +++ b/src-ui/src/app/services/rest/share-link-bundle.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core' +import { Observable } from 'rxjs' +import { map } from 'rxjs/operators' +import { + ShareLinkBundleCreatePayload, + ShareLinkBundleSummary, +} from 'src/app/data/share-link-bundle' +import { AbstractNameFilterService } from './abstract-name-filter-service' + +@Injectable({ + providedIn: 'root', +}) +export class ShareLinkBundleService extends AbstractNameFilterService { + constructor() { + super() + this.resourceName = 'share_link_bundles' + } + + createBundle( + payload: ShareLinkBundleCreatePayload + ): Observable { + this.clearCache() + return this.http.post( + this.getResourceUrl(), + payload + ) + } + rebuildBundle(bundleId: number): Observable { + this.clearCache() + return this.http.post( + this.getResourceUrl(bundleId, 'rebuild'), + {} + ) + } + + listAllBundles(): Observable { + return this.list(1, 1000, 'created', true).pipe( + map((response) => response.results) + ) + } +} diff --git a/src/documents/admin.py b/src/documents/admin.py index ca4fe8723..c59e08972 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -12,8 +12,8 @@ from documents.models import Note from documents.models import PaperlessTask from documents.models import SavedView from documents.models import SavedViewFilterRule -from documents.models import ShareBundle from documents.models import ShareLink +from documents.models import ShareLinkBundle from documents.models import StoragePath from documents.models import Tag from documents.tasks import update_document_parent_tags @@ -186,7 +186,7 @@ class ShareLinksAdmin(GuardedModelAdmin): return super().get_queryset(request).select_related("document__correspondent") -class ShareBundleAdmin(GuardedModelAdmin): +class ShareLinkBundleAdmin(GuardedModelAdmin): list_display = ("created", "status", "expiration", "owner", "slug") list_filter = ("status", "created", "expiration", "owner") search_fields = ("slug",) @@ -233,7 +233,7 @@ admin.site.register(StoragePath, StoragePathAdmin) admin.site.register(PaperlessTask, TaskAdmin) admin.site.register(Note, NotesAdmin) admin.site.register(ShareLink, ShareLinksAdmin) -admin.site.register(ShareBundle, ShareBundleAdmin) +admin.site.register(ShareLinkBundle, ShareLinkBundleAdmin) admin.site.register(CustomField, CustomFieldsAdmin) admin.site.register(CustomFieldInstance, CustomFieldInstancesAdmin) diff --git a/src/documents/filters.py b/src/documents/filters.py index 289d49d0d..7b9d8dd4d 100644 --- a/src/documents/filters.py +++ b/src/documents/filters.py @@ -38,8 +38,8 @@ from documents.models import CustomFieldInstance from documents.models import Document from documents.models import DocumentType from documents.models import PaperlessTask -from documents.models import ShareBundle from documents.models import ShareLink +from documents.models import ShareLinkBundle from documents.models import StoragePath from documents.models import Tag @@ -794,11 +794,11 @@ class ShareLinkFilterSet(FilterSet): } -class ShareBundleFilterSet(FilterSet): +class ShareLinkBundleFilterSet(FilterSet): documents = Filter(method="filter_documents") class Meta: - model = ShareBundle + model = ShareLinkBundle fields = { "created": DATETIME_KWARGS, "expiration": DATETIME_KWARGS, diff --git a/src/documents/migrations/1075_sharebundle.py b/src/documents/migrations/1075_sharelinkbundle.py similarity index 84% rename from src/documents/migrations/1075_sharebundle.py rename to src/documents/migrations/1075_sharelinkbundle.py index a64ad29d3..b48eb5ec3 100644 --- a/src/documents/migrations/1075_sharebundle.py +++ b/src/documents/migrations/1075_sharelinkbundle.py @@ -11,7 +11,7 @@ from django.db import migrations from django.db import models -def grant_sharebundle_permissions(apps, schema_editor): +def grant_share_link_bundle_permissions(apps, schema_editor): # Ensure newly introduced permissions are created for all apps for app_config in apps.get_app_configs(): app_config.models_module = True @@ -22,27 +22,27 @@ def grant_sharebundle_permissions(apps, schema_editor): if add_document_perm is None: return - sharebundle_permissions = Permission.objects.filter( - codename__contains="sharebundle", + share_bundle_permissions = Permission.objects.filter( + codename__contains="sharelinkbundle", ) users = User.objects.filter(user_permissions=add_document_perm).distinct() for user in users: - user.user_permissions.add(*sharebundle_permissions) + user.user_permissions.add(*share_bundle_permissions) groups = Group.objects.filter(permissions=add_document_perm).distinct() for group in groups: - group.permissions.add(*sharebundle_permissions) + group.permissions.add(*share_bundle_permissions) -def revoke_sharebundle_permissions(apps, schema_editor): - sharebundle_permissions = Permission.objects.filter( - codename__contains="sharebundle", +def revoke_share_link_bundle_permissions(apps, schema_editor): + share_bundle_permissions = Permission.objects.filter( + codename__contains="sharelinkbundle", ) for user in User.objects.all(): - user.user_permissions.remove(*sharebundle_permissions) + user.user_permissions.remove(*share_bundle_permissions) for group in Group.objects.all(): - group.permissions.remove(*sharebundle_permissions) + group.permissions.remove(*share_bundle_permissions) class Migration(migrations.Migration): @@ -53,7 +53,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name="ShareBundle", + name="ShareLinkBundle", fields=[ ( "id", @@ -150,7 +150,7 @@ class Migration(migrations.Migration): blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, - related_name="share_bundles", + related_name="share_link_bundles", to=settings.AUTH_USER_MODEL, verbose_name="owner", ), @@ -170,21 +170,21 @@ class Migration(migrations.Migration): ], options={ "ordering": ("-created",), - "verbose_name": "share bundle", - "verbose_name_plural": "share bundles", + "verbose_name": "share link bundle", + "verbose_name_plural": "share link bundles", }, ), migrations.AddField( - model_name="sharebundle", + model_name="sharelinkbundle", name="documents", field=models.ManyToManyField( - related_name="share_bundles", + related_name="share_link_bundles", to="documents.document", verbose_name="documents", ), ), migrations.RunPython( - grant_sharebundle_permissions, - reverse_code=revoke_sharebundle_permissions, + grant_share_link_bundle_permissions, + reverse_code=revoke_share_link_bundle_permissions, ), ] diff --git a/src/documents/models.py b/src/documents/models.py index 0bd4aedd8..7871983b6 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -777,7 +777,7 @@ class ShareLink(SoftDeleteModel): return f"Share Link for {self.document.title}" -class ShareBundle(SoftDeleteModel): +class ShareLinkBundle(SoftDeleteModel): class Status(models.TextChoices): PENDING = ("pending", _("Pending")) PROCESSING = ("processing", _("Processing")) @@ -786,8 +786,8 @@ class ShareBundle(SoftDeleteModel): class Meta: ordering = ("-created",) - verbose_name = _("share bundle") - verbose_name_plural = _("share bundles") + verbose_name = _("share link bundle") + verbose_name_plural = _("share link bundles") created = models.DateTimeField( _("created"), @@ -816,7 +816,7 @@ class ShareBundle(SoftDeleteModel): User, blank=True, null=True, - related_name="share_bundles", + related_name="share_link_bundles", on_delete=models.SET_NULL, verbose_name=_("owner"), ) @@ -858,12 +858,12 @@ class ShareBundle(SoftDeleteModel): documents = models.ManyToManyField( "documents.Document", - related_name="share_bundles", + related_name="share_link_bundles", verbose_name=_("documents"), ) def __str__(self): - return _("Share bundle %(slug)s") % {"slug": self.slug} + return _("Share link bundle %(slug)s") % {"slug": self.slug} @property def absolute_file_path(self) -> Path | None: diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 5fd3dd0c5..85691a1bc 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -58,8 +58,8 @@ from documents.models import Note from documents.models import PaperlessTask from documents.models import SavedView from documents.models import SavedViewFilterRule -from documents.models import ShareBundle from documents.models import ShareLink +from documents.models import ShareLinkBundle from documents.models import StoragePath from documents.models import Tag from documents.models import UiSettings @@ -2130,7 +2130,7 @@ class ShareLinkSerializer(OwnedObjectSerializer): return super().create(validated_data) -class ShareBundleSerializer(OwnedObjectSerializer): +class ShareLinkBundleSerializer(OwnedObjectSerializer): document_ids = serializers.ListField( child=serializers.IntegerField(min_value=1), allow_empty=False, @@ -2149,7 +2149,7 @@ class ShareBundleSerializer(OwnedObjectSerializer): document_count = SerializerMethodField() class Meta: - model = ShareBundle + model = ShareLinkBundle fields = ( "id", "created", @@ -2198,7 +2198,7 @@ class ShareBundleSerializer(OwnedObjectSerializer): else: validated_data["expiration"] = None - share_bundle = super().create(validated_data) + share_link_bundle = super().create(validated_data) if documents is None: documents = list( @@ -2224,12 +2224,12 @@ class ShareBundleSerializer(OwnedObjectSerializer): ) ordered_documents = [documents_by_id[doc_id] for doc_id in document_ids] - share_bundle.documents.set(ordered_documents) - share_bundle.document_total = len(ordered_documents) + share_link_bundle.documents.set(ordered_documents) + share_link_bundle.document_total = len(ordered_documents) - return share_bundle + return share_link_bundle - def get_document_count(self, obj: ShareBundle) -> int: + def get_document_count(self, obj: ShareLinkBundle) -> int: count = getattr(obj, "document_total", None) if count is not None: return count diff --git a/src/documents/tasks.py b/src/documents/tasks.py index 055b3cc59..9dad945c0 100644 --- a/src/documents/tasks.py +++ b/src/documents/tasks.py @@ -43,8 +43,8 @@ from documents.models import CustomFieldInstance from documents.models import Document from documents.models import DocumentType from documents.models import PaperlessTask -from documents.models import ShareBundle from documents.models import ShareLink +from documents.models import ShareLinkBundle from documents.models import StoragePath from documents.models import Tag from documents.models import Workflow @@ -572,17 +572,19 @@ def update_document_parent_tags(tag: Tag, new_parent: Tag) -> None: @shared_task -def build_share_bundle(bundle_id: int): +def build_share_link_bundle(bundle_id: int): try: bundle = ( - ShareBundle.objects.filter(pk=bundle_id).prefetch_related("documents").get() + ShareLinkBundle.objects.filter(pk=bundle_id) + .prefetch_related("documents") + .get() ) - except ShareBundle.DoesNotExist: - logger.warning("Share bundle %s no longer exists.", bundle_id) + except ShareLinkBundle.DoesNotExist: + logger.warning("Share link bundle %s no longer exists.", bundle_id) return bundle.remove_file() - bundle.status = ShareBundle.Status.PROCESSING + bundle.status = ShareLinkBundle.Status.PROCESSING bundle.last_error = "" bundle.size_bytes = None bundle.built_at = None @@ -617,7 +619,7 @@ def build_share_bundle(bundle_id: int): for document in documents: strategy.add_document(document) - output_dir = settings.SHARE_BUNDLE_DIR + output_dir = settings.SHARE_LINK_BUNDLE_DIR output_dir.mkdir(parents=True, exist_ok=True) final_path = (output_dir / f"{bundle.slug}.zip").resolve() if final_path.exists(): @@ -629,7 +631,7 @@ def build_share_bundle(bundle_id: int): except ValueError: bundle.file_path = str(final_path) bundle.size_bytes = final_path.stat().st_size - bundle.status = ShareBundle.Status.READY + bundle.status = ShareLinkBundle.Status.READY bundle.built_at = timezone.now() bundle.last_error = "" bundle.save( @@ -641,10 +643,14 @@ def build_share_bundle(bundle_id: int): "last_error", ], ) - logger.info("Built share bundle %s", bundle.pk) + logger.info("Built share link bundle %s", bundle.pk) except Exception as exc: - logger.exception("Failed to build share bundle %s: %s", bundle_id, exc) - bundle.status = ShareBundle.Status.FAILED + logger.exception( + "Failed to build share link bundle %s: %s", + bundle_id, + exc, + ) + bundle.status = ShareLinkBundle.Status.FAILED bundle.last_error = str(exc) bundle.save(update_fields=["status", "last_error"]) try: @@ -661,9 +667,9 @@ def build_share_bundle(bundle_id: int): @shared_task -def cleanup_expired_share_bundles(): +def cleanup_expired_share_link_bundles(): now = timezone.now() - expired_qs = ShareBundle.objects.filter( + expired_qs = ShareLinkBundle.objects.filter( deleted_at__isnull=True, expiration__isnull=False, expiration__lt=now, @@ -675,9 +681,9 @@ def cleanup_expired_share_bundles(): bundle.hard_delete() except Exception as exc: logger.warning( - "Failed to delete expired share bundle %s: %s", + "Failed to delete expired share link bundle %s: %s", bundle.pk, exc, ) if count: - logger.info("Deleted %s expired share bundle(s)", count) + logger.info("Deleted %s expired share link bundle(s)", count) diff --git a/src/documents/views.py b/src/documents/views.py index 87ae16e26..8817ee15e 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -119,7 +119,7 @@ from documents.filters import DocumentTypeFilterSet from documents.filters import ObjectOwnedOrGrantedPermissionsFilter from documents.filters import ObjectOwnedPermissionsFilter from documents.filters import PaperlessTaskFilterSet -from documents.filters import ShareBundleFilterSet +from documents.filters import ShareLinkBundleFilterSet from documents.filters import ShareLinkFilterSet from documents.filters import StoragePathFilterSet from documents.filters import TagFilterSet @@ -135,8 +135,8 @@ from documents.models import DocumentType from documents.models import Note from documents.models import PaperlessTask from documents.models import SavedView -from documents.models import ShareBundle from documents.models import ShareLink +from documents.models import ShareLinkBundle from documents.models import StoragePath from documents.models import Tag from documents.models import UiSettings @@ -170,7 +170,7 @@ from documents.serialisers import PostDocumentSerializer from documents.serialisers import RunTaskViewSerializer from documents.serialisers import SavedViewSerializer from documents.serialisers import SearchResultSerializer -from documents.serialisers import ShareBundleSerializer +from documents.serialisers import ShareLinkBundleSerializer from documents.serialisers import ShareLinkSerializer from documents.serialisers import StoragePathSerializer from documents.serialisers import StoragePathTestSerializer @@ -183,7 +183,7 @@ from documents.serialisers import WorkflowActionSerializer from documents.serialisers import WorkflowSerializer from documents.serialisers import WorkflowTriggerSerializer from documents.signals import document_updated -from documents.tasks import build_share_bundle +from documents.tasks import build_share_link_bundle from documents.tasks import consume_file from documents.tasks import empty_trash from documents.tasks import index_optimize @@ -2604,12 +2604,12 @@ class ShareLinkViewSet(ModelViewSet, PassUserMixin): ordering_fields = ("created", "expiration", "document") -class ShareBundleViewSet(ModelViewSet, PassUserMixin): - model = ShareBundle +class ShareLinkBundleViewSet(ModelViewSet, PassUserMixin): + model = ShareLinkBundle - queryset = ShareBundle.objects.all() + queryset = ShareLinkBundle.objects.all() - serializer_class = ShareBundleSerializer + serializer_class = ShareLinkBundleSerializer pagination_class = StandardPagination permission_classes = (IsAuthenticated, PaperlessObjectPermissions) filter_backends = ( @@ -2617,7 +2617,7 @@ class ShareBundleViewSet(ModelViewSet, PassUserMixin): OrderingFilter, ObjectOwnedOrGrantedPermissionsFilter, ) - filterset_class = ShareBundleFilterSet + filterset_class = ShareLinkBundleFilterSet ordering_fields = ("created", "expiration", "status") def get_queryset(self): @@ -2667,7 +2667,7 @@ class ShareBundleViewSet(ModelViewSet, PassUserMixin): documents=ordered_documents, ) bundle.remove_file() - bundle.status = ShareBundle.Status.PENDING + bundle.status = ShareLinkBundle.Status.PENDING bundle.last_error = "" bundle.size_bytes = None bundle.built_at = None @@ -2681,7 +2681,7 @@ class ShareBundleViewSet(ModelViewSet, PassUserMixin): "file_path", ], ) - build_share_bundle.delay(bundle.pk) + build_share_link_bundle.delay(bundle.pk) bundle.document_total = len(ordered_documents) response_serializer = self.get_serializer(bundle) headers = self.get_success_headers(response_serializer.data) @@ -2694,13 +2694,13 @@ class ShareBundleViewSet(ModelViewSet, PassUserMixin): @action(detail=True, methods=["post"]) def rebuild(self, request, pk=None): bundle = self.get_object() - if bundle.status == ShareBundle.Status.PROCESSING: + if bundle.status == ShareLinkBundle.Status.PROCESSING: return Response( {"detail": _("Bundle is already being processed.")}, status=status.HTTP_400_BAD_REQUEST, ) bundle.remove_file() - bundle.status = ShareBundle.Status.PENDING + bundle.status = ShareLinkBundle.Status.PENDING bundle.last_error = "" bundle.size_bytes = None bundle.built_at = None @@ -2714,7 +2714,7 @@ class ShareBundleViewSet(ModelViewSet, PassUserMixin): "file_path", ], ) - build_share_bundle.delay(bundle.pk) + build_share_link_bundle.delay(bundle.pk) bundle.document_total = ( getattr(bundle, "document_total", None) or bundle.documents.count() ) @@ -2740,35 +2740,32 @@ class SharedLinkView(View): disposition="inline", ) - share_bundle = ShareBundle.objects.filter(slug=slug).first() - if share_bundle is None: + bundle = ShareLinkBundle.objects.filter(slug=slug).first() + if bundle is None: return HttpResponseRedirect("/accounts/login/?sharelink_notfound=1") - if ( - share_bundle.expiration is not None - and share_bundle.expiration < timezone.now() - ): + if bundle.expiration is not None and bundle.expiration < timezone.now(): return HttpResponseRedirect("/accounts/login/?sharelink_expired=1") - if share_bundle.status in { - ShareBundle.Status.PENDING, - ShareBundle.Status.PROCESSING, + if bundle.status in { + ShareLinkBundle.Status.PENDING, + ShareLinkBundle.Status.PROCESSING, }: return HttpResponse( _( - "The shared bundle is still being prepared. Please try again later.", + "The share link bundle is still being prepared. Please try again later.", ), status=status.HTTP_202_ACCEPTED, ) - if share_bundle.status == ShareBundle.Status.FAILED: - share_bundle.remove_file() - share_bundle.status = ShareBundle.Status.PENDING - share_bundle.last_error = "" - share_bundle.size_bytes = None - share_bundle.built_at = None - share_bundle.file_path = "" - share_bundle.save( + if bundle.status == ShareLinkBundle.Status.FAILED: + bundle.remove_file() + bundle.status = ShareLinkBundle.Status.PENDING + bundle.last_error = "" + bundle.size_bytes = None + bundle.built_at = None + bundle.file_path = "" + bundle.save( update_fields=[ "status", "last_error", @@ -2777,22 +2774,22 @@ class SharedLinkView(View): "file_path", ], ) - build_share_bundle.delay(share_bundle.pk) + build_share_link_bundle.delay(bundle.pk) return HttpResponse( _( - "The shared bundle is temporarily unavailable. A rebuild has been scheduled. Please try again later.", + "The share link bundle is temporarily unavailable. A rebuild has been scheduled. Please try again later.", ), status=status.HTTP_503_SERVICE_UNAVAILABLE, ) - file_path = share_bundle.absolute_file_path + file_path = bundle.absolute_file_path if file_path is None or not file_path.exists(): - share_bundle.status = ShareBundle.Status.PENDING - share_bundle.last_error = "" - share_bundle.size_bytes = None - share_bundle.built_at = None - share_bundle.file_path = "" - share_bundle.save( + bundle.status = ShareLinkBundle.Status.PENDING + bundle.last_error = "" + bundle.size_bytes = None + bundle.built_at = None + bundle.file_path = "" + bundle.save( update_fields=[ "status", "last_error", @@ -2801,16 +2798,16 @@ class SharedLinkView(View): "file_path", ], ) - build_share_bundle.delay(share_bundle.pk) + build_share_link_bundle.delay(bundle.pk) return HttpResponse( _( - "The shared bundle is being prepared. Please try again later.", + "The share link bundle is being prepared. Please try again later.", ), status=status.HTTP_202_ACCEPTED, ) response = FileResponse(file_path.open("rb"), content_type="application/zip") - short_slug = share_bundle.slug[:12] + short_slug = bundle.slug[:12] download_name = f"paperless-share-{short_slug}.zip" filename_normalized = ( normalize("NFKD", download_name) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index cfcc6692a..425d7462a 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -231,11 +231,11 @@ def _parse_beat_schedule() -> dict: }, }, { - "name": "Cleanup expired share bundles", - "env_key": "PAPERLESS_SHARE_BUNDLE_CLEANUP_CRON", + "name": "Cleanup expired share link bundles", + "env_key": "PAPERLESS_SHARE_LINK_BUNDLE_CLEANUP_CRON", # Default daily at 02:00 "env_default": "0 2 * * *", - "task": "documents.tasks.cleanup_expired_share_bundles", + "task": "documents.tasks.cleanup_expired_share_link_bundles", "options": { # 1 hour before default schedule sends again "expires": 23.0 * 60.0 * 60.0, @@ -279,7 +279,7 @@ MEDIA_ROOT = __get_path("PAPERLESS_MEDIA_ROOT", BASE_DIR.parent / "media") ORIGINALS_DIR = MEDIA_ROOT / "documents" / "originals" ARCHIVE_DIR = MEDIA_ROOT / "documents" / "archive" THUMBNAIL_DIR = MEDIA_ROOT / "documents" / "thumbnails" -SHARE_BUNDLE_DIR = MEDIA_ROOT / "documents" / "share_bundles" +SHARE_LINK_BUNDLE_DIR = MEDIA_ROOT / "documents" / "share_link_bundles" DATA_DIR = __get_path("PAPERLESS_DATA_DIR", BASE_DIR.parent / "data") diff --git a/src/paperless/urls.py b/src/paperless/urls.py index 505eb3fa0..2307d939b 100644 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -29,8 +29,8 @@ from documents.views import RemoteVersionView from documents.views import SavedViewViewSet from documents.views import SearchAutoCompleteView from documents.views import SelectionDataView -from documents.views import ShareBundleViewSet from documents.views import SharedLinkView +from documents.views import ShareLinkBundleViewSet from documents.views import ShareLinkViewSet from documents.views import StatisticsView from documents.views import StoragePathViewSet @@ -73,7 +73,7 @@ api_router.register(r"users", UserViewSet, basename="users") api_router.register(r"groups", GroupViewSet, basename="groups") api_router.register(r"mail_accounts", MailAccountViewSet) api_router.register(r"mail_rules", MailRuleViewSet) -api_router.register(r"share_bundles", ShareBundleViewSet) +api_router.register(r"share_link_bundles", ShareLinkBundleViewSet) api_router.register(r"share_links", ShareLinkViewSet) api_router.register(r"workflow_triggers", WorkflowTriggerViewSet) api_router.register(r"workflow_actions", WorkflowActionViewSet)