diff --git a/api/src/org/labkey/api/attachments/AttachmentService.java b/api/src/org/labkey/api/attachments/AttachmentService.java index 39e0eca2425..1eb3710969a 100644 --- a/api/src/org/labkey/api/attachments/AttachmentService.java +++ b/api/src/org/labkey/api/attachments/AttachmentService.java @@ -140,7 +140,10 @@ static AttachmentService get() HttpView getFindAttachmentParentsView(); - void logOrphanedAttachments(); + /** + * Logs the first 20 orphaned attachments it detects. Returns the total number of orphaned attachments detected. + */ + int logOrphanedAttachments(); void deleteOrphanedAttachments(); diff --git a/api/src/org/labkey/api/data/ContainerManager.java b/api/src/org/labkey/api/data/ContainerManager.java index fd459df101d..121d82885f3 100644 --- a/api/src/org/labkey/api/data/ContainerManager.java +++ b/api/src/org/labkey/api/data/ContainerManager.java @@ -1944,7 +1944,9 @@ private static boolean delete(final Container c, User user, @Nullable String com setContainerTabDeleted(c.getParent(), c.getName(), c.getParent().getFolderType().getName()); } - AttachmentService.get().logOrphanedAttachments(); + // Log orphaned attachments in this server, but only in dev mode, since this is for our testing + if (AppProps.getInstance().isDevMode()) + AttachmentService.get().logOrphanedAttachments(); fireDeleteContainer(c, user); diff --git a/core/src/org/labkey/core/admin/AdminController.java b/core/src/org/labkey/core/admin/AdminController.java index f5378956d20..42521e5f2f9 100644 --- a/core/src/org/labkey/core/admin/AdminController.java +++ b/core/src/org/labkey/core/admin/AdminController.java @@ -3710,6 +3710,20 @@ public void addNavTrail(NavTree root) } } + @AdminConsoleAction + public static class LogOrphanedAttachmentsAction extends ReadOnlyApiAction + { + @Override + public Object execute(Object o, BindException errors) throws Exception + { + int count = 0; + AttachmentService svc = AttachmentService.get(); + if (svc != null) + count = svc.logOrphanedAttachments(); + return Map.of("count", count); + } + } + public static ActionURL getMemTrackerURL(boolean clearCaches, boolean gc) { ActionURL url = new ActionURL(MemTrackerAction.class, ContainerManager.getRoot()); diff --git a/core/src/org/labkey/core/attachment/AttachmentServiceImpl.java b/core/src/org/labkey/core/attachment/AttachmentServiceImpl.java index 5ccd4c0477f..d7f59578e9c 100644 --- a/core/src/org/labkey/core/attachment/AttachmentServiceImpl.java +++ b/core/src/org/labkey/core/attachment/AttachmentServiceImpl.java @@ -1101,10 +1101,10 @@ public int available() private record Orphan(String documentName, String parentType){} @Override - public void logOrphanedAttachments() + public int logOrphanedAttachments() { - // Log orphaned attachments in this server, but in dev mode only, since this is for our testing. Also, we - // don't yet offer a way to delete orphaned attachments via the UI, so it's not helpful to inform admins. + int ret = 0; + if (AppProps.getInstance().isDevMode()) { User user = ElevatedUser.getElevatedUser(User.getSearchUser(), TroubleshooterRole.class); @@ -1118,7 +1118,8 @@ public void logOrphanedAttachments() List orphans = new TableSelector(documents, new CsvSet("DocumentName, ParentType"), filter, null).getArrayList(Orphan.class); if (!orphans.isEmpty()) { - LOG.error("Found {}, which likely indicates a problem with a delete method or a container listener.", StringUtilsLabKey.pluralize(orphans.size(), "orphaned attachment")); + ret = orphans.size(); + LOG.error("Found {}, which likely indicates a problem with a delete method or a container listener.", StringUtilsLabKey.pluralize(ret, "orphaned attachment")); final String message; if (orphans.size() > MAX_ORPHANS_TO_LOG) @@ -1136,6 +1137,8 @@ public void logOrphanedAttachments() } } } + + return ret; } record OrphanedAttachment(String container, String parent, String parentType, String documentName)