Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 12999 delete content types #13214

Merged
merged 10 commits into from
Dec 19, 2017
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.dotmarketing.factories;

import com.dotmarketing.util.UtilMethods;
import com.dotcms.util.transform.DBTransformer;
import com.dotcms.util.transform.TransformerLocator;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.dotmarketing.beans.Identifier;
Expand All @@ -29,6 +32,7 @@
import com.dotmarketing.util.InodeUtils;
import com.dotmarketing.util.Logger;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;

/**
* This class provides utility routines to interact with the Multi-Tree
Expand All @@ -46,6 +50,13 @@ public class MultiTreeFactory {
private static final String DELETE_MULTITREE_ERROR_MSG = "Deleting MultiTree Object failed:";
private static final String SAVE_MULTITREE_ERROR_MSG = "Saving MultiTree Object failed:";

//MultiTree fields
private static final String CHILD = "child";
private static final String PARENT1 = "parent1";
private static final String PARENT2 = "parent2";
private static final String RELATION_TYPE = "relation_type";
private static final String TREE_ORDER = "tree_order";

public static void deleteMultiTree(Object o1, Object o2, Object o3) {
Inode inode1 = (Inode) o1;
Inode inode2 = (Inode) o2;
Expand Down Expand Up @@ -332,6 +343,55 @@ public static java.util.List<MultiTree> getContainerMultiTree(String containerId
throw new DotRuntimeException(e.toString());
}
}

/**
* Get a list of MultiTree for Contentlets using a specific Structure and specific Container
* @param containerIdentifier
* @param structureIdentifier
* @return List of MultiTree
*/
public static List<MultiTree> getContainerStructureMultiTree(String containerIdentifier, String structureInode) {
try {
DotConnect dc = new DotConnect();
StringBuilder query = new StringBuilder();
query.append("SELECT mt.* FROM multi_tree mt JOIN contentlet c ON c.identifier = mt.child ");
query.append("WHERE mt.parent2 = ? AND c.structure_inode = ? ");

dc.setSQL(query.toString());
dc.addParam(containerIdentifier);
dc.addParam(structureInode);

List<MultiTree> ret = new ArrayList<>();
List<Map<String,String>> results = dc.loadResults();
if(results != null && !results.isEmpty()){
for (Map<String, String> map : results) {
ret.add(getMultiTreeFields(map));
}
}

return ret;

} catch (DotDataException e) {
Logger.error(MultiTreeFactory.class, "getContainerStructureMultiTree failed:" + e, e);
throw new DotRuntimeException(e.toString());
}
}

@NotNull
private static MultiTree getMultiTreeFields(Map<String, String> map) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MultiTree mt = new MultiTree();
mt.setChild(map.get(CHILD));
mt.setParent1(map.get(PARENT1));
mt.setParent2(map.get(PARENT2));
if (UtilMethods.isSet(map.get(RELATION_TYPE))) {
mt.setRelationType(map.get(RELATION_TYPE));
}
if (UtilMethods.isSet(map.get(TREE_ORDER))) {
mt.setTreeOrder(Integer.parseInt(map.get(TREE_ORDER)));
}
return mt;
}

@SuppressWarnings("unchecked")
public static java.util.List<MultiTree> getMultiTreeByChild(String contentIdentifier) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.dotmarketing.portlets.containers.action;

import com.dotmarketing.beans.MultiTree;
import com.dotmarketing.exception.DotDataException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.factories.MultiTreeFactory;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Iterator;
Expand Down Expand Up @@ -559,43 +563,78 @@ public void _saveWebAsset(ActionRequest req, ActionResponse res,
identifier.setHostId(host.getIdentifier());
APILocator.getIdentifierAPI().save(identifier);


//Save/Update/Delete the ContainerStructures associated
saveContainerStructures(req, currentContainer, container);


SessionMessages.add(httpReq, "message", "message.containers.save");
ActivityLogger.logInfo(this.getClass(), "Save WebAsset action", "User " + user.getPrimaryKey() + " saved " + container.getTitle(), HostUtil.hostNameUtil(req, _getUser(req)));
// saves to working folder under velocity
ContainerServices.invalidate(container, true);

// copies the information back into the form bean
BeanUtils.copyProperties(form, req
.getAttribute(WebKeys.CONTAINER_FORM_EDIT));

APILocator.getVersionableAPI().setWorking(container);




HibernateUtil.flush();
}

