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