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.parser; 020 021import org.apache.logging.log4j.LogManager; 022import org.apache.logging.log4j.Logger; 023import org.apache.oro.text.regex.MatchResult; 024import org.apache.oro.text.regex.PatternMatcher; 025import org.apache.oro.text.regex.Perl5Matcher; 026import org.apache.wiki.InternalWikiException; 027import org.apache.wiki.api.core.Context; 028import org.apache.wiki.api.core.Engine; 029import org.apache.wiki.api.exceptions.PluginException; 030import org.apache.wiki.api.plugin.ParserStagePlugin; 031import org.apache.wiki.api.plugin.Plugin; 032import org.apache.wiki.api.plugin.PluginElement; 033import org.apache.wiki.plugin.PluginManager; 034import org.apache.wiki.preferences.Preferences; 035import org.apache.wiki.variables.VariableManager; 036import org.jdom2.Text; 037 038import java.io.IOException; 039import java.text.MessageFormat; 040import java.util.HashMap; 041import java.util.Map; 042import java.util.NoSuchElementException; 043import java.util.ResourceBundle; 044 045 046/** 047 * Stores the contents of a plugin in a WikiDocument DOM tree. 048 * <p/> 049 * If the Context.VAR_WYSIWYG_EDITOR_MODE is set to Boolean.TRUE in the context, then the plugin is rendered as WikiMarkup. 050 * This allows an HTML editor to work without rendering the plugin each time as well. 051 * <p/> 052 * If Context.VAR_EXECUTE_PLUGINS is set to Boolean.FALSE, then the plugin is not executed. 053 * 054 * @since 2.4 055 */ 056public class PluginContent extends Text implements PluginElement { 057 058 private static final String BLANK = ""; 059 private static final String CMDLINE = "_cmdline"; 060 private static final String ELEMENT_BR = "<br/>"; 061 private static final String EMITTABLE_PLUGINS = "Image|FormOpen|FormClose|FormInput|FormTextarea|FormSelect"; 062 private static final String LINEBREAK = "\n"; 063 private static final String PLUGIN_START = "[{"; 064 private static final String PLUGIN_END = "}]"; 065 private static final String SPACE = " "; 066 067 private static final long serialVersionUID = 1L; 068 private static final Logger LOG = LogManager.getLogger(PluginContent.class); 069 070 private final String m_pluginName; 071 private final Map< String, String > m_params; 072 073 /** 074 * Creates a new DOM element with the given plugin name and a map of parameters. 075 * 076 * @param pluginName The FQN of a plugin. 077 * @param parameters A Map of parameters. 078 */ 079 public PluginContent( final String pluginName, final Map< String, String > parameters) { 080 m_pluginName = pluginName; 081 m_params = parameters; 082 } 083 084 /**{@inheritDoc}*/ 085 @Override 086 public String getPluginName() { 087 return m_pluginName; 088 } 089 090 /**{@inheritDoc}*/ 091 @Override 092 public String getParameter( final String name) { 093 return m_params.get( name ); 094 } 095 096 /**{@inheritDoc}*/ 097 @Override 098 public Map< String, String > getParameters() { 099 return m_params; 100 } 101 102 /**{@inheritDoc}*/ 103 @Override 104 public String getValue() { 105 return getText(); 106 } 107 108 /**{@inheritDoc}*/ 109 @Override 110 public String getText() { 111 final WikiDocument doc = ( WikiDocument )getDocument(); 112 if( doc == null ) { 113 // 114 // This element has not yet been attached anywhere, so we simply assume there is no rendering and return the plugin name. 115 // This is required e.g. when the paragraphify() checks whether the element is empty or not. We can't of course know 116 // whether the rendering would result in an empty string or not, but let us assume it does not. 117 // 118 return getPluginName(); 119 } 120 121 final Context context = doc.getContext(); 122 if( context == null ) { 123 LOG.info( "WikiContext garbage-collected, cannot proceed" ); 124 return getPluginName(); 125 } 126 127 return invoke( context ); 128 } 129 130 /**{@inheritDoc}*/ 131 @Override 132 public String invoke( final Context context ) { 133 String result; 134 final Boolean wysiwygVariable = context.getVariable( Context.VAR_WYSIWYG_EDITOR_MODE ); 135 boolean wysiwygEditorMode = false; 136 if( wysiwygVariable != null ) { 137 wysiwygEditorMode = wysiwygVariable; 138 } 139 140 try { 141 // 142 // Determine whether we should emit the actual code for this plugin or 143 // whether we should execute it. For some plugins we always execute it, 144 // since they can be edited visually. 145 // 146 // FIXME: The plugin name matching should not be done here, but in a per-editor resource 147 if( wysiwygEditorMode && !m_pluginName.matches( EMITTABLE_PLUGINS ) ) { 148 result = PLUGIN_START + m_pluginName + SPACE; 149 150 // convert newlines to <br> in case the plugin has a body. 151 final String cmdLine = m_params.get( CMDLINE ).replaceAll( LINEBREAK, ELEMENT_BR ); 152 result = result + cmdLine + PLUGIN_END; 153 } else { 154 final Boolean b = context.getVariable( Context.VAR_EXECUTE_PLUGINS ); 155 if (b != null && !b ) { 156 return BLANK; 157 } 158 159 final Engine engine = context.getEngine(); 160 final Map< String, String > parsedParams = new HashMap<>(); 161 162 // Parse any variable instances from the string 163 for( final Map.Entry< String, String > e : m_params.entrySet() ) { 164 String val = e.getValue(); 165 val = engine.getManager( VariableManager.class).expandVariables( context, val ); 166 parsedParams.put( e.getKey(), val ); 167 } 168 final PluginManager pm = engine.getManager( PluginManager.class ); 169 result = pm.execute( context, m_pluginName, parsedParams ); 170 } 171 } catch( final Exception e ) { 172 if( wysiwygEditorMode ) { 173 result = ""; 174 } else { 175 // LOG.info("Failed to execute plugin",e); 176 final ResourceBundle rb = Preferences.getBundle( context, Plugin.CORE_PLUGINS_RESOURCEBUNDLE ); 177 result = MarkupParser.makeError( MessageFormat.format( rb.getString( "plugin.error.insertionfailed" ), 178 context.getRealPage().getWiki(), 179 context.getRealPage().getName(), 180 e.getMessage() ) ).getText(); 181 } 182 } 183 184 return result; 185 } 186 187 /**{@inheritDoc}*/ 188 @Override 189 public void executeParse( final Context context ) throws PluginException { 190 final PluginManager pm = context.getEngine().getManager( PluginManager.class ); 191 if( pm.pluginsEnabled() ) { 192 final ResourceBundle rb = Preferences.getBundle( context, Plugin.CORE_PLUGINS_RESOURCEBUNDLE); 193 final Map< String, String > params = getParameters(); 194 final Plugin plugin = pm.newWikiPlugin( getPluginName(), rb ); 195 try { 196 if( plugin instanceof ParserStagePlugin ) { 197 ( ( ParserStagePlugin )plugin ).executeParser(this, context, params ); 198 } 199 } catch( final ClassCastException e ) { 200 throw new PluginException( MessageFormat.format( rb.getString("plugin.error.notawikiplugin"), getPluginName() ), e ); 201 } 202 } 203 } 204 205 /** 206 * Parses a plugin invocation and returns a DOM element. 207 * 208 * @param context The WikiContext 209 * @param commandline The line to parse 210 * @param pos The position in the stream parsing. 211 * @return A DOM element 212 * @throws PluginException If plugin invocation is faulty 213 * @since 2.10.0 214 */ 215 public static PluginContent parsePluginLine( final Context context, final String commandline, final int pos ) throws PluginException { 216 final PatternMatcher matcher = new Perl5Matcher(); 217 218 try { 219 final PluginManager pm = context.getEngine().getManager( PluginManager.class ); 220 if( matcher.contains( commandline, pm.getPluginPattern() ) ) { 221 final MatchResult res = matcher.getMatch(); 222 final String plugin = res.group( 2 ); 223 final String args = commandline.substring( res.endOffset( 0 ), 224 commandline.length() - ( commandline.charAt( commandline.length() - 1 ) == '}' ? 1 : 0 ) ); 225 final Map< String, String > arglist = pm.parseArgs( args ); 226 227 // set wikitext bounds of plugin as '_bounds' parameter, e.g., [345,396] 228 if( pos != -1 ) { 229 final int end = pos + commandline.length() + 2; 230 final String bounds = pos + "|" + end; 231 arglist.put( PluginManager.PARAM_BOUNDS, bounds ); 232 } 233 234 return new PluginContent( plugin, arglist ); 235 } 236 } catch( final ClassCastException e ) { 237 LOG.error( "Invalid type offered in parsing plugin arguments.", e ); 238 throw new InternalWikiException( "Oops, someone offered !String!", e ); 239 } catch( final NoSuchElementException e ) { 240 final String msg = "Missing parameter in plugin definition: " + commandline; 241 LOG.warn( msg, e ); 242 throw new PluginException( msg ); 243 } catch( final IOException e ) { 244 final String msg = "Zyrf. Problems with parsing arguments: " + commandline; 245 LOG.warn( msg, e ); 246 throw new PluginException( msg ); 247 } 248 249 return null; 250 } 251 252}