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.markdown.extensions.jspwikilinks.postprocessor; 020 021import com.vladsch.flexmark.ast.HtmlInline; 022import com.vladsch.flexmark.ext.toc.TocBlock; 023import com.vladsch.flexmark.util.ast.Node; 024import com.vladsch.flexmark.util.ast.NodeTracker; 025import com.vladsch.flexmark.util.sequence.CharSubSequence; 026import org.apache.log4j.Logger; 027import org.apache.wiki.api.core.Context; 028import org.apache.wiki.api.exceptions.PluginException; 029import org.apache.wiki.api.plugin.Plugin; 030import org.apache.wiki.markdown.nodes.JSPWikiLink; 031import org.apache.wiki.parser.PluginContent; 032import org.apache.wiki.preferences.Preferences; 033 034import java.text.MessageFormat; 035import java.util.ResourceBundle; 036 037 038/** 039 * {@link NodePostProcessorState} which further post processes plugin links. 040 */ 041public class PluginLinkNodePostProcessorState implements NodePostProcessorState< JSPWikiLink > { 042 043 private static final Logger LOG = Logger.getLogger( PluginLinkNodePostProcessorState.class ); 044 private final Context wikiContext; 045 private final boolean m_wysiwygEditorMode; 046 047 public PluginLinkNodePostProcessorState( final Context wikiContext ) { 048 this.wikiContext = wikiContext; 049 final Boolean wysiwygVariable = wikiContext.getVariable( Context.VAR_WYSIWYG_EDITOR_MODE ); 050 m_wysiwygEditorMode = wysiwygVariable != null ? wysiwygVariable : false; 051 } 052 053 /** 054 * {@inheritDoc} 055 * 056 * @see NodePostProcessorState#process(NodeTracker, Node) 057 */ 058 @Override 059 public void process( final NodeTracker state, final JSPWikiLink link ) { 060 if( link.getText().toString().startsWith( "{TableOfContents" ) ) { 061 handleTableOfContentsPlugin( state, link ); 062 return; 063 } 064 PluginContent pluginContent = null; 065 try { 066 pluginContent = PluginContent.parsePluginLine( wikiContext, link.getUrl().toString(), -1 ); // -1 == do not generate _bounds parameter 067 // 068 // This might sometimes fail, especially if there is something which looks 069 // like a plugin invocation but is really not. 070 // 071 if( pluginContent != null ) { 072 final String pluginInvocation = pluginInvocation( link.getText().toString(), pluginContent ); 073 final HtmlInline content = new HtmlInline( CharSubSequence.of( pluginInvocation ) ); 074 pluginContent.executeParse( wikiContext ); 075 NodePostProcessorStateCommonOperations.addContent( state, link, content ); 076 } 077 } catch( final PluginException e ) { 078 LOG.info( wikiContext.getRealPage().getWiki() + " : " + wikiContext.getRealPage().getName() + " - Failed to insert plugin: " + e.getMessage() ); 079 if( !m_wysiwygEditorMode ) { 080 final ResourceBundle rbPlugin = Preferences.getBundle( wikiContext, Plugin.CORE_PLUGINS_RESOURCEBUNDLE ); 081 NodePostProcessorStateCommonOperations.makeError( state, link, MessageFormat.format( rbPlugin.getString( "plugin.error.insertionfailed" ), 082 wikiContext.getRealPage().getWiki(), 083 wikiContext.getRealPage().getName(), 084 e.getMessage() ) ); 085 } 086 } finally { 087 if( pluginContent != null ) { 088 removeLink( state, link ); 089 } 090 } 091 } 092 093 /** 094 * Return plugin execution. As plugin execution may not fire the plugin (i.e., on WYSIWYG editors), on those cases, the plugin line is returned. 095 * 096 * @param pluginMarkup plugin markup line 097 * @param pluginContent the plugin content. 098 * @return plugin execution, or plugin markup line if it wasn't executed. 099 */ 100 String pluginInvocation( final String pluginMarkup, final PluginContent pluginContent ) { 101 final String pluginInvocation = pluginContent.invoke( wikiContext ); 102 if( pluginMarkup.equals( pluginInvocation + "()" ) ) { // plugin line markup == plugin execution + "()" -> hasn't been executed 103 return pluginMarkup; 104 } else { 105 return pluginInvocation; 106 } 107 } 108 109 void handleTableOfContentsPlugin(final NodeTracker state, final JSPWikiLink link) { 110 if( !m_wysiwygEditorMode ) { 111 final ResourceBundle rb = Preferences.getBundle( wikiContext, Plugin.CORE_PLUGINS_RESOURCEBUNDLE ); 112 final HtmlInline divToc = new HtmlInline( CharSubSequence.of( "<div class=\"toc\">\n" ) ); 113 final HtmlInline divCollapseBox = new HtmlInline( CharSubSequence.of( "<div class=\"collapsebox\">\n" ) ); 114 final HtmlInline divsClosing = new HtmlInline( CharSubSequence.of( "</div>\n</div>\n" ) ); 115 final HtmlInline h4Title = new HtmlInline( CharSubSequence.of( "<h4 id=\"section-TOC\">" + // FIXME proper plugin parameters handling 116 rb.getString( "tableofcontents.title" ) + 117 "</h4>\n" ) ); 118 final TocBlock toc = new TocBlock( CharSubSequence.of( "[TOC]" ), CharSubSequence.of( "levels=1-3" ) ); 119 120 link.insertAfter( divToc ); 121 divToc.insertAfter( divCollapseBox ); 122 divCollapseBox.insertAfter( h4Title ); 123 h4Title.insertAfter( toc ); 124 toc.insertAfter( divsClosing ); 125 126 } else { 127 NodePostProcessorStateCommonOperations.inlineLinkTextOnWysiwyg( state, link, m_wysiwygEditorMode ); 128 } 129 removeLink( state, link ); 130 } 131 132 void removeLink(final NodeTracker state, final JSPWikiLink link) { 133 link.unlink(); 134 state.nodeRemoved( link ); 135 } 136 137}