CCM NG/ccm-cms: Finding invalid targets for copying/moving items/folders.

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4612 8810af33-2d31-482b-a856-94f89814c4df
ccm-docs
jensp 2017-03-02 11:42:46 +00:00
parent 1a8cb99aa2
commit 7baea10b61
4 changed files with 322 additions and 263 deletions

View File

@ -43,6 +43,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -60,6 +61,8 @@ import javax.persistence.TypedQuery;
import javax.persistence.criteria.Order; import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path; import javax.persistence.criteria.Path;
import static org.librecms.CmsConstants.*;
/** /**
* The {@code FolderBrowserController} wraps all database operations (queries) * The {@code FolderBrowserController} wraps all database operations (queries)
* required by the {@link FolderBrowser}, the * required by the {@link FolderBrowser}, the
@ -136,8 +139,7 @@ public class FolderBrowserController {
* @param filterTerm The filter term. * @param filterTerm The filter term.
* *
* @return The number of objects (subfolders and content items) in the * @return The number of objects (subfolders and content items) in the
* provided {@code folder} which match the provided * provided {@code folder} which match the provided {@code filterTerm}.
* {@code filterTerm}.
*/ */
public long countObjects(final Folder folder, public long countObjects(final Folder folder,
final String filterTerm) { final String filterTerm) {
@ -187,8 +189,8 @@ public class FolderBrowserController {
* @param orderDirection The direction for ordering the objects. * @param orderDirection The direction for ordering the objects.
* *
* @return A list with {@link FolderBrowserTableRow} objects for each object * @return A list with {@link FolderBrowserTableRow} objects for each object
* in the provided {@code folder} ordered by the provided field and * in the provided {@code folder} ordered by the provided field and in the
* in the provided direction. * provided direction.
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
List<FolderBrowserTableRow> getObjectRows(final Folder folder, List<FolderBrowserTableRow> getObjectRows(final Folder folder,
@ -208,8 +210,8 @@ public class FolderBrowserController {
* *
* @return A list with {@link FolderBrowserTableRow} objects for each object * @return A list with {@link FolderBrowserTableRow} objects for each object
* in the provided {@code folder} which matches the provided * in the provided {@code folder} which matches the provided
* {@code filterTerm}, ordered by the provided field and in the * {@code filterTerm}, ordered by the provided field and in the provided
* provided direction. * direction.
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
List<FolderBrowserTableRow> getObjectRows(final Folder folder, List<FolderBrowserTableRow> getObjectRows(final Folder folder,
@ -236,10 +238,10 @@ public class FolderBrowserController {
* @param maxResults The maximum number of objects to retrieve. * @param maxResults The maximum number of objects to retrieve.
* *
* @return A list with {@link FolderBrowserTableRow} objects for each object * @return A list with {@link FolderBrowserTableRow} objects for each object
* in the provided {@code folder} ordered by the provided field and * in the provided {@code folder} ordered by the provided field and in the
* in the provided direction. The list will start with the object * provided direction. The list will start with the object with index
* with index provided as {@code firstResult} and contain at most * provided as {@code firstResult} and contain at most {@code maxResults}
* {@code maxResults} items. * items.
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
List<FolderBrowserTableRow> getObjectRows(final Folder folder, List<FolderBrowserTableRow> getObjectRows(final Folder folder,
@ -269,10 +271,9 @@ public class FolderBrowserController {
* *
* @return A list with {@link FolderBrowserTableRow} objects for each object * @return A list with {@link FolderBrowserTableRow} objects for each object
* in the provided {@code folder} which matches the provided * in the provided {@code folder} which matches the provided
* {@code filterTerm}, ordered by the provided field and in the * {@code filterTerm}, ordered by the provided field and in the provided
* provided direction. The list will start with the object with * direction. The list will start with the object with index provided as
* index provided as {@code firstResult} and contain at most * {@code firstResult} and contain at most {@code maxResults} items.
* {@code maxResults} items.
*/ */
@Transactional(Transactional.TxType.REQUIRED) @Transactional(Transactional.TxType.REQUIRED)
List<FolderBrowserTableRow> getObjectRows(final Folder folder, List<FolderBrowserTableRow> getObjectRows(final Folder folder,
@ -436,8 +437,8 @@ public class FolderBrowserController {
* *
* @param folder The folder which contains the subfolders. * @param folder The folder which contains the subfolders.
* @param filterTerm The filter term. * @param filterTerm The filter term.
* @param orderBy Field to use for ordering. If the value is negative * @param orderBy Field to use for ordering. If the value is negative the
* the parameter is ignored. * parameter is ignored.
* @param orderDirection Direction for ordering. If the value is negative * @param orderDirection Direction for ordering. If the value is negative
* the parameter is ignored. * the parameter is ignored.
* @param firstResult Index of the first result to retrieve. * @param firstResult Index of the first result to retrieve.
@ -445,10 +446,9 @@ public class FolderBrowserController {
* *
* *
* @return A list of the subfolders of the provided {@code folder} which * @return A list of the subfolders of the provided {@code folder} which
* match the provided {@code filterTerm}. The list is ordered as * match the provided {@code filterTerm}. The list is ordered as described
* described above. The list will contain at most {@code maxResults} * above. The list will contain at most {@code maxResults} starting with the
* starting with the result with the index provided as * result with the index provided as {@code firstResult}.
* {@code firstResult}.
*/ */
private List<Folder> findSubFolders(final Folder folder, private List<Folder> findSubFolders(final Folder folder,
final String filterTerm, final String filterTerm,
@ -475,7 +475,8 @@ public class FolderBrowserController {
criteria.where(builder.and( criteria.where(builder.and(
builder.equal(from.get("parentCategory"), folder), builder.equal(from.get("parentCategory"), folder),
builder builder
.like(builder.lower(from.get("name")), filterTerm))) .like(builder.lower(from.get("name")),
filterTerm)))
.orderBy(order)); .orderBy(order));
if (firstResult >= 0) { if (firstResult >= 0) {
@ -493,8 +494,8 @@ public class FolderBrowserController {
* *
* @param folder The folder which contains the subfolders. * @param folder The folder which contains the subfolders.
* @param filterTerm The filter term. * @param filterTerm The filter term.
* @param orderBy Field to use for ordering. If the value is negative * @param orderBy Field to use for ordering. If the value is negative the
* the parameter is ignored. * parameter is ignored.
* @param orderDirection Direction for ordering. If the value is negative * @param orderDirection Direction for ordering. If the value is negative
* the parameter is ignored. * the parameter is ignored.
* @param firstResult Index of the first result to retrieve. * @param firstResult Index of the first result to retrieve.
@ -502,11 +503,10 @@ public class FolderBrowserController {
* *
* *
* @return A list of the subfolders of the provided {@code folder} which * @return A list of the subfolders of the provided {@code folder} which
* match the provided {@code filterTerm}. The list is ordered the * match the provided {@code filterTerm}. The list is ordered the field
* field provided as {@code orderBy} in the direction provided by * provided as {@code orderBy} in the direction provided by
* {@code orderDirection}. The list will contain at most * {@code orderDirection}. The list will contain at most {@code maxResults}
* {@code maxResults} starting with the result with the index * starting with the result with the index provided as {@code firstResult}.
* provided as {@code firstResult}.
*/ */
private List<ContentItem> findItemsInFolder(final Folder folder, private List<ContentItem> findItemsInFolder(final Folder folder,
final String filterTerm, final String filterTerm,
@ -606,4 +606,111 @@ public class FolderBrowserController {
} }
} }
/**
* Get the IDs of the folders invalid for copy/move actions.
*
* @param sources
* @return
*/
@Transactional(Transactional.TxType.REQUIRED)
public List<String> createInvalidTargetsList(final List<String> sources) {
Objects.requireNonNull(sources);
final List<String> sourceFolderIds = sources
.stream()
.filter(source -> source.startsWith(
FOLDER_BROWSER_KEY_PREFIX_FOLDER))
.collect(Collectors.toList());
final List<String> parentFolderIds = sourceFolderIds
.stream()
.map(sourceFolderId -> findParentFolderId(sourceFolderId))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
final List<List<String>> subFolderIds = sourceFolderIds
.stream()
.map(sourceFolderId -> findSubFolderIds(sourceFolderId))
.collect(Collectors.toList());
final List<String> invalidTargetIds = new ArrayList<>();
invalidTargetIds.addAll(sourceFolderIds);
invalidTargetIds.addAll(parentFolderIds);
for(final List<String> subFolderIdList : subFolderIds) {
invalidTargetIds.addAll(subFolderIdList);
}
return invalidTargetIds;
}
private Optional<String> findParentFolderId(final String folderId) {
Objects.requireNonNull(folderId);
if (!folderId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
throw new IllegalArgumentException(String.format(
"Provided string '%s' is not an ID of a folder.",
folderId));
}
final long objectId = Long.parseLong(folderId.substring(
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
final Folder folder = folderRepo.findById(objectId)
.orElseThrow(() -> new IllegalArgumentException(String.format(
"No folder with ID %d found in database. "
+ "Where did that ID come form?",
objectId)));
if (folder.getParentFolder() == null) {
return Optional.empty();
} else {
return Optional.ofNullable(String.format(
"%s%d",
FOLDER_BROWSER_KEY_PREFIX_FOLDER,
folder.getParentFolder().getObjectId()));
}
}
private List<String> findSubFolderIds(final String folderId) {
Objects.requireNonNull(folderId);
if (!folderId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
throw new IllegalArgumentException(String.format(
"Provided string '%s' is not the ID of a folder.",
folderId));
}
final long objectId = Long.parseLong(folderId.substring(
FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
final Folder folder = folderRepo.findById(objectId)
.orElseThrow(() -> new IllegalArgumentException(String.format(
"No folder with ID %d found in database. "
+ "Where did that ID come form?",
objectId)));
return findSubFolders(folder)
.stream()
.map(subFolder -> String.format("%s%d",
FOLDER_BROWSER_KEY_PREFIX_FOLDER,
subFolder.getObjectId()))
.collect(Collectors.toList());
}
private List<Folder> findSubFolders(final Folder folder) {
Objects.requireNonNull(folder);
if (folder.getSubFolders() == null
|| folder.getSubFolders().isEmpty()) {
return Collections.emptyList();
}
final List<Folder> subFolders = new ArrayList<>();
for(final Folder subFolder : folder.getSubFolders()) {
subFolders.add(subFolder);
subFolders.addAll(findSubFolders(subFolder));
}
return subFolders;
}
} }

View File

@ -55,7 +55,6 @@ import com.arsdigita.bebop.form.SingleSelect;
import com.arsdigita.bebop.form.Submit; import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.TextField; import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.ArrayParameter; import com.arsdigita.bebop.parameters.ArrayParameter;
import com.arsdigita.bebop.parameters.LongParameter;
import com.arsdigita.bebop.parameters.StringParameter; import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.table.TableCellRenderer; import com.arsdigita.bebop.table.TableCellRenderer;
import com.arsdigita.bebop.table.TableColumn; import com.arsdigita.bebop.table.TableColumn;
@ -67,6 +66,8 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.arsdigita.cms.CMSConfig; import org.arsdigita.cms.CMSConfig;
import org.libreccm.categorization.Category; import org.libreccm.categorization.Category;
@ -605,7 +606,8 @@ public class FolderManipulator extends SimpleContainer implements
"item--".length())); "item--".length()));
final ContentItem item = itemRepo.findById(itemId).orElseThrow( final ContentItem item = itemRepo.findById(itemId).orElseThrow(
() -> new IllegalArgumentException(String.format( () -> new IllegalArgumentException(String.format(
"No content item with id %d in database.", itemId))); "No content item with id %d in database.",
itemId)));
name = item.getDisplayName(); name = item.getDisplayName();
@ -1086,7 +1088,8 @@ public class FolderManipulator extends SimpleContainer implements
// } // }
private class FolderTreeCellRenderer implements TreeCellRenderer { private class FolderTreeCellRenderer implements TreeCellRenderer {
private RequestLocal m_invalidFolders = new RequestLocal(); private final RequestLocal invalidFoldersRequestLocal
= new RequestLocal();
/** /**
* Render the folders appropriately. The selected folder is a bold * Render the folders appropriately. The selected folder is a bold
@ -1104,72 +1107,19 @@ public class FolderManipulator extends SimpleContainer implements
final Object key) { final Object key) {
// Get the list of invalid folders once per request. // Get the list of invalid folders once per request.
@SuppressWarnings("unchecked") final List<String> invalidFolders;
ArrayList<String> invalidFolders if (invalidFoldersRequestLocal.get(state) == null) {
= (ArrayList<String>) m_invalidFolders.get(state); final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final FolderBrowserController controller = cdiUtil.findBean(
if (invalidFolders == null) { FolderBrowserController.class);
// The list of invalid folders has not been set for this invalidFolders = controller.createInvalidTargetsList(
// request. Setting now. Arrays.asList(getSources(state)));
invalidFolders = new ArrayList<>(); invalidFoldersRequestLocal.set(state, invalidFolders);
final String[] sources = getSources(state); } else {
invalidFolders = (List<String>) invalidFoldersRequestLocal
for (final String source : sources) { .get(state);
if (source.startsWith("folder--")) {
invalidFolders.addAll(source);
}
} }
}
// if (invalidFolders == null) {
// // The list of invalid folders has not been set for this
// // request. Setting now.
// invalidFolders = new ArrayList();
//
// final DataCollection collection = SessionManager.getSession().
// retrieve(
// ContentItem.BASE_DATA_OBJECT_TYPE);
// CompoundFilter filter = collection.getFilterFactory().or();
// // The sources themselves are not valid.
// final Long[] sources = getSources(state);
//
// for (int i = 0; i < sources.length; i++) {
// invalidFolders.add(sources[i].toString());
//
// final Filter temp = filter.addFilter("id = :id" + i);
// temp.set("id" + i, sources[i]);
// }
// collection.addFilter(filter);
//
// final DataCollection folders = SessionManager.getSession().
// retrieve(
// Folder.BASE_DATA_OBJECT_TYPE);
// folders.addEqualsFilter(Folder.IS_DELETED, Boolean.FALSE);
//
// filter = collection.getFilterFactory().or();
// int count = 0;
// while (collection.next()) {
// filter.addFilter(Folder.ANCESTORS + " like :ancestors"
// + count + " || '%'");
// filter.set("ancestors" + count,
// collection.get(ContentItem.ANCESTORS));
// count++;
// }
// folders.addFilter(filter);
//
// while (folders.next()) {
// invalidFolders.add(folders.get(Folder.ID).toString());
// }
//
// invalidFolders.add(sourceFolderModel.getSelectedKey(state).
// toString());
//
// // Save the invalid folder list
// m_invalidFolders.set(state, invalidFolders);
// }
final Label label = new Label(value.toString()); final Label label = new Label(value.toString());
if (invalidFolders.contains(key.toString())) { if (invalidFolders.contains(key.toString())) {

View File

@ -26,7 +26,6 @@ import org.librecms.contentsection.Folder;
import org.librecms.contentsection.FolderRepository; import org.librecms.contentsection.FolderRepository;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;

View File

@ -54,6 +54,9 @@ public class CmsConstants {
public static final String CATEGORIZATION_TYPE_FOLDER = "folder"; public static final String CATEGORIZATION_TYPE_FOLDER = "folder";
public static final String FOLDER_BROWSER_KEY_PREFIX_FOLDER = "folder-";
public static final String FOLDER_BROWSER_KEY_PREFIX_ITEM = "item-";
/** /**
* Constant string used as key for creating service package as a legacy * Constant string used as key for creating service package as a legacy
* application. * application.