001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: /* @version $Id: SitetreeFragmentGenerator.java 159584 2005-03-31 12:49:41Z andreas $*/
020:
021: package org.apache.lenya.cms.cocoon.generation;
022:
023: import java.io.IOException;
024: import java.util.Map;
025:
026: import org.apache.avalon.framework.parameters.Parameters;
027: import org.apache.avalon.framework.service.ServiceSelector;
028: import org.apache.cocoon.ProcessingException;
029: import org.apache.cocoon.environment.ObjectModelHelper;
030: import org.apache.cocoon.environment.Request;
031: import org.apache.cocoon.environment.SourceResolver;
032: import org.apache.cocoon.generation.ServiceableGenerator;
033: import org.apache.lenya.cms.publication.Publication;
034: import org.apache.lenya.cms.publication.PublicationException;
035: import org.apache.lenya.cms.publication.PublicationUtil;
036: import org.apache.lenya.cms.site.Link;
037: import org.apache.lenya.cms.site.SiteException;
038: import org.apache.lenya.cms.site.SiteManager;
039: import org.apache.lenya.cms.site.SiteNode;
040: import org.apache.lenya.cms.site.SiteStructure;
041: import org.xml.sax.SAXException;
042: import org.xml.sax.helpers.AttributesImpl;
043:
044: /**
045: * Generates a fragment of the navigation XML from the sitetree, corresponding to a given node. The
046: * node is specified by the sitemap parameters area/path. If the sitemap parameter initialTree
047: * is true, the top nodes of the tree will be generated and the node given by the sitemap parameters
048: * area/path will be unfolded. If initialTree is false, only the children of the selected node
049: * will be generated.
050: */
051: public class SitetreeFragmentGenerator extends ServiceableGenerator {
052:
053: protected Publication publication;
054:
055: /** Parameter which denotes the path of the clicked node */
056: protected String path;
057:
058: /** Parameter which denotes the area of the clicked node */
059: protected String area;
060:
061: /**
062: * Parameter which decides if the initial tree with the root nodes is generated
063: */
064: protected boolean initialTree;
065:
066: /**
067: * Parameter which decides if the node mime types should be reported
068: */
069: protected boolean showType;
070:
071: /** FIXME: should pass this as a parameter */
072: protected String[] areas = null;
073:
074: /**
075: * Convenience object, so we don't need to create an AttributesImpl for every element.
076: */
077: protected AttributesImpl attributes;
078:
079: protected static final String PARAM_AREA = "area";
080: protected static final String PARAM_PATH = "path";
081: protected static final String PARAM_INITIAL = "initial";
082: protected static final String PARAM_TYPE = "mimetype";
083: protected static final String PARAM_AREAS = "areas";
084:
085: /** The URI of the namespace of this generator. */
086: protected static final String URI = "http://apache.org/cocoon/lenya/sitetree/1.0";
087: protected static final String XML_URI = "http://www.w3.org/XML/1998/namespace";
088:
089: /** The namespace prefix for this namespace. */
090: protected static final String PREFIX = "site";
091: protected static final String XML_PREFIX = "xml";
092:
093: protected static final String NODE_NODE = "node";
094: protected static final String NODE_LABEL = "label";
095: protected static final String NODE_SITE = "site";
096: protected static final String NODE_FRAGMENT = "fragment";
097:
098: protected static final String ATTR_ID = "id";
099: protected static final String ATTR_FOLDER = "folder";
100: protected static final String ATTR_AREA = "area";
101: protected static final String ATTR_PUBLICATION = "publication";
102: protected static final String ATTR_LABEL = "label";
103: protected static final String ATTR_VISIBLEINNAV = "visibleinnav";
104: protected static final String ATTR_LINK = "link";
105: protected static final String ATTR_BASE = "base";
106: protected static final String ATTR_SUFFIX = "suffix";
107: protected static final String ATTR_HREF = "href";
108: protected static final String ATTR_UUID = "uuid";
109: protected static final String ATTR_LANG = "lang";
110: protected static final String ATTR_TYPE = "mimetype";
111:
112: /**
113: * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(org.apache.cocoon.environment.SourceResolver,
114: * java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
115: */
116: public void setup(SourceResolver _resolver, Map _objectModel,
117: String src, Parameters par) throws ProcessingException,
118: SAXException, IOException {
119: super .setup(_resolver, _objectModel, src, par);
120:
121: Request request = ObjectModelHelper.getRequest(_objectModel);
122: if (getLogger().isDebugEnabled()) {
123: getLogger().debug(
124: "Resolving page envelope for URL ["
125: + request.getRequestURI() + "]");
126: }
127:
128: this .area = par.getParameter(PARAM_AREA, null);
129: this .path = par.getParameter(PARAM_PATH, null);
130:
131: if (par.isParameter(PARAM_INITIAL)) {
132: this .initialTree = Boolean.valueOf(
133: par.getParameter(PARAM_INITIAL, null))
134: .booleanValue();
135: } else {
136: this .initialTree = false;
137: }
138:
139: if (par.isParameter(PARAM_TYPE)) {
140: this .showType = Boolean.valueOf(
141: par.getParameter(PARAM_TYPE, null)).booleanValue();
142: } else {
143: this .showType = false;
144: }
145:
146: if (par.isParameter(PARAM_AREAS)) {
147: String parAreas = par.getParameter(PARAM_AREAS, null);
148: this .areas = parAreas.split(",");
149: } else {
150: String temp[] = { "authoring", "archive", "trash" };
151: this .areas = temp;
152: }
153:
154: if (this .getLogger().isDebugEnabled()) {
155: this .getLogger().debug("Parameter area: " + this .area);
156: this .getLogger().debug("Parameter path: " + this .path);
157: this .getLogger().debug(
158: "Parameter initialTree: " + this .initialTree);
159: StringBuffer areasStr = new StringBuffer();
160: for (int i = 0; i < this .areas.length; i++) {
161: areasStr.append(this .areas[i]).append(" ");
162: }
163: this .getLogger().debug(
164: "Parameter areas: " + areasStr.toString());
165: }
166:
167: try {
168: this .publication = PublicationUtil.getPublication(
169: this .manager, _objectModel);
170: } catch (Exception e) {
171: throw new ProcessingException(
172: "Could not create publication: ", e);
173: }
174: this .attributes = new AttributesImpl();
175:
176: }
177:
178: /**
179: * @see org.apache.cocoon.generation.Generator#generate()
180: */
181: public void generate() throws IOException, SAXException,
182: ProcessingException {
183:
184: try {
185:
186: this .contentHandler.startDocument();
187: this .contentHandler.startPrefixMapping(PREFIX, URI);
188:
189: this .attributes.clear();
190: this .attributes
191: .addAttribute("", ATTR_PUBLICATION,
192: ATTR_PUBLICATION, "CDATA", this .publication
193: .getId());
194:
195: if (!this .initialTree) {
196: this .attributes.addAttribute("", ATTR_AREA, ATTR_AREA,
197: "CDATA", this .area);
198: this .attributes.addAttribute("", ATTR_BASE, ATTR_BASE,
199: "CDATA", this .path);
200: }
201:
202: this .contentHandler.startElement(URI, NODE_FRAGMENT, PREFIX
203: + ':' + NODE_FRAGMENT, this .attributes);
204:
205: if (this .initialTree) {
206: for (int i = 0; i < this .areas.length; i++) {
207: generateFragmentInitial(this .areas[i]);
208: }
209: } else {
210: generateFragment();
211: }
212:
213: this .contentHandler.endElement(URI, NODE_FRAGMENT, PREFIX
214: + ':' + NODE_FRAGMENT);
215:
216: this .contentHandler.endPrefixMapping(PREFIX);
217: this .contentHandler.endDocument();
218:
219: } catch (final SAXException e) {
220: throw new ProcessingException(e);
221: } catch (final SiteException e) {
222: throw new ProcessingException(e);
223: }
224:
225: }
226:
227: /**
228: * Generates a fragment of the tree which contains the children of a given node.
229: * @throws SiteException
230: * @throws SAXException
231: * @throws ProcessingException
232: */
233: protected void generateFragment() throws SiteException,
234: SAXException, ProcessingException {
235:
236: if (!this .area.equals(Publication.AUTHORING_AREA)
237: && !this .area.equals(Publication.ARCHIVE_AREA)
238: && !this .area.equals(Publication.TRASH_AREA)
239: && !this .area.equals(Publication.LIVE_AREA)
240: && !this .area.equals(Publication.STAGING_AREA)) {
241: throw new ProcessingException("Invalid area: " + this .area);
242: }
243:
244: try {
245:
246: SiteStructure site = this .publication.getArea(this .area)
247: .getSite();
248:
249: SiteNode[] children;
250:
251: if (this .path.equals("/")) {
252: children = site.getTopLevelNodes();
253: } else {
254: SiteNode node = site.getNode(this .path);
255: children = node.getChildren();
256: }
257:
258: addNodes(children);
259: } catch (PublicationException e) {
260: throw new ProcessingException(e);
261: }
262: }
263:
264: /**
265: * Adds the given nodes (not recursive).
266: * @param children
267: * @throws SAXException
268: * @throws SiteException
269: */
270: protected void addNodes(SiteNode[] children) throws SAXException,
271: SiteException {
272: for (int i = 0; i < children.length; i++) {
273: startNode(NODE_NODE, children[i]);
274: addLabels(children[i]);
275: endNode(NODE_NODE);
276: }
277: }
278:
279: /**
280: * Generates the top node of the given area and then calls a recursive method to traverse the
281: * tree, if the node given by area/path is in this area.
282: * @param siteArea
283: * @throws SiteException
284: * @throws SAXException
285: * @throws ProcessingException
286: */
287: protected void generateFragmentInitial(String siteArea)
288: throws SiteException, SAXException, ProcessingException {
289:
290: ServiceSelector selector = null;
291: SiteManager siteManager = null;
292: try {
293: selector = (ServiceSelector) this .manager
294: .lookup(SiteManager.ROLE + "Selector");
295: siteManager = (SiteManager) selector
296: .select(this .publication.getSiteManagerHint());
297: SiteStructure siteTree = this .publication.getArea(siteArea)
298: .getSite();
299:
300: String label = "";
301: String isFolder = "";
302:
303: // FIXME: don't hardcode area label
304: if (siteArea.equals(Publication.AUTHORING_AREA))
305: label = "Authoring";
306: if (siteArea.equals(Publication.ARCHIVE_AREA))
307: label = "Archive";
308: if (siteArea.equals(Publication.TRASH_AREA))
309: label = "Trash";
310: if (siteArea.equals(Publication.LIVE_AREA))
311: label = "Live";
312: if (siteArea.equals(Publication.STAGING_AREA))
313: label = "Staging";
314:
315: if (siteTree.getTopLevelNodes().length > 0)
316: isFolder = "true";
317: else
318: isFolder = "false";
319:
320: this .attributes.clear();
321: this .attributes.addAttribute("", ATTR_AREA, ATTR_AREA,
322: "CDATA", siteArea);
323: this .attributes.addAttribute("", ATTR_FOLDER, ATTR_FOLDER,
324: "CDATA", isFolder);
325: this .attributes.addAttribute("", ATTR_LABEL, ATTR_LABEL,
326: "CDATA", label);
327:
328: startNode(NODE_SITE);
329:
330: if (this .area.equals(siteArea)) {
331: generateFragmentRecursive(siteTree.getTopLevelNodes(),
332: this .path);
333: }
334:
335: endNode(NODE_SITE);
336: } catch (Exception e) {
337: throw new ProcessingException(e);
338: } finally {
339: if (selector != null) {
340: if (siteManager != null) {
341: selector.release(siteManager);
342: }
343: this .manager.release(selector);
344: }
345: }
346: }
347:
348: /**
349: * Follows the path to find the way in the sitetree to the specified node and opens all
350: * folders on its way.
351: * @param nodes
352: * @param path
353: * @throws SiteException
354: * @throws SAXException
355: */
356: protected void generateFragmentRecursive(SiteNode[] nodes,
357: String path) throws SiteException, SAXException {
358: String nodeid;
359: String childid;
360:
361: if (nodes == null)
362: return;
363: if (path.startsWith("/"))
364: path = path.substring(1);
365: if (path.indexOf("/") != -1) {
366: nodeid = path.substring(0, path.indexOf("/"));
367: childid = path.substring(path.indexOf("/") + 1);
368: } else {
369: nodeid = path;
370: childid = "";
371: }
372:
373: for (int i = 0; i < nodes.length; i++) {
374: addNodeRecursive(nodes[i], nodeid, childid);
375: }
376: }
377:
378: /**
379: * Adds the given node, and if the node's id matched the given nodeid, it continues recursively.
380: * @param node
381: * @param nodeid
382: * @param childid
383: * @throws SAXException
384: * @throws SiteException
385: */
386: protected void addNodeRecursive(SiteNode node, String nodeid,
387: String childid) throws SAXException, SiteException {
388: startNode(NODE_NODE, node);
389: addLabels(node);
390: if (node.getName().equals(nodeid)) {
391: generateFragmentRecursive(node.getChildren(), childid);
392: }
393: endNode(NODE_NODE);
394: }
395:
396: /**
397: * Begins a named node and calls setNodeAttributes to set its attributes.
398: * @param nodeName the name of the new node
399: * @throws SAXException if an error occurs while creating the node
400: */
401: protected void startNode(String nodeName) throws SAXException {
402: this .contentHandler.startElement(URI, nodeName, PREFIX + ':'
403: + nodeName, this .attributes);
404: }
405:
406: /**
407: * Begins a named node and calls setNodeAttributes to set its attributes.
408: * @param nodeName the name of the new node
409: * @param node The attributes are taken from this node
410: * @throws SAXException if an error occurs while creating the node
411: * @throws SiteException
412: */
413: protected void startNode(String nodeName, SiteNode node)
414: throws SAXException, SiteException {
415: setNodeAttributes(node);
416: this .contentHandler.startElement(URI, nodeName, PREFIX + ':'
417: + nodeName, this .attributes);
418: }
419:
420: /**
421: * Sets the attributes for a given node. Sets attributes id, href, folder, suffix, basic-url,
422: * language-suffix.
423: * @param node
424: * @throws SAXException if an error occurs while setting the attributes
425: * @throws SiteException
426: */
427: protected void setNodeAttributes(SiteNode node)
428: throws SAXException, SiteException {
429: this .attributes.clear();
430:
431: String id = node.getName();
432: // String isVisible = Boolean.toString(node.visibleInNav());
433: String hasLink = Boolean.toString(node.hasLink());
434: String href = node.getHref();
435: String suffix = node.getSuffix();
436: String isFolder = Boolean.toString(isFolder(node));
437: String uuid = node.getUuid();
438:
439: if (this .getLogger().isDebugEnabled()) {
440: this .getLogger().debug("adding attribute id: " + id);
441: // this.getLogger().debug("adding attribute visibleinnav: " +
442: // isVisible);
443: this .getLogger().debug("adding attribute link: " + hasLink);
444: if (href != null)
445: this .getLogger()
446: .debug("adding attribute href: " + href);
447: if (suffix != null)
448: this .getLogger().debug(
449: "adding attribute suffix: " + suffix);
450: this .getLogger().debug(
451: "adding attribute folder: " + isFolder);
452: }
453: this .attributes.addAttribute("", ATTR_ID, ATTR_ID, "CDATA", id);
454: // attributes.addAttribute("", ATTR_VISIBLEINNAV, ATTR_VISIBLEINNAV,
455: // "CDATA", isVisible);
456: this .attributes.addAttribute("", ATTR_LINK, ATTR_LINK, "CDATA",
457: hasLink);
458: if (href != null)
459: this .attributes.addAttribute("", ATTR_HREF, ATTR_HREF,
460: "CDATA", href);
461: if (suffix != null)
462: this .attributes.addAttribute("", ATTR_SUFFIX, ATTR_SUFFIX,
463: "CDATA", suffix);
464: if (uuid != null)
465: this .attributes.addAttribute("", ATTR_UUID, ATTR_UUID,
466: "CDATA", uuid);
467: this .attributes.addAttribute("", ATTR_FOLDER, ATTR_FOLDER,
468: "CDATA", isFolder);
469:
470: if (this .showType) {
471: try {
472: String type = this .publication.getArea(this .area)
473: .getDocument(node.getUuid(),
474: this .publication.getDefaultLanguage())
475: .getMimeType();
476: this .attributes.addAttribute("", ATTR_TYPE, ATTR_TYPE,
477: "CDATA", type);
478: } catch (PublicationException e) {
479: throw new SiteException(e);
480: }
481: }
482:
483: }
484:
485: /**
486: * Returns a value to indicate whether a node is a folder (contains subnodes). With the
487: * incremental sitetree loading, we sometimes load nodes which are folders, but we don't load
488: * their children. But we still have to know if it's a folder or not, i.e. if it can be opened.
489: * @param node
490: * @return A boolean value.
491: */
492: protected boolean isFolder(SiteNode node) {
493: if (node.getChildren().length > 0)
494: return true;
495: return false;
496: }
497:
498: /**
499: * Ends the named node.
500: * @param nodeName the name of the new node
501: * @throws SAXException if an error occurs while closing the node
502: */
503: protected void endNode(String nodeName) throws SAXException {
504: this .contentHandler.endElement(URI, nodeName, PREFIX + ':'
505: + nodeName);
506: }
507:
508: /**
509: * Finds all the label children of a node and adds them to the nav xml.
510: * @param node
511: * @throws SAXException
512: */
513: protected void addLabels(SiteNode node) throws SAXException {
514: String[] languages = node.getLanguages();
515:
516: for (int i = 0; i < languages.length; i++) {
517: Link link;
518: try {
519: link = node.getLink(languages[i]);
520: } catch (SiteException e) {
521: throw new RuntimeException(e);
522: }
523: addLabel(link.getLabel(), languages[i]);
524: }
525: }
526:
527: /**
528: * Adds a label element of a given language.
529: * @param label the value of the label
530: * @param language the language of the label
531: * @throws SAXException
532: */
533: protected void addLabel(String label, String language)
534: throws SAXException {
535: this .attributes.clear();
536: this .attributes.addAttribute(XML_URI, ATTR_LANG, XML_PREFIX
537: + ":" + ATTR_LANG, "CDATA", language);
538:
539: this .contentHandler.startElement(URI, NODE_LABEL, PREFIX + ':'
540: + NODE_LABEL, this .attributes);
541: char[] labelArray = label.toCharArray();
542: this .contentHandler
543: .characters(labelArray, 0, labelArray.length);
544: this .contentHandler.endElement(URI, NODE_LABEL, PREFIX + ':'
545: + NODE_LABEL);
546: }
547:
548: }
|