    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 org.apache.wiki.api.core.Context;
    022import org.apache.wiki.htmltowiki.XHtmlToWikiConfig;
    023import org.apache.wiki.parser.MarkupParser;
    024import org.apache.wiki.parser.WikiDocument;
    025import org.jdom2.Attribute;
    026import org.jdom2.Element;
    027import org.jdom2.output.Format;
    028import org.jdom2.output.XMLOutputter;
    029
    030import java.io.IOException;
    031import java.io.StringWriter;
    032import java.util.Iterator;
    033
    034/**
    035 *  Implements a WikiRenderer that outputs XHTML in a format that is suitable for use by a WYSIWYG XHTML editor.
    036 *
    037 *  @since  2.5
    038 */
    039public class WysiwygEditingRenderer extends WikiRenderer {
    040
    041    private static final String A_ELEMENT = "a";
    042    private static final String IMG_ELEMENT = "img";
    043    // private static final String PRE_ELEMENT = "pre";
    044    private static final String CLASS_ATTRIBUTE = "class";
    045    private static final String HREF_ATTRIBUTE = "href";
    046    private static final String TITLE_ATTRIBUTE = "title";
    047    private static final String LINEBREAK = "\n";
    048
    049    /**
    050     *  Creates a WYSIWYG editing renderer.
    051     *
    052     *  @param context A WikiContext in which the rendering will take place.
    053     *  @param doc The WikiDocument which shall be rendered.
    054     */
    055    public WysiwygEditingRenderer( final Context context, final WikiDocument doc )
    056    {
    057        super( context, doc );
    058    }
    059
    060    /*
    061     * Recursively walk the XHTML DOM tree and manipulate specific elements to
    062     * make them better for WYSIWYG editing.
    063     */
    064    private void processChildren( final Element baseElement ) {
    065        for( final Iterator< Element > itr = baseElement.getChildren().iterator(); itr.hasNext(); ) {
    066            final Element element = itr.next();
    067            final String elementName = element.getName().toLowerCase();
    068            final Attribute classAttr = element.getAttribute( CLASS_ATTRIBUTE );
    069
    070            if( elementName.equals( A_ELEMENT ) ) {
    071                if( classAttr != null ) {
    072                    final String classValue = classAttr.getValue();
    073                    final Attribute hrefAttr = element.getAttribute( HREF_ATTRIBUTE );
    074                    final XHtmlToWikiConfig wikiConfig = new XHtmlToWikiConfig( m_context );
    075
    076                    // Get the url for wiki page link - it's typically "Wiki.jsp?page=MyPage"
    077                    // or when using the ShortURLConstructor option, it's "wiki/MyPage" .
    078                    final String wikiPageLinkUrl = wikiConfig.getWikiJspPage();
    079                    final String editPageLinkUrl = wikiConfig.getEditJspPage();
    080
    081                    //if( classValue.equals( WIKIPAGE )
    082                    //    || ( hrefAttr != null && hrefAttr.getValue().startsWith( wikiPageLinkUrl ) ) )
    083                    if( //classValue.equals( WIKIPAGE ) &&
    084                        ( hrefAttr != null ) &&  ( hrefAttr.getValue().startsWith( wikiPageLinkUrl ) ) ) {
    085                        // Remove the leading url string so that users will only see the
    086                        // wikipage's name when editing an existing wiki link.
    087                        // For example, change "Wiki.jsp?page=MyPage" to just "MyPage".
    088
    089                        String newHref = hrefAttr.getValue().substring( wikiPageLinkUrl.length() );
    090
    091                        // Convert "This%20Pagename%20Has%20Spaces" to "This Pagename Has Spaces"
    092                        newHref = m_context.getEngine().decodeName( newHref );
    093
    094                        // Handle links with section anchors.
    095                        // For example, we need to translate the html string "TargetPage#section-TargetPage-Heading2"
    096                        // to this wiki string: "TargetPage#Heading2".
    097                        hrefAttr.setValue( newHref.replaceFirst( LINKS_SOURCE, LINKS_TRANSLATION ) );
    098
    099                    } else if( //classValue.equals( EDITPAGE ) &&
    100                            ( hrefAttr != null ) && ( hrefAttr.getValue().startsWith( editPageLinkUrl ) ) ) {
    101
    102                        final Attribute titleAttr = element.getAttribute( TITLE_ATTRIBUTE );
    103                        if( titleAttr != null ) {
    104                                // remove the title since we don't want to eventually save the default undefined page title.
    105                                titleAttr.detach();
    106                        }
    107
    108                        String newHref = hrefAttr.getValue().substring( editPageLinkUrl.length() );
    109                        newHref = m_context.getEngine().decodeName( newHref );
    110
    111                        hrefAttr.setValue( newHref );
    112                    } else if( classValue.equals( MarkupParser.HASHLINK ) ) {
    113                        itr.remove(); //remove element without disturbing the ongoing iteration
    114                        continue;  //take next iteration of the for loop
    115                    }
    116                }
    117            // end of check for "a" element
    118            } else if ( elementName.equals( IMG_ELEMENT ) ) {
    119                if( classAttr != null ) {
    120                    final String classValue = classAttr.getValue();
    121                    if( classValue.equals( MarkupParser.OUTLINK ) ) {
    122                        itr.remove(); // remove element without disturbing the ongoing iteration
    123                        continue; // take next iteration of the for loop
    124                    }
    125                }
    126            }
    127
    128            processChildren( element );
    129        }
    130    }
    131
    132    /**
    133     *  {@inheritDoc}
    134     */
    135    @Override
    136    public String getString() throws IOException {
    137        final Element rootElement = m_document.getRootElement();
    138        processChildren( rootElement );
    139
    140        m_document.setContext( m_context );
    141
    142        final CustomXMLOutputProcessor processor = new CustomXMLOutputProcessor();
    143        final XMLOutputter output = new XMLOutputter(processor);
    144        final StringWriter out = new StringWriter();
    145        final Format fmt = Format.getRawFormat();
    146        fmt.setExpandEmptyElements( false );
    147        fmt.setLineSeparator( LINEBREAK );
    148
    149        output.setFormat( fmt );
    150        output.outputElementContent( m_document.getRootElement(), out );
    151
    152        return out.toString();
    153    }
    154
    155}