0001: /**
0002: * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, version 2.1, dated February 1999.
0003: *
0004: * This program is free software; you can redistribute it and/or modify
0005: * it under the terms of the latest version of the GNU Lesser General
0006: * Public License as published by the Free Software Foundation;
0007: *
0008: * This program is distributed in the hope that it will be useful,
0009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0011: * GNU Lesser General Public License for more details.
0012: *
0013: * You should have received a copy of the GNU Lesser General Public License
0014: * along with this program (LICENSE.txt); if not, write to the Free Software
0015: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0016: */package org.jamwiki.servlets;
0017:
0018: import java.io.File;
0019: import java.util.Collection;
0020: import java.util.Iterator;
0021: import java.util.LinkedHashMap;
0022: import java.util.List;
0023: import java.util.Locale;
0024: import java.util.Properties;
0025: import java.util.Vector;
0026: import javax.servlet.http.HttpServletRequest;
0027: import net.sf.ehcache.Element;
0028: import org.acegisecurity.Authentication;
0029: import org.acegisecurity.AuthenticationCredentialsNotFoundException;
0030: import org.acegisecurity.context.SecurityContext;
0031: import org.acegisecurity.context.SecurityContextHolder;
0032: import org.apache.commons.fileupload.disk.DiskFileItemFactory;
0033: import org.apache.commons.fileupload.servlet.ServletFileUpload;
0034: import org.apache.commons.io.FilenameUtils;
0035: import org.apache.commons.lang.ClassUtils;
0036: import org.apache.commons.lang.StringUtils;
0037: import org.jamwiki.Environment;
0038: import org.jamwiki.WikiBase;
0039: import org.jamwiki.WikiException;
0040: import org.jamwiki.WikiMessage;
0041: import org.jamwiki.authentication.WikiUserAuth;
0042: import org.jamwiki.db.DatabaseConnection;
0043: import org.jamwiki.model.Category;
0044: import org.jamwiki.model.Role;
0045: import org.jamwiki.model.Topic;
0046: import org.jamwiki.model.VirtualWiki;
0047: import org.jamwiki.model.Watchlist;
0048: import org.jamwiki.model.WikiFileVersion;
0049: import org.jamwiki.model.WikiUser;
0050: import org.jamwiki.parser.ParserInput;
0051: import org.jamwiki.parser.ParserOutput;
0052: import org.jamwiki.parser.ParserUtil;
0053: import org.jamwiki.utils.Encryption;
0054: import org.jamwiki.utils.LinkUtil;
0055: import org.jamwiki.utils.NamespaceHandler;
0056: import org.jamwiki.utils.Pagination;
0057: import org.jamwiki.utils.SpamFilter;
0058: import org.jamwiki.utils.Utilities;
0059: import org.jamwiki.utils.WikiCache;
0060: import org.jamwiki.utils.WikiLink;
0061: import org.jamwiki.utils.WikiLogger;
0062: import org.jamwiki.utils.WikiUtil;
0063: import org.apache.commons.lang.LocaleUtils;
0064: import org.springframework.web.servlet.ModelAndView;
0065:
0066: /**
0067: * Utility methods useful when processing JAMWiki servlet requests.
0068: */
0069: public class ServletUtil {
0070:
0071: private static final WikiLogger logger = WikiLogger
0072: .getLogger(ServletUtil.class.getName());
0073: protected static final String JSP_ERROR = "error-display.jsp";
0074: protected static final String JSP_LOGIN = "login.jsp";
0075: public static final String PARAMETER_PAGE_INFO = "pageInfo";
0076: public static final String PARAMETER_TOPIC_OBJECT = "topicObject";
0077: private static final String SPRING_REDIRECT_PREFIX = "redirect:";
0078:
0079: /**
0080: *
0081: */
0082: private ServletUtil() {
0083: }
0084:
0085: /**
0086: * This method ensures that the left menu, logo, and other required values
0087: * have been loaded into the session object.
0088: *
0089: * @param request The servlet request object.
0090: * @param next A ModelAndView object corresponding to the page being
0091: * constructed.
0092: */
0093: private static void buildLayout(HttpServletRequest request,
0094: ModelAndView next) {
0095: String virtualWikiName = WikiUtil
0096: .getVirtualWikiFromURI(request);
0097: if (virtualWikiName == null) {
0098: logger.severe("No virtual wiki available for page request "
0099: + request.getRequestURI());
0100: virtualWikiName = WikiBase.DEFAULT_VWIKI;
0101: }
0102: VirtualWiki virtualWiki = retrieveVirtualWiki(virtualWikiName);
0103: // build the layout contents
0104: String leftMenu = ServletUtil.cachedContent(request
0105: .getContextPath(), request.getLocale(),
0106: virtualWikiName, WikiBase.SPECIAL_PAGE_LEFT_MENU, true);
0107: next.addObject("leftMenu", leftMenu);
0108: next.addObject("defaultTopic", virtualWiki
0109: .getDefaultTopicName());
0110: next.addObject("virtualWiki", virtualWiki.getName());
0111: next.addObject("logo", Environment
0112: .getValue(Environment.PROP_BASE_LOGO_IMAGE));
0113: String bottomArea = ServletUtil.cachedContent(request
0114: .getContextPath(), request.getLocale(), virtualWiki
0115: .getName(), WikiBase.SPECIAL_PAGE_BOTTOM_AREA, true);
0116: next.addObject("bottomArea", bottomArea);
0117: next.addObject(WikiUtil.PARAMETER_VIRTUAL_WIKI, virtualWiki
0118: .getName());
0119: Integer cssRevision = new Integer(0);
0120: try {
0121: cssRevision = WikiBase.getDataHandler().lookupTopic(
0122: virtualWiki.getName(),
0123: WikiBase.SPECIAL_PAGE_STYLESHEET, false, null)
0124: .getCurrentVersionId();
0125: } catch (Exception e) {
0126: }
0127: next.addObject("cssRevision", cssRevision);
0128: }
0129:
0130: /**
0131: * Build a map of links and the corresponding link text to be used as the
0132: * tab menu links for the WikiPageInfo object.
0133: */
0134: private static LinkedHashMap buildTabMenu(
0135: HttpServletRequest request, WikiPageInfo pageInfo) {
0136: LinkedHashMap links = new LinkedHashMap();
0137: WikiUserAuth user = ServletUtil.currentUser();
0138: String pageName = pageInfo.getTopicName();
0139: String virtualWiki = WikiUtil.getVirtualWikiFromURI(request);
0140: try {
0141: if (pageInfo.getAdmin()) {
0142: if (user.hasRole(Role.ROLE_SYSADMIN)) {
0143: links.put("Special:Admin", new WikiMessage(
0144: "tab.admin.configuration"));
0145: links.put("Special:Maintenance", new WikiMessage(
0146: "tab.admin.maintenance"));
0147: links.put("Special:Roles", new WikiMessage(
0148: "tab.admin.roles"));
0149: }
0150: if (user.hasRole(Role.ROLE_TRANSLATE)) {
0151: links.put("Special:Translation", new WikiMessage(
0152: "tab.admin.translations"));
0153: }
0154: } else if (pageInfo.getSpecial()) {
0155: // append query params for pages such as Special:Contributions that need it
0156: String specialUrl = pageName;
0157: if (!StringUtils.isBlank(request.getQueryString())) {
0158: specialUrl = pageName + "?"
0159: + request.getQueryString();
0160: }
0161: links.put(specialUrl, new WikiMessage(
0162: "tab.common.special"));
0163: } else {
0164: String article = WikiUtil.extractTopicLink(pageName);
0165: String comments = WikiUtil
0166: .extractCommentsLink(pageName);
0167: links.put(article,
0168: new WikiMessage("tab.common.article"));
0169: links.put(comments, new WikiMessage(
0170: "tab.common.comments"));
0171: if (ServletUtil.isEditable(virtualWiki, pageName, user)) {
0172: String editLink = "Special:Edit?topic="
0173: + Utilities.encodeForURL(pageName);
0174: if (!StringUtils.isBlank(request
0175: .getParameter("topicVersionId"))) {
0176: editLink += "&topicVersionId="
0177: + request
0178: .getParameter("topicVersionId");
0179: }
0180: links.put(editLink, new WikiMessage(
0181: "tab.common.edit"));
0182: }
0183: String historyLink = "Special:History?topic="
0184: + Utilities.encodeForURL(pageName);
0185: links.put(historyLink, new WikiMessage(
0186: "tab.common.history"));
0187: if (ServletUtil.isMoveable(virtualWiki, pageName, user)) {
0188: String moveLink = "Special:Move?topic="
0189: + Utilities.encodeForURL(pageName);
0190: links.put(moveLink, new WikiMessage(
0191: "tab.common.move"));
0192: }
0193: if (user.hasRole(Role.ROLE_USER)) {
0194: Watchlist watchlist = ServletUtil.currentWatchlist(
0195: request, virtualWiki);
0196: boolean watched = (watchlist
0197: .containsTopic(pageName));
0198: String watchlistLabel = (watched) ? "tab.common.unwatch"
0199: : "tab.common.watch";
0200: String watchlistLink = "Special:Watchlist?topic="
0201: + Utilities.encodeForURL(pageName);
0202: links.put(watchlistLink, new WikiMessage(
0203: watchlistLabel));
0204: }
0205: if (pageInfo.isUserPage()) {
0206: WikiLink wikiLink = LinkUtil
0207: .parseWikiLink(pageName);
0208: String contributionsLink = "Special:Contributions?contributor="
0209: + Utilities.encodeForURL(wikiLink
0210: .getArticle());
0211: links.put(contributionsLink, new WikiMessage(
0212: "tab.common.contributions"));
0213: }
0214: String linkToLink = "Special:LinkTo?topic="
0215: + Utilities.encodeForURL(pageName);
0216: links.put(linkToLink, new WikiMessage(
0217: "tab.common.links"));
0218: if (user.hasRole(Role.ROLE_ADMIN)) {
0219: String manageLink = "Special:Manage?topic="
0220: + Utilities.encodeForURL(pageName);
0221: links.put(manageLink, new WikiMessage(
0222: "tab.common.manage"));
0223: }
0224: String printLink = "Special:Print?topic="
0225: + Utilities.encodeForURL(pageName);
0226: links.put(printLink,
0227: new WikiMessage("tab.common.print"));
0228: }
0229: } catch (Exception e) {
0230: logger.severe("Unable to build tabbed menu links", e);
0231: }
0232: return links;
0233: }
0234:
0235: /**
0236: * Build a map of links and the corresponding link text to be used as the
0237: * user menu links for the WikiPageInfo object.
0238: */
0239: private static LinkedHashMap buildUserMenu() {
0240: LinkedHashMap links = new LinkedHashMap();
0241: WikiUserAuth user = ServletUtil.currentUser();
0242: if (user.hasRole(Role.ROLE_ANONYMOUS)
0243: && !user.hasRole(Role.ROLE_EMBEDDED)) {
0244: links.put("Special:Login", new WikiMessage("common.login"));
0245: links.put("Special:Account", new WikiMessage(
0246: "usermenu.register"));
0247: }
0248: if (user.hasRole(Role.ROLE_USER)) {
0249: String userPage = NamespaceHandler.NAMESPACE_USER
0250: + NamespaceHandler.NAMESPACE_SEPARATOR
0251: + user.getUsername();
0252: String userCommentsPage = NamespaceHandler.NAMESPACE_USER_COMMENTS
0253: + NamespaceHandler.NAMESPACE_SEPARATOR
0254: + user.getUsername();
0255: String username = user.getUsername();
0256: if (!StringUtils.isBlank(user.getDisplayName())) {
0257: username = user.getDisplayName();
0258: }
0259: // user name will be escaped by the jamwiki:link tag
0260: WikiMessage userMenuMessage = new WikiMessage(
0261: "usermenu.user");
0262: userMenuMessage
0263: .setParamsWithoutEscaping(new String[] { username });
0264: links.put(userPage, userMenuMessage);
0265: links.put(userCommentsPage, new WikiMessage(
0266: "usermenu.usercomments"));
0267: links.put("Special:Watchlist", new WikiMessage(
0268: "usermenu.watchlist"));
0269: }
0270: if (user.hasRole(Role.ROLE_USER)
0271: && !user.hasRole(Role.ROLE_NO_ACCOUNT)) {
0272: links.put("Special:Account", new WikiMessage(
0273: "usermenu.account"));
0274: }
0275: if (user.hasRole(Role.ROLE_USER)
0276: && !user.hasRole(Role.ROLE_EMBEDDED)) {
0277: links.put("Special:Logout",
0278: new WikiMessage("common.logout"));
0279: }
0280: if (user.hasRole(Role.ROLE_SYSADMIN)) {
0281: links.put("Special:Admin",
0282: new WikiMessage("usermenu.admin"));
0283: } else if (user.hasRole(Role.ROLE_TRANSLATE)) {
0284: links.put("Special:Translation", new WikiMessage(
0285: "tab.admin.translations"));
0286: }
0287: return links;
0288: }
0289:
0290: /**
0291: * Retrieve the content of a topic from the cache, or if it is not yet in
0292: * the cache then add it to the cache.
0293: *
0294: * @param context The servlet context for the topic being retrieved. May
0295: * be <code>null</code> if the <code>cook</code> parameter is set to
0296: * <code>false</code>.
0297: * @param locale The locale for the topic being retrieved. May be
0298: * <code>null</code> if the <code>cook</code> parameter is set to
0299: * <code>false</code>.
0300: * @param virtualWiki The virtual wiki for the topic being retrieved.
0301: * @param topicName The name of the topic being retrieved.
0302: * @param cook A parameter indicating whether or not the content should be
0303: * parsed before it is added to the cache. Stylesheet content (CSS) is not
0304: * parsed, but most other content is parsed.
0305: * @return The parsed or unparsed (depending on the <code>cook</code>
0306: * parameter) topic content.
0307: */
0308: protected static String cachedContent(String context,
0309: Locale locale, String virtualWiki, String topicName,
0310: boolean cook) {
0311: String content = null;
0312: String key = WikiCache.key(virtualWiki, topicName);
0313: Element cacheElement = WikiCache.retrieveFromCache(
0314: WikiBase.CACHE_PARSED_TOPIC_CONTENT, key);
0315: if (cacheElement != null) {
0316: content = (String) cacheElement.getObjectValue();
0317: return (content == null) ? null : new String(content);
0318: }
0319: try {
0320: Topic topic = WikiBase.getDataHandler().lookupTopic(
0321: virtualWiki, topicName, false, null);
0322: content = topic.getTopicContent();
0323: if (cook) {
0324: ParserInput parserInput = new ParserInput();
0325: parserInput.setContext(context);
0326: parserInput.setLocale(locale);
0327: parserInput.setVirtualWiki(virtualWiki);
0328: parserInput.setTopicName(topicName);
0329: content = ParserUtil.parse(parserInput, null, content);
0330: }
0331: WikiCache.addToCache(WikiBase.CACHE_PARSED_TOPIC_CONTENT,
0332: key, content);
0333: } catch (Exception e) {
0334: logger.warning("error getting cached page " + virtualWiki
0335: + " / " + topicName, e);
0336: return null;
0337: }
0338: return content;
0339: }
0340:
0341: /**
0342: * This is a utility method that will check topic content for spam, and
0343: * return <code>null</code> if no matching values are found, or if a spam
0344: * pattern is found then that pattern will be returned. It will also log
0345: * information about the offending spam and user to the logs.
0346: *
0347: * @param request The current servlet request.
0348: * @param topicName The name of the current topic being edited.
0349: * @param contents The text for the current topic that the user is trying to
0350: * add.
0351: * @return <code>null</code> if nothing in the topic content matches a current
0352: * spam pattern, or the text that matches a spam pattern if one is found.
0353: */
0354: protected static String checkForSpam(HttpServletRequest request,
0355: String topicName, String contents) throws Exception {
0356: String result = SpamFilter.containsSpam(contents);
0357: if (StringUtils.isBlank(result)) {
0358: return null;
0359: }
0360: String message = "SPAM found in topic " + topicName + " (";
0361: WikiUserAuth user = ServletUtil.currentUser();
0362: if (user.hasRole(Role.ROLE_USER)) {
0363: message += user.getUsername() + " / ";
0364: }
0365: message += ServletUtil.getIpAddress(request) + "): " + result;
0366: logger.info(message);
0367: return result;
0368: }
0369:
0370: /**
0371: * Retrieve the current <code>WikiUserAuth</code> from Acegi
0372: * <code>SecurityContextHolder</code>. If the current user is not
0373: * logged-in then this method will return an empty <code>WikiUserAuth</code>
0374: * object.
0375: *
0376: * @return The current logged-in <code>WikiUserAuth</code>, or an empty
0377: * <code>WikiUserAuth</code> if there is no user currently logged in.
0378: * This method will never return <code>null</code>.
0379: * @throws AuthenticationCredentialsNotFoundException If authentication
0380: * credentials are unavailable.
0381: */
0382: public static WikiUserAuth currentUser()
0383: throws AuthenticationCredentialsNotFoundException {
0384: SecurityContext ctx = SecurityContextHolder.getContext();
0385: Authentication auth = ctx.getAuthentication();
0386: return WikiUserAuth.initWikiUserAuth(auth);
0387: }
0388:
0389: /**
0390: * Retrieve the current logged-in user's watchlist from the session. If
0391: * there is no watchlist return an empty watchlist.
0392: *
0393: * @param request The servlet request object.
0394: * @param virtualWiki The virtual wiki for the watchlist being parsed.
0395: * @return The current logged-in user's watchlist, or an empty watchlist
0396: * if there is no watchlist in the session.
0397: */
0398: public static Watchlist currentWatchlist(
0399: HttpServletRequest request, String virtualWiki)
0400: throws Exception {
0401: // get watchlist stored in session
0402: Watchlist watchlist = (Watchlist) request.getSession()
0403: .getAttribute(WikiUtil.PARAMETER_WATCHLIST);
0404: if (watchlist != null) {
0405: return watchlist;
0406: }
0407: // no watchlist in session, retrieve from database
0408: watchlist = new Watchlist();
0409: WikiUserAuth user = ServletUtil.currentUser();
0410: if (!user.hasRole(Role.ROLE_USER)) {
0411: return watchlist;
0412: }
0413: watchlist = WikiBase.getDataHandler().getWatchlist(virtualWiki,
0414: user.getUserId());
0415: request.getSession().setAttribute(WikiUtil.PARAMETER_WATCHLIST,
0416: watchlist);
0417: return watchlist;
0418: }
0419:
0420: /**
0421: * Duplicate the functionality of the request.getRemoteAddr() method, but
0422: * for IPv6 addresses strip off any local interface information (anything
0423: * following a "%").
0424: *
0425: * @param request the HTTP request object.
0426: * @return The IP address that the request originated from, or 0.0.0.0 if
0427: * the originating address cannot be determined.
0428: */
0429: public static String getIpAddress(HttpServletRequest request) {
0430: if (request == null) {
0431: throw new IllegalArgumentException(
0432: "Request object cannot be null");
0433: }
0434: String ipAddress = request.getRemoteAddr();
0435: int pos = ipAddress.indexOf("%");
0436: if (pos != -1) {
0437: ipAddress = ipAddress.substring(0, pos);
0438: }
0439: if (!Utilities.isIpAddress(ipAddress)) {
0440: logger.info("Invalid IP address found in request: "
0441: + ipAddress);
0442: ipAddress = "0.0.0.0";
0443: }
0444: return ipAddress;
0445: }
0446:
0447: /**
0448: * Initialize topic values for a Topic object. This method will check to
0449: * see if a topic with the specified name exists, and if it does exist
0450: * then that topic will be returned. Otherwise a new topic will be
0451: * initialized, setting initial parameters such as topic name, virtual
0452: * wiki, and topic type.
0453: *
0454: * @param virtualWiki The virtual wiki name for the topic being
0455: * initialized.
0456: * @param topicName The name of the topic being initialized.
0457: * @return A new topic object with basic fields initialized, or if a topic
0458: * with the given name already exists then the pre-existing topic is
0459: * returned.
0460: * @throws Exception Thrown if any error occurs while retrieving or
0461: * initializing the topic object.
0462: */
0463: protected static Topic initializeTopic(String virtualWiki,
0464: String topicName) throws Exception {
0465: WikiUtil.validateTopicName(topicName);
0466: Topic topic = WikiBase.getDataHandler().lookupTopic(
0467: virtualWiki, topicName, false, null);
0468: if (topic != null) {
0469: return topic;
0470: }
0471: topic = new Topic();
0472: topic.setName(topicName);
0473: topic.setVirtualWiki(virtualWiki);
0474: WikiLink wikiLink = LinkUtil.parseWikiLink(topicName);
0475: String namespace = wikiLink.getNamespace();
0476: if (namespace != null) {
0477: if (namespace.equals(NamespaceHandler.NAMESPACE_CATEGORY)) {
0478: topic.setTopicType(Topic.TYPE_CATEGORY);
0479: } else if (namespace
0480: .equals(NamespaceHandler.NAMESPACE_TEMPLATE)) {
0481: topic.setTopicType(Topic.TYPE_TEMPLATE);
0482: }
0483: }
0484: return topic;
0485: }
0486:
0487: /**
0488: * Determine if a user has permission to edit a topic.
0489: *
0490: * @param virtualWiki The virtual wiki name for the topic in question.
0491: * @param topicName The name of the topic in question.
0492: * @param user The current Wiki user, or <code>null</code> if there is
0493: * no current user.
0494: * @return <code>true</code> if the user is allowed to edit the topic,
0495: * <code>false</code> otherwise.
0496: */
0497: protected static boolean isEditable(String virtualWiki,
0498: String topicName, WikiUserAuth user) throws Exception {
0499: if (user == null || !user.hasRole(Role.ROLE_EDIT_EXISTING)) {
0500: // user does not have appropriate permissions
0501: return false;
0502: }
0503: if (!user.hasRole(Role.ROLE_EDIT_NEW)
0504: && WikiBase.getDataHandler().lookupTopic(virtualWiki,
0505: topicName, false, null) == null) {
0506: // user does not have appropriate permissions
0507: return false;
0508: }
0509: Topic topic = WikiBase.getDataHandler().lookupTopic(
0510: virtualWiki, topicName, false, null);
0511: if (topic == null) {
0512: // new topic, edit away...
0513: return true;
0514: }
0515: if (topic.getAdminOnly() && !user.hasRole(Role.ROLE_ADMIN)) {
0516: return false;
0517: }
0518: if (topic.getReadOnly()) {
0519: return false;
0520: }
0521: return true;
0522: }
0523:
0524: /**
0525: * Determine if a user has permission to move a topic.
0526: *
0527: * @param virtualWiki The virtual wiki name for the topic in question.
0528: * @param topicName The name of the topic in question.
0529: * @param user The current Wiki user, or <code>null</code> if there is
0530: * no current user.
0531: * @return <code>true</code> if the user is allowed to move the topic,
0532: * <code>false</code> otherwise.
0533: */
0534: protected static boolean isMoveable(String virtualWiki,
0535: String topicName, WikiUserAuth user) throws Exception {
0536: if (user == null || !user.hasRole(Role.ROLE_MOVE)) {
0537: // no permission granted to move pages
0538: return false;
0539: }
0540: Topic topic = WikiBase.getDataHandler().lookupTopic(
0541: virtualWiki, topicName, false, null);
0542: if (topic == null) {
0543: // cannot move a topic that doesn't exist
0544: return false;
0545: }
0546: if (topic.getReadOnly()) {
0547: return false;
0548: }
0549: if (topic.getAdminOnly() && !user.hasRole(Role.ROLE_ADMIN)) {
0550: return false;
0551: }
0552: return true;
0553: }
0554:
0555: /**
0556: * Examine the request object, and see if the requested topic or page
0557: * matches a given value.
0558: *
0559: * @param request The servlet request object.
0560: * @param value The value to match against the current topic or page name.
0561: * @return <code>true</code> if the value matches the current topic or
0562: * page name, <code>false</code> otherwise.
0563: */
0564: protected static boolean isTopic(HttpServletRequest request,
0565: String value) {
0566: try {
0567: String topic = WikiUtil.getTopicFromURI(request);
0568: if (StringUtils.isBlank(topic)) {
0569: return false;
0570: }
0571: if (value != null && topic.equals(value)) {
0572: return true;
0573: }
0574: } catch (Exception e) {
0575: }
0576: return false;
0577: }
0578:
0579: /**
0580: * Utility method for adding categories associated with the current topic
0581: * to the ModelAndView object. This method adds a hashmap of category
0582: * names and sort keys to the session that can then be retrieved for
0583: * display during rendering.
0584: *
0585: * @param next The current ModelAndView object used to return rendering
0586: * information.
0587: * @param virtualWiki The virtual wiki name for the topic being rendered.
0588: * @param topicName The name of the topic that is being rendered.
0589: */
0590: protected static void loadCategoryContent(ModelAndView next,
0591: String virtualWiki, String topicName) throws Exception {
0592: String categoryName = topicName
0593: .substring(NamespaceHandler.NAMESPACE_CATEGORY.length()
0594: + NamespaceHandler.NAMESPACE_SEPARATOR.length());
0595: next.addObject("categoryName", categoryName);
0596: List categoryTopics = WikiBase.getDataHandler()
0597: .lookupCategoryTopics(virtualWiki, topicName);
0598: List categoryImages = new Vector();
0599: LinkedHashMap subCategories = new LinkedHashMap();
0600: int i = 0;
0601: // loop through the results and split out images and sub-categories
0602: while (i < categoryTopics.size()) {
0603: Category category = (Category) categoryTopics.get(i);
0604: if (category.getTopicType() == Topic.TYPE_IMAGE) {
0605: categoryTopics.remove(i);
0606: categoryImages.add(category);
0607: continue;
0608: }
0609: if (category.getTopicType() == Topic.TYPE_CATEGORY) {
0610: categoryTopics.remove(i);
0611: String value = category.getChildTopicName().substring(
0612: NamespaceHandler.NAMESPACE_CATEGORY.length()
0613: + NamespaceHandler.NAMESPACE_SEPARATOR
0614: .length());
0615: subCategories.put(category.getChildTopicName(), value);
0616: continue;
0617: }
0618: i++;
0619: }
0620: next.addObject("categoryTopics", categoryTopics);
0621: next.addObject("numCategoryTopics", new Integer(categoryTopics
0622: .size()));
0623: next.addObject("categoryImages", categoryImages);
0624: next.addObject("numCategoryImages", new Integer(categoryImages
0625: .size()));
0626: next.addObject("subCategories", subCategories);
0627: next.addObject("numSubCategories", new Integer(subCategories
0628: .size()));
0629: }
0630:
0631: /**
0632: * This method ensures that values required for rendering a JSP page have
0633: * been loaded into the ModelAndView object. Examples of values that
0634: * may be handled by this method include topic name, username, etc.
0635: *
0636: * @param request The current servlet request object.
0637: * @param next The current ModelAndView object.
0638: * @param pageInfo The current WikiPageInfo object, containing basic page
0639: * rendering information.
0640: */
0641: protected static void loadDefaults(HttpServletRequest request,
0642: ModelAndView next, WikiPageInfo pageInfo) throws Exception {
0643: if (next.getViewName() != null
0644: && next.getViewName().startsWith(
0645: ServletUtil.SPRING_REDIRECT_PREFIX)) {
0646: // if this is a redirect, no need to load anything
0647: return;
0648: }
0649: // load cached top area, nav bar, etc.
0650: ServletUtil.buildLayout(request, next);
0651: if (StringUtils.isBlank(pageInfo.getTopicName())) {
0652: pageInfo.setTopicName(WikiUtil.getTopicFromURI(request));
0653: }
0654: pageInfo.setUserMenu(ServletUtil.buildUserMenu());
0655: pageInfo
0656: .setTabMenu(ServletUtil.buildTabMenu(request, pageInfo));
0657: next.addObject(ServletUtil.PARAMETER_PAGE_INFO, pageInfo);
0658: }
0659:
0660: /**
0661: * Create a Pagination object and load all necessary values into the
0662: * request for processing by a JSP.
0663: *
0664: * @param request The servlet request object.
0665: * @param next A ModelAndView object corresponding to the page being
0666: * constructed.
0667: * @return A Pagination object constructed from parameters found in the
0668: * request object.
0669: */
0670: public static Pagination loadPagination(HttpServletRequest request,
0671: ModelAndView next) {
0672: if (next == null) {
0673: throw new IllegalArgumentException(
0674: "A non-null ModelAndView object must be specified when loading pagination values");
0675: }
0676: Pagination pagination = WikiUtil.buildPagination(request);
0677: next.addObject("num", new Integer(pagination.getNumResults()));
0678: next.addObject("offset", new Integer(pagination.getOffset()));
0679: return pagination;
0680: }
0681:
0682: /**
0683: * Utility method for parsing a multipart servlet request. This method returns
0684: * an iterator of FileItem objects that corresponds to the request.
0685: *
0686: * @param request The servlet request containing the multipart request.
0687: * @param uploadDirectory The directory into which files will be uploaded.
0688: * @param maxFileSize The maximum allowed file size in bytes.
0689: * @return Returns an iterator of FileItem objects the corresponds to the request.
0690: * @throws Exception Thrown if any problems occur while processing the request.
0691: */
0692: public static Iterator processMultipartRequest(
0693: HttpServletRequest request, String uploadDirectory,
0694: long maxFileSize) throws Exception {
0695: // Create a factory for disk-based file items
0696: DiskFileItemFactory factory = new DiskFileItemFactory();
0697: factory.setRepository(new File(uploadDirectory));
0698: ServletFileUpload upload = new ServletFileUpload(factory);
0699: upload.setHeaderEncoding("UTF-8");
0700: upload.setSizeMax(maxFileSize);
0701: return upload.parseRequest(request).iterator();
0702: }
0703:
0704: /**
0705: * Modify the current ModelAndView object to create a Spring redirect
0706: * response, meaning that the view name becomes "redirect:" followed by
0707: * the redirection target.
0708: *
0709: * @param next The current ModelAndView object, which will be reset by
0710: * this method.
0711: * @param virtualWiki The virtual wiki name for the page being redirected
0712: * to.
0713: * @param destination The topic or page name that is the redirection
0714: * target. An example might be "Special:Login".
0715: */
0716: protected static void redirect(ModelAndView next,
0717: String virtualWiki, String destination) throws Exception {
0718: String target = LinkUtil.buildInternalLinkUrl(null,
0719: virtualWiki, destination);
0720: String view = ServletUtil.SPRING_REDIRECT_PREFIX + target;
0721: next.clear();
0722: next.setViewName(view);
0723: }
0724:
0725: /**
0726: * Users can specify a default locale in their preferences, so determine
0727: * if the current user is logged-in and has chosen a locale. If not, use
0728: * the default locale from the request object.
0729: *
0730: * @param request The request object for the HTTP request.
0731: * @return Either the user's default locale (for logged-in users) or the
0732: * locale specified in the request if no default locale is available.
0733: */
0734: public static Locale retrieveUserLocale(HttpServletRequest request) {
0735: WikiUser user = null;
0736: try {
0737: user = ServletUtil.currentUser();
0738: if (user.getDefaultLocale() != null) {
0739: return LocaleUtils.toLocale(user.getDefaultLocale());
0740: }
0741: } catch (AuthenticationCredentialsNotFoundException e) {
0742: // ignore
0743: }
0744: return request.getLocale();
0745: }
0746:
0747: /**
0748: * Given a virtual wiki name, return a <code>VirtualWiki</code> object.
0749: * If there is no virtual wiki available with the given name then the
0750: * default virtual wiki is returned.
0751: *
0752: * @param virtualWikiName The name of the virtual wiki that is being
0753: * retrieved.
0754: * @return A <code>VirtualWiki</code> object. If there is no virtual
0755: * wiki available with the given name then the default virtual wiki is
0756: * returned.
0757: */
0758: public static VirtualWiki retrieveVirtualWiki(String virtualWikiName) {
0759: VirtualWiki virtualWiki = null;
0760: if (virtualWikiName == null) {
0761: virtualWikiName = WikiBase.DEFAULT_VWIKI;
0762: }
0763: // FIXME - the check here for initialized properties is due to this
0764: // change being made late in a release cycle. Revisit in a future
0765: // release & clean this up.
0766: if (Environment
0767: .getBooleanValue(Environment.PROP_BASE_INITIALIZED)) {
0768: try {
0769: virtualWiki = WikiBase.getDataHandler()
0770: .lookupVirtualWiki(virtualWikiName);
0771: } catch (Exception e) {
0772: }
0773: }
0774: if (virtualWiki == null) {
0775: logger.severe("No virtual wiki found for "
0776: + virtualWikiName);
0777: virtualWiki = new VirtualWiki();
0778: virtualWiki.setName(WikiBase.DEFAULT_VWIKI);
0779: virtualWiki.setDefaultTopicName(Environment
0780: .getValue(Environment.PROP_BASE_DEFAULT_TOPIC));
0781: }
0782: return virtualWiki;
0783: }
0784:
0785: /**
0786: * Validate that vital system properties, such as database connection settings,
0787: * have been specified properly.
0788: *
0789: * @param props The property object to validate against.
0790: * @return A Vector of WikiMessage objects containing any errors encountered,
0791: * or an empty Vector if no errors are encountered.
0792: */
0793: protected static Vector validateSystemSettings(Properties props) {
0794: Vector errors = new Vector();
0795: // test directory permissions & existence
0796: WikiMessage baseDirError = WikiUtil.validateDirectory(props
0797: .getProperty(Environment.PROP_BASE_FILE_DIR));
0798: if (baseDirError != null) {
0799: errors.add(baseDirError);
0800: }
0801: WikiMessage fullDirError = WikiUtil.validateDirectory(props
0802: .getProperty(Environment.PROP_FILE_DIR_FULL_PATH));
0803: if (fullDirError != null) {
0804: errors.add(fullDirError);
0805: }
0806: String classesDir = null;
0807: try {
0808: classesDir = Utilities.getClassLoaderRoot().getPath();
0809: WikiMessage classesDirError = WikiUtil
0810: .validateDirectory(classesDir);
0811: if (classesDirError != null) {
0812: errors.add(classesDirError);
0813: }
0814: } catch (Exception e) {
0815: errors.add(new WikiMessage("error.directorywrite",
0816: classesDir, e.getMessage()));
0817: }
0818: // test database
0819: String driver = props.getProperty(Environment.PROP_DB_DRIVER);
0820: String url = props.getProperty(Environment.PROP_DB_URL);
0821: String userName = props
0822: .getProperty(Environment.PROP_DB_USERNAME);
0823: String password = Encryption.getEncryptedProperty(
0824: Environment.PROP_DB_PASSWORD, props);
0825: try {
0826: DatabaseConnection.testDatabase(driver, url, userName,
0827: password, false);
0828: } catch (Exception e) {
0829: logger.severe("Invalid database settings", e);
0830: errors.add(new WikiMessage("error.databaseconnection", e
0831: .getMessage()));
0832: }
0833: // verify valid parser class
0834: boolean validParser = true;
0835: String parserClass = props
0836: .getProperty(Environment.PROP_PARSER_CLASS);
0837: String abstractParserClass = "org.jamwiki.parser.AbstractParser";
0838: if (parserClass == null
0839: || parserClass.equals(abstractParserClass)) {
0840: validParser = false;
0841: }
0842: try {
0843: Class parent = ClassUtils.getClass(parserClass);
0844: Class child = ClassUtils.getClass(abstractParserClass);
0845: if (!child.isAssignableFrom(parent)) {
0846: validParser = false;
0847: }
0848: } catch (Exception e) {
0849: validParser = false;
0850: }
0851: if (!validParser) {
0852: errors
0853: .add(new WikiMessage("error.parserclass",
0854: parserClass));
0855: }
0856: return errors;
0857: }
0858:
0859: /**
0860: * Utility method used when redirecting to an error page.
0861: *
0862: * @param request The servlet request object.
0863: * @param t The exception that is the source of the error.
0864: * @return Returns a ModelAndView object corresponding to the error page display.
0865: */
0866: protected static ModelAndView viewError(HttpServletRequest request,
0867: Throwable t) {
0868: if (!(t instanceof WikiException)) {
0869: logger.severe("Servlet error", t);
0870: }
0871: ModelAndView next = new ModelAndView("wiki");
0872: WikiPageInfo pageInfo = new WikiPageInfo();
0873: pageInfo.setPageTitle(new WikiMessage("error.title"));
0874: pageInfo.setContentJsp(JSP_ERROR);
0875: pageInfo.setSpecial(true);
0876: if (t instanceof WikiException) {
0877: WikiException we = (WikiException) t;
0878: next.addObject("messageObject", we.getWikiMessage());
0879: } else {
0880: next.addObject("messageObject", new WikiMessage(
0881: "error.unknown", t.toString()));
0882: }
0883: try {
0884: ServletUtil.loadDefaults(request, next, pageInfo);
0885: } catch (Exception err) {
0886: logger.severe("Unable to load default layout", err);
0887: }
0888: return next;
0889: }
0890:
0891: /**
0892: * Utility method used when redirecting to a login page.
0893: *
0894: * @param request The servlet request object.
0895: * @param pageInfo The current WikiPageInfo object, which contains
0896: * information needed for rendering the final JSP page.
0897: * @param topic The topic to be redirected to. Valid examples are
0898: * "Special:Admin", "StartingPoints", etc.
0899: * @param messageObject A WikiMessage object to be displayed on the login
0900: * page.
0901: * @return Returns a ModelAndView object corresponding to the login page
0902: * display.
0903: * @throws Exception Thrown if any error occurs during processing.
0904: */
0905: protected static ModelAndView viewLogin(HttpServletRequest request,
0906: WikiPageInfo pageInfo, String topic,
0907: WikiMessage messageObject) throws Exception {
0908: ModelAndView next = new ModelAndView("wiki");
0909: pageInfo.reset();
0910: String virtualWikiName = WikiUtil
0911: .getVirtualWikiFromURI(request);
0912: String target = request.getParameter("target");
0913: if (StringUtils.isBlank(target)) {
0914: if (StringUtils.isBlank(topic)) {
0915: VirtualWiki virtualWiki = WikiBase.getDataHandler()
0916: .lookupVirtualWiki(virtualWikiName);
0917: topic = virtualWiki.getDefaultTopicName();
0918: }
0919: target = topic;
0920: if (!StringUtils.isBlank(request.getQueryString())) {
0921: target += "?" + request.getQueryString();
0922: }
0923: }
0924: next.addObject("target", target);
0925: pageInfo.setPageTitle(new WikiMessage("login.title"));
0926: pageInfo.setContentJsp(JSP_LOGIN);
0927: pageInfo.setSpecial(true);
0928: if (messageObject != null) {
0929: next.addObject("messageObject", messageObject);
0930: }
0931: return next;
0932: }
0933:
0934: /**
0935: * Utility method used when viewing a topic.
0936: *
0937: * @param request The current servlet request object.
0938: * @param next The current Spring ModelAndView object.
0939: * @param pageInfo The current WikiPageInfo object, which contains
0940: * information needed for rendering the final JSP page.
0941: * @param topicName The topic being viewed. This value must be a valid
0942: * topic that can be loaded as a org.jamwiki.model.Topic object.
0943: * @throws Exception Thrown if any error occurs during processing.
0944: */
0945: protected static void viewTopic(HttpServletRequest request,
0946: ModelAndView next, WikiPageInfo pageInfo, String topicName)
0947: throws Exception {
0948: String virtualWiki = WikiUtil.getVirtualWikiFromURI(request);
0949: if (StringUtils.isBlank(virtualWiki)) {
0950: virtualWiki = WikiBase.DEFAULT_VWIKI;
0951: }
0952: Topic topic = ServletUtil.initializeTopic(virtualWiki,
0953: topicName);
0954: if (topic.getTopicId() <= 0) {
0955: // topic does not exist, display empty page
0956: next.addObject("notopic", new WikiMessage(
0957: "topic.notcreated", topicName));
0958: }
0959: WikiMessage pageTitle = new WikiMessage("topic.title",
0960: topicName);
0961: viewTopic(request, next, pageInfo, pageTitle, topic, true);
0962: }
0963:
0964: /**
0965: * Utility method used when viewing a topic.
0966: *
0967: * @param request The current servlet request object.
0968: * @param next The current Spring ModelAndView object.
0969: * @param pageInfo The current WikiPageInfo object, which contains
0970: * information needed for rendering the final JSP page.
0971: * @param pageTitle The title of the page being rendered.
0972: * @param topic The Topic object for the topic being displayed.
0973: * @param sectionEdit Set to <code>true</code> if edit links should be displayed
0974: * for each section of the topic.
0975: * @throws Exception Thrown if any error occurs during processing.
0976: */
0977: protected static void viewTopic(HttpServletRequest request,
0978: ModelAndView next, WikiPageInfo pageInfo,
0979: WikiMessage pageTitle, Topic topic, boolean sectionEdit)
0980: throws Exception {
0981: // FIXME - what should the default be for topics that don't exist?
0982: if (topic == null) {
0983: throw new WikiException(new WikiMessage(
0984: "common.exception.notopic"));
0985: }
0986: WikiUtil.validateTopicName(topic.getName());
0987: if (topic.getTopicType() == Topic.TYPE_REDIRECT
0988: && (request.getParameter("redirect") == null || !request
0989: .getParameter("redirect")
0990: .equalsIgnoreCase("no"))) {
0991: Topic child = WikiUtil.findRedirectedTopic(topic, 0);
0992: if (!child.getName().equals(topic.getName())) {
0993: pageInfo.setRedirectName(topic.getName());
0994: pageTitle = new WikiMessage("topic.title", child
0995: .getName());
0996: topic = child;
0997: }
0998: }
0999: String virtualWiki = topic.getVirtualWiki();
1000: String topicName = topic.getName();
1001: WikiUserAuth user = ServletUtil.currentUser();
1002: if (sectionEdit
1003: && !ServletUtil
1004: .isEditable(virtualWiki, topicName, user)) {
1005: sectionEdit = false;
1006: }
1007: ParserInput parserInput = new ParserInput();
1008: parserInput.setContext(request.getContextPath());
1009: parserInput.setLocale(request.getLocale());
1010: parserInput.setWikiUser(user);
1011: parserInput.setTopicName(topicName);
1012: parserInput.setUserIpAddress(ServletUtil.getIpAddress(request));
1013: parserInput.setVirtualWiki(virtualWiki);
1014: parserInput.setAllowSectionEdit(sectionEdit);
1015: ParserOutput parserOutput = new ParserOutput();
1016: String content = ParserUtil.parse(parserInput, parserOutput,
1017: topic.getTopicContent());
1018: if (parserOutput.getCategories().size() > 0) {
1019: LinkedHashMap categories = new LinkedHashMap();
1020: for (Iterator iterator = parserOutput.getCategories()
1021: .keySet().iterator(); iterator.hasNext();) {
1022: String key = (String) iterator.next();
1023: String value = key
1024: .substring(NamespaceHandler.NAMESPACE_CATEGORY
1025: .length()
1026: + NamespaceHandler.NAMESPACE_SEPARATOR
1027: .length());
1028: categories.put(key, value);
1029: }
1030: next.addObject("categories", categories);
1031: }
1032: topic.setTopicContent(content);
1033: if (topic.getTopicType() == Topic.TYPE_CATEGORY) {
1034: loadCategoryContent(next, virtualWiki, topic.getName());
1035: }
1036: if (topic.getTopicType() == Topic.TYPE_IMAGE
1037: || topic.getTopicType() == Topic.TYPE_FILE) {
1038: Collection fileVersions = WikiBase.getDataHandler()
1039: .getAllWikiFileVersions(virtualWiki, topicName,
1040: true);
1041: for (Iterator iterator = fileVersions.iterator(); iterator
1042: .hasNext();) {
1043: // update version urls to include web root path
1044: WikiFileVersion fileVersion = (WikiFileVersion) iterator
1045: .next();
1046: String url = FilenameUtils
1047: .normalize(Environment
1048: .getValue(Environment.PROP_FILE_DIR_RELATIVE_PATH)
1049: + "/" + fileVersion.getUrl());
1050: url = FilenameUtils.separatorsToUnix(url);
1051: fileVersion.setUrl(url);
1052: }
1053: next.addObject("fileVersions", fileVersions);
1054: if (topic.getTopicType() == Topic.TYPE_IMAGE) {
1055: next.addObject("topicImage", new Boolean(true));
1056: } else {
1057: next.addObject("topicFile", new Boolean(true));
1058: }
1059: }
1060: pageInfo.setSpecial(false);
1061: pageInfo.setTopicName(topicName);
1062: next.addObject(ServletUtil.PARAMETER_TOPIC_OBJECT, topic);
1063: if (pageTitle != null) {
1064: pageInfo.setPageTitle(pageTitle);
1065: }
1066: }
1067: }
|