/**
* Containers are associated with multiple Structures. This method takes care of the ContainerStructure save/update/delete logic
* @param req Request
* @param currentContainer Container as currently exists
* @param container Container with the new changes to be persisted
*/
private void saveContainerStructures (ActionRequest req, Container currentContainer, Container container)
throws DotDataException, DotSecurityException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic on this code can be optimized keeping a map for oldContainerStructures, then filter to exclude newContainerStructures (https://www.mkyong.com/java8/java-8-filter-a-map-examples/). With the resulting map you can delete in batch. I think it's a faster and less verbose implementation

List<ContainerStructure> oldContainerStructures = APILocator.getContainerAPI().getContainerStructures(currentContainer);
List<ContainerStructure> csList = new LinkedList<>();

// saving the multiple structures
if(container.getMaxContentlets()>0) {
String structuresIdsStr = req.getParameter("structuresIds");

String[] structuresIds = structuresIdsStr.split("#");
List<ContainerStructure> csList = new LinkedList<ContainerStructure>();

for (String structureId : structuresIds) {
String code = req.getParameter("code_"+structureId);
ContainerStructure cs = new ContainerStructure();
cs.setContainerId(container.getIdentifier());
cs.setContainerInode(container.getInode());
cs.setContainerInode(container.getInode());
cs.setStructureId(structureId);
cs.setCode(code);
csList.add(cs);
}

//Save new structures
APILocator.getContainerAPI().saveContainerStructures(csList);

}

//Delete MultiTree for old / unused ContainerStructures
for (ContainerStructure oldCS : oldContainerStructures) {
boolean unused = true;
for (ContainerStructure newCS : csList) {
if (newCS.getStructureId().equals(oldCS.getStructureId())) {
unused = false;
break;
}
}

SessionMessages.add(httpReq, "message", "message.containers.save");
ActivityLogger.logInfo(this.getClass(), "Save WebAsset action", "User " + user.getPrimaryKey() + " saved " + container.getTitle(), HostUtil.hostNameUtil(req, _getUser(req)));
// saves to working folder under velocity
ContainerServices.invalidate(container, true);

// copies the information back into the form bean
BeanUtils.copyProperties(form, req
.getAttribute(WebKeys.CONTAINER_FORM_EDIT));

APILocator.getVersionableAPI().setWorking(container);




HibernateUtil.flush();
if (unused) {
List<MultiTree> multiTreeList = MultiTreeFactory
.getContainerStructureMultiTree(oldCS.getContainerId(), oldCS.getStructureId());
for (MultiTree mt : multiTreeList) {
MultiTreeFactory.deleteMultiTree(mt);
}
}
}
}

