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 */
019 package org.apache.wiki.parser;
020
021 import java.io.IOException;
022 import java.text.MessageFormat;
023 import java.util.HashMap;
024 import java.util.Map;
025 import java.util.NoSuchElementException;
026 import java.util.ResourceBundle;
027
028 import org.apache.log4j.Logger;
029 import org.apache.oro.text.regex.MatchResult;
030 import org.apache.oro.text.regex.PatternMatcher;
031 import org.apache.oro.text.regex.Perl5Matcher;
032 import org.apache.wiki.InternalWikiException;
033 import org.apache.wiki.WikiContext;
034 import org.apache.wiki.WikiEngine;
035 import org.apache.wiki.api.engine.PluginManager;
036 import org.apache.wiki.api.exceptions.PluginException;
037 import org.apache.wiki.api.plugin.ParserStagePlugin;
038 import org.apache.wiki.api.plugin.WikiPlugin;
039 import org.apache.wiki.preferences.Preferences;
040 import org.apache.wiki.render.RenderingManager;
041 import 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 */
057 public 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 String result;
133
134 WikiDocument doc = (WikiDocument) getDocument();
135
136 if (doc == null) {
137 //
138 // This element has not yet been attached anywhere, so we simply assume there is
139 // no rendering and return the plugin name. This is required e.g. when the
140 // paragraphify() checks whether the element is empty or not. We can't of course
141 // know whether the rendering would result in an empty string or not, but let us
142 // assume it does not.
143 //
144
145 return getPluginName();
146 }
147
148 WikiContext context = doc.getContext();
149
150 if( context == null ) {
151 log.info( "WikiContext garbage-collected, cannot proceed" );
152 return getPluginName();
153 }
154
155 Boolean wysiwygVariable = (Boolean) context.getVariable(RenderingManager.WYSIWYG_EDITOR_MODE);
156 boolean wysiwygEditorMode = false;
157 if (wysiwygVariable != null) {
158 wysiwygEditorMode = wysiwygVariable.booleanValue();
159 }
160
161 try {
162 //
163 // Determine whether we should emit the actual code for this plugin or
164 // whether we should execute it. For some plugins we always execute it,
165 // since they can be edited visually.
166 //
167 // FIXME: The plugin name matching should not be done here, but in a per-editor resource
168 if (wysiwygEditorMode && !m_pluginName.matches(EMITTABLE_PLUGINS)) {
169 result = PLUGIN_START + m_pluginName + SPACE;
170
171 // convert newlines to <br> in case the plugin has a body.
172 String cmdLine = (m_params.get(CMDLINE)).replaceAll(LINEBREAK, ELEMENT_BR);
173
174 result = result + cmdLine + PLUGIN_END;
175 } else {
176 Boolean b = (Boolean) context.getVariable(RenderingManager.VAR_EXECUTE_PLUGINS);
177 if (b != null && !b.booleanValue()) {
178 return BLANK;
179 }
180
181 WikiEngine engine = context.getEngine();
182
183 Map<String, String> parsedParams = new HashMap<String, String>();
184
185 //
186 // Parse any variable instances from the string
187 //
188 for (Map.Entry<String, String> e : m_params.entrySet()) {
189 String val = e.getValue();
190 val = engine.getVariableManager().expandVariables(context, val);
191 parsedParams.put(e.getKey(), val);
192 }
193 PluginManager pm = engine.getPluginManager();
194 result = pm.execute(context, m_pluginName, parsedParams);
195 }
196 } catch (Exception e) {
197 if (wysiwygEditorMode) {
198 result = "";
199 } else {
200 // log.info("Failed to execute plugin",e);
201 ResourceBundle rb = Preferences.getBundle(context, WikiPlugin.CORE_PLUGINS_RESOURCEBUNDLE);
202 result = JSPWikiMarkupParser.makeError(
203 MessageFormat.format(rb.getString("plugin.error.insertionfailed"), e.getMessage())).getText();
204 }
205 }
206
207
208 return result;
209 }
210
211 /**
212 * Executes the executeParse() method.
213 *
214 * @param context The WikiContext
215 * @throws PluginException If something goes wrong.
216 */
217 public void executeParse(WikiContext context) throws PluginException {
218 PluginManager pm = context.getEngine().getPluginManager();
219 if (pm.pluginsEnabled()) {
220 ResourceBundle rb = Preferences.getBundle(context, WikiPlugin.CORE_PLUGINS_RESOURCEBUNDLE);
221 Map<String, String> params = getParameters();
222 WikiPlugin plugin = pm.newWikiPlugin(getPluginName(), rb);
223 try {
224 if (plugin != null && plugin instanceof ParserStagePlugin) {
225 ((ParserStagePlugin) plugin).executeParser(this, context, params);
226 }
227 } catch (ClassCastException e) {
228 throw new PluginException(MessageFormat.format(rb.getString("plugin.error.notawikiplugin"), getPluginName()), e);
229 }
230 }
231 }
232
233 /**
234 * Parses a plugin invocation and returns a DOM element.
235 *
236 * @param context The WikiContext
237 * @param commandline The line to parse
238 * @param pos The position in the stream parsing.
239 * @return A DOM element
240 * @throws PluginException If plugin invocation is faulty
241 * @since 2.10.0
242 */
243 public static PluginContent parsePluginLine(WikiContext context, String commandline, int pos) throws PluginException {
244 PatternMatcher matcher = new Perl5Matcher();
245
246 try {
247 PluginManager pm = context.getEngine().getPluginManager();
248 if (matcher.contains(commandline, pm.getPluginPattern())) {
249
250 MatchResult res = matcher.getMatch();
251
252 String plugin = res.group(2);
253 String args = commandline.substring(res.endOffset(0),
254 commandline.length() -
255 (commandline.charAt(commandline.length() - 1) == '}' ? 1 : 0));
256 Map<String, String> arglist = pm.parseArgs(args);
257
258 // set wikitext bounds of plugin as '_bounds' parameter, e.g., [345,396]
259 if (pos != -1) {
260 int end = pos + commandline.length() + 2;
261 String bounds = pos + "|" + end;
262 arglist.put(PluginManager.PARAM_BOUNDS, bounds);
263 }
264
265 PluginContent result = new PluginContent(plugin, arglist);
266
267 return result;
268 }
269 } catch (ClassCastException e) {
270 log.error("Invalid type offered in parsing plugin arguments.", e);
271 throw new InternalWikiException("Oops, someone offered !String!");
272 } catch (NoSuchElementException e) {
273 String msg = "Missing parameter in plugin definition: " + commandline;
274 log.warn(msg, e);
275 throw new PluginException(msg);
276 } catch (IOException e) {
277 String msg = "Zyrf. Problems with parsing arguments: " + commandline;
278 log.warn(msg, e);
279 throw new PluginException(msg);
280 }
281
282 return null;
283 }
284
285 }