From 26faf4b5e95edf33f6df61c84093c6534fe9ffb6 Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Sat, 21 Mar 2026 16:17:42 -0700 Subject: [PATCH] Delete all attachments when deleting multi-folder lists --- .../org/labkey/core/query/DocumentsTable.java | 11 +++- .../labkey/list/model/ListDefinitionImpl.java | 4 +- .../list/model/ListQueryUpdateService.java | 58 +++++++++---------- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/core/src/org/labkey/core/query/DocumentsTable.java b/core/src/org/labkey/core/query/DocumentsTable.java index 9e453d2f2fe..cf7ceb4d576 100644 --- a/core/src/org/labkey/core/query/DocumentsTable.java +++ b/core/src/org/labkey/core/query/DocumentsTable.java @@ -2,6 +2,7 @@ import org.jetbrains.annotations.NotNull; import org.labkey.api.attachments.AttachmentService; +import org.labkey.api.collections.CsvSet; import org.labkey.api.collections.LabKeyCollectors; import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ContainerFilter; @@ -11,6 +12,7 @@ import org.labkey.api.data.SQLFragment; import org.labkey.api.data.dialect.SqlDialect; import org.labkey.api.query.AliasManager; +import org.labkey.api.query.FieldKey; import org.labkey.api.query.FilteredTable; import org.labkey.api.query.UserIdQueryForeignKey; @@ -26,7 +28,6 @@ public DocumentsTable(@NotNull CoreQuerySchema userSchema, ContainerFilter cf) getMutableColumnOrThrow("ModifiedBy").setHidden(true); getMutableColumnOrThrow("Modified").setHidden(true); MutableColumnInfo owner = getMutableColumnOrThrow("Owner"); - owner.setHidden(true); owner.setFk(new UserIdQueryForeignKey(_userSchema)); getMutableColumnOrThrow("Parent").setHidden(true); getMutableColumnOrThrow("DocumentSize").setFormat("#,##0"); @@ -34,8 +35,14 @@ public DocumentsTable(@NotNull CoreQuerySchema userSchema, ContainerFilter cf) getMutableColumnOrThrow("LastIndexed").setHidden(true); addColumn(new BaseColumnInfo("ParentDescription", this, JdbcType.VARCHAR)); BaseColumnInfo orphaned = new BaseColumnInfo("Orphaned", this, JdbcType.BOOLEAN); - orphaned.setHidden(true); addColumn(orphaned); + + setDefaultVisibleColumns( + new CsvSet("CreatedBy, Created, Container, DocumentName, DocumentSize, DocumentType, ParentType, ParentDescription") + .stream() + .map(FieldKey::fromParts) + .toList() + ); } @Override diff --git a/list/src/org/labkey/list/model/ListDefinitionImpl.java b/list/src/org/labkey/list/model/ListDefinitionImpl.java index d70f9652601..dc4fbb7e074 100644 --- a/list/src/org/labkey/list/model/ListDefinitionImpl.java +++ b/list/src/org/labkey/list/model/ListDefinitionImpl.java @@ -576,10 +576,10 @@ public void delete(User user, @Nullable String auditUserComment) throws DomainNo try (DbScope.Transaction transaction = (table != null) ? table.getSchema().getScope().ensureTransaction() : ExperimentService.get().ensureTransaction()) { - // remove related attachments, discussions, and indices + // remove related full-text search docs and attachments ListManager.get().deleteIndexedList(this); if (qus instanceof ListQueryUpdateService listQus) - listQus.deleteRelatedListData(user, getContainer()); + listQus.deleteRelatedListData(null); // then delete the list itself ListManager.get().deleteListDef(getContainer(), getListId()); diff --git a/list/src/org/labkey/list/model/ListQueryUpdateService.java b/list/src/org/labkey/list/model/ListQueryUpdateService.java index 936cf53af26..b4747339634 100644 --- a/list/src/org/labkey/list/model/ListQueryUpdateService.java +++ b/list/src/org/labkey/list/model/ListQueryUpdateService.java @@ -26,6 +26,7 @@ import org.labkey.api.audit.AuditLogService; import org.labkey.api.audit.TransactionAuditProvider; import org.labkey.api.collections.CaseInsensitiveHashMap; +import org.labkey.api.collections.CsvSet; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.CompareType; import org.labkey.api.data.Container; @@ -90,6 +91,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static org.labkey.api.util.IntegerUtils.isIntegral; @@ -759,45 +761,39 @@ protected Map deleteRow(User user, Container container, Map() + if (hasAttachmentProperties()) { - @Override - public boolean accept(String entityId) - { - return null != entityId; - } + SimpleFilter filter = null != container ? SimpleFilter.createContainerFilter(container) : null; - @Override - public void exec(List entityIds) + // Delete attachments associated with a list in batches of 1,000 + new TableSelector(getDbTable(), new CsvSet("Container, EntityId"), filter, null).forEachBatch(ListRow.class, 1000, new ForEachBatchBlock<>() { - // delete the related list data for this block - deleteRelatedListData(user, container, entityIds); - } - }); - } - - // delete the related list data for this block of entityIds - private void deleteRelatedListData(User user, Container container, List entityIds) - { - // Build up set of entityIds and AttachmentParents - List attachmentParents = new ArrayList<>(); + @Override + public boolean accept(ListRow row) + { + return null != row.entityId(); + } - // Delete Attachments - if (hasAttachmentProperties()) - { - for (String entityId : entityIds) - { - attachmentParents.add(new ListItemAttachmentParent(entityId, container)); - } - AttachmentService.get().deleteAttachments(attachmentParents); + @Override + public void exec(List rows) + { + // delete the related attachments for this block + AttachmentService.get().deleteAttachments( + rows.stream() + .map(row -> new ListItemAttachmentParent(row.entityId(), row.container())) + .collect(Collectors.toList()) + ); + } + }); } } @@ -807,7 +803,7 @@ protected int truncateRows(User user, Container container) throws QueryUpdateSer int result; try (DbScope.Transaction transaction = getDbTable().getSchema().getScope().ensureTransaction()) { - deleteRelatedListData(user, container); + deleteRelatedListData(container); result = super.truncateRows(getListUser(user, container), container); transaction.addCommitTask(() -> ListManager.get().addAuditEvent(_list, user, "Deleted " + result + " rows from list."), DbScope.CommitTaskOption.POSTCOMMIT); transaction.commit();