001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki.render; 020 021import java.io.IOException; 022import java.io.StringWriter; 023import java.util.Iterator; 024 025import org.jdom2.Attribute; 026import org.jdom2.Element; 027import org.jdom2.output.Format; 028import org.jdom2.output.XMLOutputter; 029 030import org.apache.wiki.WikiContext; 031import org.apache.wiki.htmltowiki.XHtmlToWikiConfig; 032import org.apache.wiki.parser.WikiDocument; 033 034/** 035 * Implements a WikiRendered that outputs XHTML in a format that is suitable 036 * for use by a WYSIWYG XHTML editor. 037 * 038 * @since 2.5 039 */ 040public class WysiwygEditingRenderer 041 extends WikiRenderer 042{ 043 044 private static final String A_ELEMENT = "a"; 045 private static final String IMG_ELEMENT = "img"; 046// private static final String PRE_ELEMENT = "pre"; 047 private static final String CLASS_ATTRIBUTE = "class"; 048 private static final String HREF_ATTRIBUTE = "href"; 049 private static final String TITLE_ATTRIBUTE = "title"; 050 private static final String EDITPAGE = "createpage"; 051 private static final String WIKIPAGE = "wikipage"; 052 private static final String HASHLINK = "hashlink"; 053 private static final String OUTLINK = "outlink"; 054 private static final String LINEBREAK = "\n"; 055 private static final String LINKS_TRANSLATION = "$1#$2"; 056 private static final String LINKS_SOURCE = "(.+)#section-.+-(.+)"; 057 058 /** 059 * Creates a WYSIWYG editing renderer. 060 * 061 * @param context {@inheritDoc} 062 * @param doc {@inheritDoc} 063 */ 064 public WysiwygEditingRenderer( WikiContext context, WikiDocument doc ) 065 { 066 super( context, doc ); 067 } 068 069 /* 070 * Recursively walk the XHTML DOM tree and manipulate specific elements to 071 * make them better for WYSIWYG editing. 072 */ 073 private void processChildren(Element baseElement) 074 { 075 for( Iterator itr = baseElement.getChildren().iterator(); itr.hasNext(); ) 076 { 077 Object childElement = itr.next(); 078 if( childElement instanceof Element ) 079 { 080 Element element = (Element)childElement; 081 String elementName = element.getName().toLowerCase(); 082 Attribute classAttr = element.getAttribute( CLASS_ATTRIBUTE ); 083 084 if( elementName.equals( A_ELEMENT ) ) 085 { 086 if( classAttr != null ) 087 { 088 String classValue = classAttr.getValue(); 089 Attribute hrefAttr = element.getAttribute( HREF_ATTRIBUTE ); 090 091 XHtmlToWikiConfig wikiConfig = new XHtmlToWikiConfig( m_context ); 092 093 // Get the url for wiki page link - it's typically "Wiki.jsp?page=MyPage" 094 // or when using the ShortURLConstructor option, it's "wiki/MyPage" . 095 String wikiPageLinkUrl = wikiConfig.getWikiJspPage(); 096 String editPageLinkUrl = wikiConfig.getEditJspPage(); 097 098 //if( classValue.equals( WIKIPAGE ) 099 // || ( hrefAttr != null && hrefAttr.getValue().startsWith( wikiPageLinkUrl ) ) ) 100 if( //classValue.equals( WIKIPAGE ) && 101 ( hrefAttr != null ) 102 && ( hrefAttr.getValue().startsWith( wikiPageLinkUrl ) ) ) 103 { 104 // Remove the leading url string so that users will only see the 105 // wikipage's name when editing an existing wiki link. 106 // For example, change "Wiki.jsp?page=MyPage" to just "MyPage". 107 108 String newHref = hrefAttr.getValue().substring( wikiPageLinkUrl.length() ); 109 110 // Convert "This%20Pagename%20Has%20Spaces" to "This Pagename Has Spaces" 111 newHref = m_context.getEngine().decodeName( newHref ); 112 113 // Handle links with section anchors. 114 // For example, we need to translate the html string "TargetPage#section-TargetPage-Heading2" 115 // to this wiki string: "TargetPage#Heading2". 116 hrefAttr.setValue( newHref.replaceFirst( LINKS_SOURCE, LINKS_TRANSLATION ) ); 117 118 } 119 else if( //classValue.equals( EDITPAGE ) && 120 ( hrefAttr != null ) 121 && ( hrefAttr.getValue().startsWith( editPageLinkUrl ) ) ) 122 { 123 124 Attribute titleAttr = element.getAttribute( TITLE_ATTRIBUTE ); 125 if( titleAttr != null ) 126 { 127 // remove the title since we don't want to eventually save the default undefined page title. 128 titleAttr.detach(); 129 } 130 131 String newHref = hrefAttr.getValue().substring( editPageLinkUrl.length() ); 132 newHref = m_context.getEngine().decodeName( newHref ); 133 134 hrefAttr.setValue( newHref ); 135 } 136 137 else if( classValue.equals( HASHLINK ) ) 138 { 139 itr.remove(); //remove element without disturbing the ongoing iteration 140 continue; //take next iteration of the for loop 141 } 142 } 143 } // end of check for "a" element 144 145 else if ( elementName.equals( IMG_ELEMENT ) ) 146 { 147 if( classAttr != null ) 148 { 149 String classValue = classAttr.getValue(); 150 151 if( classValue.equals( OUTLINK ) ) 152 { 153 itr.remove(); //remove element without disturbing the ongoing iteration 154 continue; //take next iteration of the for loop 155 } 156 157 } 158 159 } 160 161 processChildren( element ); 162 } 163 } 164 } 165 166 /** 167 * {@inheritDoc} 168 */ 169 public String getString() 170 throws IOException 171 { 172 Element rootElement = m_document.getRootElement(); 173 processChildren( rootElement ); 174 175 m_document.setContext( m_context ); 176 177 XMLOutputter output = new XMLOutputter(); 178 179 StringWriter out = new StringWriter(); 180 181 Format fmt = Format.getRawFormat(); 182 fmt.setExpandEmptyElements( false ); 183 fmt.setLineSeparator( LINEBREAK ); 184 185 output.setFormat( fmt ); 186 output.outputElementContent( m_document.getRootElement(), out ); 187 188 return out.toString(); 189 } 190}