001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020:
021: package com.ecyrd.jspwiki.plugin;
022:
023: import java.security.Principal;
024: import java.util.Map;
025:
026: import org.apache.commons.lang.StringUtils;
027: import org.apache.oro.text.regex.*;
028:
029: import com.ecyrd.jspwiki.TextUtil;
030: import com.ecyrd.jspwiki.WikiContext;
031: import com.ecyrd.jspwiki.WikiProvider;
032:
033: /**
034: * The IfPlugin allows parts of a WikiPage to be executed conditionally.
035: * You can also use shorthand "If" to run it.
036: * Parameters:
037: * <ul>
038: * <li>group - A "|" -separated list of group names.
039: * <li>user - A "|" -separated list of user names.
040: * <li>ip - A "|" -separated list of ip addresses.
041: * <li>var - A wiki variable
042: * <li>page - A page name
043: * <li>contains - A Perl5 regexp pattern
044: * <li>is - A Perl5 regexp pattern
045: * <li>exists - "true" or "false".
046: * </ul>
047: *
048: * <p>If any of them match, the body of the plugin is executed. You can
049: * negate the content by prefixing it with a "!". For example, to greet
050: * all admins, put the following in your LeftMenu:</p>
051: * <pre>
052: * [{If group='Admin'
053: *
054: * Hello, Admin, and your mighty powers!}]
055: * </pre>
056: *
057: * <p>In order to send a message to everybody except Jack use</p>
058: * <pre>
059: * [{If user='!Jack'
060: *
061: * %%warning
062: * Jack's surprise birthday party at eleven!
063: * %%}]
064: * </pre>
065: *
066: * <p>Note that you can't use "!Jack|!Jill", because for Jack, !Jill matches;
067: * and for Jill, !Jack matches. These are not regular expressions (though
068: * they might become so in the future).<p>
069: *
070: * <p>To check for page content, use</p>
071: * <pre>
072: * [{If page='TestPage' contains='xyzzy'
073: *
074: * Page contains the text "xyzzy"}]
075: * </pre>
076: *
077: * <p>The difference between "contains" and "is" is that "is" is always an exact match,
078: * whereas "contains" just checks if a pattern is available.</p>
079: *
080: * <p>To check for page existence, use</p>
081: * <pre>
082: * [{If page='TestPage' exists='true'
083: *
084: * Page "TestPage" exists.}]
085: * </pre>
086: * <p>With the same mechanism, it's also possible to test for the existence
087: * of a variable - just use "var" instead of "page".</p>
088: *
089: * <p>Another caveat is that the plugin body content is not counted
090: * towards ReferenceManager links. So any links do not appear on any reference
091: * lists. Depending on your position, this may be a good or a bad
092: * thing.</p>
093: *
094: * <h3>Calling Externally</h3>
095: *
096: * <p>The functional, decision-making part of this plugin may be called from
097: * other code (e.g., other plugins) since it is available as a static method
098: * {@link #ifInclude(WikiContext,Map)}. Note that the plugin body may contain
099: * references to other plugins.</p>
100: *
101: * @author Janne Jalkanen
102: * @author Murray Altheim
103: * @since 2.6
104: */
105: public class IfPlugin implements WikiPlugin {
106: public static final String PARAM_GROUP = "group";
107: public static final String PARAM_USER = "user";
108: public static final String PARAM_IP = "ip";
109: public static final String PARAM_PAGE = "page";
110: public static final String PARAM_CONTAINS = "contains";
111: public static final String PARAM_VAR = "var";
112: public static final String PARAM_IS = "is";
113: public static final String PARAM_EXISTS = "exists";
114:
115: /**
116: * {@inheritDoc}
117: */
118: public String execute(WikiContext context, Map params)
119: throws PluginException {
120: return ifInclude(context, params) ? context.getEngine()
121: .textToHTML(context,
122: (String) params.get(PluginManager.PARAM_BODY))
123: : "";
124: }
125:
126: /**
127: * Returns a boolean result based on processing the WikiContext and
128: * parameter Map as according to the rules stated in the IfPlugin
129: * documentation.
130: * As a static method this may be called by other classes.
131: *
132: * @param context The current WikiContext.
133: * @param params The parameter Map which contains key-value pairs.
134: */
135: public static boolean ifInclude(WikiContext context, Map params)
136: throws PluginException {
137: boolean include = false;
138:
139: String group = (String) params.get(PARAM_GROUP);
140: String user = (String) params.get(PARAM_USER);
141: String ip = (String) params.get(PARAM_IP);
142: String page = (String) params.get(PARAM_PAGE);
143: String contains = (String) params.get(PARAM_CONTAINS);
144: String var = (String) params.get(PARAM_VAR);
145: String is = (String) params.get(PARAM_IS);
146: String exists = (String) params.get(PARAM_EXISTS);
147:
148: include |= checkGroup(context, group);
149: include |= checkUser(context, user);
150: include |= checkIP(context, ip);
151:
152: if (page != null) {
153: String content = context.getEngine().getPureText(page,
154: WikiProvider.LATEST_VERSION).trim();
155: include |= checkContains(content, contains);
156: include |= checkIs(content, is);
157: include |= checkExists(context, page, exists);
158: }
159:
160: if (var != null) {
161: String content = context.getEngine().getVariable(context,
162: var);
163: include |= checkContains(content, contains);
164: include |= checkIs(content, is);
165: include |= checkVarExists(content, exists);
166: }
167:
168: return include;
169: }
170:
171: private static boolean checkExists(WikiContext context,
172: String page, String exists) {
173: if (exists == null)
174: return false;
175: return !context.getEngine().pageExists(page)
176: ^ TextUtil.isPositive(exists);
177: }
178:
179: private static boolean checkVarExists(String varContent,
180: String exists) {
181: if (exists == null)
182: return false;
183: return (varContent == null) ^ TextUtil.isPositive(exists);
184: }
185:
186: private static boolean checkGroup(WikiContext context, String group) {
187: if (group == null)
188: return false;
189: String[] groupList = StringUtils.split(group, '|');
190: boolean include = false;
191:
192: for (int i = 0; i < groupList.length; i++) {
193: String gname = groupList[i];
194: boolean invert = false;
195: if (groupList[i].startsWith("!")) {
196: gname = groupList[i].substring(1);
197: invert = true;
198: }
199:
200: Principal g = context.getEngine().getAuthorizationManager()
201: .resolvePrincipal(gname);
202:
203: include |= context.getEngine().getAuthorizationManager()
204: .isUserInRole(context.getWikiSession(), g)
205: ^ invert;
206: }
207: return include;
208: }
209:
210: private static boolean checkUser(WikiContext context, String user) {
211: if (user == null || context.getCurrentUser() == null)
212: return false;
213:
214: String[] list = StringUtils.split(user, '|');
215: boolean include = false;
216:
217: for (int i = 0; i < list.length; i++) {
218: boolean invert = false;
219: if (list[i].startsWith("!")) {
220: invert = true;
221: }
222:
223: include |= user.equals(context.getCurrentUser().getName())
224: ^ invert;
225: }
226: return include;
227: }
228:
229: // TODO: Add subnetwork matching, e.g. 10.0.0.0/8
230: private static boolean checkIP(WikiContext context, String ipaddr) {
231: if (ipaddr == null || context.getHttpRequest() == null)
232: return false;
233:
234: String[] list = StringUtils.split(ipaddr, '|');
235: boolean include = false;
236:
237: for (int i = 0; i < list.length; i++) {
238: boolean invert = false;
239: if (list[i].startsWith("!")) {
240: invert = true;
241: }
242:
243: include |= ipaddr.equals(context.getHttpRequest()
244: .getRemoteAddr())
245: ^ invert;
246: }
247: return include;
248: }
249:
250: private static boolean doMatch(String content, String pattern)
251: throws PluginException {
252: PatternCompiler compiler = new Perl5Compiler();
253: PatternMatcher matcher = new Perl5Matcher();
254:
255: try {
256: Pattern matchp = compiler.compile(pattern,
257: Perl5Compiler.SINGLELINE_MASK);
258: // m_exceptPattern = compiler.compile( exceptPattern, Perl5Compiler.SINGLELINE_MASK );
259: return matcher.matches(content, matchp);
260: } catch (MalformedPatternException e) {
261: throw new PluginException("Faulty pattern " + pattern);
262: }
263:
264: }
265:
266: private static boolean checkContains(String pagecontent,
267: String matchPattern) throws PluginException {
268: if (pagecontent == null || matchPattern == null)
269: return false;
270:
271: return doMatch(pagecontent, ".*" + matchPattern + ".*");
272: }
273:
274: private static boolean checkIs(String content, String matchPattern)
275: throws PluginException {
276: if (content == null || matchPattern == null)
277: return false;
278:
279: matchPattern = "^" + matchPattern + "$";
280:
281: return doMatch(content, matchPattern);
282: }
283: }
|