From 24f01f6f8881b09370ba916a43d7fda2aab40b25 Mon Sep 17 00:00:00 2001 From: jensp Date: Mon, 22 May 2017 13:21:29 +0000 Subject: [PATCH] CCM NG/ccm-cms: ItemCategoryStep migrated to CCM NG git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4752 8810af33-2d31-482b-a856-94f89814c4df --- .../cms/ui/authoring/CategoryWidget.java | 220 ++++++++++++++ .../cms/ui/authoring/ItemCategoryForm.java | 66 +++++ .../cms/ui/authoring/ItemCategoryStep.java | 180 ++++++++++++ .../cms/ui/authoring/ItemCategorySummary.java | 95 ++++++ .../ui/ACSObjectCategoryForm.java | 192 ++++++++++++ .../ui/ACSObjectCategorySummary.java | 276 ++++++++++++++++++ 6 files changed, 1029 insertions(+) create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CategoryWidget.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/ItemCategoryForm.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/ItemCategoryStep.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/ItemCategorySummary.java create mode 100755 ccm-core/src/main/java/com/arsdigita/categorization/ui/ACSObjectCategoryForm.java create mode 100755 ccm-core/src/main/java/com/arsdigita/categorization/ui/ACSObjectCategorySummary.java diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CategoryWidget.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CategoryWidget.java new file mode 100755 index 000000000..8810def22 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/CategoryWidget.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.authoring; + +import com.arsdigita.bebop.form.Widget; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.parameters.ArrayParameter; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.bebop.parameters.BigDecimalParameter; +import com.arsdigita.bebop.parameters.LongParameter; +import com.arsdigita.xml.Element; +import com.arsdigita.xml.XML; + +import org.libreccm.categorization.Category; + +import com.arsdigita.cms.CMS; +import com.arsdigita.kernel.KernelConfig; + +import org.arsdigita.cms.CMSConfig; +import org.libreccm.categorization.CategoryRepository; +import org.libreccm.cdi.utils.CdiUtil; +import org.librecms.contentsection.ContentSection; + +import java.util.Arrays; +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.math.BigDecimal; + +import static javax.naming.ldap.SortControl.*; + +public class CategoryWidget extends Widget { + + private LongParameter rootParameter; + private StringParameter modeParameter; + + public CategoryWidget(final String name, + final LongParameter rootParameter, + final StringParameter modeParameter) { + + super(new ArrayParameter(new BigDecimalParameter(name))); + + this.rootParameter = rootParameter; + this.modeParameter = modeParameter; + } + + @Override + protected String getType() { + return "category"; + } + + @Override + public boolean isCompound() { + return false; + } + + @Override + protected void generateWidget(final PageState state, + final Element parent) { + + Element widget = parent.newChildElement("cms:categoryWidget", + CMS.CMS_XML_NS); + exportAttributes(widget); + + widget.addAttribute("mode", (String) state.getValue(modeParameter)); + widget.addAttribute("name", getName()); + + final Set selectedCategories = new HashSet<>(); + + final Long[] values = (Long[]) getValue(state); + if (values != null) { + selectedCategories.addAll(Arrays.asList(values)); + } + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final CategoryRepository categoryRepo = cdiUtil + .findBean(CategoryRepository.class); + final Category rootCategory = categoryRepo + .findById((Long) state.getValue(rootParameter)) + .orElseThrow(() -> new IllegalArgumentException( + String.format( + "No Category with ID %d in the database. " + + "Where did that ID come from?", + state.getValue(rootParameter)))); + + final List categories = rootCategory.getSubCategories(); + + final Map children = new HashMap(); +// ToDo, I don't understand was is done here... +// while (categories.next()) { +// final Category cat = categories.getCategory(); +// final BigDecimal parentID = (BigDecimal) categories +// .get("parents.id"); +// +// List childList = (List) children.get(parentID); +// if (childList == null) { +// childList = new ArrayList(); +// children.put(parentID, childList); +// } +// +// childList.add( +// new CategorySortKeyPair(cat, (BigDecimal) categories.get( +// "parents.link.sortKey"))); +// } + + generateCategory(widget, null, rootCategory, null, selectedCategories, + children); + } + + public void generateCategory(final Element parent, + final String path, + final Category category, + final Long sortKey, + final Set selected, + final Map children) { + + final Element element = new Element("cms:category", + CMS.CMS_XML_NS); + + element.addAttribute("id", XML.format(category.getObjectId())); + element.addAttribute("name", category.getName()); + element.addAttribute("description", + category + .getDescription() + .getValue(KernelConfig + .getConfig() + .getDefaultLocale())); + if (selected.contains(category.getObjectId())) { + element.addAttribute("isSelected", "1"); + } else { + element.addAttribute("isSelected", "0"); + } + if (category.isAbstractCategory()) { + element.addAttribute("isAbstract", "1"); + } else { + element.addAttribute("isAbstract", "0"); + } + if (category.isEnabled()) { + element.addAttribute("isEnabled", "1"); + } else { + element.addAttribute("isEnabled", "0"); + } + if (sortKey != null) { + element.addAttribute("sortKey", sortKey.toString()); + } + // sort order attribute added to every node in order that same xsl may + // be used to transform xml fragments returned by ajax in the Aplaws + // extension +// element.addAttribute("order", +// CMSConfig.getConfig().getCategoryTreeOrder()); + + String fullname = path == null ? "/" : path + " > " + category.getName(); + element.addAttribute("fullname", fullname); + StringBuilder nodeID = new StringBuilder(parent.getAttribute("node-id")); + if (nodeID.length() > 0) { + nodeID.append("-"); + } + nodeID.append(category.getObjectId()); + element.addAttribute("node-id", nodeID.toString()); + parent.addContent(element); + + List c = (List) children.get(category.getObjectId()); + if (c != null) { + Iterator i = c.iterator(); + while (i.hasNext()) { + CategorySortKeyPair pair = (CategorySortKeyPair) i.next(); + Category child = pair.getCategory(); + Long childSortKey = pair.getSortKey(); + generateCategory(element, + fullname, + child, + childSortKey, + selected, + children); + } + } + } + + private class CategorySortKeyPair { + + private Category category; + private Long sortKey; + + public CategorySortKeyPair(final Category category, + final Long sortKey) { + this.category = category; + this.sortKey = sortKey; + } + + public Category getCategory() { + return category; + } + + public Long getSortKey() { + return sortKey; + } + + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/ItemCategoryForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/ItemCategoryForm.java new file mode 100755 index 000000000..35a0ebb09 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/ItemCategoryForm.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.authoring; + +import org.apache.logging.log4j.Logger; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.form.Widget; +import com.arsdigita.bebop.parameters.LongParameter; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.categorization.ui.ACSObjectCategoryForm; +import com.arsdigita.cms.CMS; + +import org.apache.logging.log4j.LogManager; +import org.libreccm.core.CcmObject; + +/** + * (No description available yet). + * + * Usage of this class is configured in parameter + * c.ad.cms.category_authoring_add_form + */ +public class ItemCategoryForm extends ACSObjectCategoryForm { + + private static Logger LOGGER = LogManager.getLogger(ItemCategoryForm.class); + + public ItemCategoryForm(final LongParameter rootParameter, + final StringParameter modeParameter, + final Widget widget) { + + super(rootParameter, modeParameter, widget); + LOGGER.debug("creating new ItemTerm Form with widget " + widget); + } + + public ItemCategoryForm(final LongParameter root, + final StringParameter mode) { + + this(root, mode, new CategoryWidget("category", root, mode)); + } + + /* + * @see com.arsdigita.categorization.ui.ACSObjectCategoryForm#getObject() + */ + @Override + protected CcmObject getObject(final PageState state) { + return CMS.getContext().getContentItem(); + + } + +} diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/ItemCategoryStep.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/ItemCategoryStep.java new file mode 100755 index 000000000..ad7e7a211 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/authoring/ItemCategoryStep.java @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.cms.ui.authoring; + +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.SimpleComponent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.bebop.Resettable; +import com.arsdigita.bebop.parameters.LongParameter; +import com.arsdigita.web.RedirectSignal; + + +import com.arsdigita.cms.CMS; + + +import com.arsdigita.cms.ItemSelectionModel; + +import java.math.BigDecimal; + + +/** + * + * + */ +public class ItemCategoryStep extends SimpleContainer implements Resettable{ + + + private final LongParameter rootParameter; + private final StringParameter modeParameter; + + private final ItemCategorySummary itemCategorySummary; + + private final SimpleComponent addComponent; + +// private SimpleComponent[] extensionSummaries; +// private SimpleComponent[] extensionForms; +// private int extensionsCount; + + + public ItemCategoryStep(final ItemSelectionModel itemSelectionModel, + final AuthoringKitWizard authoringKitWizard) { + + super("cms:categoryStep", CMS.CMS_XML_NS); + + rootParameter = new LongParameter("root"); + modeParameter = new StringParameter("mode"); + + itemCategorySummary = new ItemCategorySummary(); + itemCategorySummary.registerAction(ItemCategorySummary.ACTION_ADD, + new AddActionListener("plain")); + itemCategorySummary.registerAction(ItemCategorySummary.ACTION_ADD_JS, + new AddActionListener("javascript")); + +// Class addForm = CMSConfig.getConfig().getCategoryAuthoringAddForm(); +// addComponent = (SimpleComponent) +// Classes.newInstance(addForm, +// new Class[] { BigDecimalParameter.class, +// StringParameter.class }, +// new Object[] { rootParameter, modeParameter }); + addComponent = new ItemCategoryForm(rootParameter, modeParameter); + addComponent.addCompletionListener(new ResetListener()); + +// Class extensionClass = ContentSection.getConfig().getCategoryAuthoringExtension(); +// ItemCategoryExtension extension = (ItemCategoryExtension) +// Classes.newInstance(extensionClass); +// +// extensionSummaries = extension.getSummary(); +// extensionForms = extension.getForm(); +// int nSummaries = extensionSummaries.length; +// int nForms= extensionForms.length; +// Assert.isTrue(nSummaries==nForms, "invalid CategoryStep extension"); +// extensionsCount = nForms; +// for (int i=0;i getRootCategories(final PageState state) { + + final ContentSection section = CMS.getContext().getContentSection(); + return section + .getDomains() + .stream() + .map(domainOwnership -> domainOwnership.getDomain()) + .map(domain -> domain.getRoot()) + .collect(Collectors.toList()); + } + +} diff --git a/ccm-core/src/main/java/com/arsdigita/categorization/ui/ACSObjectCategoryForm.java b/ccm-core/src/main/java/com/arsdigita/categorization/ui/ACSObjectCategoryForm.java new file mode 100755 index 000000000..bb9052e5d --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/categorization/ui/ACSObjectCategoryForm.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2004 Chris Gilbert + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.categorization.ui; + +import java.util.ArrayList; +import java.util.List; + +import com.arsdigita.bebop.BoxPanel; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SaveCancelSection; +import com.arsdigita.bebop.event.FormInitListener; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormSubmissionListener; +import com.arsdigita.bebop.form.Widget; +import com.arsdigita.bebop.parameters.LongParameter; +import com.arsdigita.bebop.parameters.NotNullValidationListener; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.bebop.util.GlobalizationUtil; + +import org.libreccm.categorization.Category; +import org.libreccm.categorization.CategoryManager; +import org.libreccm.categorization.CategoryRepository; +import org.libreccm.categorization.ObjectNotAssignedToCategoryException; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.CcmObject; +import org.libreccm.core.UnexpectedErrorException; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Abstract form for assigning categories to acs_objects. The assigned + * categories are those specified by the category widget, which is retrieved by + * the concrete subclass' implementation of getCategoryWidget. + * + * The category widget may be an implementation of CategoryWidget, which + * generates a javascript tree of categories. Implementations need only specify + * an XML prefix and namespace. + * + * The object that is to be assigned to the categories is specified by the + * concrete subclass' implentation of getObject + * + * @author chris.gilbert@westsussex.gov.uk + * + * + */ +// this class has been abstracted out from the original cms specific +// category form in ccm-cms +public abstract class ACSObjectCategoryForm extends Form { + + private final Widget categoryWidget; + private final SaveCancelSection saveCancelSection; + + protected abstract CcmObject getObject(PageState state); + + public ACSObjectCategoryForm(final LongParameter root, + final StringParameter mode, + final Widget categoryWidget) { + super("category", new BoxPanel(BoxPanel.VERTICAL)); + + this.categoryWidget = categoryWidget; + categoryWidget.addValidationListener(new NotNullValidationListener()); + saveCancelSection = new SaveCancelSection(); + + add(categoryWidget); + add(saveCancelSection); + + addInitListener(new FormInitListener() { + + @Override + public void init(final FormSectionEvent event) + throws FormProcessException { + + final PageState state = event.getPageState(); + final CcmObject object = getObject(state); + + final List selectedCats = new ArrayList<>(); + final Set ancestorCats = new HashSet<>(); + final List categories = object + .getCategories() + .stream() + .map(categorization -> categorization.getCategory()) + .collect(Collectors.toList()); + for (final Category category : categories) { + selectedCats.add(category.getObjectId()); + addAncestorCats(ancestorCats, category); + } + + final Long[][] paramArray = new Long[2][]; + paramArray[0] = selectedCats + .toArray(new Long[selectedCats.size()]); + paramArray[1] = ancestorCats + .toArray(new Long[ancestorCats.size()]); + + categoryWidget.setValue(state, paramArray); + } + + }); + + addProcessListener(new FormProcessListener() { + + @Override + public void process(final FormSectionEvent event) + throws FormProcessException { + + final PageState state = event.getPageState(); + + final CcmObject object = getObject(state); + + final Set curSelectedCat = object + .getCategories() + .stream() + .map(categorization -> categorization.getCategory()) + .map(category -> category.getObjectId()) + .collect(Collectors.toSet()); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final CategoryRepository categoryRepo = cdiUtil + .findBean(CategoryRepository.class); + final CategoryManager categoryManager = cdiUtil + .findBean(CategoryManager.class); + final Long[] ids = (Long[]) categoryWidget.getValue(state); + for (final Long id : ids) { + final Category cat = categoryRepo + .findById(id) + .orElseThrow(() -> new IllegalArgumentException( + String.format("No Category with ID %d in the database. " + + "Where did that ID come from?", + id))); + if (!curSelectedCat.contains(id)) { + categoryManager.addObjectToCategory(object, cat); + } else { + try { + categoryManager + .removeObjectFromCategory(object, cat); + } catch (ObjectNotAssignedToCategoryException ex) { + throw new UnexpectedErrorException(ex); + } + } + } + + fireCompletionEvent(state); + } + + }); + addSubmissionListener(new FormSubmissionListener() { + + @Override + public void submitted(final FormSectionEvent event) + throws FormProcessException { + + final PageState state = event.getPageState(); + + if (saveCancelSection.getCancelButton().isSelected(state)) { + fireCompletionEvent(state); + throw new FormProcessException("Submission cancelled", + GlobalizationUtil.globalize( + "categorization.cancel.msg")); + } + } + + }); + } + + private void addAncestorCats(final Set ancestorCats, + final Category category) { + + ancestorCats.add(category.getParentCategory().getObjectId()); + + } + +} diff --git a/ccm-core/src/main/java/com/arsdigita/categorization/ui/ACSObjectCategorySummary.java b/ccm-core/src/main/java/com/arsdigita/categorization/ui/ACSObjectCategorySummary.java new file mode 100755 index 000000000..2e1c5104d --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/categorization/ui/ACSObjectCategorySummary.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2007 Chris Gilbert + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.categorization.ui; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.ServletException; + +import org.apache.logging.log4j.Logger; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleComponent; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.kernel.KernelConfig; + +import org.libreccm.core.CcmObject; +import org.libreccm.categorization.Category; + +import com.arsdigita.util.Assert; +import com.arsdigita.web.RedirectSignal; +import com.arsdigita.xml.Element; +import com.arsdigita.xml.XML; + +import org.apache.logging.log4j.LogManager; +import org.libreccm.categorization.CategoryManager; +import org.libreccm.categorization.CategoryRepository; +import org.libreccm.categorization.ObjectNotAssignedToCategoryException; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.UnexpectedErrorException; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + + +/** + * abstract class for displaying the categories assigned to an object under one + * or more root nodes. Subclasses should retrieve the object to be assigned and + * supply the logic to retrieve root categories. + * + * * abstracted from ItemCategorySummary in ccm-cms + * + * @author chris.gilbert@westsussex.gov.uk + * @author Jens Pelzetter + * + * + * + */ +public abstract class ACSObjectCategorySummary extends SimpleComponent { + + private static final Logger LOGGER = LogManager + .getLogger(ACSObjectCategorySummary.class); + + public static final String ACTION_DELETE = "delete"; + public static final String ACTION_ADD = "add"; + public static final String ACTION_ADD_JS = "addJS"; + + private final Map listenersLap = new HashMap<>(); + + public ACSObjectCategorySummary() { + registerAction(ACTION_DELETE, + new DeleteActionListener()); + } + + public void registerAction(final String name, + final ActionListener listener) { + listenersLap.put(name, listener); + } + + @Override + public void respond(final PageState state) throws ServletException { + + super.respond(state); + + Assert.isTrue(canEdit(state), "User can edit object"); + + final String name = state.getControlEventName(); + final ActionListener listener = listenersLap.get(name); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Got event {} listener {}", + name, + listener); + } + + if (listener != null) { + listener.actionPerformed(new ActionEvent(this, state)); + } + } + + /** + * default behaviour is to check for edit access on the current resource. as + * defined by getCategorizedObject. This can be overridden by subclasses, if + * for instance a specific privilege should be checked + * + * @param state + * + * @return + */ + protected abstract boolean canEdit(final PageState state); + + protected abstract CcmObject getObject(PageState state); + + protected abstract String getXMLPrefix(); + + protected abstract String getXMLNameSpace(); + + protected abstract List getRootCategories(PageState state); + + @Override + public void generateXML(final PageState state, final Element parent) { + + final boolean canEdit = canEdit(state); + + final Element content = parent + .newChildElement(String.format("%s:categoryStepSummary", + getXMLPrefix()), + getXMLNameSpace()); + exportAttributes(content); + + final Element rootCats = content + .newChildElement(String.format("%s:categoryRoots", + getXMLPrefix()), + getXMLNameSpace()); + + final List roots = getRootCategories(state); + for (final Category rootCategory : roots) { + + final Element root = rootCats + .newChildElement(String.format("%s:categoryRoot", + getXMLPrefix()), + getXMLNameSpace()); + root.addAttribute("name", rootCategory.getName()); + root.addAttribute("description", + rootCategory + .getDescription() + .getValue(KernelConfig + .getConfig() + .getDefaultLocale())); + + if (canEdit) { + state.setControlEvent(this, + ACTION_ADD, + Long.toString(rootCategory.getObjectId())); + try { + root.addAttribute("addAction", + XML.format(state.stateAsURL())); + } catch (IOException ex) { + throw new UnexpectedErrorException("cannot generate URL", + ex); + } + state.clearControlEvent(); + state.setControlEvent(this, + ACTION_ADD_JS, + Long.toString(rootCategory.getObjectId())); + try { + root.addAttribute("addJsAction", + XML.format(state.stateAsURL())); + } catch (IOException ex) { + throw new UnexpectedErrorException("cannot generate URL", + ex); + } + state.clearControlEvent(); + } + } + + final Element itemCats = content + .newChildElement(String.format("%s:itemCategories", + getXMLPrefix()), + getXMLNameSpace()); + + final List categories = getObject(state) + .getCategories() + .stream() + .map(categorization -> categorization.getCategory()) + .collect(Collectors.toList()); + + final CategoryManager categoryManager = CdiUtil + .createCdiUtil() + .findBean(CategoryManager.class); + + for (final Category category : categories) { + + final String path = categoryManager.getCategoryPath(category); + + final Element categoryElem = itemCats + .newChildElement(String.format("%s:itemCategory", + getXMLPrefix()), + getXMLNameSpace()); + categoryElem.addAttribute("name", category.getName()); + categoryElem.addAttribute("description", + category + .getDescription() + .getValue(KernelConfig + .getConfig() + .getDefaultLocale())); + categoryElem.addAttribute("path", XML.format(path)); + + if (canEdit) { + state.setControlEvent(this, + ACTION_DELETE, + Long.toString(category.getObjectId())); + try { + categoryElem.addAttribute("deleteAction", + XML.format(state.stateAsURL())); + } catch (IOException ex) { + throw new UnexpectedErrorException("cannot generate URL", + ex); + } + state.clearControlEvent(); + } + } + + } + + private class DeleteActionListener implements ActionListener { + + @Override + public void actionPerformed(final ActionEvent event) { + + final PageState state = event.getPageState(); + final String value = state.getControlEventValue(); + + final CdiUtil cdiUtil = CdiUtil.createCdiUtil(); + final CategoryRepository categoryRepo = cdiUtil + .findBean(CategoryRepository.class); + final Category category = categoryRepo + .findById(Long.parseLong(value)) + .orElseThrow(() -> new IllegalArgumentException( + String.format( + "No Category with ID %s in the database. " + + "Where did that ID come from?", + value))); + + final CcmObject object = getObject(state); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Removing category {} from {}", + Objects.toString(category), + Objects.toString(object)); + } + + final CategoryManager categoryManager = cdiUtil + .findBean(CategoryManager.class); + try { + categoryManager.removeObjectFromCategory(object, category); + } catch(ObjectNotAssignedToCategoryException ex) { + throw new UnexpectedErrorException(ex); + } + + state.clearControlEvent(); + throw new RedirectSignal(state.toURL(), true); + } + + } + +}