Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion api/src/org/labkey/api/attachments/AttachmentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ static AttachmentService get()

HttpView<?> getFindAttachmentParentsView();

void detectOrphans();
void logOrphanedAttachments();

void deleteOrphanedAttachments();

class DuplicateFilenameException extends IOException implements SkipMothershipLogging
{
Expand Down
2 changes: 1 addition & 1 deletion api/src/org/labkey/api/data/ContainerManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -1944,7 +1944,7 @@ private static boolean delete(final Container c, User user, @Nullable String com
setContainerTabDeleted(c.getParent(), c.getName(), c.getParent().getFolderType().getName());
}

AttachmentService.get().detectOrphans();
AttachmentService.get().logOrphanedAttachments();

fireDeleteContainer(c, user);

Expand Down
2 changes: 1 addition & 1 deletion core/module.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Name: Core
ModuleClass: org.labkey.core.CoreModule
SchemaVersion: 26.002
SchemaVersion: 26.003
Label: Administration and Essential Services
Description: The Core module provides central services such as login, \
security, administration, folder management, user management, \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT core.executeJavaUpgradeCode('deleteOrphanedAttachments');
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
EXEC core.executeJavaUpgradeCode 'deleteOrphanedAttachments';
19 changes: 19 additions & 0 deletions core/src/org/labkey/core/CoreUpgradeCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public static void migrateAllowedExternalConnectionHosts(ModuleContext context)
if (context.isNewInstall())
return;

// TODO: Remove getExternalSourceHosts() method when this upgrade code is deleted
List<String> hosts = AppProps.getInstance().getExternalSourceHosts();
List<AllowedHost> allowedHosts = hosts.stream()
.map(host -> new AllowedHost(Directive.Connection, host))
Expand Down Expand Up @@ -106,4 +107,22 @@ public static void populateAttachmentParentTypeColumn(ModuleContext context)
new SqlExecutor(CoreSchema.getInstance().getSchema()).execute(updateSql);
}
}

/**
* Called from core-26.002-26.003.sql
*/
@DeferredUpgrade // Need to execute this after AttachmentTypes are registered
@SuppressWarnings("unused")
public static void deleteOrphanedAttachments(ModuleContext context)
{
if (context.isNewInstall())
return;

AttachmentService svc = AttachmentService.get();

if (svc != null)
{
svc.deleteOrphanedAttachments();
}
}
}
51 changes: 50 additions & 1 deletion core/src/org/labkey/core/attachment/AttachmentServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.labkey.api.data.ColumnRenderProperties;
import org.labkey.api.data.CompareType;
import org.labkey.api.data.Container;
import org.labkey.api.data.ContainerFilter;
import org.labkey.api.data.ContainerFilter.AllFolders;
import org.labkey.api.data.ContainerManager;
import org.labkey.api.data.CoreSchema;
Expand Down Expand Up @@ -1098,7 +1099,7 @@ public int available()
private record Orphan(String documentName, String parentType){}

@Override
public void detectOrphans()
public void 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.
Expand Down Expand Up @@ -1135,6 +1136,54 @@ public void detectOrphans()
}
}

record OrphanedAttachment(String container, String parent, String documentName)
{
AttachmentParent getAttachmentParent()
{
return new AttachmentParent()
{
@Override
public String getEntityId()
{
return parent;
}

@Override
public String getContainerId()
{
return container;
}

@Override
public @NotNull AttachmentParentType getAttachmentParentType()
{
return AttachmentParentType.UNKNOWN;
}
};
}
}

@Override
public void deleteOrphanedAttachments()
{
User user = ElevatedUser.getElevatedUser(User.getSearchUser(), TroubleshooterRole.class);
UserSchema core = DefaultSchema.get(user, ContainerManager.getRoot()).getUserSchema(CoreQuerySchema.NAME);
if (core != null)
{
// Use "unsafe everything" container filter because it's possible that orphaned attachments have a container
// that no longer exists.
TableInfo documents = core.getTable(CoreQuerySchema.DOCUMENTS_TABLE_NAME, ContainerFilter.getUnsafeEverythingFilter());
if (null != documents)
{
SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("Orphaned"), true);
new TableSelector(documents, new CsvSet("Container, Parent, DocumentName"), filter, null).forEach(OrphanedAttachment.class, orphan -> {
LOG.info("Deleting orphaned attachment: {}", orphan);
deleteAttachment(orphan.getAttachmentParent(), orphan.documentName(), user);
});
}
}
}

private CoreSchema coreTables()
{
return CoreSchema.getInstance();
Expand Down
Loading