Skip to content
Closed
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
46 changes: 0 additions & 46 deletions api/src/org/labkey/api/query/QueryView.java
Original file line number Diff line number Diff line change
Expand Up @@ -1050,52 +1050,6 @@ public ActionButton createDeleteButton(boolean showConfirmation)
return null;
}

public ActionButton createDeleteAllRowsButton(String tableNoun)
{
ActionButton deleteAllRows = new ActionButton("Delete All Rows");
deleteAllRows.setDisplayPermission(AdminPermission.class);
deleteAllRows.setActionType(ActionButton.Action.SCRIPT);
deleteAllRows.setScript(
"LABKEY.requiresExt4Sandbox(function() {" +
"Ext4.Msg.confirm('Confirm Deletion', 'Are you sure you wish to delete all rows in this " + tableNoun + "? This action cannot be undone and will result in an empty " + tableNoun + ".', function(button){" +
"if (button == 'yes'){" +
"var waitMask = Ext4.Msg.wait('Deleting Rows...', 'Delete Rows'); " +
"Ext4.Ajax.request({ " +
"url : LABKEY.ActionURL.buildURL('query', 'truncateTable'), " +
"method : 'POST', " +
"success: function(response) " +
"{" +
"waitMask.close(); " +
"var data = Ext4.JSON.decode(response.responseText); " +
"Ext4.Msg.show({ " +
"title : 'Success', " +
"buttons : Ext4.MessageBox.OK, " +
"msg : data.deletedRows + ' rows deleted', " +
"fn: function(btn) " +
"{ " +
"if(btn == 'ok') " +
"{ " +
"window.location.reload(); " +
"} " +
"} " +
"})" +
"}, " +
"failure : function(response, opts) " +
"{ " +
"waitMask.close(); " +
"Ext4.getBody().unmask(); " +
"LABKEY.Utils.displayAjaxErrorResponse(response, opts); " +
"}, " +
"jsonData : {schemaName : " + PageFlowUtil.jsString(getQueryDef().getSchema().getName()) + ", queryName : " + PageFlowUtil.jsString(getQueryDef().getName()) + "}, " +
"scope : this " +
"});" +
"}" +
"});" +
"});"
);
return deleteAllRows;
}

public ActionButton createInsertMenuButton()
{
return createInsertMenuButton(null, null);
Expand Down
96 changes: 94 additions & 2 deletions list/src/org/labkey/list/controllers/ListController.java
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,97 @@ public List<Pair<Integer, Container>> getListContainerMap()
}
}

@RequiresPermission(AdminPermission.class)
public static class TruncateListDataAction extends ConfirmAction<ListDeletionForm>
{
private boolean canTruncate(Container listContainer, int listId)
{
ListDef listDef = ListManager.get().getList(listContainer, listId);
ListDefinitionImpl list = ListDefinitionImpl.of(listDef);

if (list == null || !list.getAllowDelete())
return false;

return list.getContainer().hasPermission(getUser(), AdminPermission.class);
}

@Override
public String getConfirmText()
{
return "Confirm Delete All Data";
}

@Override
public void validateCommand(ListDeletionForm form, Errors errors)
{
Container currentContainer = getContainer();
List<String> errorMessages = new ArrayList<>();
Collection<String> listIDs;
if (form.getListIds() != null)
listIDs = form.getListIds();
else
listIDs = DataRegionSelection.getSelected(form.getViewContext(), true);

for (Pair<Integer, Container> pair : getListIdContainerPairs(listIDs, currentContainer, errorMessages))
{
var listId = pair.first;
var listContainer = pair.second;

if (canTruncate(listContainer, listId))
{
form.getListContainerMap().add(pair);
}
else
errorMessages.add(String.format("You do not have permission to delete data for list %s in container %s", listId, listContainer.getName()));
}

if (!errorMessages.isEmpty())
errors.reject(ERROR_MSG, StringUtils.join(errorMessages, "\n"));

if (form.getListContainerMap().isEmpty())
errors.reject(ERROR_MSG, "You must specify a list or lists to delete data from.");
}

@Override
public ModelAndView getConfirmView(ListDeletionForm form, BindException errors)
{
if (getPageConfig().getTitle() == null)
setTitle("Confirm Delete All Data");
return new JspView<>("/org/labkey/list/view/truncateListData.jsp", form, errors);
}

@Override
public boolean handlePost(ListDeletionForm form, BindException errors)
{
Container containerDataToDelete = getContainer();
for (Pair<Integer, Container> pair : form.getListContainerMap())
{
Container listDefContainer = pair.second;
ListDefinition listDef = ListService.get().getList(listDefContainer, pair.first);
if (null != listDef)
{
try
{
TableInfo table = listDef.getTable(getUser(), listDefContainer);
if (table != null && table.getUpdateService() != null)
table.getUpdateService().truncateRows(getUser(), containerDataToDelete, null, null);
}
catch (Exception e)
{
errors.reject(ERROR_MSG, "Error deleting data from list '" + listDef.getName() + "': " + e.getMessage());
}
}
}

return !errors.hasErrors();
}

@Override @NotNull
public URLHelper getSuccessURL(ListDeletionForm form)
{
return form.getReturnUrlHelper(getBeginURL(getContainer()));
}
}

