From d5a0f86a8f3824d77c64cb46e5580f4219f6f81b Mon Sep 17 00:00:00 2001 From: jensp Date: Wed, 1 Mar 2017 19:48:33 +0000 Subject: [PATCH] CCM NG/ccm-cms: FolderManipulator Copy/Move (not finished yet). git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4611 8810af33-2d31-482b-a856-94f89814c4df --- ccm-cms/pom.xml | 19 +++ .../ui/folder/FolderBrowserController.java | 56 +++++-- .../cms/ui/folder/FolderManipulator.java | 142 ++++++++++++------ ccm-core/pom.xml | 6 +- .../categorization/CategoryManager.java | 11 +- ccm-docrepo/pom.xml | 28 ++-- ccm-shortcuts/pom.xml | 9 ++ .../foundry/scripts/manipulate-input.js | 47 ++++++ ccm.sh | 4 +- 9 files changed, 241 insertions(+), 81 deletions(-) create mode 100755 ccm-theme-foundry/src/main/resources/themes/foundry/foundry/scripts/manipulate-input.js 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