Making it harder to delete all rows from lists#7471
Making it harder to delete all rows from lists#7471XingY wants to merge 3 commits intorelease26.3-SNAPSHOTfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to reduce the risk of accidental “delete all rows” operations on Lists by removing the one-click delete-all UI and replacing it with an Admin-only, explicit confirmation flow for truncating list data within the current container.
Changes:
- Removed the “Delete All Rows” button from list grids and removed the helper that generated it.
- Added an Admin-only “Delete All Data from List” action with a new confirmation JSP that summarizes impacted lists and row counts.
- Tightened container scoping in list related-data cleanup and updated attachment download resolution to use the correct data container.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
list/src/org/labkey/list/view/truncateListData.jsp |
New confirmation view for truncating list data and showing per-list row counts. |
list/src/org/labkey/list/view/ListQueryView.java |
Removes the grid-level “Delete All Rows” button. |
list/src/org/labkey/list/model/ListQueryUpdateService.java |
Scopes related-data cleanup to the target container when enumerating entityIds. |
list/src/org/labkey/list/model/ListManagerSchema.java |
Reworks delete UI into an Admin-only menu that includes “Delete All Data from List”. |
list/src/org/labkey/list/model/ListDefinitionImpl.java |
Adds helper to resolve the correct container for list-item attachment downloads. |
list/src/org/labkey/list/controllers/ListController.java |
Adds TruncateListDataAction and updates attachment download to use resolved data container. |
api/src/org/labkey/api/query/QueryView.java |
Removes createDeleteAllRowsButton() implementation (API surface change). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| long count = (table != null) ? new TableSelector(table).getRowCount() : 0; | ||
| %> | ||
| <li> | ||
| <%= simpleLink(listDef.getName(), listDef.urlFor(ListController.GridAction.class, listDef.getContainer())) %> |
There was a problem hiding this comment.
The list link points to the list definition’s container (listDef.getContainer()), but the operation is truncating rows in the current container. This link is likely to show a different folder’s data than what will be deleted. Consider linking to the grid in the current container so the user can inspect the rows that will actually be removed.
| <%= simpleLink(listDef.getName(), listDef.urlFor(ListController.GridAction.class, listDef.getContainer())) %> | |
| <%= simpleLink(listDef.getName(), listDef.urlFor(ListController.GridAction.class, currentContainer)) %> |
| TableInfo table = listQuerySchema.getTable(listDef.getName()); | ||
| long count = (table != null) ? new TableSelector(table).getRowCount() : 0; | ||
| %> |
There was a problem hiding this comment.
Row counts are computed via listQuerySchema.getTable(listDef.getName()), which resolves the list by name in the current container. If multiple selected lists share the same name but are defined in different containers, the displayed count can be for the wrong list. Consider deriving the TableInfo from the selected ListDefinition (e.g., use the list’s definition container to resolve the correct list, and apply an explicit container filter for the current container when counting rows).
| return null; | ||
| } | ||
|
|
There was a problem hiding this comment.
This removes the public QueryView.createDeleteAllRowsButton() method from the API module. Even though there are no in-repo references, this is a binary/source breaking change for external modules that compile against the LabKey API. Consider keeping the method (deprecated) and altering its behavior/visibility, or providing a compatible replacement path so downstream modules don’t break on upgrade.
| } | ||
| catch (IllegalStateException e) | ||
| { | ||
| /* more than one row matches */ |
There was a problem hiding this comment.
Swallowing the IllegalStateException when multiple rows match the same EntityId makes diagnosing data issues difficult and turns a real match into a NotFound for downloads. Consider logging at least a warning/debug with the list id/entityId, or handling deterministically (e.g., limit/order) if duplicates are expected to be possible.
| /* more than one row matches */ | |
| // More than one row matches the specified EntityId; log for diagnosis and return null as before | |
| Logger logger = LogManager.getLogger(ListDefinitionImpl.class); | |
| logger.warn("Multiple list items match EntityId '{}' when resolving download container. List: '{}', Container: '{}'. Returning null.", | |
| entityId, getName(), getContainer().getPath(), e); |
| @RequiresPermission(AdminPermission.class) | ||
| public static class TruncateListDataAction extends ConfirmAction<ListDeletionForm> | ||
| { |
There was a problem hiding this comment.
PR description still contains the template placeholders for rationale and change list. Please update the PR description with the actual rationale/behavioral change summary (and any manual testing notes) before merging so reviewers/release notes have accurate context.
| }); | ||
| %> | ||
| <labkey:errors></labkey:errors> | ||
| <p>Are you sure you want to delete all data in <%= h(currentPath) %> from the following Lists? This action cannot be undone and will result in an empty list.</p> |
There was a problem hiding this comment.
The confirmation text says truncating will "result in an empty list", but this action deletes rows only from the current folder/container (not necessarily all rows across the project/shared list). Suggest rewording to explicitly state it will delete all rows for this list in the current folder/container path shown.
| <p>Are you sure you want to delete all data in <%= h(currentPath) %> from the following Lists? This action cannot be undone and will result in an empty list.</p> | |
| <p>Are you sure you want to delete all data in <%= h(currentPath) %> from the following Lists? This action cannot be undone and will delete all rows for these lists in the folder/container path shown, but will not affect rows in other folders/containers.</p> |
| public boolean handlePost(ListDeletionForm form, BindException errors) | ||
| { | ||
| Container containerDataToDelete = getContainer(); | ||
| for (Pair<Integer, Container> pair : form.getListContainerMap()) |
There was a problem hiding this comment.
Seems like this whole loop should be wrapped in a transaction.
| } | ||
|
|
||
| public boolean hasListItemForEntityId(String entityId, User user) | ||
| public Container getListItemContainerForDownload(String entityId, User user, Class<? extends Permission> permissionClass) |
There was a problem hiding this comment.
Annotate with @Nullable
Rationale
This PR aims to reduce the risk of accidental “delete all rows” operations on Lists by removing the delete-all button from the list grid itself, replacing it with a lists webpart menu action with explicit confirmation flow for truncating list data within the current container.
Related Pull Requests
Changes