public void _copyWebAsset(ActionRequest req, ActionResponse res,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,17 @@ public void deleteContainerContentTypesByContainerInode(final Container containe
List<Container> findContainersForStructure(String structureInode)
throws DotDataException;

/**
* Retrieves containers using the specified structure
*
* @param structureInode
* @param workingOrLiveOnly
* @return
* @throws DotDataException
*/
List<Container> findContainersForStructure(String structureInode, boolean workingOrLiveOnly)
throws DotDataException;

/**
*
* @param assetsOlderThan
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,14 @@ public List<Container> findContainersForStructure(String structureInode) throws
return containerFactory.findContainersForStructure(structureInode);
}

@Override
@CloseDBIfOpened
@Override
public List<Container> findContainersForStructure(String structureInode,
boolean workingOrLiveOnly) throws DotDataException {
return containerFactory.findContainersForStructure(structureInode, workingOrLiveOnly);
}

@Override
public int deleteOldVersions(Date assetsOlderThan) throws DotStateException, DotDataException {
return deleteOldVersions(assetsOlderThan, Inode.Type.CONTAINERS.getValue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,16 @@ public interface ContainerFactory {
*/
public List<Container> findContainers(User user, boolean includeArchived, Map<String,Object> params, String hostId, String inode, String identifier, String parent, int offset, int limit, String orderBy) throws DotSecurityException, DotDataException;

public List<Container> findContainersForStructure(String structureInode) throws DotDataException;
public List<Container> findContainersForStructure(String structureIdentifier) throws DotDataException;

/**
* Search Containers associated with a specific Structure
* @param structureIdentifier
* @param workingOrLiveOnly True to filter the Containers if the version associated with the Structure is live or Working only
* @return list of container
* @throws DotDataException
*/
public List<Container> findContainersForStructure(String structureIdentifier, boolean workingOrLiveOnly) throws DotDataException;

/**
* Method will replace user references of the given userId in containers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.portlets.containers.model.Container;
import com.dotmarketing.portlets.containers.model.ContainerVersionInfo;
import com.dotmarketing.util.InodeUtils;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.PaginatedArrayList;
Expand Down Expand Up @@ -304,22 +305,35 @@ else if(resultList.size() < internalLimit)
return assets;
}

public List<Container> findContainersForStructure(String structureInode) throws DotDataException {
HibernateUtil dh = new HibernateUtil(Container.class);
public List<Container> findContainersForStructure(String structureIdentifier) throws DotDataException {
return findContainersForStructure(structureIdentifier, false);
}

StringBuilder query = new StringBuilder();
@Override
public List<Container> findContainersForStructure(String structureIdentifier,
boolean workingOrLiveOnly) throws DotDataException {
HibernateUtil dh = new HibernateUtil(Container.class);

query.append("FROM c IN CLASS ");
StringBuilder query = new StringBuilder();

query.append("FROM c IN CLASS ");
query.append(Container.class);
query.append(" WHERE exists ( from cs in class ");
query.append(ContainerStructure.class.getName());
query.append(" where cs.containerId = c.identifier and cs.structureId = ? ) ");
dh.setQuery(query.toString());
dh.setParam(structureInode);
return dh.list();
}

/**
query.append(" where cs.containerId = c.identifier and cs.structureId = ? ");
if (workingOrLiveOnly) {
query.append(" AND EXISTS ( FROM vi IN CLASS ");
query.append(ContainerVersionInfo.class.getName());
query.append(" WHERE vi.identifier = c.identifier AND ");
query.append(" (cs.containerInode = vi.workingInode OR cs.containerInode = vi.liveInode ) ) ");
}
query.append(") ");
dh.setQuery(query.toString());
dh.setParam(structureIdentifier);
return dh.list();
}

/**
* Method will replace user references of the given userId in containers
* with the replacement user id
* @param userId User Identifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ public Map<Object,Object> checkDependencies(String structureInode) throws DotDat
List<Map<String,String>> containersList = new ArrayList<Map<String,String>>();

// checking if there are containers using this structure
List<Container> containers=APILocator.getContainerAPI().findContainersForStructure(structureInode);
List<Container> containers=APILocator.getContainerAPI().findContainersForStructure(structureInode, true);
Map<String, Container> containersInUse = new HashMap<String, Container>();

for(Container c : containers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2171,7 +2171,7 @@ message.field.values=The <i>Values</i> field is mandatory when the Display Type
message.structure.savestructure=The Content Type has been saved
message.structure.deletestructure=The Content Type has been deleted
message.structure.notdeletestructure=The default Content Type could not be deleted
message.structure.notdeletestructure.container=The Content Type cannot be deleted because it used by the following Containers:
message.structure.notdeletestructure.container=The Content Type cannot be deleted because it is being used by the following Containers:
message.structure.cantdelete=Delete Content Type
message.structure.savefield=The Field has been saved.
message.structure.nodatatype=You have reached the maximum number of fields of this Data Type in this Content Type; please choose another Display Type.
Expand Down