From 51f333c7ff92ab3f3743bbd428b2bf5e94497c79 Mon Sep 17 00:00:00 2001 From: baka Date: Wed, 22 Mar 2017 17:13:11 +0000 Subject: [PATCH] Missed two files in last commit, now everything is fine git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4637 8810af33-2d31-482b-a856-94f89814c4df --- .../com/arsdigita/cms/ui/CategoryForm.java | 516 ++++++++++++++++++ .../arsdigita/cms/ui/category/LinkForm.java | 136 +++++ 2 files changed, 652 insertions(+) create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/CategoryForm.java create mode 100755 ccm-cms/src/main/java/com/arsdigita/cms/ui/category/LinkForm.java 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.List excludedSubTree = getExcludedCategory(state).getSubCategories(); + excludedSubTree.forEach(excluded::add); + } + CategoryMap assigned = getAssignedCategories(state); + SortedMap sortedCats = new TreeMap(); + java.util.List children = root.getSubCategories(); + children.forEach(x -> sortedCats.put(x.getName(), x.getUniqueId())); + + Iterator it = sortedCats.entrySet().iterator(); + Map.Entry entry; + String path; + String id; + boolean notExcluded; + boolean notAlreadyAssigned; + boolean notRoot; + + while (it.hasNext()) { + entry = (Map.Entry) it.next(); + path = (String) entry.getKey(); + id = (String) entry.getValue(); + + notExcluded = !excluded.containsKey(id); + notAlreadyAssigned = !assigned.containsKey(id); + notRoot = !id.equals(root.getUniqueId()); + + if (notExcluded && notAlreadyAssigned && notRoot) { + target.addOption(new Option(id, new Text(path))); + } + + } + + addFillerOption(target); + } + } + + /** + * Populate a {@link CategoryMap} with all categories which are assigned to + * the item. Child classes should override this method to do the right thing. + * + * @param map The sequential map of all categories which are assigned to + * the current item. Overridden method should repeatedly + * call 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); + } +}