diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/CategoryForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/CategoryForm.java new file mode 100755 index 000000000..9cfe2da90 --- /dev/null +++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/CategoryForm.java @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2001-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; + +import com.arsdigita.bebop.*; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormValidationListener; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +import com.arsdigita.bebop.form.Option; +import com.arsdigita.bebop.form.OptionGroup; +import com.arsdigita.bebop.form.SingleSelect; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.parameters.BigDecimalParameter; +import com.arsdigita.bebop.util.GlobalizationUtil; +import com.arsdigita.bebop.util.SequentialMap; +import com.arsdigita.cms.CMS; +import com.arsdigita.cms.ui.authoring.BasicItemForm; +import com.arsdigita.util.StringUtils; +import com.arsdigita.util.UncheckedWrapperException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.libreccm.categorization.Category; +import org.libreccm.core.CcmObject; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TooManyListenersException; +import java.util.TreeMap; + +/** + * This is an abstract class which displays the category assignment UI. + * + * Displays two listboxes for assigning categories to items, with two + * submit buttons to move categories back and forth. The left + * listbox displays all available categories which have not been + * assigned to the current item. The right listbox displays all categories + * assigned to the current item. + *
+ *
+ *
+ * @author Stanislav Freidin (sfreidin@arsdigita.com)
+ * @author Yannick Bülter
+ */
+public abstract class CategoryForm extends Form
+ implements FormProcessListener, FormValidationListener {
+
+ private static final Logger LOGGER = LogManager.getLogger(
+ CategoryForm.class);
+ private static final String SEPARATOR = ">";
+ public static final String FREE = "free";
+ public static final String ASSIGNED = "assigned";
+ public static final String ASSIGN = "assign";
+ public static final String REMOVE = "remove";
+ public static final int SELECT_WIDTH = 30;
+ public static final int SELECT_HEIGHT = 10;
+ public static final String FILLER_OPTION = StringUtils.repeat("_", SELECT_WIDTH);
+
+ private final RequestLocal m_assigned;
+ private Submit m_assign, m_remove;
+
+ private final Label m_freeLabel;
+ private final Label m_assignedLabel;
+
+ /**
+ * Construct a new CategoryForm component
+ *
+ * @param name the name of the form
+ */
+ public CategoryForm(String name) {
+ super(name, new ColumnPanel(3));
+
+ ColumnPanel panel = (ColumnPanel) getPanel();
+ panel.setBorder(false);
+ panel.setPadColor("#FFFFFF");
+ panel.setColumnWidth(1, "0%");
+ panel.setColumnWidth(2, "0%");
+ panel.setColumnWidth(3, "0");
+ panel.setWidth("0%");
+ panel.setClassAttr("CMS Admin");
+
+ // Create the request local
+ m_assigned = new RequestLocal() {
+
+ @Override
+ public Object initialValue(PageState state) {
+ CategoryMap m = new CategoryMap();
+ initAssignedCategories(state, m);
+ return m;
+ }
+ };
+
+ // Top row
+ m_freeLabel = new Label(GlobalizationUtil.globalize("cms.ui.item.categories.available"), false);
+ m_freeLabel.setFontWeight(Label.BOLD);
+ add(m_freeLabel, ColumnPanel.LEFT);
+
+ //add(new Label(" ", false));
+ add(new Embedded(" ", false));
+
+ m_assignedLabel = new Label(GlobalizationUtil.globalize("cms.ui.item.categories.assigned"), false);
+ m_assignedLabel.setFontWeight(Label.BOLD);
+ add(m_assignedLabel, ColumnPanel.LEFT);
+
+ // Middle Row
+ SingleSelect freeWidget = new SingleSelect(new BigDecimalParameter(FREE));
+ try {
+ freeWidget.addPrintListener(new FreePrintListener());
+ } catch (TooManyListenersException e) {
+ UncheckedWrapperException.throwLoggedException(getClass(), "Too many listeners", e);
+ }
+ freeWidget.setSize(SELECT_HEIGHT);
+ add(freeWidget);
+
+ BoxPanel box = new BoxPanel(BoxPanel.VERTICAL, true);
+ box.setWidth("2%");
+ addSubmitButtons(box);
+ add(box, ColumnPanel.CENTER | ColumnPanel.MIDDLE);
+
+ SingleSelect assignedWidget =
+ new SingleSelect(new BigDecimalParameter(ASSIGNED));
+ try {
+ assignedWidget.addPrintListener(new AssignedPrintListener());
+ } catch (TooManyListenersException e) {
+ UncheckedWrapperException.throwLoggedException(getClass(), "Too many listeners", e);
+ }
+ assignedWidget.setSize(SELECT_HEIGHT);
+ add(assignedWidget);
+
+ // Add listeners
+ addProcessListener(this);
+ addValidationListener(this);
+
+ setClassAttr("CategoryForm");
+ }
+
+ protected void addSubmitButtons(Container c) {
+ addAssignButton(c);
+ addRemoveButton(c);
+ }
+
+ protected void addAssignButton(Container c) {
+ m_assign = new Submit(ASSIGN, ">>");
+ m_assign.setSize(10);
+ c.add(m_assign);
+ }
+
+ protected void addRemoveButton(Container c) {
+ m_remove = new Submit(REMOVE, "<<");
+ m_remove.setSize(10);
+ c.add(m_remove);
+ }
+
+ /**
+ * Set the caption of the unassigned categories label
+ *
+ * @param caption the new caption
+ */
+ public void setUnassignedCaption(String caption) {
+ m_freeLabel.setLabel(caption);
+ }
+
+ /**
+ * Set the caption of the assigned categories label
+ *
+ * @param caption the new caption
+ */
+ public void setAssignedCaption(String caption) {
+ m_assignedLabel.setLabel(caption);
+ }
+
+ /**
+ * @param s the page state
+ * @return a {@link CategoryMap} of all assigned categories
+ */
+ public CategoryMap getAssignedCategories(PageState s) {
+ return (CategoryMap) m_assigned.get(s);
+ }
+
+ // A print listener which populates the listbox with all
+ // unassigned categories, apart from result of getExcludedCategory()
+ // (if not null), and the root category.
+ // Ordering is alphabetical based on qualified path, so entries are
+ // ordered like a tree with all nodes expanded.
+ // Ideally ordering should be like an expanded tree but based on
+ // the sortkey order of the categories. However, I don't know
+ // if it would be possible to write a comparison function that
+ // could do this efficiently, and I'm not even going to try
+ // chris.gilbert@westsussex.gov.uk
+ //
+ private class FreePrintListener implements PrintListener {
+
+ @Override
+ public void prepare(PrintEvent e) {
+
+ OptionGroup target = (OptionGroup) e.getTarget();
+ target.clearOptions();
+ PageState state = e.getPageState();
+ Category root = getRootCategory(state);
+ if (root == null) {
+ return;
+ }
+
+ // exclude children of the excluded category (as per javadoc on
+ // getExcludedCategory() method. This prevents attempts
+ // to create circular category graph (which causes
+ // exception in Category during addMapping if not checked here
+ Category excludedCat = getExcludedCategory(state);
+ CategoryMap excluded = new CategoryMap();
+ if (excludedCat != null) {
+ java.util.Listcall map.addCategory(someCategory);
+ * @param state The page state
+ */
+ protected abstract void initAssignedCategories(PageState state, CategoryMap map);
+
+ /**
+ * Assign a category, moving it from the list on the left
+ * to the list on the right
+ *
+ * @param s the page state
+ * @param cat the category to assign
+ */
+ protected abstract void assignCategory(PageState s, Category cat);
+
+ /**
+ * Unassign a category, moving it from the list on the right
+ * to the list on the left
+ *
+ * @param s the page state
+ * @param cat the category to unassign
+ */
+ protected abstract void unassignCategory(PageState s, Category cat);
+
+ /**
+ * This method returns the URL for the givne item to make sure that
+ * the item it is not possible to have two objects in the same category
+ * with the same URL.
+ * @param state The Page State
+ */
+ protected abstract String getItemURL(PageState state);
+
+ /**
+ * This allows the validation code to validate the properties of the
+ * object
+ */
+ protected abstract CcmObject getObject(PageState state);
+
+ /**
+ * Get the category which will act as the root for the lists
+ * of assigned and unassigned categories. The default implementation
+ * returns the root category for the content section. Child classes
+ * should override this method if they wish to provide an alternate root category.
+ *
+ * @param state the page state
+ * @return the root category which should be used to populate the lists
+ * of assigned and unassigned categories
+ */
+ public Category getRootCategory(PageState state) {
+ return null;
+// return CMS.getContext().getContentSection().getRootCategory();
+ }
+
+ /**
+ * Return a category which should be excluded from the list of
+ * free categories. It is permissible to return null
+ *
+ * @param s the page state
+ * @return a category whose subtree will not be shown in the
+ * category list
+ */
+ protected Category getExcludedCategory(PageState s) {
+ return null;
+ }
+
+ // Populates the "assigned categories" widget
+ @Deprecated
+ private class AssignedPrintListener implements PrintListener {
+
+ @Override
+ public void prepare(PrintEvent e) {
+ OptionGroup o = (OptionGroup) e.getTarget();
+ o.clearOptions();
+ PageState state = e.getPageState();
+ CategoryMap m = getAssignedCategories(state);
+
+ if (!m.isEmpty()) {
+ for (Iterator i = m.values().iterator(); i.hasNext();) {
+ Category c = (Category) i.next();
+ o.addOption(new Option(c.getUniqueId(), new Text(getCategoryPath(c))));
+ }
+ } else {
+ o.addOption(new Option("", new Text("-- none --")));
+ }
+
+ addFillerOption(o);
+ }
+ }
+
+ // Process the form: assign/unassign categories
+ @Override
+ public void process(FormSectionEvent e) throws FormProcessException {
+ PageState state = e.getPageState();
+ FormData data = e.getFormData();
+ BigDecimal id;
+
+// if (m_assign.isSelected(state)) {
+// id = (BigDecimal) data.get(FREE);
+//
+// // Assign a new category
+// try {
+// Category cat = new Category(
+// new OID(Category.BASE_DATA_OBJECT_TYPE, id));
+// if (!cat.canMap()) {
+// data.addError(
+// (String) GlobalizationUtil.globalize(
+// "cms.ui.need_category_map_privilege").localize());
+// return;
+// }
+// assignCategory(state, cat);
+// // Highlight the item
+// data.put(ASSIGNED, id);
+// } catch (DataObjectNotFoundException ex) {
+// s_log.error("Couldn't find Category", ex);
+// throw new FormProcessException(ex);
+// }
+//
+// } else if (m_remove.isSelected(state)) {
+// id = (BigDecimal) data.get(ASSIGNED);
+//
+// // Unassign a category
+// try {
+// Category cat = new Category(
+// new OID(Category.BASE_DATA_OBJECT_TYPE, id));
+// if (!cat.canMap()) {
+// data.addError(
+// (String) GlobalizationUtil.globalize(
+// "cms.ui.need_category_map_privilege").localize());
+// return;
+// }
+// unassignCategory(state, cat);
+// // Highlight the item
+// data.put(FREE, id);
+// } catch (DataObjectNotFoundException ex) {
+// s_log.error("Couldn't find category");
+// throw new FormProcessException(ex);
+// }
+// }
+ }
+
+ // Validate the form: make sure that a category is selected
+ // for the remove/assign buttons
+ @Override
+ public void validate(FormSectionEvent e) throws FormProcessException {
+ PageState state = e.getPageState();
+ FormData data = e.getFormData();
+
+// if (m_assign.isSelected(state)) {
+// if (data.get(FREE) == null) {
+// data.addError("Please select a category to assign");
+// } else {
+// // we need to make sure that no other item in this
+// // category has the same name (url)
+// BigDecimal id = (BigDecimal) data.get(FREE);
+//
+// // Assign a new category
+// try {
+// Category cat =
+// new Category(new OID(Category.BASE_DATA_OBJECT_TYPE, id));
+// String url = getItemURL(state);
+//
+// if (url != null) {
+// DataQuery query = SessionManager.getSession().retrieveQuery("com.arsdigita.categorization.getAllItemURLsForCategory");
+// query.setParameter("categoryID", id);
+// query.addEqualsFilter("lower(url)", url.toLowerCase());
+//
+// if (query.size() > 0) {
+// // we need to make sure that there is not an item
+// ACSObject item = getObject(state);
+// Collection list;
+// if (item instanceof ContentItem) {
+// list = BasicItemForm.getAllVersionIDs((ContentItem) item);
+// } else {
+// list = new ArrayList();
+// list.add(item.getID());
+// }
+// BigDecimal itemID;
+// while (query.next()) {
+// itemID = (BigDecimal) query.get("itemID");
+// if (!list.contains(itemID)) {
+// data.addError("There is already an item "
+// + "with the url " + url
+// + " in the category "
+// + cat.getName());
+// break;
+// }
+// }
+// }
+// }
+// } catch (DataObjectNotFoundException ex) {
+// s_log.error("Error processing category. Unable to find "
+// + "category with id " + id);
+// throw new FormProcessException(ex);
+// }
+// }
+// } else if (m_remove.isSelected(state)) {
+// if (data.get(ASSIGNED) == null) {
+// data.addError("Please select a category to remove");
+// }
+// }
+ }
+
+ // Add a "filler" option to the option group in order to ensure
+ // the correct horizontal width
+ private static void addFillerOption(OptionGroup o) {
+ o.addOption(new Option("", FILLER_OPTION));
+ }
+
+ /**
+ * @return the full path to a category
+ */
+ public static String getCategoryPath(Category c) {
+ return c.getName();
+// final StringBuffer s = new StringBuffer();
+// final CategoryCollection ancestors = c.getDefaultAscendants();
+// ancestors.addOrder(Category.DEFAULT_ANCESTORS);
+//
+// boolean isFirst = true;
+//
+// while (ancestors.next()) {
+// if (!isFirst) {
+// s.append(SEPARATOR);
+// }
+// s.append(ancestors.getCategory().getName());
+// isFirst = false;
+// }
+//
+// return s.toString();
+ }
+
+ /**
+ * A convenience method that abstracts SequentialMap
+ * to deal with categories
+ */
+ protected static class CategoryMap extends SequentialMap {
+
+ public CategoryMap() {
+ super();
+ }
+
+ public void add(Category c) {
+ super.put(c.getUniqueId(), c);
+ }
+ }
+}
diff --git a/ccm-cms/src/main/java/com/arsdigita/cms/ui/category/LinkForm.java b/ccm-cms/src/main/java/com/arsdigita/cms/ui/category/LinkForm.java
new file mode 100755
index 000000000..e24fce0e7
--- /dev/null
+++ b/ccm-cms/src/main/java/com/arsdigita/cms/ui/category/LinkForm.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2001-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.category;
+
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.form.Submit;
+import com.arsdigita.cms.ui.CategoryForm;
+import com.arsdigita.cms.ui.FormSecurityListener;
+import com.arsdigita.toolbox.ui.Cancellable;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+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.librecms.contentsection.privileges.AdminPrivileges;
+
+/**
+ * A form which edits secondary parents
+ *
+ * @author Michael Pih
+ * @author Stanislav Freidin
+ * @author Yannick Bülter
+ */
+public class LinkForm extends CategoryForm implements Cancellable {
+
+ private static final Logger LOGGER = LogManager.getLogger(
+ LinkForm.class);
+
+ private final CategoryRequestLocal m_category;
+ private final Submit m_cancelButton;
+
+ public LinkForm(final CategoryRequestLocal category) {
+ super("LinkForm");
+
+ m_category = category;
+
+ m_cancelButton = new Submit("Finish");
+ add(m_cancelButton, ColumnPanel.FULL_WIDTH | ColumnPanel.LEFT);
+
+ setAssignedCaption("Linked Categories");
+
+ addSubmissionListener
+ (new FormSecurityListener(AdminPrivileges.ADMINISTER_CATEGORIES));
+ }
+
+ public final boolean isCancelled(final PageState state) {
+ return m_cancelButton.isSelected(state);
+ }
+
+ /**
+ * Load all categories which are assigned to the current item.
+ */
+ protected void initAssignedCategories(PageState state, CategoryMap m) {
+ final Category category = m_category.getCategory(state);
+ m.add(category.getParentCategory());
+ /*final BigDecimal parentID = category.getDefaultParentCategory().getID();
+ CategoryCollection links = category.getParents();
+
+ while ( links.next() ) {
+ Category cat = links.getCategory();
+
+ if ( !cat.getID().equals(parentID) ) {
+ m.add(cat);
+ }
+ }
+ links.close();*/
+ }
+
+ /**
+ * Assign a secondary parent.
+ */
+ public void assignCategory(PageState state, Category category) {
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final PermissionChecker permissionChecker = cdiUtil.findBean(PermissionChecker.class);
+ final CategoryManager categoryManager = cdiUtil.findBean(CategoryManager.class);
+ final Category child = m_category.getCategory(state);
+ if (permissionChecker.isPermitted(AdminPrivileges.ADMINISTER_CATEGORIES, category)) {
+ categoryManager.addSubCategoryToCategory(child, category);
+ }
+ }
+
+ /**
+ * Unassign a secondary parent.
+ */
+ public void unassignCategory(PageState state, Category category) {
+ final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
+ final PermissionChecker permissionChecker = cdiUtil.findBean(PermissionChecker.class);
+ final CategoryManager categoryManager = cdiUtil.findBean(CategoryManager.class);
+ final Category child = m_category.getCategory(state);
+ if (permissionChecker.isPermitted(AdminPrivileges.ADMINISTER_CATEGORIES, category)) {
+ categoryManager.removeSubCategoryFromCategory(child, category);
+ }
+ }
+
+ /**
+ * The category cannot be its own parent. Its children cannot
+ * be parents either.
+ */
+ @Override
+ public Category getExcludedCategory(PageState state) {
+ return m_category.getCategory(state);
+ }
+
+ /**
+ * This method returns the URL for the given item to make sure that
+ * there are not two objects in the same category with the same URL.
+ */
+ protected final String getItemURL(final PageState state) {
+ return m_category.getCategory(state).getName();
+ //return m_category.getCategory(state).getURL();
+ }
+
+ protected final CcmObject getObject(final PageState state) {
+ return (Category) m_category.getCategory(state);
+ }
+}