| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| /* @version $Id: SitetreeFragmentGenerator.java 159584 2005-03-31 12:49:41Z andreas $*/ |
| |
| package org.apache.lenya.cms.cocoon.generation; |
| |
| import java.io.IOException; |
| import java.util.Map; |
| |
| import org.apache.avalon.framework.parameters.Parameters; |
| import org.apache.avalon.framework.service.ServiceSelector; |
| import org.apache.cocoon.ProcessingException; |
| import org.apache.cocoon.environment.ObjectModelHelper; |
| import org.apache.cocoon.environment.Request; |
| import org.apache.cocoon.environment.SourceResolver; |
| import org.apache.cocoon.generation.ServiceableGenerator; |
| import org.apache.lenya.cms.publication.Publication; |
| import org.apache.lenya.cms.publication.PublicationException; |
| import org.apache.lenya.cms.publication.PublicationUtil; |
| import org.apache.lenya.cms.site.Link; |
| import org.apache.lenya.cms.site.SiteException; |
| import org.apache.lenya.cms.site.SiteManager; |
| import org.apache.lenya.cms.site.SiteNode; |
| import org.apache.lenya.cms.site.SiteStructure; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.AttributesImpl; |
| |
| /** |
| * Generates a fragment of the navigation XML from the sitetree, corresponding to a given node. The |
| * node is specified by the sitemap parameters area/path. If the sitemap parameter initialTree |
| * is true, the top nodes of the tree will be generated and the node given by the sitemap parameters |
| * area/path will be unfolded. If initialTree is false, only the children of the selected node |
| * will be generated. |
| */ |
| public class SitetreeFragmentGenerator extends ServiceableGenerator { |
| |
| protected Publication publication; |
| |
| /** Parameter which denotes the path of the clicked node */ |
| protected String path; |
| |
| /** Parameter which denotes the area of the clicked node */ |
| protected String area; |
| |
| /** |
| * Parameter which decides if the initial tree with the root nodes is generated |
| */ |
| protected boolean initialTree; |
| |
| /** |
| * Parameter which decides if the node mime types should be reported |
| */ |
| protected boolean showType; |
| |
| /** FIXME: should pass this as a parameter */ |
| protected String[] areas = null; |
| |
| /** |
| * Convenience object, so we don't need to create an AttributesImpl for every element. |
| */ |
| protected AttributesImpl attributes; |
| |
| protected static final String PARAM_AREA = "area"; |
| protected static final String PARAM_PATH = "path"; |
| protected static final String PARAM_INITIAL = "initial"; |
| protected static final String PARAM_TYPE = "mimetype"; |
| protected static final String PARAM_AREAS = "areas"; |
| |
| /** The URI of the namespace of this generator. */ |
| protected static final String URI = "http://apache.org/cocoon/lenya/sitetree/1.0"; |
| protected static final String XML_URI = "http://www.w3.org/XML/1998/namespace"; |
| |
| /** The namespace prefix for this namespace. */ |
| protected static final String PREFIX = "site"; |
| protected static final String XML_PREFIX = "xml"; |
| |
| protected static final String NODE_NODE = "node"; |
| protected static final String NODE_LABEL = "label"; |
| protected static final String NODE_SITE = "site"; |
| protected static final String NODE_FRAGMENT = "fragment"; |
| |
| protected static final String ATTR_ID = "id"; |
| protected static final String ATTR_FOLDER = "folder"; |
| protected static final String ATTR_AREA = "area"; |
| protected static final String ATTR_PUBLICATION = "publication"; |
| protected static final String ATTR_LABEL = "label"; |
| protected static final String ATTR_VISIBLEINNAV = "visibleinnav"; |
| protected static final String ATTR_LINK = "link"; |
| protected static final String ATTR_BASE = "base"; |
| protected static final String ATTR_SUFFIX = "suffix"; |
| protected static final String ATTR_HREF = "href"; |
| protected static final String ATTR_UUID = "uuid"; |
| protected static final String ATTR_LANG = "lang"; |
| protected static final String ATTR_TYPE = "mimetype"; |
| |
| /** |
| * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(org.apache.cocoon.environment.SourceResolver, |
| * java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters) |
| */ |
| public void setup(SourceResolver _resolver, Map _objectModel, String src, Parameters par) |
| throws ProcessingException, SAXException, IOException { |
| super.setup(_resolver, _objectModel, src, par); |
| |
| Request request = ObjectModelHelper.getRequest(_objectModel); |
| if (getLogger().isDebugEnabled()) { |
| getLogger().debug("Resolving page envelope for URL [" + request.getRequestURI() + "]"); |
| } |
| |
| this.area = par.getParameter(PARAM_AREA, null); |
| this.path = par.getParameter(PARAM_PATH, null); |
| |
| if (par.isParameter(PARAM_INITIAL)) { |
| this.initialTree = Boolean.valueOf(par.getParameter(PARAM_INITIAL, null)) |
| .booleanValue(); |
| } else { |
| this.initialTree = false; |
| } |
| |
| if (par.isParameter(PARAM_TYPE)) { |
| this.showType = Boolean.valueOf(par.getParameter(PARAM_TYPE, null)).booleanValue(); |
| } else { |
| this.showType = false; |
| } |
| |
| if (par.isParameter(PARAM_AREAS)) { |
| String parAreas = par.getParameter(PARAM_AREAS, null); |
| this.areas = parAreas.split(","); |
| } else { |
| String temp[] = { "authoring", "archive", "trash" }; |
| this.areas = temp; |
| } |
| |
| if (this.getLogger().isDebugEnabled()) { |
| this.getLogger().debug("Parameter area: " + this.area); |
| this.getLogger().debug("Parameter path: " + this.path); |
| this.getLogger().debug("Parameter initialTree: " + this.initialTree); |
| StringBuffer areasStr = new StringBuffer(); |
| for (int i = 0; i < this.areas.length; i++) { |
| areasStr.append(this.areas[i]).append(" "); |
| } |
| this.getLogger().debug("Parameter areas: " + areasStr.toString()); |
| } |
| |
| try { |
| this.publication = PublicationUtil.getPublication(this.manager, _objectModel); |
| } catch (Exception e) { |
| throw new ProcessingException("Could not create publication: ", e); |
| } |
| this.attributes = new AttributesImpl(); |
| |
| } |
| |
| /** |
| * @see org.apache.cocoon.generation.Generator#generate() |
| */ |
| public void generate() throws IOException, SAXException, ProcessingException { |
| |
| try { |
| |
| this.contentHandler.startDocument(); |
| this.contentHandler.startPrefixMapping(PREFIX, URI); |
| |
| this.attributes.clear(); |
| this.attributes.addAttribute("", ATTR_PUBLICATION, ATTR_PUBLICATION, "CDATA", |
| this.publication.getId()); |
| |
| if (!this.initialTree) { |
| this.attributes.addAttribute("", ATTR_AREA, ATTR_AREA, "CDATA", this.area); |
| this.attributes.addAttribute("", ATTR_BASE, ATTR_BASE, "CDATA", this.path); |
| } |
| |
| this.contentHandler.startElement(URI, |
| NODE_FRAGMENT, |
| PREFIX + ':' + NODE_FRAGMENT, |
| this.attributes); |
| |
| if (this.initialTree) { |
| for (int i = 0; i < this.areas.length; i++) { |
| generateFragmentInitial(this.areas[i]); |
| } |
| } else { |
| generateFragment(); |
| } |
| |
| this.contentHandler.endElement(URI, NODE_FRAGMENT, PREFIX + ':' + NODE_FRAGMENT); |
| |
| this.contentHandler.endPrefixMapping(PREFIX); |
| this.contentHandler.endDocument(); |
| |
| } catch (final SAXException e) { |
| throw new ProcessingException(e); |
| } catch (final SiteException e) { |
| throw new ProcessingException(e); |
| } |
| |
| } |
| |
| /** |
| * Generates a fragment of the tree which contains the children of a given node. |
| * @throws SiteException |
| * @throws SAXException |
| * @throws ProcessingException |
| */ |
| protected void generateFragment() throws SiteException, SAXException, ProcessingException { |
| |
| if (!this.area.equals(Publication.AUTHORING_AREA) |
| && !this.area.equals(Publication.ARCHIVE_AREA) |
| && !this.area.equals(Publication.TRASH_AREA) |
| && !this.area.equals(Publication.LIVE_AREA) |
| && !this.area.equals(Publication.STAGING_AREA)) { |
| throw new ProcessingException("Invalid area: " + this.area); |
| } |
| |
| try { |
| |
| SiteStructure site = this.publication.getArea(this.area).getSite(); |
| |
| SiteNode[] children; |
| |
| if (this.path.equals("/")) { |
| children = site.getTopLevelNodes(); |
| } |
| else { |
| SiteNode node = site.getNode(this.path); |
| children = node.getChildren(); |
| } |
| |
| addNodes(children); |
| } catch (PublicationException e) { |
| throw new ProcessingException(e); |
| } |
| } |
| |
| /** |
| * Adds the given nodes (not recursive). |
| * @param children |
| * @throws SAXException |
| * @throws SiteException |
| */ |
| protected void addNodes(SiteNode[] children) throws SAXException, SiteException { |
| for (int i = 0; i < children.length; i++) { |
| startNode(NODE_NODE, children[i]); |
| addLabels(children[i]); |
| endNode(NODE_NODE); |
| } |
| } |
| |
| /** |
| * Generates the top node of the given area and then calls a recursive method to traverse the |
| * tree, if the node given by area/path is in this area. |
| * @param siteArea |
| * @throws SiteException |
| * @throws SAXException |
| * @throws ProcessingException |
| */ |
| protected void generateFragmentInitial(String siteArea) throws SiteException, SAXException, |
| ProcessingException { |
| |
| ServiceSelector selector = null; |
| SiteManager siteManager = null; |
| try { |
| selector = (ServiceSelector) this.manager.lookup(SiteManager.ROLE + "Selector"); |
| siteManager = (SiteManager) selector.select(this.publication.getSiteManagerHint()); |
| SiteStructure siteTree = this.publication.getArea(siteArea).getSite(); |
| |
| String label = ""; |
| String isFolder = ""; |
| |
| // FIXME: don't hardcode area label |
| if (siteArea.equals(Publication.AUTHORING_AREA)) |
| label = "Authoring"; |
| if (siteArea.equals(Publication.ARCHIVE_AREA)) |
| label = "Archive"; |
| if (siteArea.equals(Publication.TRASH_AREA)) |
| label = "Trash"; |
| if (siteArea.equals(Publication.LIVE_AREA)) |
| label = "Live"; |
| if (siteArea.equals(Publication.STAGING_AREA)) |
| label = "Staging"; |
| |
| if (siteTree.getTopLevelNodes().length > 0) |
| isFolder = "true"; |
| else |
| isFolder = "false"; |
| |
| this.attributes.clear(); |
| this.attributes.addAttribute("", ATTR_AREA, ATTR_AREA, "CDATA", siteArea); |
| this.attributes.addAttribute("", ATTR_FOLDER, ATTR_FOLDER, "CDATA", isFolder); |
| this.attributes.addAttribute("", ATTR_LABEL, ATTR_LABEL, "CDATA", label); |
| |
| startNode(NODE_SITE); |
| |
| if (this.area.equals(siteArea)) { |
| generateFragmentRecursive(siteTree.getTopLevelNodes(), this.path); |
| } |
| |
| endNode(NODE_SITE); |
| } catch (Exception e) { |
| throw new ProcessingException(e); |
| } finally { |
| if (selector != null) { |
| if (siteManager != null) { |
| selector.release(siteManager); |
| } |
| this.manager.release(selector); |
| } |
| } |
| } |
| |
| /** |
| * Follows the path to find the way in the sitetree to the specified node and opens all |
| * folders on its way. |
| * @param nodes |
| * @param path |
| * @throws SiteException |
| * @throws SAXException |
| */ |
| protected void generateFragmentRecursive(SiteNode[] nodes, String path) |
| throws SiteException, SAXException { |
| String nodeid; |
| String childid; |
| |
| if (nodes == null) |
| return; |
| if (path.startsWith("/")) |
| path = path.substring(1); |
| if (path.indexOf("/") != -1) { |
| nodeid = path.substring(0, path.indexOf("/")); |
| childid = path.substring(path.indexOf("/") + 1); |
| } else { |
| nodeid = path; |
| childid = ""; |
| } |
| |
| for (int i = 0; i < nodes.length; i++) { |
| addNodeRecursive(nodes[i], nodeid, childid); |
| } |
| } |
| |
| /** |
| * Adds the given node, and if the node's id matched the given nodeid, it continues recursively. |
| * @param node |
| * @param nodeid |
| * @param childid |
| * @throws SAXException |
| * @throws SiteException |
| */ |
| protected void addNodeRecursive(SiteNode node, String nodeid, String childid) |
| throws SAXException, SiteException { |
| startNode(NODE_NODE, node); |
| addLabels(node); |
| if (node.getName().equals(nodeid)) { |
| generateFragmentRecursive(node.getChildren(), childid); |
| } |
| endNode(NODE_NODE); |
| } |
| |
| /** |
| * Begins a named node and calls setNodeAttributes to set its attributes. |
| * @param nodeName the name of the new node |
| * @throws SAXException if an error occurs while creating the node |
| */ |
| protected void startNode(String nodeName) throws SAXException { |
| this.contentHandler.startElement(URI, nodeName, PREFIX + ':' + nodeName, this.attributes); |
| } |
| |
| /** |
| * Begins a named node and calls setNodeAttributes to set its attributes. |
| * @param nodeName the name of the new node |
| * @param node The attributes are taken from this node |
| * @throws SAXException if an error occurs while creating the node |
| * @throws SiteException |
| */ |
| protected void startNode(String nodeName, SiteNode node) throws SAXException, SiteException { |
| setNodeAttributes(node); |
| this.contentHandler.startElement(URI, nodeName, PREFIX + ':' + nodeName, this.attributes); |
| } |
| |
| /** |
| * Sets the attributes for a given node. Sets attributes id, href, folder, suffix, basic-url, |
| * language-suffix. |
| * @param node |
| * @throws SAXException if an error occurs while setting the attributes |
| * @throws SiteException |
| */ |
| protected void setNodeAttributes(SiteNode node) throws SAXException, SiteException { |
| this.attributes.clear(); |
| |
| String id = node.getName(); |
| // String isVisible = Boolean.toString(node.visibleInNav()); |
| String hasLink = Boolean.toString(node.hasLink()); |
| String href = node.getHref(); |
| String suffix = node.getSuffix(); |
| String isFolder = Boolean.toString(isFolder(node)); |
| String uuid = node.getUuid(); |
| |
| if (this.getLogger().isDebugEnabled()) { |
| this.getLogger().debug("adding attribute id: " + id); |
| // this.getLogger().debug("adding attribute visibleinnav: " + |
| // isVisible); |
| this.getLogger().debug("adding attribute link: " + hasLink); |
| if (href != null) |
| this.getLogger().debug("adding attribute href: " + href); |
| if (suffix != null) |
| this.getLogger().debug("adding attribute suffix: " + suffix); |
| this.getLogger().debug("adding attribute folder: " + isFolder); |
| } |
| this.attributes.addAttribute("", ATTR_ID, ATTR_ID, "CDATA", id); |
| // attributes.addAttribute("", ATTR_VISIBLEINNAV, ATTR_VISIBLEINNAV, |
| // "CDATA", isVisible); |
| this.attributes.addAttribute("", ATTR_LINK, ATTR_LINK, "CDATA", hasLink); |
| if (href != null) |
| this.attributes.addAttribute("", ATTR_HREF, ATTR_HREF, "CDATA", href); |
| if (suffix != null) |
| this.attributes.addAttribute("", ATTR_SUFFIX, ATTR_SUFFIX, "CDATA", suffix); |
| if (uuid != null) |
| this.attributes.addAttribute("", ATTR_UUID, ATTR_UUID, "CDATA", uuid); |
| this.attributes.addAttribute("", ATTR_FOLDER, ATTR_FOLDER, "CDATA", isFolder); |
| |
| if (this.showType) { |
| try { |
| String type = this.publication.getArea(this.area) |
| .getDocument(node.getUuid(),this.publication.getDefaultLanguage()) |
| .getMimeType(); |
| this.attributes.addAttribute("",ATTR_TYPE, ATTR_TYPE, "CDATA", type); |
| } catch (PublicationException e) { |
| throw new SiteException(e); |
| } |
| } |
| |
| } |
| |
| /** |
| * Returns a value to indicate whether a node is a folder (contains subnodes). With the |
| * incremental sitetree loading, we sometimes load nodes which are folders, but we don't load |
| * their children. But we still have to know if it's a folder or not, i.e. if it can be opened. |
| * @param node |
| * @return A boolean value. |
| */ |
| protected boolean isFolder(SiteNode node) { |
| if (node.getChildren().length > 0) |
| return true; |
| return false; |
| } |
| |
| /** |
| * Ends the named node. |
| * @param nodeName the name of the new node |
| * @throws SAXException if an error occurs while closing the node |
| */ |
| protected void endNode(String nodeName) throws SAXException { |
| this.contentHandler.endElement(URI, nodeName, PREFIX + ':' + nodeName); |
| } |
| |
| /** |
| * Finds all the label children of a node and adds them to the nav xml. |
| * @param node |
| * @throws SAXException |
| */ |
| protected void addLabels(SiteNode node) throws SAXException { |
| String[] languages = node.getLanguages(); |
| |
| for (int i = 0; i < languages.length; i++) { |
| Link link; |
| try { |
| link = node.getLink(languages[i]); |
| } catch (SiteException e) { |
| throw new RuntimeException(e); |
| } |
| addLabel(link.getLabel(), languages[i]); |
| } |
| } |
| |
| /** |
| * Adds a label element of a given language. |
| * @param label the value of the label |
| * @param language the language of the label |
| * @throws SAXException |
| */ |
| protected void addLabel(String label, String language) throws SAXException { |
| this.attributes.clear(); |
| this.attributes.addAttribute(XML_URI, |
| ATTR_LANG, |
| XML_PREFIX + ":" + ATTR_LANG, |
| "CDATA", |
| language); |
| |
| this.contentHandler.startElement(URI, |
| NODE_LABEL, |
| PREFIX + ':' + NODE_LABEL, |
| this.attributes); |
| char[] labelArray = label.toCharArray(); |
| this.contentHandler.characters(labelArray, 0, labelArray.length); |
| this.contentHandler.endElement(URI, NODE_LABEL, PREFIX + ':' + NODE_LABEL); |
| } |
| |
| } |