diff --git a/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml b/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml
index 88e77939f..47ce2c010 100644
--- a/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml
+++ b/ccm-bundle-devel-wildfly-web/src/main/resources/log4j2.xml
@@ -36,6 +36,9 @@
+
+
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetFolderBrowserController.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetFolderBrowserController.java
index 37e0bcb17..3e9490db2 100644
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetFolderBrowserController.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetFolderBrowserController.java
@@ -18,7 +18,6 @@
*/
package com.arsdigita.cms.ui.assets;
-import com.arsdigita.cms.ui.folder.FolderBrowser;
import com.arsdigita.kernel.KernelConfig;
import org.libreccm.categorization.Category;
@@ -29,13 +28,10 @@ import org.librecms.CmsConstants;
import org.librecms.assets.AssetTypeInfo;
import org.librecms.assets.AssetTypesManager;
import org.librecms.contentsection.Asset;
-import org.librecms.contentsection.ContentType;
import org.librecms.contentsection.Folder;
-import org.librecms.contenttypes.ContentTypeInfo;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -53,10 +49,18 @@ import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
+
import org.libreccm.core.CcmObject;
+import org.librecms.contentsection.AssetManager;
import org.librecms.contentsection.AssetRepository;
+import org.librecms.contentsection.FolderManager;
import org.librecms.contentsection.FolderRepository;
+import java.util.Collections;
+import java.util.Optional;
+
+import static org.librecms.CmsConstants.*;
+
/**
*
* @author Jens Pelzetter
@@ -79,9 +83,15 @@ public class AssetFolderBrowserController {
@Inject
private FolderRepository folderRepo;
+ @Inject
+ private FolderManager folderManager;
+
@Inject
private AssetRepository assetRepo;
+ @Inject
+ private AssetManager assetManager;
+
@Inject
private GlobalizationHelper globalizationHelper;
@@ -96,7 +106,7 @@ public class AssetFolderBrowserController {
@PostConstruct
private void init() {
final KernelConfig kernelConfig = confManager.findConfiguration(
- KernelConfig.class);
+ KernelConfig.class);
defaultLocale = kernelConfig.getDefaultLocale();
}
@@ -107,14 +117,15 @@ public class AssetFolderBrowserController {
final int firstResult,
final int maxResults) {
final List subFolders = findSubFolders(folder,
+ "%",
orderBy,
orderDirection,
firstResult,
maxResults);
final List subFolderRows = subFolders
- .stream()
- .map(subFolder -> buildRow(subFolder))
- .collect(Collectors.toList());
+ .stream()
+ .map(subFolder -> buildRow(subFolder))
+ .collect(Collectors.toList());
if (subFolders.size() > maxResults) {
return subFolderRows;
@@ -123,14 +134,15 @@ public class AssetFolderBrowserController {
final int firstAsset = firstResult - subFolders.size();
final List assets = findAssetsInFolder(folder,
+ "%",
orderBy,
orderDirection,
firstAsset,
maxAssets);
final List assetRows = assets
- .stream()
- .map(asset -> buildRow(asset))
- .collect(Collectors.toList());
+ .stream()
+ .map(asset -> buildRow(asset))
+ .collect(Collectors.toList());
final List rows = new ArrayList<>();
rows.addAll(subFolderRows);
@@ -143,7 +155,15 @@ public class AssetFolderBrowserController {
@Transactional(Transactional.TxType.REQUIRED)
protected long countObjects(final Folder folder) {
+ return countObjects(folder, "%");
+
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected long countObjects(final Folder folder, final String filterTerm) {
+
Objects.requireNonNull(folder);
+ Objects.requireNonNull(filterTerm);
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = builder.createQuery(Long.class);
@@ -152,21 +172,23 @@ public class AssetFolderBrowserController {
criteriaQuery = criteriaQuery.select(builder.count(from));
final List subFolders = findSubFolders(
- folder,
- AssetFolderBrowser.SORT_KEY_NAME,
- AssetFolderBrowser.SORT_ACTION_UP,
- -1,
- -1);
+ folder,
+ filterTerm,
+ AssetFolderBrowser.SORT_KEY_NAME,
+ AssetFolderBrowser.SORT_ACTION_UP,
+ -1,
+ -1);
final List assets = findAssetsInFolder(
- folder,
- AssetFolderBrowser.SORT_KEY_NAME,
- AssetFolderBrowser.SORT_ACTION_UP,
- -1,
- -1);
-
+ folder,
+ filterTerm,
+ AssetFolderBrowser.SORT_KEY_NAME,
+ AssetFolderBrowser.SORT_ACTION_UP,
+ -1,
+ -1);
+
if (subFolders.isEmpty() && assets.isEmpty()) {
return 0;
- } else if(subFolders.isEmpty() && !assets.isEmpty()) {
+ } else if (subFolders.isEmpty() && !assets.isEmpty()) {
criteriaQuery = criteriaQuery.where(from.in(assets));
} else if (!subFolders.isEmpty() && assets.isEmpty()) {
criteriaQuery = criteriaQuery.where(from.in(subFolders));
@@ -180,6 +202,218 @@ public class AssetFolderBrowserController {
}
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected void copyObjects(final Folder targetFolder,
+ final String[] objectIds) {
+
+ Objects.requireNonNull(targetFolder);
+ Objects.requireNonNull(objectIds);
+
+ for (final String objectId : objectIds) {
+ if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
+ copyFolder(targetFolder,
+ Long.parseLong(objectId.substring(
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER.length())));
+ } else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) {
+ copyAsset(targetFolder,
+ Long.parseLong(objectId.substring(
+ FOLDER_BROWSER_KEY_PREFIX_ITEM.length())));
+ } else {
+ throw new IllegalArgumentException(String.format(
+ "ID '%s' does not start with '%s' or '%s'.",
+ objectId,
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER,
+ FOLDER_BROWSER_KEY_PREFIX_ASSET));
+ }
+ }
+
+ }
+
+ private void copyFolder(final Folder targetFolder,
+ final long folderId) {
+
+ Objects.requireNonNull(targetFolder);
+
+ final Folder folder = folderRepo.findById(folderId)
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ "No folder with ID %d in the database. "
+ + "Where did that ID come from?",
+ folderId)));
+
+ folderManager.copyFolder(folder, targetFolder);
+
+ }
+
+ private void copyAsset(final Folder targetFolder,
+ final long assetId) {
+
+ Objects.requireNonNull(targetFolder);
+
+ final Asset asset = assetRepo
+ .findById(assetId)
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ "No asset ith ID %d in the database. Where did that ID come from?",
+ assetId)));
+
+ assetManager.copy(asset, targetFolder);
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void moveObjects(final Folder targetFolder,
+ final String[] objectIds) {
+
+ Objects.requireNonNull(targetFolder);
+ Objects.requireNonNull(objectIds);
+
+ for (final String objectId : objectIds) {
+ if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
+ moveFolder(targetFolder,
+ Long.parseLong(objectId.substring(
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER.length())));
+ } else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) {
+ moveAsset(targetFolder,
+ Long.parseLong(objectId.substring(
+ FOLDER_BROWSER_KEY_PREFIX_ASSET.length())));
+ } else {
+ throw new IllegalArgumentException(String.format(
+ "ID '%s' does not start with '%s' or '%s'.",
+ objectId,
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER,
+ FOLDER_BROWSER_KEY_PREFIX_ASSET));
+ }
+ }
+ }
+
+ private void moveFolder(final Folder targetFolder, final long folderId) {
+
+ Objects.requireNonNull(targetFolder);
+
+ final Folder folder = folderRepo.findById(folderId)
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ "No folder with ID %d in the database. "
+ + "Where did that ID come from?",
+ folderId)));
+
+ folderManager.moveFolder(folder, targetFolder);
+ }
+
+ private void moveAsset(final Folder targetFolder, final long assetId) {
+
+ Objects.requireNonNull(targetFolder);
+
+ final Asset asset = assetRepo
+ .findById(assetId)
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ "No asset with ID %d in the database. Where did that ID come from?",
+ assetId)));
+
+ assetManager.move(asset, targetFolder);
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ protected List createInvalidTargetsList(final List sources) {
+
+ Objects.requireNonNull(sources);
+
+ final List sourceFolderIds = sources
+ .stream()
+ .filter(source -> source.startsWith(
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER))
+ .collect(Collectors.toList());
+ final List parentFolderIds = sourceFolderIds
+ .stream()
+ .map(sourceFolderId -> findParentFolderId(sourceFolderId))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(Collectors.toList());
+ final List> subFolderIds = sourceFolderIds
+ .stream()
+ .map(sourceFolderId -> findSubFolderIds(sourceFolderId))
+ .collect(Collectors.toList());
+
+ final List invalidTargetIds = new ArrayList<>();
+ invalidTargetIds.addAll(sourceFolderIds);
+ invalidTargetIds.addAll(parentFolderIds);
+ for (final List subFolderIdList : subFolderIds) {
+ invalidTargetIds.addAll(subFolderIdList);
+ }
+
+ return invalidTargetIds;
+
+ }
+
+ private Optional 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)));
+ final Optional parentFolder = folderManager.getParentFolder(
+ folder);
+ if (parentFolder.isPresent()) {
+ return Optional.empty();
+ } else {
+ return Optional.ofNullable(String.format(
+ "%s%d",
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER,
+ parentFolder.get().getObjectId()));
+ }
+ }
+
+ private List 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 findSubFolders(final Folder folder) {
+
+ Objects.requireNonNull(folder);
+
+ if (folder.getSubFolders() == null
+ || folder.getSubFolders().isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ final List subFolders = new ArrayList<>();
+ for (final Folder subFolder : folder.getSubFolders()) {
+ subFolders.add(subFolder);
+ subFolders.addAll(findSubFolders(subFolder));
+ }
+
+ return subFolders;
+ }
+
/**
* Called by the {@link AssetFolderBrowser} to delete an object.
*
@@ -192,21 +426,21 @@ public class AssetFolderBrowserController {
if (objectId.startsWith("folder-")) {
final long folderId = Long.parseLong(
- objectId.substring("folder-".length()));
+ objectId.substring("folder-".length()));
folderRepo
- .findById(folderId)
- .ifPresent(folderRepo::delete);
+ .findById(folderId)
+ .ifPresent(folderRepo::delete);
} else if (objectId.startsWith("asset-")) {
final long assetId = Long.parseLong(
- objectId.substring("asset-".length()));
+ objectId.substring("asset-".length()));
assetRepo
- .findById(assetId)
- .ifPresent(assetRepo::delete);
+ .findById(assetId)
+ .ifPresent(assetRepo::delete);
} else {
throw new IllegalArgumentException(
- "The objectId is expected to start with 'folder-' or 'item.'.");
+ "The objectId is expected to start with 'folder-' or 'item.'.");
}
}
@@ -218,15 +452,15 @@ public class AssetFolderBrowserController {
row.setObjectUuid(folder.getUuid());
row.setName(folder.getName());
if (folder.getTitle().hasValue(globalizationHelper
- .getNegotiatedLocale())) {
+ .getNegotiatedLocale())) {
row.setTitle(folder.getTitle().getValue(globalizationHelper
- .getNegotiatedLocale()));
+ .getNegotiatedLocale()));
} else {
row.setTitle(folder.getTitle().getValue(defaultLocale));
}
row.setFolder(true);
row.setDeletable(!categoryManager.hasSubCategories(folder)
- && !categoryManager.hasObjects(folder));
+ && !categoryManager.hasObjects(folder));
return row;
}
@@ -239,23 +473,26 @@ public class AssetFolderBrowserController {
row.setObjectUuid(asset.getUuid());
row.setName(asset.getDisplayName());
if (asset.getTitle().hasValue(globalizationHelper
- .getNegotiatedLocale())) {
+ .getNegotiatedLocale())) {
row.setTitle(asset.getTitle().getValue(globalizationHelper
- .getNegotiatedLocale()));
+ .getNegotiatedLocale()));
} else {
row.setTitle(asset.getTitle().getValue(defaultLocale));
}
final AssetTypeInfo typeInfo = typesManager
- .getAssetTypeInfo(asset.getClass());
+ .getAssetTypeInfo(asset.getClass());
row.setTypeLabelBundle(typeInfo.getLabelBundle());
row.setTypeLabelKey(typeInfo.getLabelKey());
row.setFolder(false);
+ row.setDeletable(!assetManager.isAssetInUse(asset));
+
return row;
}
private List findSubFolders(final Folder folder,
+ final String filterTerm,
final String orderBy,
final String orderDirection,
final int firstResult,
@@ -264,26 +501,30 @@ public class AssetFolderBrowserController {
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
final CriteriaQuery criteria = builder
- .createQuery(Folder.class);
+ .createQuery(Folder.class);
final Root from = criteria.from(Folder.class);
final Order order;
if (AssetFolderBrowser.SORT_KEY_NAME.equals(orderBy)
- && AssetFolderBrowser.SORT_ACTION_DOWN.
- equals(orderDirection)) {
+ && AssetFolderBrowser.SORT_ACTION_DOWN.
+ equals(orderDirection)) {
order = builder.desc(from.get("name"));
} else {
order = builder.asc(from.get("name"));
}
final TypedQuery query = entityManager
- .createQuery(
- criteria.where(
- builder.
- equal(from.get("parentCategory"), folder)
- )
- .orderBy(order)
- );
+ .createQuery(
+ criteria.where(
+ builder.and(
+ builder.equal(from.get("parentCategory"),
+ folder),
+ builder.like(builder.lower(from.get("name")),
+ filterTerm)
+ )
+ )
+ .orderBy(order)
+ );
if (firstResult >= 0) {
query.setFirstResult(firstResult);
@@ -297,6 +538,7 @@ public class AssetFolderBrowserController {
}
private List findAssetsInFolder(final Folder folder,
+ final String filterTerm,
final String orderBy,
final String orderDirection,
final int firstResult,
@@ -332,18 +574,21 @@ public class AssetFolderBrowserController {
}
final TypedQuery query = entityManager
- .createQuery(
- criteria.select(fromAsset)
- .where(
- builder.and(
- builder.equal(join.get(
- "category"), folder),
- builder.equal(join.get("type"),
- CmsConstants.CATEGORIZATION_TYPE_FOLDER)
- )
- )
- .orderBy(order)
- );
+ .createQuery(
+ criteria.select(fromAsset)
+ .where(
+ builder.and(
+ builder.equal(join.get(
+ "category"), folder),
+ builder.equal(join.get("type"),
+ CmsConstants.CATEGORIZATION_TYPE_FOLDER),
+ builder.like(builder.lower(fromAsset.get(
+ "displayName")),
+ filterTerm)
+ )
+ )
+ .orderBy(order)
+ );
if (firstResult >= 0) {
query.setFirstResult(firstResult);
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetPane.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetPane.java
index 446b5572d..abfb073c2 100644
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetPane.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/assets/AssetPane.java
@@ -19,27 +19,35 @@
package com.arsdigita.cms.ui.assets;
import com.arsdigita.bebop.ActionLink;
+import com.arsdigita.bebop.BoxPanel;
import com.arsdigita.bebop.Component;
+import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.FormData;
import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.Paginator;
+import com.arsdigita.bebop.RequestLocal;
import com.arsdigita.bebop.Resettable;
+import com.arsdigita.bebop.SaveCancelSection;
import com.arsdigita.bebop.SegmentedPanel;
import com.arsdigita.bebop.SimpleContainer;
import com.arsdigita.bebop.SingleSelectionModel;
import com.arsdigita.bebop.Table;
import com.arsdigita.bebop.Text;
+import com.arsdigita.bebop.Tree;
import com.arsdigita.bebop.event.ActionEvent;
import com.arsdigita.bebop.event.ActionListener;
import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.event.FormSubmissionListener;
+import com.arsdigita.bebop.event.FormValidationListener;
import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.event.PrintListener;
import com.arsdigita.bebop.form.CheckboxGroup;
+import com.arsdigita.bebop.form.FormErrorDisplay;
import com.arsdigita.bebop.form.Option;
import com.arsdigita.bebop.form.SingleSelect;
import com.arsdigita.bebop.form.Submit;
@@ -47,6 +55,7 @@ import com.arsdigita.bebop.parameters.ArrayParameter;
import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.table.TableCellRenderer;
import com.arsdigita.bebop.table.TableColumn;
+import com.arsdigita.bebop.tree.TreeCellRenderer;
import com.arsdigita.cms.CMS;
import com.arsdigita.cms.ui.BaseTree;
import com.arsdigita.cms.ui.folder.FolderCreateForm;
@@ -59,6 +68,8 @@ import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.toolbox.ui.ActionGroup;
import com.arsdigita.toolbox.ui.LayoutPanel;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.libreccm.categorization.Category;
import org.libreccm.cdi.utils.CdiUtil;
import org.librecms.CmsConstants;
@@ -68,8 +79,21 @@ import org.librecms.contentsection.Folder;
import java.util.List;
import org.arsdigita.cms.CMSConfig;
+import org.libreccm.categorization.CategoryManager;
+import org.libreccm.core.CcmObject;
+import org.libreccm.core.UnexpectedErrorException;
+import org.libreccm.security.PermissionChecker;
+import org.librecms.contentsection.Asset;
+import org.librecms.contentsection.AssetManager;
+import org.librecms.contentsection.AssetRepository;
+import org.librecms.contentsection.FolderManager;
+import org.librecms.contentsection.FolderRepository;
+import org.librecms.contentsection.privileges.ItemPrivileges;
-import javax.swing.CellRendererPane;
+import java.util.Arrays;
+import java.util.Objects;
+
+import static org.librecms.CmsConstants.*;
/**
*
@@ -77,6 +101,8 @@ import javax.swing.CellRendererPane;
*/
public class AssetPane extends LayoutPanel implements Resettable {
+ private static final Logger LOGGER = LogManager.getLogger(AssetPane.class);
+
public static final String SET_FOLDER = "set_folder";
private static final String SOURCES_PARAM = "sources";
private static final String ACTION_PARAM = "action";
@@ -89,10 +115,14 @@ public class AssetPane extends LayoutPanel implements Resettable {
private final FolderRequestLocal folderRequestLocal;
private final ArrayParameter sourcesParameter = new ArrayParameter(
new StringParameter(SOURCES_PARAM));
+ private final StringParameter actionParameter = new StringParameter(
+ ACTION_PARAM);
private AssetFolderBrowser folderBrowser;
+ private Form browserForm;
private SingleSelect actionSelect;
private Submit actionSubmit;
+ private TargetSelector targetSelector;
private SegmentedPanel.Segment browseSegment;
private SegmentedPanel.Segment currentFolderSegment;
@@ -145,8 +175,8 @@ public class AssetPane extends LayoutPanel implements Resettable {
final SegmentedPanel panel = new SegmentedPanel();
browseSegment = panel.addSegment();
- final Form browserForm = new Form("assetFolderBrowser",
- new SimpleContainer());
+ browserForm = new Form("assetFolderBrowser",
+ new SimpleContainer());
browserForm.setMethod(Form.GET);
folderBrowser = new AssetFolderBrowser(folderSelectionModel);
final Paginator paginator = new Paginator(
@@ -186,7 +216,7 @@ public class AssetPane extends LayoutPanel implements Resettable {
new GlobalizedMessage(
"cms.ui.folder.edit_selection",
CmsConstants.CMS_FOLDER_BUNDLE)));
- actionSelect = new SingleSelect(ACTION_PARAM);
+ actionSelect = new SingleSelect(actionParameter);
actionSelect.addOption(
new Option(COPY,
new Label(new GlobalizedMessage(
@@ -203,8 +233,68 @@ public class AssetPane extends LayoutPanel implements Resettable {
new GlobalizedMessage("cms.ui.folder.go",
CmsConstants.CMS_FOLDER_BUNDLE));
actionFormContainer.add(actionSubmit);
+ browserForm.addProcessListener(new FormProcessListener() {
+
+ @Override
+ public void process(final FormSectionEvent event)
+ throws FormProcessException {
+
+ final PageState state = event.getPageState();
+
+ moveCopyMode(state);
+
+ }
+
+ });
browserForm.add(actionFormContainer);
+ targetSelector = new TargetSelector();
+ targetSelector.addProcessListener(new FormProcessListener() {
+
+ @Override
+ public void process(final FormSectionEvent event)
+ throws FormProcessException {
+
+ final PageState state = event.getPageState();
+
+ browseMode(state);
+ targetSelector.setVisible(state, false);
+
+ final Folder folder = targetSelector.getTarget(state);
+ final String[] objectIds = getSources(state);
+
+ if (isCopy(state)) {
+ copyObjects(folder, objectIds);
+ } else if (isMove(state)) {
+ moveObjects(folder, objectIds);
+ }
+
+ reset(state);
+ }
+
+ });
+ targetSelector.addValidationListener(
+ new TargetSelectorValidationListener());
+ targetSelector.addSubmissionListener(new FormSubmissionListener() {
+
+ @Override
+ public void submitted(final FormSectionEvent event)
+ throws FormProcessException {
+
+ final PageState state = event.getPageState();
+
+ if (targetSelector.isCancelled(state)) {
+ reset(state);
+ browseMode(state);
+ throw new FormProcessException(new GlobalizedMessage(
+ "cms.ui.folder.cancelled",
+ CmsConstants.CMS_FOLDER_BUNDLE));
+ }
+ }
+
+ });
+ browseSegment.add(targetSelector);
+
// browseSegment.add(paginator);
// browseSegment.add(folderBrowser);
browseSegment.add(browserForm);
@@ -352,22 +442,44 @@ public class AssetPane extends LayoutPanel implements Resettable {
}
protected void browseMode(final PageState state) {
+ tree.setVisible(state, true);
browseSegment.setVisible(state, true);
+ folderBrowser.setVisible(state, true);
+ browserForm.setVisible(state, true);
+ targetSelector.setVisible(state, false);
actionsSegment.setVisible(state, true);
newFolderSegment.setVisible(state, false);
editFolderSegment.setVisible(state, false);
}
+ protected void moveCopyMode(final PageState state) {
+ tree.setVisible(state, false);
+ browseSegment.setVisible(state, true);
+ folderBrowser.setVisible(state, false);
+ browserForm.setVisible(state, false);
+ targetSelector.setVisible(state, true);
+ actionsSegment.setVisible(state, false);
+ newFolderSegment.setVisible(state, false);
+ editFolderSegment.setVisible(state, false);
+ targetSelector.expose(state);
+ }
+
protected void newFolderMode(final PageState state) {
+ tree.setVisible(state, false);
browseSegment.setVisible(state, false);
+ folderBrowser.setVisible(state, false);
+ browserForm.setVisible(state, false);
+ targetSelector.setVisible(state, false);
actionsSegment.setVisible(state, false);
newFolderSegment.setVisible(state, true);
editFolderSegment.setVisible(state, false);
}
protected void editFolderMode(final PageState state) {
+ tree.setVisible(state, false);
browseSegment.setVisible(state, false);
+ targetSelector.setVisible(state, false);
actionsSegment.setVisible(state, false);
newFolderSegment.setVisible(state, false);
editFolderSegment.setVisible(state, true);
@@ -381,10 +493,17 @@ public class AssetPane extends LayoutPanel implements Resettable {
page.addActionListener(new TreeListener());
page.addActionListener(new FolderListener());
+ page.setVisibleDefault(tree, true);
page.setVisibleDefault(browseSegment, true);
+ page.setVisibleDefault(folderBrowser, true);
+ page.setVisibleDefault(browserForm, true);
+ page.setVisibleDefault(targetSelector, false);
page.setVisibleDefault(actionsSegment, true);
page.setVisibleDefault(newFolderSegment, false);
page.setVisibleDefault(editFolderSegment, false);
+
+ page.addComponentStateParam(this, actionParameter);
+ page.addComponentStateParam(this, sourcesParameter);
}
@Override
@@ -394,6 +513,50 @@ public class AssetPane extends LayoutPanel implements Resettable {
folderBrowser.getPaginator().reset(state);
+ state.setValue(actionParameter, null);
+ state.setValue(sourcesParameter, null);
+
+ }
+
+ private String[] getSources(final PageState state) {
+
+ final String[] result = (String[]) state.getValue(sourcesParameter);
+
+ if (result == null) {
+ return new String[0];
+ } else {
+ return result;
+ }
+ }
+
+ protected final boolean isMove(final PageState state) {
+ return MOVE.equals(getAction(state));
+ }
+
+ protected final boolean isCopy(final PageState state) {
+ return COPY.equals(getAction(state));
+ }
+
+ private String getAction(final PageState state) {
+ return (String) state.getValue(actionParameter);
+ }
+
+ protected void moveObjects(final Folder target, final String[] objectIds) {
+
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final AssetFolderBrowserController controller = cdiUtil.findBean(
+ AssetFolderBrowserController.class);
+
+ controller.moveObjects(target, objectIds);
+ }
+
+ protected void copyObjects(final Folder target, final String[] objectIds) {
+
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final AssetFolderBrowserController controller = cdiUtil.findBean(
+ AssetFolderBrowserController.class);
+
+ controller.copyObjects(target, objectIds);
}
private final class FolderListener implements ActionListener {
@@ -452,4 +615,385 @@ public class AssetPane extends LayoutPanel implements Resettable {
}
+ private class TargetSelector extends Form implements Resettable {
+
+ private final FolderSelectionModel targetFolderModel;
+ private final AssetFolderTree folderTree;
+ private final Submit cancelButton;
+
+ public TargetSelector() {
+ super("targetSelector", new BoxPanel());
+ setMethod(GET);
+ targetFolderModel = new FolderSelectionModel("target") {
+
+ @Override
+ protected Long getRootFolderID(final PageState state) {
+ final ContentSection section = CMS
+ .getContext()
+ .getContentSection();
+ return section.getRootAssetsFolder().getObjectId();
+ }
+
+ };
+ folderTree = new AssetFolderTree(targetFolderModel);
+
+ folderTree.setCellRenderer(new FolderTreeCellRenderer());
+
+ final Label label = new Label(new PrintListener() {
+
+ @Override
+ public void prepare(final PrintEvent event) {
+
+ final PageState state = event.getPageState();
+ final Label label = (Label) event.getTarget();
+ final int numberOfItems = getSources(state).length;
+ final Category folder = (Category) folderSelectionModel
+ .getSelectedObject(state);
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final CategoryManager categoryManager = cdiUtil
+ .findBean(CategoryManager.class);
+
+ if (isMove(state)) {
+ label.setLabel(new GlobalizedMessage(
+ "cms.ui.folder.move",
+ CmsConstants.CMS_FOLDER_BUNDLE,
+ new Object[]{numberOfItems,
+ categoryManager.getCategoryPath(folder)}));
+ } else if (isCopy(state)) {
+ label.setLabel(new GlobalizedMessage(
+ "cms.ui.folder.copy",
+ CMS_BUNDLE,
+ new Object[]{numberOfItems,
+ categoryManager.getCategoryPath(
+ folder)}));
+ }
+ }
+
+ });
+
+ label.setOutputEscaping(false);
+ add(label);
+ add(folderTree);
+ add(new FormErrorDisplay(this));
+ final SaveCancelSection saveCancelSection = new SaveCancelSection();
+ cancelButton = saveCancelSection.getCancelButton();
+ add(saveCancelSection);
+ }
+
+ @Override
+ public void register(final Page page) {
+ super.register(page);
+ page.addComponentStateParam(this, targetFolderModel
+ .getStateParameter());
+ }
+
+ public void expose(final PageState state) {
+
+ final Folder folder = folderSelectionModel.getSelectedObject(state);
+ targetFolderModel.clearSelection(state);
+ if (folder != null) {
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final FolderManager folderManager = cdiUtil.findBean(
+ FolderManager.class);
+ if (!folderManager.getParentFolder(folder).isPresent()) {
+ folderTree.expand(Long.toString(folder.getObjectId()),
+ state);
+ } else {
+ final List parents = folderManager
+ .getParentFolders(folder);
+ parents
+ .stream()
+ .map(parent -> Long.toString(parent.getObjectId()))
+ .forEach(folderId -> folderTree.expand(folderId, state));
+ }
+ }
+ }
+
+ @Override
+ public void reset(final PageState state) {
+ folderTree.clearSelection(state);
+ state.setValue(folderTree.getSelectionModel().getStateParameter(),
+ null);
+ }
+
+ public Folder getTarget(final PageState state) {
+ return targetFolderModel.getSelectedObject(state);
+ }
+
+ public boolean isCancelled(final PageState state) {
+ return cancelButton.isSelected(state);
+ }
+
+ }
+
+ private class FolderTreeCellRenderer implements TreeCellRenderer {
+
+ private final RequestLocal invalidFoldersRequestLocal
+ = new RequestLocal();
+
+ /**
+ * Render the folders appropriately. The selected folder is a bold
+ * label. Invalid folders are plain labels. Unselected, valid folders
+ * are control links. Invalid folders are: the parent folder of the
+ * sources, any of the sources, and any subfolders of the sources.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public Component getComponent(final Tree tree,
+ final PageState state,
+ final Object value,
+ final boolean isSelected,
+ final boolean isExpanded,
+ final boolean isLeaf,
+ final Object key) {
+
+ // Get the list of invalid folders once per request.
+ final List invalidFolders;
+
+ if (invalidFoldersRequestLocal.get(state) == null) {
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final AssetFolderBrowserController controller = cdiUtil
+ .findBean(AssetFolderBrowserController.class);
+ invalidFolders = controller.createInvalidTargetsList(
+ Arrays.asList(getSources(state)));
+ invalidFoldersRequestLocal.set(state, invalidFolders);
+ } else {
+ invalidFolders = (List) invalidFoldersRequestLocal
+ .get(state);
+ }
+ final Label label = new Label(value.toString());
+
+ if (invalidFolders.contains(String.format(
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER + "%s", key))) {
+ return label;
+ }
+
+ // Bold if selected
+ if (isSelected) {
+ label.setFontWeight(Label.BOLD);
+ return label;
+ }
+
+ return new ControlLink(label);
+ }
+
+ }
+
+ private class TargetSelectorValidationListener
+ implements FormValidationListener {
+
+ @Override
+ public void validate(final FormSectionEvent event)
+ throws FormProcessException {
+
+ final PageState state = event.getPageState();
+
+ if (getSources(state).length <= 0) {
+ throw new IllegalStateException("No source items specified");
+ }
+
+ final Folder target = targetSelector.getTarget(state);
+ final FormData data = event.getFormData();
+ if (target == null) {
+ data.addError(new GlobalizedMessage(
+ "cms.ui.folder.need_select_target_folder",
+ CmsConstants.CMS_FOLDER_BUNDLE));
+ //If the target is null, we can skip the rest of the checks
+ return;
+ }
+
+ if (target.equals(folderSelectionModel.getSelectedObject(state))) {
+ data.addError(new GlobalizedMessage(
+ "cms.ui.folder.not_within_same_folder",
+ CmsConstants.CMS_FOLDER_BUNDLE));
+ }
+
+ // check create item permission
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final PermissionChecker permissionChecker = cdiUtil.findBean(
+ PermissionChecker.class);
+ if (!permissionChecker.isPermitted(
+ ItemPrivileges.CREATE_NEW, target)) {
+ data.addError("cms.ui.folder.no_permission_for_item",
+ CmsConstants.CMS_FOLDER_BUNDLE);
+ }
+
+ for (String source : getSources(state)) {
+
+ validateObject(source, target, state, data);
+
+ }
+ }
+
+ private void validateObject(final String objectId,
+ final Folder target,
+ final PageState state,
+ final FormData data) {
+
+ Objects.requireNonNull(objectId, "objectId can't be null.");
+
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final FolderRepository folderRepo = cdiUtil
+ .findBean(FolderRepository.class);
+ final AssetRepository assetRepo = cdiUtil
+ .findBean(AssetRepository.class);
+ final AssetManager assetManager = cdiUtil
+ .findBean(AssetManager.class);
+ final AssetFolderBrowserController controller = cdiUtil
+ .findBean(AssetFolderBrowserController.class);
+ final FolderManager folderManager = cdiUtil
+ .findBean(FolderManager.class);
+ final PermissionChecker permissionChecker = cdiUtil.findBean(
+ PermissionChecker.class);
+
+ final CcmObject object;
+ final String name;
+ if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
+
+ final long folderId = Long.parseLong(objectId.substring(
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
+ final Folder folder = folderRepo.findById(folderId).orElseThrow(
+ () -> new IllegalArgumentException(String.format(
+ "No folder with id %d in database.", folderId)));
+
+ name = folder.getName();
+
+ //Check if folder or subfolder contains in use assets
+ if (isMove(state)) {
+ final FolderManager.FolderIsMovable movable = folderManager
+ .folderIsMovable(folder, target);
+ switch (movable) {
+ case DIFFERENT_SECTIONS:
+ addErrorMessage(data,
+ "cms.ui.folder.different_sections",
+ name);
+ break;
+ case HAS_IN_USE_ASSETS:
+ addErrorMessage(data,
+ "cms.ui.folder.has_in_use_assets",
+ name);
+ break;
+ case DIFFERENT_TYPES:
+ addErrorMessage(data,
+ "cms.ui.folder.different_folder_types",
+ name);
+ break;
+ case IS_ROOT_FOLDER:
+ addErrorMessage(data,
+ "cms.ui.folder.is_root_folder",
+ name);
+ break;
+ case SAME_FOLDER:
+ addErrorMessage(data,
+ "cms.ui.folder.same_folder",
+ name);
+ break;
+ case YES:
+ //Nothing
+ break;
+ default:
+ throw new UnexpectedErrorException(String.format(
+ "Unknown state '%s' for '%s'.",
+ movable,
+ FolderManager.FolderIsMovable.class.getName()));
+ }
+ }
+
+ object = folder;
+ } else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ASSET)) {
+ final long assetId = Long.parseLong(objectId.substring(
+ FOLDER_BROWSER_KEY_PREFIX_ASSET.length()));
+ final Asset asset = assetRepo
+ .findById(assetId)
+ .orElseThrow(() -> new IllegalArgumentException(
+ String.format(
+ "No asset with id %d in the database.",
+ assetId)));
+
+ name = asset.getDisplayName();
+
+ if (isMove(state) && assetManager.isAssetInUse(asset)) {
+ addErrorMessage(data, "cms.ui.folder.item_is_live", name);
+ }
+
+ object = asset;
+ } else {
+ throw new IllegalArgumentException(String.format(
+ "Provided objectId '%s' does not start with '%s' "
+ + "or '%s'.",
+ objectId,
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER,
+ FOLDER_BROWSER_KEY_PREFIX_ASSET));
+ }
+
+ final long count = controller.countObjects(target, name);
+ if (count > 0) {
+ // there is an item or subfolder in the target folder that already has this name
+ addErrorMessage(data, "cms.ui.folder.item_already_exists",
+ name);
+ }
+
+ if (!(permissionChecker.isPermitted(
+ ItemPrivileges.DELETE, object))
+ && isMove(state)) {
+ addErrorMessage(data,
+ "cms.ui.folder.no_permission_for_item",
+ object.getDisplayName());
+ }
+
+ }
+
+ }
+
+ private void addErrorMessage(final FormData data,
+ final String message,
+ final String itemName) {
+ data.addError(new GlobalizedMessage(message,
+ CmsConstants.CMS_FOLDER_BUNDLE,
+ new Object[]{itemName}));
+ }
+
+ private class AssetFolderTree extends Tree {
+
+ public AssetFolderTree(final FolderSelectionModel folderSelectionModel) {
+
+ super(new FolderTreeModelBuilder() {
+
+ @Override
+ protected Folder getRootFolder(final PageState state) {
+ final ContentSection section = CMS
+ .getContext()
+ .getContentSection();
+
+ return section.getRootAssetsFolder();
+ }
+
+ });
+ setSelectionModel(selectionModel);
+ }
+
+ @Override
+ public void setSelectedKey(final PageState state, final Object key) {
+ if (key instanceof String) {
+ final Long keyAsLong;
+ if (((String) key).startsWith(
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER)) {
+ keyAsLong = Long.parseLong(((String) key).substring(
+ FOLDER_BROWSER_KEY_PREFIX_FOLDER.length()));
+ } else {
+ keyAsLong = Long.parseLong((String) key);
+ }
+ super.setSelectedKey(state, keyAsLong);
+ } else if (key instanceof Long) {
+ super.setSelectedKey(state, key);
+ } else {
+ //We know that a FolderSelectionModel only takes keys of type Long.
+ //Therefore we try to cast here
+ final Long keyAsLong = (Long) key;
+ super.setSelectedKey(state, keyAsLong);
+ }
+ }
+
+ }
+
}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserController.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserController.java
index a55b0a1f1..ce35cd74d 100644
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserController.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderBrowserController.java
@@ -573,7 +573,7 @@ public class FolderBrowserController {
CmsConstants.CATEGORIZATION_TYPE_FOLDER),
builder.equal(fromItem.get("version"),
ContentItemVersion.DRAFT),
- builder.like(fromItem.get("displayName"),
+ builder.like(builder.lower(fromItem.get("displayName")),
filterTerm)
)
)
@@ -780,7 +780,8 @@ public class FolderBrowserController {
Objects.requireNonNull(targetFolder);
- final ContentItem item = itemRepo.findById(itemId)
+ final ContentItem item = itemRepo
+ .findById(itemId)
.orElseThrow(() -> new IllegalArgumentException(String.format(
"No content item with ID %d in the database. "
+ "Where did that ID come from?",
@@ -834,7 +835,8 @@ public class FolderBrowserController {
Objects.requireNonNull(targetFolder);
- final ContentItem item = itemRepo.findById(itemId)
+ final ContentItem item = itemRepo
+ .findById(itemId)
.orElseThrow(() -> new IllegalArgumentException(String.format(
"No content item with ID %d in the database. "
+ "Where did that ID come from?",
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java
index 3b4cb8152..d97fb7399 100755
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderManipulator.java
@@ -414,8 +414,8 @@ public class FolderManipulator extends SimpleContainer implements
}
@Override
- public void process(final FormSectionEvent event) throws
- FormProcessException {
+ public void process(final FormSectionEvent event)
+ throws FormProcessException {
final PageState state = event.getPageState();
@@ -484,16 +484,16 @@ public class FolderManipulator extends SimpleContainer implements
}
- private class TargetSelectorValidationListener implements
- FormValidationListener {
+ private class TargetSelectorValidationListener
+ implements FormValidationListener {
public TargetSelectorValidationListener() {
//Nothing
}
@Override
- public void validate(final FormSectionEvent event) throws
- FormProcessException {
+ public void validate(final FormSectionEvent event)
+ throws FormProcessException {
final PageState state = event.getPageState();
@@ -519,7 +519,6 @@ public class FolderManipulator extends SimpleContainer implements
// check create item permission
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
- final Shiro shiro = cdiUtil.findBean(Shiro.class);
final PermissionChecker permissionChecker = cdiUtil.findBean(
PermissionChecker.class);
if (!permissionChecker.isPermitted(
@@ -574,28 +573,28 @@ public class FolderManipulator extends SimpleContainer implements
.folderIsMovable(folder, target);
switch (movable) {
case DIFFERENT_SECTIONS:
- addErrorMessage(data,
- "cms.ui.folder.different_sections",
+ addErrorMessage(data,
+ "cms.ui.folder.different_sections",
name);
break;
case HAS_LIVE_ITEMS:
- addErrorMessage(data,
- "cms.ui.folder.item_is_live",
+ addErrorMessage(data,
+ "cms.ui.folder.item_is_live",
name);
break;
case DIFFERENT_TYPES:
- addErrorMessage(data,
- "cms.ui.folder.different_folder_types",
+ addErrorMessage(data,
+ "cms.ui.folder.different_folder_types",
name);
break;
case IS_ROOT_FOLDER:
- addErrorMessage(data,
- "cms.ui.folder.is_root_folder",
+ addErrorMessage(data,
+ "cms.ui.folder.is_root_folder",
name);
break;
case SAME_FOLDER:
- addErrorMessage(data,
- "cms.ui.folder.same_folder",
+ addErrorMessage(data,
+ "cms.ui.folder.same_folder",
name);
break;
case YES:
@@ -614,8 +613,10 @@ public class FolderManipulator extends SimpleContainer implements
} else if (objectId.startsWith(FOLDER_BROWSER_KEY_PREFIX_ITEM)) {
final long itemId = Long.parseLong(objectId.substring(
FOLDER_BROWSER_KEY_PREFIX_ITEM.length()));
- final ContentItem item = itemRepo.findById(itemId).orElseThrow(
- () -> new IllegalArgumentException(String.format(
+ final ContentItem item = itemRepo
+ .findById(itemId)
+ .orElseThrow(() -> new IllegalArgumentException(
+ String.format(
"No content item with id %d in database.",
itemId)));
@@ -726,11 +727,11 @@ public class FolderManipulator extends SimpleContainer implements
@Override
protected Long getRootFolderID(final PageState state) {
final ContentSection section = CMS
- .getContext()
- .getContentSection();
+ .getContext()
+ .getContentSection();
return section.getRootDocumentsFolder().getObjectId();
}
-
+
};
folderTree = new FolderTree(targetModel);
folderTree.setCellRenderer(new FolderTreeCellRenderer());
@@ -742,8 +743,8 @@ public class FolderManipulator extends SimpleContainer implements
final PageState state = event.getPageState();
final Label label = (Label) event.getTarget();
final int numberOfItems = getSources(state).length;
- final Category folder = (Category) sourceFolderModel.
- getSelectedObject(state);
+ final Category folder = (Category) sourceFolderModel
+ .getSelectedObject(state);
final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
final CategoryManager categoryManager = cdiUtil.
findBean(CategoryManager.class);
@@ -795,9 +796,10 @@ public class FolderManipulator extends SimpleContainer implements
folderTree.expand(Long.toString(folder.getObjectId()),
state);
} else {
- final List parents = folderManager.getParentFolders(
- folder);
- parents.stream()
+ final List parents = folderManager
+ .getParentFolders(folder);
+ parents
+ .stream()
.map(parent -> Long.toString(parent.getObjectId()))
.forEach(folderId -> folderTree.expand(folderId, state));
}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderTree.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderTree.java
index 6af564622..ccc1687a7 100755
--- a/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderTree.java
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/folder/FolderTree.java
@@ -60,7 +60,7 @@ public class FolderTree extends Tree {
} else if (key instanceof Long) {
super.setSelectedKey(state, key);
} else {
- //We now that a FolderSelectionModel only takes keys of type Long.
+ //We know that a FolderSelectionModel only takes keys of type Long.
//Therefore we try to cast here
final Long keyAsLong = (Long) key;
super.setSelectedKey(state, keyAsLong);
diff --git a/ccm-cms/src/main/java/org/librecms/CmsConstants.java b/ccm-cms/src/main/java/org/librecms/CmsConstants.java
index 7618ba8f5..662f9a92d 100644
--- a/ccm-cms/src/main/java/org/librecms/CmsConstants.java
+++ b/ccm-cms/src/main/java/org/librecms/CmsConstants.java
@@ -55,6 +55,7 @@ public class CmsConstants {
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_ASSET = "asset-";
public static final String FOLDER_BROWSER_KEY_PREFIX_ITEM = "item-";
/**
diff --git a/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java b/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java
index ab3c368a4..d334162e2 100644
--- a/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java
+++ b/ccm-cms/src/main/java/org/librecms/contentsection/FolderManager.java
@@ -62,10 +62,13 @@ public class FolderManager {
@Inject
private ContentItemRepository itemRepo;
-
+
@Inject
private ContentItemManager itemManager;
+ @Inject
+ private AssetManager assetManager;
+
/**
* An enum describing if a folder can be deleted or not and why.
*
@@ -118,7 +121,11 @@ public class FolderManager {
/**
* The folder to move contains live items.
*/
- HAS_LIVE_ITEMS
+ HAS_LIVE_ITEMS,
+ /**
+ * The folder to contains assets which are in use.
+ */
+ HAS_IN_USE_ASSETS
}
@Transactional(Transactional.TxType.REQUIRED)
@@ -368,6 +375,10 @@ public class FolderManager {
return FolderIsMovable.HAS_LIVE_ITEMS;
}
+ if (usedAssetsInFolder(movingFolder)) {
+ return FolderIsMovable.HAS_IN_USE_ASSETS;
+ }
+
return FolderIsMovable.YES;
}
@@ -379,17 +390,17 @@ public class FolderManager {
final Folder copiedFolder = folderRepo.findById(
folder.getObjectId())
- .orElseThrow(() -> new IllegalArgumentException(String.format(
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
"No folder with ID %d in the database. Where did that ID come from?",
folder.getObjectId())));
final Folder targetFolder = folderRepo.findById(
target.getObjectId())
- .orElseThrow(() -> new IllegalArgumentException(String.format(
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
"No folder with ID %d in the database. Where did that ID come from?",
target.getObjectId())));
-
+
final Folder copy = createFolder(copiedFolder.getName(), targetFolder);
-
+
final List items = itemRepo.findByFolder(copiedFolder);
// final List items = copiedFolder.getObjects()
// .stream()
@@ -434,6 +445,22 @@ public class FolderManager {
return liveItemsInFolder || liveItemsInSubFolders;
}
+ private boolean usedAssetsInFolder(final Folder folder) {
+
+ final boolean usedAssetsInFolder = folder.getObjects()
+ .stream()
+ .map(categorization -> categorization.getCategorizedObject())
+ .filter(object -> object instanceof Asset)
+ .map(asset -> (Asset) asset)
+ .anyMatch(asset -> assetManager.isAssetInUse(asset));
+
+ final boolean usedAssetsInSubFolders = folder.getSubFolders()
+ .stream()
+ .anyMatch(subFolder -> usedAssetsInFolder(folder));
+
+ return usedAssetsInFolder || usedAssetsInSubFolders;
+ }
+
/**
* Returns the path of folder.
*