001: /**
002: * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, version 2.1, dated February 1999.
003: *
004: * This program is free software; you can redistribute it and/or modify
005: * it under the terms of the latest version of the GNU Lesser General
006: * Public License as published by the Free Software Foundation;
007: *
008: * This program is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
011: * GNU Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public License
014: * along with this program (LICENSE.txt); if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
016: */package org.jamwiki.utils;
017:
018: import java.io.File;
019: import java.io.IOException;
020: import java.lang.reflect.Constructor;
021: import java.util.List;
022: import java.util.Locale;
023: import java.util.Vector;
024: import java.util.regex.Matcher;
025: import java.util.regex.Pattern;
026: import javax.servlet.http.HttpServletRequest;
027: import org.apache.commons.io.FileUtils;
028: import org.apache.commons.lang.ClassUtils;
029: import org.apache.commons.lang.StringUtils;
030: import org.jamwiki.DataHandler;
031: import org.jamwiki.Environment;
032: import org.jamwiki.SearchEngine;
033: import org.jamwiki.UserHandler;
034: import org.jamwiki.WikiBase;
035: import org.jamwiki.WikiException;
036: import org.jamwiki.WikiMessage;
037: import org.jamwiki.WikiVersion;
038: import org.jamwiki.model.Role;
039: import org.jamwiki.model.Topic;
040:
041: /**
042: * This class provides a variety of general utility methods for handling
043: * wiki-specific functionality such as retrieving topics from the URL.
044: */
045: public class WikiUtil {
046:
047: private static final WikiLogger logger = WikiLogger
048: .getLogger(WikiUtil.class.getName());
049:
050: private static Pattern INVALID_ROLE_NAME_PATTERN = null;
051: private static Pattern INVALID_TOPIC_NAME_PATTERN = null;
052: private static Pattern VALID_USER_LOGIN_PATTERN = null;
053: public static final String PARAMETER_TOPIC = "topic";
054: public static final String PARAMETER_VIRTUAL_WIKI = "virtualWiki";
055: public static final String PARAMETER_WATCHLIST = "watchlist";
056:
057: static {
058: try {
059: INVALID_ROLE_NAME_PATTERN = Pattern
060: .compile(Environment
061: .getValue(Environment.PROP_PATTERN_INVALID_ROLE_NAME));
062: INVALID_TOPIC_NAME_PATTERN = Pattern
063: .compile(Environment
064: .getValue(Environment.PROP_PATTERN_INVALID_TOPIC_NAME));
065: VALID_USER_LOGIN_PATTERN = Pattern
066: .compile(Environment
067: .getValue(Environment.PROP_PATTERN_VALID_USER_LOGIN));
068: } catch (Exception e) {
069: logger.severe("Unable to compile pattern", e);
070: }
071: }
072:
073: /**
074: * Create a pagination object based on parameters found in the current
075: * request.
076: *
077: * @param request The servlet request object.
078: * @param next A ModelAndView object corresponding to the page being
079: * constructed.
080: * @return A Pagination object constructed from parameters found in the
081: * request object.
082: */
083: public static Pagination buildPagination(HttpServletRequest request) {
084: int num = Environment
085: .getIntValue(Environment.PROP_RECENT_CHANGES_NUM);
086: if (request.getParameter("num") != null) {
087: try {
088: num = new Integer(request.getParameter("num"))
089: .intValue();
090: } catch (Exception e) {
091: // invalid number
092: }
093: }
094: int offset = 0;
095: if (request.getParameter("offset") != null) {
096: try {
097: offset = new Integer(request.getParameter("offset"))
098: .intValue();
099: } catch (Exception e) {
100: // invalid number
101: }
102: }
103: return new Pagination(num, offset);
104: }
105:
106: /**
107: * Utility method to retrieve an instance of the current data handler.
108: *
109: * @return An instance of the current data handler.
110: * @throws Exception Thrown if a data handler instance can not be
111: * instantiated.
112: */
113: public static DataHandler dataHandlerInstance() throws Exception {
114: // FIXME - remove this conditional after the ability to upgrade to
115: // 0.5.0 is removed.
116: if (Environment.getValue(Environment.PROP_DB_TYPE) == null) {
117: // this is a problem, but it should never occur
118: logger
119: .warning("WikiUtil.dataHandlerInstance called without a valid PROP_DB_TYPE value");
120: } else if (Environment.getValue(Environment.PROP_DB_TYPE)
121: .equals("ansi")) {
122: Environment.setValue(Environment.PROP_DB_TYPE,
123: WikiBase.DATA_HANDLER_ANSI);
124: Environment.saveProperties();
125: } else if (Environment.getValue(Environment.PROP_DB_TYPE)
126: .equals("hsql")) {
127: Environment.setValue(Environment.PROP_DB_TYPE,
128: WikiBase.DATA_HANDLER_HSQL);
129: Environment.saveProperties();
130: } else if (Environment.getValue(Environment.PROP_DB_TYPE)
131: .equals("mssql")) {
132: Environment.setValue(Environment.PROP_DB_TYPE,
133: WikiBase.DATA_HANDLER_MSSQL);
134: Environment.saveProperties();
135: } else if (Environment.getValue(Environment.PROP_DB_TYPE)
136: .equals("mysql")) {
137: Environment.setValue(Environment.PROP_DB_TYPE,
138: WikiBase.DATA_HANDLER_MYSQL);
139: Environment.saveProperties();
140: } else if (Environment.getValue(Environment.PROP_DB_TYPE)
141: .equals("oracle")) {
142: Environment.setValue(Environment.PROP_DB_TYPE,
143: WikiBase.DATA_HANDLER_ORACLE);
144: Environment.saveProperties();
145: } else if (Environment.getValue(Environment.PROP_DB_TYPE)
146: .equals("postgres")) {
147: Environment.setValue(Environment.PROP_DB_TYPE,
148: WikiBase.DATA_HANDLER_POSTGRES);
149: Environment.saveProperties();
150: } else if (Environment.getValue(Environment.PROP_DB_TYPE)
151: .equals("db2")) {
152: Environment.setValue(Environment.PROP_DB_TYPE,
153: WikiBase.DATA_HANDLER_DB2);
154: Environment.saveProperties();
155: } else if (Environment.getValue(Environment.PROP_DB_TYPE)
156: .equals("db2/400")) {
157: Environment.setValue(Environment.PROP_DB_TYPE,
158: WikiBase.DATA_HANDLER_DB2400);
159: Environment.saveProperties();
160: } else if (Environment.getValue(Environment.PROP_DB_TYPE)
161: .equals("asa")) {
162: Environment.setValue(Environment.PROP_DB_TYPE,
163: WikiBase.DATA_HANDLER_ASA);
164: }
165: String dataHandlerClass = Environment
166: .getValue(Environment.PROP_DB_TYPE);
167: logger.fine("Using data handler: " + dataHandlerClass);
168: Class clazz = ClassUtils.getClass(dataHandlerClass);
169: Class[] parameterTypes = new Class[0];
170: Constructor constructor = clazz.getConstructor(parameterTypes);
171: Object[] initArgs = new Object[0];
172: return (DataHandler) constructor.newInstance(initArgs);
173: }
174:
175: /**
176: * Given an article name, return the appropriate comments topic article name.
177: * For example, if the article name is "Topic" then the return value is
178: * "Comments:Topic".
179: *
180: * @param name The article name from which a comments article name is to
181: * be constructed.
182: * @return The comments article name for the article name.
183: */
184: public static String extractCommentsLink(String name)
185: throws Exception {
186: if (StringUtils.isBlank(name)) {
187: throw new Exception("Empty topic name " + name);
188: }
189: WikiLink wikiLink = LinkUtil.parseWikiLink(name);
190: if (StringUtils.isBlank(wikiLink.getNamespace())) {
191: return NamespaceHandler.NAMESPACE_COMMENTS
192: + NamespaceHandler.NAMESPACE_SEPARATOR + name;
193: }
194: String namespace = wikiLink.getNamespace();
195: String commentsNamespace = NamespaceHandler
196: .getCommentsNamespace(namespace);
197: return (!StringUtils.isBlank(commentsNamespace)) ? commentsNamespace
198: + NamespaceHandler.NAMESPACE_SEPARATOR
199: + wikiLink.getArticle()
200: : NamespaceHandler.NAMESPACE_COMMENTS
201: + NamespaceHandler.NAMESPACE_SEPARATOR
202: + wikiLink.getArticle();
203: }
204:
205: /**
206: * Given an article name, extract an appropriate topic article name. For
207: * example, if the article name is "Comments:Topic" then the return value
208: * is "Topic".
209: *
210: * @param name The article name from which a topic article name is to be
211: * constructed.
212: * @return The topic article name for the article name.
213: */
214: public static String extractTopicLink(String name) throws Exception {
215: if (StringUtils.isBlank(name)) {
216: throw new Exception("Empty topic name " + name);
217: }
218: WikiLink wikiLink = LinkUtil.parseWikiLink(name);
219: if (StringUtils.isBlank(wikiLink.getNamespace())) {
220: return name;
221: }
222: String namespace = wikiLink.getNamespace();
223: String mainNamespace = NamespaceHandler
224: .getMainNamespace(namespace);
225: return (!StringUtils.isBlank(mainNamespace)) ? mainNamespace
226: + NamespaceHandler.NAMESPACE_SEPARATOR
227: + wikiLink.getArticle() : wikiLink.getArticle();
228: }
229:
230: /**
231: *
232: */
233: public static Topic findRedirectedTopic(Topic parent, int attempts)
234: throws Exception {
235: int count = attempts;
236: if (parent.getTopicType() != Topic.TYPE_REDIRECT
237: || StringUtils.isBlank(parent.getRedirectTo())) {
238: logger
239: .severe("getRedirectTarget() called for non-redirect topic "
240: + parent.getName());
241: return parent;
242: }
243: // avoid infinite redirection
244: count++;
245: if (count > 10) {
246: //TODO throw new WikiException(new WikiMessage("topic.redirect.infinite"));
247: }
248: // get the topic that is being redirected to
249: Topic child = WikiBase.getDataHandler().lookupTopic(
250: parent.getVirtualWiki(), parent.getRedirectTo(), false,
251: null);
252: if (child == null) {
253: // child being redirected to doesn't exist, return parent
254: return parent;
255: }
256: if (StringUtils.isBlank(child.getRedirectTo())) {
257: // found a topic that is not a redirect, return
258: return child;
259: }
260: if (WikiBase.getDataHandler().lookupTopic(
261: child.getVirtualWiki(), child.getRedirectTo(), false,
262: null) == null) {
263: // child is a redirect, but its target does not exist
264: return child;
265: }
266: // topic is a redirect, keep looking
267: return findRedirectedTopic(child, count);
268: }
269:
270: /**
271: * Retrieve a parameter from the servlet request. This method works around
272: * some issues encountered when retrieving non-ASCII values from URL
273: * parameters.
274: *
275: * @param request The servlet request object.
276: * @param name The parameter name to be retrieved.
277: * @param decodeUnderlines Set to <code>true</code> if underlines should
278: * be automatically converted to spaces.
279: * @return The decoded parameter value retrieved from the request.
280: */
281: public static String getParameterFromRequest(
282: HttpServletRequest request, String name,
283: boolean decodeUnderlines) throws Exception {
284: String value = null;
285: if (request.getMethod().equalsIgnoreCase("GET")) {
286: // parameters passed via the URL are URL encoded, so request.getParameter may
287: // not interpret non-ASCII characters properly. This code attempts to work
288: // around that issue by manually decoding. yes, this is ugly and it would be
289: // great if someone could eventually make it unnecessary.
290: String query = request.getQueryString();
291: if (StringUtils.isBlank(query)) {
292: return null;
293: }
294: String prefix = name + "=";
295: int pos = query.indexOf(prefix);
296: if (pos != -1 && (pos + prefix.length()) < query.length()) {
297: value = query.substring(pos + prefix.length());
298: if (value.indexOf('&') != -1) {
299: value = value.substring(0, value.indexOf('&'));
300: }
301: }
302: return Utilities.decodeFromURL(value, decodeUnderlines);
303: }
304: value = request.getParameter(name);
305: if (value == null) {
306: value = (String) request.getAttribute(name);
307: }
308: if (value == null) {
309: return null;
310: }
311: return Utilities.decodeFromRequest(value, decodeUnderlines);
312: }
313:
314: /**
315: * Retrieve a topic name from the servlet request. This method will
316: * retrieve a request parameter matching the PARAMETER_TOPIC value,
317: * and will decode it appropriately.
318: *
319: * @param request The servlet request object.
320: * @return The decoded topic name retrieved from the request.
321: */
322: public static String getTopicFromRequest(HttpServletRequest request)
323: throws Exception {
324: return WikiUtil.getParameterFromRequest(request,
325: WikiUtil.PARAMETER_TOPIC, true);
326: }
327:
328: /**
329: * Retrieve a topic name from the request URI. This method will retrieve
330: * the portion of the URI that follows the virtual wiki and decode it
331: * appropriately.
332: *
333: * @param request The servlet request object.
334: * @return The decoded topic name retrieved from the URI.
335: */
336: public static String getTopicFromURI(HttpServletRequest request) {
337: // skip one directory, which is the virutal wiki
338: String topic = retrieveDirectoriesFromURI(request, 1);
339: if (topic == null) {
340: logger.warning("No topic in URL: "
341: + request.getRequestURI());
342: return null;
343: }
344: int pos = topic.indexOf('?');
345: if (pos != -1) {
346: // strip everything after and including '?'
347: if (pos == 0) {
348: logger.warning("No topic in URL: "
349: + request.getRequestURI());
350: return null;
351: }
352: topic = topic.substring(0, topic.indexOf('?'));
353: }
354: pos = topic.indexOf('#');
355: if (pos != -1) {
356: // strip everything after and including '#'
357: if (pos == 0) {
358: logger.warning("No topic in URL: "
359: + request.getRequestURI());
360: return null;
361: }
362: topic = topic.substring(0, topic.indexOf('#'));
363: }
364: topic = Utilities.decodeFromURL(topic, true);
365: return topic;
366: }
367:
368: /**
369: * Retrieve a virtual wiki name from the servlet request. This method
370: * will retrieve a request parameter matching the PARAMETER_VIRTUAL_WIKI
371: * value, and will decode it appropriately.
372: *
373: * @param request The servlet request object.
374: * @return The decoded virtual wiki name retrieved from the request.
375: */
376: public static String getVirtualWikiFromRequest(
377: HttpServletRequest request) {
378: String virtualWiki = request
379: .getParameter(WikiUtil.PARAMETER_VIRTUAL_WIKI);
380: if (virtualWiki == null) {
381: virtualWiki = (String) request
382: .getAttribute(WikiUtil.PARAMETER_VIRTUAL_WIKI);
383: }
384: if (virtualWiki == null) {
385: return null;
386: }
387: return Utilities.decodeFromRequest(virtualWiki, true);
388: }
389:
390: /**
391: * Retrieve a virtual wiki name from the request URI. This method will
392: * retrieve the portion of the URI that immediately follows the servlet
393: * context and decode it appropriately.
394: *
395: * @param request The servlet request object.
396: * @return The decoded virtual wiki name retrieved from the URI.
397: */
398: public static String getVirtualWikiFromURI(
399: HttpServletRequest request) {
400: String uri = retrieveDirectoriesFromURI(request, 0);
401: if (StringUtils.isBlank(uri)) {
402: logger.warning("No virtual wiki found in URL: "
403: + request.getRequestURI());
404: return null;
405: }
406: int slashIndex = uri.indexOf('/');
407: if (slashIndex == -1) {
408: logger.warning("No virtual wiki found in URL: "
409: + request.getRequestURI());
410: return null;
411: }
412: String virtualWiki = uri.substring(0, slashIndex);
413: return Utilities.decodeFromURL(virtualWiki, true);
414: }
415:
416: /**
417: * Given a topic name, determine if that name corresponds to a comments
418: * page.
419: *
420: * @param topicName The topic name (non-null) to examine to determine if it
421: * is a comments page or not.
422: * @return <code>true</code> if the page is a comments page, <code>false</code>
423: * otherwise.
424: */
425: public static boolean isCommentsPage(String topicName) {
426: WikiLink wikiLink = LinkUtil.parseWikiLink(topicName);
427: if (StringUtils.isBlank(wikiLink.getNamespace())) {
428: return false;
429: }
430: String namespace = wikiLink.getNamespace();
431: if (namespace.equals(NamespaceHandler.NAMESPACE_SPECIAL)) {
432: return false;
433: }
434: String commentNamespace = NamespaceHandler
435: .getCommentsNamespace(namespace);
436: return namespace.equals(commentNamespace);
437: }
438:
439: /**
440: * Determine if the system properties file exists and has been initialized.
441: * This method is primarily used to determine whether or not to display
442: * the system setup page or not.
443: *
444: * @return <code>true</code> if the properties file has NOT been initialized,
445: * <code>false</code> otherwise.
446: */
447: public static boolean isFirstUse() {
448: return !Environment
449: .getBooleanValue(Environment.PROP_BASE_INITIALIZED);
450: }
451:
452: /**
453: * Determine if the system code has been upgraded from the configured system
454: * version. Thus if the system is upgraded, this method returns <code>true</code>
455: *
456: * @return <code>true</code> if the system has been upgraded, <code>false</code>
457: * otherwise.
458: */
459: public static boolean isUpgrade() throws Exception {
460: if (WikiUtil.isFirstUse()) {
461: return false;
462: }
463: WikiVersion oldVersion = new WikiVersion(Environment
464: .getValue(Environment.PROP_BASE_WIKI_VERSION));
465: WikiVersion currentVersion = new WikiVersion(
466: WikiVersion.CURRENT_WIKI_VERSION);
467: return oldVersion.before(currentVersion);
468: }
469:
470: /**
471: * Utility method for reading special topic values from files and returning
472: * the file contents.
473: *
474: * @param locale The locale for the user viewing the special page.
475: * @param pageName The name of the special page being retrieved.
476: */
477: public static String readSpecialPage(Locale locale, String pageName)
478: throws Exception {
479: String contents = null;
480: String filename = null;
481: String language = null;
482: String country = null;
483: if (locale != null) {
484: language = locale.getLanguage();
485: country = locale.getCountry();
486: }
487: String subdirectory = "";
488: if (!StringUtils.isBlank(language)
489: && !StringUtils.isBlank(country)) {
490: try {
491: subdirectory = new File(WikiBase.SPECIAL_PAGE_DIR,
492: language + "_" + country).getPath();
493: filename = new File(subdirectory, Utilities
494: .encodeForFilename(pageName)
495: + ".txt").getPath();
496: contents = Utilities.readFile(filename);
497: } catch (Exception e) {
498: logger.info("File " + filename + " does not exist");
499: }
500: }
501: if (contents == null && !StringUtils.isBlank(language)) {
502: try {
503: subdirectory = new File(WikiBase.SPECIAL_PAGE_DIR,
504: language).getPath();
505: filename = new File(subdirectory, Utilities
506: .encodeForFilename(pageName)
507: + ".txt").getPath();
508: contents = Utilities.readFile(filename);
509: } catch (Exception e) {
510: logger.info("File " + filename + " does not exist");
511: }
512: }
513: if (contents == null) {
514: try {
515: subdirectory = new File(WikiBase.SPECIAL_PAGE_DIR)
516: .getPath();
517: filename = new File(subdirectory, Utilities
518: .encodeForFilename(pageName)
519: + ".txt").getPath();
520: contents = Utilities.readFile(filename);
521: } catch (Exception e) {
522: logger.warning("File " + filename
523: + " could not be read", e);
524: throw e;
525: }
526: }
527: return contents;
528: }
529:
530: /**
531: * Utility method for retrieving values from the URI. This method
532: * will attempt to properly convert the URI encoding, and then offers a way
533: * to return directories after the initial context directory. For example,
534: * if the URI is "/context/first/second/third" and this method is called
535: * with a skipCount of 1, the return value is "second/third".
536: *
537: * @param request The servlet request object.
538: * @param skipCount The number of directories to skip.
539: * @return A UTF-8 encoded portion of the URL that skips the web application
540: * context and skipCount directories, or <code>null</code> if the number of
541: * directories is less than skipCount.
542: */
543: private static String retrieveDirectoriesFromURI(
544: HttpServletRequest request, int skipCount) {
545: String uri = request.getRequestURI().trim();
546: // FIXME - needs testing on other platforms
547: uri = Utilities.convertEncoding(uri, "ISO-8859-1", "UTF-8");
548: String contextPath = request.getContextPath().trim();
549: if (StringUtils.isBlank(uri) || contextPath == null) {
550: return null;
551: }
552: // make sure there are no instances of "//" in the URL
553: uri = uri.replaceAll("(/){2,}", "/");
554: uri = uri.substring(contextPath.length() + 1);
555: int i = 0;
556: while (i < skipCount) {
557: int slashIndex = uri.indexOf('/');
558: if (slashIndex == -1) {
559: return null;
560: }
561: uri = uri.substring(slashIndex + 1);
562: i++;
563: }
564: return uri;
565: }
566:
567: /**
568: * If a blacklist or whitelist of allowed file upload types is being used,
569: * retrieve the list from the properties file and return as a List object.
570: * If no such list is being used then return an empty List object.
571: *
572: * @return A list consisting of lowercase versions of all file extensions
573: * for the whitelist/blacklist. Entries in the list are of the form
574: * "txt", not ".txt".
575: */
576: public static List retrieveUploadFileList() {
577: List list = new Vector();
578: int blacklistType = Environment
579: .getIntValue(Environment.PROP_FILE_BLACKLIST_TYPE);
580: String listString = "";
581: if (blacklistType == WikiBase.UPLOAD_BLACKLIST) {
582: listString = Environment
583: .getValue(Environment.PROP_FILE_BLACKLIST);
584: } else if (blacklistType == WikiBase.UPLOAD_WHITELIST) {
585: listString = Environment
586: .getValue(Environment.PROP_FILE_WHITELIST);
587: }
588: String[] tokens = listString.split("[\\s,\\.]");
589: for (int i = 0; i < tokens.length; i++) {
590: String token = tokens[i];
591: if (StringUtils.isBlank(token)) {
592: continue;
593: }
594: list.add(token.toLowerCase());
595: }
596: return list;
597: }
598:
599: /**
600: * Utility method to retrieve an instance of the current search engine.
601: *
602: * @return An instance of the current search engine.
603: * @throws Exception Thrown if a user handler instance can not be
604: * instantiated.
605: */
606: public static SearchEngine searchEngineInstance() throws Exception {
607: String searchEngineClass = Environment
608: .getValue(Environment.PROP_BASE_SEARCH_ENGINE);
609: logger.fine("Search engine: " + searchEngineClass);
610: Class clazz = ClassUtils.getClass(searchEngineClass);
611: Class[] parameterTypes = new Class[0];
612: Constructor constructor = clazz.getConstructor(parameterTypes);
613: Object[] initArgs = new Object[0];
614: return (SearchEngine) constructor.newInstance(initArgs);
615: }
616:
617: /**
618: * Utility method to retrieve an instance of the current user handler.
619: *
620: * @return An instance of the current user handler.
621: * @throws Exception Thrown if a user handler instance can not be
622: * instantiated.
623: */
624: public static UserHandler userHandlerInstance() throws Exception {
625: String userHandlerClass = Environment
626: .getValue(Environment.PROP_BASE_USER_HANDLER);
627: logger.fine("Using user handler: " + userHandlerClass);
628: Class clazz = ClassUtils.getClass(userHandlerClass);
629: Class[] parameterTypes = new Class[0];
630: Constructor constructor = clazz.getConstructor(parameterTypes);
631: Object[] initArgs = new Object[0];
632: return (UserHandler) constructor.newInstance(initArgs);
633: }
634:
635: /**
636: * Verify that a directory exists and is writable.
637: *
638: * @param name The full name (including the path) for the directory being tested.
639: * @return A WikiMessage object containing any error encountered, otherwise
640: * <code>null</code>.
641: */
642: public static WikiMessage validateDirectory(String name) {
643: File directory = new File(name);
644: if (!directory.exists() || !directory.isDirectory()) {
645: return new WikiMessage("error.directoryinvalid", name);
646: }
647: String filename = "jamwiki-test-" + System.currentTimeMillis()
648: + ".txt";
649: File file = new File(name, filename);
650: String text = "Testing";
651: String read = null;
652: try {
653: // attempt to write a temp file to the directory
654: FileUtils.writeStringToFile(file, text, "UTF-8");
655: } catch (Exception e) {
656: return new WikiMessage("error.directorywrite", name, e
657: .getMessage());
658: }
659: try {
660: // verify that the file was correctly written
661: read = FileUtils.readFileToString(file, "UTF-8");
662: if (read == null || !text.equals(read)) {
663: throw new IOException();
664: }
665: } catch (Exception e) {
666: return new WikiMessage("error.directoryread", name, e
667: .getMessage());
668: }
669: try {
670: // attempt to delete the file
671: FileUtils.forceDelete(file);
672: } catch (Exception e) {
673: return new WikiMessage("error.directorydelete", name, e
674: .getMessage());
675: }
676: return null;
677: }
678:
679: /**
680: * Utility method for determining if the parameters of a Role are valid
681: * or not.
682: *
683: * @param role The Role to validate.
684: * @throws WikiException Thrown if the role is invalid.
685: */
686: public static void validateRole(Role role) throws WikiException {
687: Matcher m = WikiUtil.INVALID_ROLE_NAME_PATTERN.matcher(role
688: .getAuthority());
689: if (!m.matches()) {
690: throw new WikiException(new WikiMessage("roles.error.name",
691: role.getAuthority()));
692: }
693: if (!StringUtils.isBlank(role.getDescription())
694: && role.getDescription().length() > 200) {
695: throw new WikiException(new WikiMessage(
696: "roles.error.description"));
697: }
698: // FIXME - throw a user-friendly error if the role name is already in use
699: }
700:
701: /**
702: * Utility method for determining if a topic name is valid for use on the Wiki,
703: * meaning that it is not empty and does not contain any invalid characters.
704: *
705: * @param name The topic name to validate.
706: * @throws WikiException Thrown if the user name is invalid.
707: */
708: public static void validateTopicName(String name)
709: throws WikiException {
710: if (StringUtils.isBlank(name)) {
711: throw new WikiException(new WikiMessage(
712: "common.exception.notopic"));
713: }
714: if (PseudoTopicHandler.isPseudoTopic(name)) {
715: throw new WikiException(new WikiMessage(
716: "common.exception.pseudotopic", name));
717: }
718: WikiLink wikiLink = LinkUtil.parseWikiLink(name);
719: String namespace = wikiLink.getNamespace();
720: if (namespace != null
721: && namespace.toLowerCase().trim().equals(
722: NamespaceHandler.NAMESPACE_SPECIAL
723: .toLowerCase())) {
724: throw new WikiException(new WikiMessage(
725: "common.exception.name", name));
726: }
727: Matcher m = WikiUtil.INVALID_TOPIC_NAME_PATTERN.matcher(name);
728: if (m.find()) {
729: throw new WikiException(new WikiMessage(
730: "common.exception.name", name));
731: }
732: }
733:
734: /**
735: * Utility method for determining if a username is valid for use on the Wiki,
736: * meaning that it is not empty and does not contain any invalid characters.
737: *
738: * @param name The username to validate.
739: * @throws WikiException Thrown if the user name is invalid.
740: */
741: public static void validateUserName(String name)
742: throws WikiException {
743: if (StringUtils.isBlank(name)) {
744: throw new WikiException(new WikiMessage("error.loginempty"));
745: }
746: Matcher m = WikiUtil.VALID_USER_LOGIN_PATTERN.matcher(name);
747: if (!m.matches()) {
748: throw new WikiException(new WikiMessage(
749: "common.exception.name", name));
750: }
751: }
752: }
|