@RequiresPermission(ReadPermission.class)
public class GridAction extends SimpleViewAction<ListQueryForm>
Expand Down Expand Up @@ -958,10 +1049,11 @@ public Pair<AttachmentParent, String> getAttachment(ListAttachmentForm form)
if (listDef == null)
throw new NotFoundException("List does not exist in this container");

if (!listDef.hasListItemForEntityId(form.getEntityId(), getUser()))
Container dataContainer = listDef.getListItemContainerForDownload(form.getEntityId(), getUser(), ReadPermission.class);
if (dataContainer == null)
throw new NotFoundException("List does not have an item for the entityid");

AttachmentParent parent = new ListItemAttachmentParent(form.getEntityId(), getContainer());
AttachmentParent parent = new ListItemAttachmentParent(form.getEntityId(), dataContainer);

return new Pair<>(parent, form.getName());
}
Expand Down
36 changes: 27 additions & 9 deletions list/src/org/labkey/list/model/ListDefinitionImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.labkey.api.reader.DataLoader;
import org.labkey.api.reader.MapLoader;
import org.labkey.api.security.User;
import org.labkey.api.security.permissions.Permission;
import org.labkey.api.util.ReentrantLockWithName;
import org.labkey.api.util.URLHelper;
import org.labkey.api.view.ActionURL;
Expand Down Expand Up @@ -521,19 +522,36 @@ private ListItem getListItem(SimpleFilter filter, User user, Container c)
return impl;
}

public boolean hasListItemForEntityId(String entityId, User user)
public Container getListItemContainerForDownload(String entityId, User user, Class<? extends Permission> permissionClass)
{
return hasListItem(new SimpleFilter(FieldKey.fromParts("EntityId"), entityId), user, getContainer());
}

private boolean hasListItem(SimpleFilter filter, User user, Container c)
{
TableInfo tbl = getTable(user, c);
Container c = getContainer();
SimpleFilter filter = new SimpleFilter(FieldKey.fromParts("EntityId"), entityId);
// Use a relax CF to find the list items, permission will be validated later
ContainerFilter cf = ContainerFilter.Type.AllInProjectPlusShared.create(c, user);
TableInfo tbl = getTable(user, c, cf);

if (null == tbl)
return false;
return null;

Map<String, Object> row = null;

try
{
row = new TableSelector(tbl, filter, null).getMap();
}
catch (IllegalStateException e)
{
/* more than one row matches */
}

if (row == null)
return null;

Container dataContainer = row.get("Container") != null ? ContainerManager.getForId(row.get("Container").toString()) : null;
if (dataContainer != null && dataContainer.hasPermission(user, permissionClass))
return dataContainer;

return new TableSelector(tbl, filter, null).exists();
return null;
}

@Override
Expand Down
38 changes: 32 additions & 6 deletions list/src/org/labkey/list/model/ListManagerSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import org.labkey.api.data.ActionButton;
import org.labkey.api.data.ButtonBar;
import org.labkey.api.data.Container;
import org.labkey.api.data.DataRegion;
import org.labkey.api.data.MenuButton;
import org.labkey.api.data.ContainerFilter;
import org.labkey.api.data.ContainerManager;
import org.labkey.api.data.DisplayColumn;
Expand All @@ -38,6 +40,7 @@
import org.labkey.api.query.QueryView;
import org.labkey.api.query.UserSchema;
import org.labkey.api.security.User;
import org.labkey.api.security.permissions.AdminPermission;
import org.labkey.api.util.ButtonBuilder;
import org.labkey.api.util.LinkBuilder;
import org.labkey.api.view.ActionURL;
Expand Down Expand Up @@ -162,14 +165,37 @@ private ButtonBuilder.Button createImportListArchiveButton()
@Override
public ActionButton createDeleteButton()
{
if (!getContainer().hasPermission(getUser(), DesignListPermission.class))
return null;

if (!getContainer().hasPermission(getUser(), AdminPermission.class))
{
ActionURL urlDelete = new ActionURL(ListController.DeleteListDefinitionAction.class, getContainer());
urlDelete.addReturnUrl(getReturnUrl());
ActionButton btnDelete = new ActionButton(urlDelete, "Delete");
btnDelete.setIconCls("trash");
btnDelete.setActionType(ActionButton.Action.GET);
btnDelete.setRequiresSelection(true);
return btnDelete;
}

MenuButton menuDelete = new MenuButton("Delete");
menuDelete.setIconCls("trash");
menuDelete.setDisplayPermission(DesignListPermission.class);
menuDelete.setRequiresSelection(true);

ActionURL urlDelete = new ActionURL(ListController.DeleteListDefinitionAction.class, getContainer());
urlDelete.addReturnUrl(getReturnUrl());
ActionButton btnDelete = new ActionButton(urlDelete, "Delete");
btnDelete.setIconCls("trash");
btnDelete.setActionType(ActionButton.Action.GET);
btnDelete.setDisplayPermission(DesignListPermission.class);
btnDelete.setRequiresSelection(true);
return btnDelete;
menuDelete.addMenuItem("Delete List", "if (verifySelected(" + DataRegion.getJavaScriptObjectReference(getDataRegionName()) + ".form, \"" +
urlDelete.getLocalURIString() + "\", \"GET\", \"rows\")) " + DataRegion.getJavaScriptObjectReference(getDataRegionName()) + ".form.submit()");


ActionURL urlTruncate = new ActionURL(ListController.TruncateListDataAction.class, getContainer());
urlTruncate.addReturnUrl(getReturnUrl());
menuDelete.addMenuItem("Delete All Data from List", "if (verifySelected(" + DataRegion.getJavaScriptObjectReference(getDataRegionName()) + ".form, \"" +
urlTruncate.getLocalURIString() + "\", \"GET\", \"rows\")) " + DataRegion.getJavaScriptObjectReference(getDataRegionName()) + ".form.submit()");

return menuDelete;
}

