/* * 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.aplaws.ui; import java.math.BigDecimal; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.arsdigita.aplaws.Aplaws; import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.form.Widget; import com.arsdigita.bebop.parameters.ArrayParameter; import com.arsdigita.bebop.parameters.BigDecimalParameter; import com.arsdigita.bebop.parameters.StringParameter; import com.arsdigita.categorization.Category; import com.arsdigita.cms.CMS; import com.arsdigita.cms.ContentItem; import com.arsdigita.cms.ContentSection; import com.arsdigita.domain.DomainCollection; import com.arsdigita.domain.DomainObjectFactory; import com.arsdigita.london.terms.Domain; import com.arsdigita.london.terms.Term; import com.arsdigita.london.terms.indexing.Indexer; import com.arsdigita.london.terms.indexing.RankedTerm; import com.arsdigita.persistence.DataCollection; import com.arsdigita.persistence.SessionManager; import com.arsdigita.xml.Element; import com.arsdigita.xml.XML; /** * A Widget for selecting Terms. Based heavily on CategoryWidget. * * @author mbooth@redhat.com * * Chris Gilbert - updated to identify each node uniquely (correct behaviour * for polyhierarchical trees) - also, allow ajax update on all branches or * just top level branch * * nb - widget applies to allocation of categories to any ACSObject hence * xml prefix should be more generic eg bebop rather than cms. cms retained * for compatibility with existing stylesheets */ public class TermWidget extends Widget { private StringParameter m_mode; private ACSObjectCategoryPicker m_picker; public TermWidget(StringParameter mode, ACSObjectCategoryPicker picker) { super(new ArrayParameter(new BigDecimalParameter("category"))); m_mode = mode; m_picker = picker; } protected String getType() { return "category"; } public boolean isCompound() { return false; } protected void generateWidget(PageState state, Element parent) { Domain domain = m_picker.getDomain(state); Element widget = parent.newChildElement("cms:categoryWidget", CMS.CMS_XML_NS); exportAttributes(widget); widget.addAttribute("mode", (String)state.getValue(m_mode)); widget.addAttribute("name", getName()); Set ids = new HashSet(); BigDecimal[] values = (BigDecimal[])getValue(state); if (values != null) { for (int i = 0 ; i < values.length ; i++) { ids.add(values[i]); } } // only root terms at first, the rest is loaded on-demand via AJAX DomainCollection terms = domain.getRootTerms(); terms.addPath("model.parents.link.sortKey"); terms.addPath("model.parents.id"); terms.addPath("domain.key"); // Pull out everything related to the category, otherwise // another query per row is executed when doing term.getModel(); terms.addPath("model.objectType"); terms.addPath("model.displayName"); terms.addPath("model.defaultDomainClass"); terms.addPath("model.name"); terms.addPath("model.description"); terms.addPath("model.url"); terms.addPath("model.isEnabled"); terms.addPath("model.isAbstract"); terms.addPath("model.defaultAncestors"); List roots = new LinkedList(); while (terms.next()) { Term term = (Term) terms.getDomainObject(); roots.add(new TermSortKeyPair (term,(BigDecimal)terms.get("model.parents.link.sortKey"))); } Element el = generateCategory(widget, domain.getModel(), ids, null); /** * Used by kea based keyphrase extraction facility. * (Added r1885) * * @Author: terry_permeance */ Indexer indexer = Indexer.retrieve(domain); if (indexer != null) { ContentItem item = CMS.getContext().getContentItem(); List autoTerms = indexer.index(item, 16); Element autoCategories = widget.newChildElement("cms:autoCategories", CMS.CMS_XML_NS); for (Iterator i = autoTerms.iterator(); i.hasNext(); ) { RankedTerm nextRankedTerm = i.next(); Category cat = nextRankedTerm.getTerm().getModel(); if (!ids.contains(cat.getID())) { String fullname = cat.getQualifiedName(" > ", false); if (fullname != null) { Element catEl = autoCategories.newChildElement("cms:category", CMS.CMS_XML_NS); catEl.addAttribute("id", XML.format(cat.getID())); catEl.addAttribute("name", cat.getName()); catEl.addAttribute("description", cat.getDescription()); catEl.addAttribute("isAbstract", cat.isAbstract() ? "1" : "0"); catEl.addAttribute("isEnabled", cat.isEnabled() ? "1" : "0"); catEl.addAttribute("sortKey", nextRankedTerm.getRanking().toString()); catEl.addAttribute("fullname", fullname); } } } } if (Aplaws.getAplawsConfig().ajaxExpandAllBranches()) { // add attribute to the parent node, so that in stylesheet // we can look for any ancestor with this attribute (can't // add attribute to categoryWidget element as that is not // visible when subbranches are transformed) el.addAttribute("expand", "all" ); } for (Iterator i=roots.iterator(); i.hasNext(); ) { TermSortKeyPair pair = (TermSortKeyPair) i.next(); Term term = pair.getTerm(); BigDecimal sortKey = pair.getSortKey(); generateRootTerm(el, term, ids, sortKey); } } public static Element generateCategory(Element parent, Category cat, Set selected, BigDecimal sortKey) { Element el = parent.newChildElement("cms:category", CMS.CMS_XML_NS); el.addAttribute("id", XML.format(cat.getID())); el.addAttribute("name", cat.getName()); el.addAttribute("description", cat.getDescription()); el.addAttribute("isSelected", selected.contains(cat.getID()) ? "1" : "0"); el.addAttribute("isAbstract", cat.isAbstract() ? "1" : "0"); el.addAttribute("isEnabled", cat.isEnabled() ? "1" : "0"); if (sortKey != null) { el.addAttribute("sortKey", sortKey.toString()); } // sort order attribute added to every node so that we can // correctly transform xml fragments returned by ajax el.addAttribute("order", ContentSection.getConfig().getCategoryTreeOrder()); StringBuffer path = new StringBuffer(parent.getAttribute("fullname")); if (path.length() > 0) path.append(" > "); path.append(cat.getName()); el.addAttribute("fullname", path.toString()); // need to uniquely identify each node in polyhierarchical trees // so that expand/contract is applied to the correct node by // javascript getElementByID function StringBuffer nodeID = new StringBuffer(parent.getAttribute("node-id")); if (nodeID.length() > 0) nodeID.append("-"); nodeID.append(cat.getID()); el.addAttribute("node-id", nodeID.toString()); return el; } public static Element generateTerm(Element parent, Term term, Set selected, BigDecimal sortKey) { Category cat = term.getModel(); Element el = generateCategory(parent, cat, selected, sortKey); el.addAttribute("pid", term.getUniqueID().toString()); el.addAttribute("domain", term.getDomain().getKey()); return el; } private static void generateRootTerm(Element parent, Term term, Set selected, BigDecimal sortKey) { Element el = generateTerm(parent, term, selected, sortKey); el.addAttribute("root","1"); } public static void generateSubtree(Element parent, Category root) { DataCollection terms = SessionManager.getSession().retrieve( Term.BASE_DATA_OBJECT_TYPE); terms.addEqualsFilter("model.roTransParents.id", root.getID()); terms.addEqualsFilter("model.parents.link.relationType", "child"); Map children = new HashMap(); while (terms.next()) { Term term = (Term) DomainObjectFactory.newInstance(terms .getDataObject()); BigDecimal parentID = (BigDecimal) terms.get("model.parents.id"); List childList = (List) children.get(parentID); if (childList == null) { childList = new LinkedList(); children.put(parentID, childList); } childList.add(new TermSortKeyPair(term, (BigDecimal) terms .get("model.parents.link.sortKey"))); } // XXX for isSelected Set ids = new HashSet(); Element el = generateCategory(parent, root, ids, null); el.addAttribute("fullname", root.getName()); el.addAttribute("node-id", root.getID().toString()); el.addAttribute("order", ContentSection.getConfig().getCategoryTreeOrder()); if (Aplaws.getAplawsConfig().ajaxExpandAllBranches()) { // recognisable attribute has to be in the XML for each snippet that is transformed, // hence add it to the parent el.addAttribute("expand", "all" ); } List roots = (List) children.get(root.getID()); if (null != roots) { Iterator i = roots.iterator(); while (i.hasNext()) { TermSortKeyPair pair = (TermSortKeyPair) i.next(); Term term = pair.getTerm(); BigDecimal sortKey = pair.getSortKey(); generateTermWithChildren(el, term, ids, sortKey, children); } } } private static void generateTermWithChildren(Element parent, Term term, Set selected, BigDecimal sortKey, Map children) { Category cat = term.getModel(); Element el = generateCategory(parent, cat, selected, sortKey); el.addAttribute("pid", term.getUniqueID().toString()); el.addAttribute("domain", term.getDomain().getKey()); List c = (List) children.get(cat.getID()); if (c != null) { Iterator i = c.iterator(); while (i.hasNext()) { TermSortKeyPair pair = (TermSortKeyPair) i.next(); Term child = pair.getTerm(); BigDecimal childSortKey = pair.getSortKey(); // either generate next level down, or get all levels below current if (Aplaws.getAplawsConfig().ajaxExpandAllBranches()) { generateTerm(el, child, selected, childSortKey); } else { generateTermWithChildren(el, child, selected, childSortKey, children); } } } } private static class TermSortKeyPair { private Term m_term; private BigDecimal m_sortKey; public TermSortKeyPair(Term term, BigDecimal sortKey) { m_term = term; m_sortKey = sortKey; } public Term getTerm() { return m_term; } public BigDecimal getSortKey() { return m_sortKey; } } }