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..1fdecba9279 100644 --- a/core/src/org/labkey/core/attachment/AttachmentServiceImpl.java +++ b/core/src/org/labkey/core/attachment/AttachmentServiceImpl.java @@ -1101,41 +1101,41 @@ 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. - if (AppProps.getInstance().isDevMode()) + int ret = 0; + User user = ElevatedUser.getElevatedUser(User.getSearchUser(), TroubleshooterRole.class); + UserSchema core = DefaultSchema.get(user, ContainerManager.getRoot()).getUserSchema(CoreQuerySchema.NAME); + + if (core != null) { - User user = ElevatedUser.getElevatedUser(User.getSearchUser(), TroubleshooterRole.class); - UserSchema core = DefaultSchema.get(user, ContainerManager.getRoot()).getUserSchema(CoreQuerySchema.NAME); - if (core != null) + TableInfo documents = core.getTable(CoreQuerySchema.DOCUMENTS_TABLE_NAME, new AllFolders(user)); + if (null != documents) { - TableInfo documents = core.getTable(CoreQuerySchema.DOCUMENTS_TABLE_NAME, new AllFolders(user)); - if (null != documents) + SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("Orphaned"), true); + List orphans = new TableSelector(documents, new CsvSet("DocumentName, ParentType"), filter, null).getArrayList(Orphan.class); + if (!orphans.isEmpty()) { - SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("Orphaned"), true); - List orphans = new TableSelector(documents, new CsvSet("DocumentName, ParentType"), filter, null).getArrayList(Orphan.class); - if (!orphans.isEmpty()) + 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) + { + orphans = orphans.subList(0, MAX_ORPHANS_TO_LOG); + message = "The first " + MAX_ORPHANS_TO_LOG; + } + else { - LOG.error("Found {}, which likely indicates a problem with a delete method or a container listener.", StringUtilsLabKey.pluralize(orphans.size(), "orphaned attachment")); - - final String message; - if (orphans.size() > MAX_ORPHANS_TO_LOG) - { - orphans = orphans.subList(0, MAX_ORPHANS_TO_LOG); - message = "The first " + MAX_ORPHANS_TO_LOG; - } - else - { - message = "All"; - } - - LOG.error("{} detected orphans are listed below:\n{}", message, orphans.stream().map(Record::toString).collect(Collectors.joining("\n"))); + message = "All"; } + + LOG.error("{} detected orphans are listed below:\n{}", message, orphans.stream().map(Record::toString).collect(Collectors.joining("\n"))); } } } + + return ret; } record OrphanedAttachment(String container, String parent, String parentType, String documentName)