private ActionButton createExportArchiveButton()
Expand Down
2 changes: 1 addition & 1 deletion list/src/org/labkey/list/model/ListQueryUpdateService.java
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ public void deleteRelatedListData(final User user, final Container container)
ListManager.get().deleteIndexedList(_list);

// Delete attachments and discussions associated with a list in batches of 1,000
new TableSelector(getDbTable(), Collections.singleton("entityId")).forEachBatch(String.class, 1000, new ForEachBatchBlock<>()
new TableSelector(getDbTable(), Collections.singleton("entityId"), SimpleFilter.createContainerFilter(container), null).forEachBatch(String.class, 1000, new ForEachBatchBlock<>()
{
@Override
public boolean accept(String entityId)
Expand Down
2 changes: 0 additions & 2 deletions list/src/org/labkey/list/view/ListQueryView.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ protected void populateButtonBar(DataView view, ButtonBar bar)
ActionButton btnUpload = new ActionButton("Design", designURL);
bar.add(btnUpload);
}
if (canDelete())
bar.add(super.createDeleteAllRowsButton("list"));
}

public ListDefinition getList()
Expand Down
70 changes: 70 additions & 0 deletions list/src/org/labkey/list/view/truncateListData.jsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<%
/*
* Copyright (c) 2009-2016 LabKey Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
%>
<%@ page import="org.labkey.api.data.Container" %>
<%@ page import="org.labkey.api.data.TableInfo" %>
<%@ page import="org.labkey.api.data.TableSelector" %>
<%@ page import="org.labkey.api.exp.list.ListDefinition" %>
<%@ page import="org.labkey.api.exp.list.ListService" %>
<%@ page import="org.labkey.list.controllers.ListController" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.Objects" %>
<%@ page import="org.labkey.list.model.ListQuerySchema" %>
<%@ page import="org.labkey.api.security.User" %>
<%@ page extends="org.labkey.api.jsp.FormPage" %>
<%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib"%>
<%
ListController.ListDeletionForm form = (ListController.ListDeletionForm)getModelBean();
Map<Container, List<ListDefinition>> definitions = new HashMap<>();
Container currentContainer = form.getContainer();
String currentPath = currentContainer.getPath();
User user = form.getUser();
ListQuerySchema listQuerySchema = new ListQuerySchema(user, currentContainer);

form.getListContainerMap()
.stream()
.map(pair -> ListService.get().getList(pair.second, pair.first))
.filter(Objects::nonNull)
.forEach(listDef -> {
var container = listDef.getContainer();
if (!definitions.containsKey(container))
definitions.put(container, new ArrayList<>());
definitions.get(container).add(listDef);
});
%>
<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>

<% for (var entry : definitions.entrySet()) { %>
<div>
Defined in <%= h(entry.getKey().getPath()) %>:
<ul>
<% for (var listDef : entry.getValue()) {
TableInfo table = listQuerySchema.getTable(listDef.getName());
long count = (table != null) ? new TableSelector(table).getRowCount() : 0;
%>
<li>
<%= simpleLink(listDef.getName(), listDef.urlFor(ListController.GridAction.class, listDef.getContainer())) %>
&mdash; <%= count %> row<%= h(count != 1 ? "s" : "") %> in <%= h(currentPath) %>
</li>
<% } %>
</ul>
</div>
<% } %>