001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.render;
021:
022: import java.io.IOException;
023: import java.io.StringWriter;
024: import java.util.Iterator;
025:
026: import org.jdom.Attribute;
027: import org.jdom.Element;
028: import org.jdom.output.Format;
029: import org.jdom.output.XMLOutputter;
030:
031: import com.ecyrd.jspwiki.WikiContext;
032: import com.ecyrd.jspwiki.htmltowiki.XHtmlToWikiConfig;
033: import com.ecyrd.jspwiki.parser.WikiDocument;
034:
035: /**
036: * Implements a WikiRendered that outputs XHTML in a format that is suitable
037: * for use by a WYSIWYG XHTML editor.
038: *
039: * @author David Au
040: * @since 2.5
041: */
042: public class WysiwygEditingRenderer extends WikiRenderer {
043:
044: private static final String A_ELEMENT = "a";
045: private static final String PRE_ELEMENT = "pre";
046: private static final String CLASS_ATTRIBUTE = "class";
047: private static final String HREF_ATTRIBUTE = "href";
048: private static final String TITLE_ATTRIBUTE = "title";
049: private static final String EDITPAGE = "createpage";
050: private static final String WIKIPAGE = "wikipage";
051: private static final String LINEBREAK = "\n";
052: private static final String LINKS_TRANSLATION = "$1#$2";
053: private static final String LINKS_SOURCE = "(.+)#section-.+-(.+)";
054:
055: public WysiwygEditingRenderer(WikiContext context, WikiDocument doc) {
056: super (context, doc);
057: }
058:
059: /*
060: * Recursively walk the XHTML DOM tree and manipulate specific elements to
061: * make them better for WYSIWYG editing.
062: */
063: private void processChildren(Element baseElement) {
064: for (Iterator itr = baseElement.getChildren().iterator(); itr
065: .hasNext();) {
066: Object childElement = itr.next();
067: if (childElement instanceof Element) {
068: Element element = (Element) childElement;
069: String elementName = element.getName().toLowerCase();
070: Attribute classAttr = element
071: .getAttribute(CLASS_ATTRIBUTE);
072:
073: if (elementName.equals(A_ELEMENT)) {
074: if (classAttr != null) {
075: String classValue = classAttr.getValue();
076: Attribute hrefAttr = element
077: .getAttribute(HREF_ATTRIBUTE);
078:
079: XHtmlToWikiConfig wikiConfig = new XHtmlToWikiConfig(
080: m_context);
081:
082: // Get the url for wiki page link - it's typically "Wiki.jsp?page=MyPage"
083: // or when using the ShortURLConstructor option, it's "wiki/MyPage" .
084: String wikiPageLinkUrl = wikiConfig
085: .getWikiJspPage();
086: String editPageLinkUrl = wikiConfig
087: .getEditJspPage();
088:
089: if (classValue.equals(WIKIPAGE)
090: || (hrefAttr != null && hrefAttr
091: .getValue().startsWith(
092: wikiPageLinkUrl))) {
093: // Remove the leading url string so that users will only see the
094: // wikipage's name when editing an existing wiki link.
095: // For example, change "Wiki.jsp?page=MyPage" to just "MyPage".
096: String newHref = hrefAttr
097: .getValue()
098: .substring(wikiPageLinkUrl.length());
099:
100: // Convert "This%20Pagename%20Has%20Spaces" to "This Pagename Has Spaces"
101: newHref = m_context.getEngine().decodeName(
102: newHref);
103:
104: // Handle links with section anchors.
105: // For example, we need to translate the html string "TargetPage#section-TargetPage-Heading2"
106: // to this wiki string: "TargetPage#Heading2".
107: hrefAttr.setValue(newHref.replaceFirst(
108: LINKS_SOURCE, LINKS_TRANSLATION));
109: } else if (classValue.equals(EDITPAGE)
110: || (hrefAttr != null && hrefAttr
111: .getValue().startsWith(
112: editPageLinkUrl))) {
113: Attribute titleAttr = element
114: .getAttribute(TITLE_ATTRIBUTE);
115: if (titleAttr != null) {
116: // remove the title since we don't want to eventually save the default undefined page title.
117: titleAttr.detach();
118: }
119:
120: String newHref = hrefAttr
121: .getValue()
122: .substring(editPageLinkUrl.length());
123: newHref = m_context.getEngine().decodeName(
124: newHref);
125:
126: hrefAttr.setValue(newHref);
127: }
128: }
129: } // end of check for "a" element
130: else if (elementName.equals(PRE_ELEMENT)) {
131: // We need to trim the surrounding whitespace to accomodate a FCK bug: when the first line
132: // of a <pre> tag contains only whitespace, then all the linebreaks in the <pre>
133: // tag will be lost due to FCK's html tidying.
134: String text = element.getTextTrim();
135: element.setText(text);
136: }
137:
138: processChildren(element);
139: }
140: }
141: }
142:
143: public String getString() throws IOException {
144: Element rootElement = m_document.getRootElement();
145: processChildren(rootElement);
146:
147: m_document.setContext(m_context);
148:
149: XMLOutputter output = new XMLOutputter();
150:
151: StringWriter out = new StringWriter();
152:
153: Format fmt = Format.getRawFormat();
154: fmt.setExpandEmptyElements(false);
155: fmt.setLineSeparator(LINEBREAK);
156:
157: output.setFormat(fmt);
158: output.outputElementContent(m_document.getRootElement(), out);
159:
160: return out.toString();
161: }
162: }
|