diff --git a/ccm-cms/pom.xml b/ccm-cms/pom.xml
index 72d67dddd..b30300f00 100644
--- a/ccm-cms/pom.xml
+++ b/ccm-cms/pom.xml
@@ -71,6 +71,25 @@
provided
+
+
+ org.apache.logging.log4j
+ log4j-core
+
+
+ org.apache.logging.log4j
+ log4j-api
+
+
+
+ org.apache.shiro
+ shiro-core
+
+
+ org.apache.shiro
+ shiro-web
+
+
junit
junit
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 d1d717fb9..79ef9a5ee 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
@@ -43,7 +43,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
-import java.util.Optional;
import java.util.stream.Collectors;
@@ -87,6 +86,12 @@ public class FolderBrowserController {
@Inject
private GlobalizationHelper globalizationHelper;
+ @Inject
+ private ContentItemRepository itemRepo;
+
+ @Inject
+ private ContentItemManager itemManager;
+
@Inject
private ContentItemL10NManager itemL10NManager;
@@ -96,12 +101,6 @@ public class FolderBrowserController {
@Inject
private FolderRepository folderRepo;
- @Inject
- private ContentItemRepository itemRepo;
-
- @Inject
- private ContentItemManager itemManager;
-
private Locale defaultLocale;
/**
@@ -406,14 +405,14 @@ public class FolderBrowserController {
if (objectId.startsWith("folder-")) {
final long folderId = Long.parseLong(
objectId.substring("folder-".length()));
-
+
folderRepo
.findById(folderId)
.ifPresent(folderRepo::delete);
} else if (objectId.startsWith("item-")) {
final long itemId = Long.parseLong(
- objectId.substring("item-".length()));
-
+ objectId.substring("item-".length()));
+
itemRepo
.findById(itemId)
.ifPresent(itemRepo::delete);
@@ -570,4 +569,41 @@ public class FolderBrowserController {
return query.getResultList();
}
+ /**
+ * Check if a folder or its subfolders contains at least one live item.
+ *
+ * @param folder The folder to check for live items.
+ *
+ * @return {@code true} if the {@code folder} or on of its subfolders
+ * contains at least one live item, {@code false} otherwise.
+ */
+ @Transactional(Transactional.TxType.REQUIRED)
+ public boolean hasLiveItems(final Folder folder) {
+
+ Objects.requireNonNull(folder,
+ "Folder to check for live items can't be null.");
+
+ //Ensure that we use an non detached entity.
+ final Folder theFolder = folderRepo.findById(folder.getObjectId())
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ "No folder with id %s in the database. Where did that ID come from?",
+ folder.getObjectId())));
+
+ final boolean hasLiveItem = theFolder.getObjects()
+ .stream()
+ .map(categorization -> categorization.getCategorizedObject())
+ .filter(object -> object instanceof ContentItem)
+ .map(object -> (ContentItem) object)
+ .filter(item -> itemManager.isLive(item))
+ .anyMatch(item -> itemManager.isLive(item));
+
+ if (hasLiveItem) {
+ return true;
+ } else {
+ return theFolder.getSubFolders()
+ .stream()
+ .anyMatch(currentFolder -> hasLiveItems(currentFolder));
+ }
+ }
+
}
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 7a4b79854..eedfe8c62 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
@@ -30,7 +30,6 @@ import com.arsdigita.bebop.GridPanel;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.Page;
import com.arsdigita.bebop.PageState;
-import com.arsdigita.bebop.PaginationModelBuilder;
import com.arsdigita.bebop.Paginator;
import com.arsdigita.bebop.RequestLocal;
import com.arsdigita.bebop.Resettable;
@@ -56,46 +55,36 @@ import com.arsdigita.bebop.form.SingleSelect;
import com.arsdigita.bebop.form.Submit;
import com.arsdigita.bebop.form.TextField;
import com.arsdigita.bebop.parameters.ArrayParameter;
-import com.arsdigita.bebop.parameters.BigDecimalParameter;
+import com.arsdigita.bebop.parameters.LongParameter;
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.globalization.GlobalizedMessage;
import com.arsdigita.toolbox.ui.ActionGroup;
-import com.arsdigita.util.Assert;
-import com.arsdigita.util.UncheckedWrapperException;
-import com.arsdigita.web.Web;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.Writer;
-
-import java.math.BigDecimal;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import javax.persistence.TypedQuery;
import org.arsdigita.cms.CMSConfig;
import org.libreccm.categorization.Category;
import org.libreccm.categorization.CategoryManager;
import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.core.CcmObject;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.Shiro;
-import org.libreccm.security.User;
import org.librecms.CmsConstants;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentItemManager;
import org.librecms.contentsection.ContentItemRepository;
-import org.librecms.contentsection.ContentSectionConfig;
+import org.librecms.contentsection.Folder;
+import org.librecms.contentsection.FolderRepository;
import org.librecms.contentsection.privileges.ItemPrivileges;
+import java.util.Objects;
+
/**
* Browse folders and manipulate them with various actions (move/copy/delete).
*
@@ -123,7 +112,7 @@ public class FolderManipulator extends SimpleContainer implements
//private static final String UNPUBLISH = "UnPublish";
private final ArrayParameter sourcesParam = new ArrayParameter(
- new BigDecimalParameter(SOURCES_PARAM));
+ new StringParameter(SOURCES_PARAM));
private final StringParameter actionParam
= new StringParameter(ACTION_PARAM);
;
@@ -174,13 +163,13 @@ public class FolderManipulator extends SimpleContainer implements
}
- public final Long[] getSources(final PageState state) {
+ public final String[] getSources(final PageState state) {
- final Long[] result = (Long[]) state.getValue(sourcesParam);
+ final String[] result = (String[]) state.getValue(sourcesParam);
//Return empty array instead of null.
if (result == null) {
- return new Long[0];
+ return new String[0];
} else {
return result;
}
@@ -214,9 +203,9 @@ public class FolderManipulator extends SimpleContainer implements
}
protected void moveItems(final Category target,
- final Long[] itemIds) {
+ final String[] itemIds) {
- for (Long itemId : itemIds) {
+ for (String itemId : itemIds) {
changeItemParent(itemId, target);
@@ -224,7 +213,8 @@ public class FolderManipulator extends SimpleContainer implements
}
- private void changeItemParent(final Long itemId, final Category newParent) {
+ private void changeItemParent(final String itemId,
+ final Category newParent) {
//ToDo
throw new UnsupportedOperationException();
@@ -244,7 +234,7 @@ public class FolderManipulator extends SimpleContainer implements
}
protected void copyItems(final Category target,
- final Long[] itemIds) {
+ final String[] itemIds) {
//ToDo
throw new UnsupportedOperationException();
@@ -432,8 +422,9 @@ 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();
itemView.setVisible(state, false);
@@ -459,7 +450,7 @@ public class FolderManipulator extends SimpleContainer implements
targetSelector.setVisible(state, false);
final Category folder = targetSelector.getTarget(state);
- final Long[] itemIds = getSources(state);
+ final String[] itemIds = getSources(state);
if (isCopy(state)) {
copyItems(folder, itemIds);
@@ -537,7 +528,7 @@ public class FolderManipulator extends SimpleContainer implements
throw new IllegalStateException("No source items specified");
}
- final Category target = targetSelector.getTarget(state);
+ final Folder target = targetSelector.getTarget(state);
final FormData data = event.getFormData();
if (target == null) {
data.addError(new GlobalizedMessage(
@@ -564,7 +555,7 @@ public class FolderManipulator extends SimpleContainer implements
CmsConstants.CMS_FOLDER_BUNDLE);
}
- for (Long source : getSources(state)) {
+ for (String source : getSources(state)) {
validateItem(source, target, state, data);
@@ -572,36 +563,77 @@ public class FolderManipulator extends SimpleContainer implements
}
- private void validateItem(final Long itemId,
- final Category target,
+ private void validateItem(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 ContentItemRepository itemRepo = cdiUtil.findBean(
ContentItemRepository.class);
final ContentItemManager itemManager = cdiUtil.findBean(
ContentItemManager.class);
+ final FolderBrowserController controller = cdiUtil.findBean(
+ FolderBrowserController.class);
final PermissionChecker permissionChecker = cdiUtil.findBean(
PermissionChecker.class);
- final ContentItem item = itemRepo.findById(itemId).get();
- final String name = item.getDisplayName();
+ final CcmObject object;
+ final String name;
+ if (objectId.startsWith("folder--")) {
+ final long folderId = Long.parseLong(objectId.substring(
+ "folder--".length()));
+ final Folder folder = folderRepo.findById(folderId).orElseThrow(
+ () -> new IllegalArgumentException(String.format(
+ "No folder with id %d in database.", folderId)));
- final long count = itemRepo.countByNameInFolder(target, name);
- if (count > 0) {
- // there is an item in the target folder that already has this name
- addErrorMessage(data, "cms.ui.folder.item_already_exists", name);
+ name = folder.getName();
+
+ //Check if folder or subfolder contains live items
+ if (isMove(state) && controller.hasLiveItems(folder)) {
+ addErrorMessage(data, "cms.ui.folder.item_is_live", name);
+ }
+
+ object = folder;
+
+ } else if (objectId.startsWith("item--")) {
+ final long itemId = Long.parseLong(objectId.substring(
+ "item--".length()));
+ final ContentItem item = itemRepo.findById(itemId).orElseThrow(
+ () -> new IllegalArgumentException(String.format(
+ "No content item with id %d in database.", itemId)));
+
+ name = item.getDisplayName();
+
+ if (isMove(state) && itemManager.isLive(item)) {
+ addErrorMessage(data, "cms.ui.folder.item_is_live", name);
+ }
+
+ object = item;
+ } else {
+ throw new IllegalArgumentException(String.format(
+ "Provided objectId '%s' does not start with 'folder--' "
+ + "or 'item--'.",
+ objectId));
}
- if (itemManager.isLive(item) && isMove(state)) {
- addErrorMessage(data, "cms.ui.folder.item_is_live", name);
+ 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, item))
+ ItemPrivileges.DELETE, object))
&& isMove(state)) {
- addErrorMessage(data, "cms.ui.folder.no_permission_for_item",
- name);
+ addErrorMessage(data,
+ "cms.ui.folder.no_permission_for_item",
+ object.getDisplayName());
}
}
@@ -755,8 +787,8 @@ public class FolderManipulator extends SimpleContainer implements
null);
}
- public Category getTarget(final PageState state) {
- return (Category) targetModel.getSelectedObject(state);
+ public Folder getTarget(final PageState state) {
+ return (Folder) targetModel.getSelectedObject(state);
}
public boolean isCancelled(final PageState state) {
@@ -1072,7 +1104,25 @@ public class FolderManipulator extends SimpleContainer implements
final Object key) {
// Get the list of invalid folders once per request.
- ArrayList invalidFolders = (ArrayList) m_invalidFolders.get(state);
+ @SuppressWarnings("unchecked")
+ ArrayList invalidFolders
+ = (ArrayList) m_invalidFolders.get(state);
+
+ if (invalidFolders == null) {
+ // The list of invalid folders has not been set for this
+ // request. Setting now.
+ invalidFolders = new ArrayList<>();
+ final String[] sources = getSources(state);
+
+ for (final String source : sources) {
+ if (source.startsWith("folder--")) {
+ invalidFolders.addAll(source);
+ }
+ }
+
+
+
+ }
// if (invalidFolders == null) {
// // The list of invalid folders has not been set for this
diff --git a/ccm-core/pom.xml b/ccm-core/pom.xml
index 46e8c3bdc..7bfeead76 100644
--- a/ccm-core/pom.xml
+++ b/ccm-core/pom.xml
@@ -76,7 +76,7 @@
flyway-core
-
+
org.apache.logging.log4j
log4j-core
@@ -85,10 +85,6 @@
org.apache.logging.log4j
log4j-api
-
commons-beanutils
diff --git a/ccm-core/src/main/java/org/libreccm/categorization/CategoryManager.java b/ccm-core/src/main/java/org/libreccm/categorization/CategoryManager.java
index c2aba0ecd..6028f976d 100644
--- a/ccm-core/src/main/java/org/libreccm/categorization/CategoryManager.java
+++ b/ccm-core/src/main/java/org/libreccm/categorization/CategoryManager.java
@@ -778,10 +778,17 @@ public class CategoryManager {
*
* @return The path of the category.
*/
+ @Transactional(Transactional.TxType.REQUIRED)
public String getCategoryPath(final Category category) {
+
+ Objects.requireNonNull(category, "Can't get the path for category null");
+
final List tokens = new ArrayList<>();
- Category current = category;
+ Category current = categoryRepo.findById(category.getObjectId())
+ .orElseThrow(() -> new IllegalArgumentException(String.format(
+ "No category with ID %d in the database. Where did that ID come from?",
+ category.getObjectId())));
while (current.getParentCategory() != null) {
tokens.add(current.getDisplayName());
current = current.getParentCategory();
@@ -794,6 +801,7 @@ public class CategoryManager {
return joiner.toString();
}
+ @Transactional(Transactional.TxType.REQUIRED)
public boolean hasIndexObject(final Category category) {
final TypedQuery query = entityManager
.createNamedQuery("Categorization.hasIndexObject", Boolean.class);
@@ -813,6 +821,7 @@ public class CategoryManager {
* @return An {@link Optional} containing the index object of the provided
* category if the category has an index object.
*/
+ @Transactional(Transactional.TxType.REQUIRED)
public Optional getIndexObject(final Category category) {
if (hasIndexObject(category)) {
final TypedQuery query = entityManager.createNamedQuery(
diff --git a/ccm-docrepo/pom.xml b/ccm-docrepo/pom.xml
index cef4aaacf..45b87c7b8 100644
--- a/ccm-docrepo/pom.xml
+++ b/ccm-docrepo/pom.xml
@@ -57,8 +57,8 @@
provided
-
-
+
org.apache.logging.log4j
log4j-core
@@ -66,28 +66,22 @@
org.apache.logging.log4j
log4j-api
+
- org.apache.logging.log4j
- log4j-1.2-api
- -->
+ org.apache.shiro
+ shiro-core
+
+
+ org.apache.shiro
+ shiro-web
+
junit
junit
test
-
-
- org.hamcrest
- hamcrest-core
- test
-
-
- org.hamcrest
- hamcrest-library
- test
-
-
+
org.libreccm
ccm-testutils
diff --git a/ccm-shortcuts/pom.xml b/ccm-shortcuts/pom.xml
index da2fea71a..a352879b0 100644
--- a/ccm-shortcuts/pom.xml
+++ b/ccm-shortcuts/pom.xml
@@ -74,6 +74,15 @@
log4j-api
+
+ org.apache.shiro
+ shiro-core
+
+
+ org.apache.shiro
+ shiro-web
+
+
junit
junit
diff --git a/ccm-theme-foundry/src/main/resources/themes/foundry/foundry/scripts/manipulate-input.js b/ccm-theme-foundry/src/main/resources/themes/foundry/foundry/scripts/manipulate-input.js
new file mode 100755
index 000000000..b76a2a416
--- /dev/null
+++ b/ccm-theme-foundry/src/main/resources/themes/foundry/foundry/scripts/manipulate-input.js
@@ -0,0 +1,47 @@
+
+// can be used to ensure that input values will be valid when used as part of urls
+//
+function urlize(title) {
+ var result = "";
+ for (var i = 0; i < title.length; i++) {
+ result = result + substitute(title.charAt(i));
+ }
+ return escape(result);
+ }
+
+ function substitute(c) {
+ var sourceChars="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_ &/";
+ var targetChars="0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz-_---";
+
+ var indexChar = sourceChars.indexOf(c);
+ if (indexChar > -1) {
+ return targetChars.charAt(indexChar);
+ } else {
+ // Replacement list for special characters
+ switch(c) {
+
+ case "Ä": return "ae";
+ break;
+
+ case "ä": return "ae";
+ break;
+
+ case "Ö": return "oe";
+ break;
+
+ case "ö": return "oe";
+ break;
+
+ case "Ü": return "ue";
+ break;
+
+ case "ü": return "ue";
+ break;
+
+ case "ß": return "ss";
+ break;
+
+ default: return "";
+ }
+ }
+ }
\ No newline at end of file
diff --git a/ccm.sh b/ccm.sh
index dcaa238c7..2249f1fb3 100755
--- a/ccm.sh
+++ b/ccm.sh
@@ -410,11 +410,11 @@ run() {
bundle="$3"
else
runtime="wildfly"
- bundle="$2"
+ bundle="$1"
fi
if [ -z $bundle ]; then
- echo "Running Wilfly $wildversion without a bundle (only starting Wilflybut not deploying LibreCCM)..."
+ echo "Running Wilfly $wildversion without a bundle (only starting Wilfly but not deploying LibreCCM)..."
else
echo "Running bundle $bundle with Wildfly $wildflyversion..."
fi