Package org.jamwiki.utils

Source Code of org.jamwiki.utils.WikiUtil

/**
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, version 2.1, dated February 1999.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the latest version of the GNU Lesser General
* Public License as published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program (LICENSE.txt); if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package org.jamwiki.utils;


import info.bliki.gae.db.GAEDataHandler;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.jamwiki.DataAccessException;
import org.jamwiki.DataHandler;
import org.jamwiki.Environment;
import org.jamwiki.WikiBase;
import org.jamwiki.WikiException;
import org.jamwiki.WikiMessage;
import org.jamwiki.WikiVersion;
import org.jamwiki.model.Role;
import org.jamwiki.model.Topic;
import org.jamwiki.model.VirtualWiki;

/**
* This class provides a variety of general utility methods for handling
* wiki-specific functionality such as retrieving topics from the URL.
*/
public class WikiUtil {

  private static final WikiLogger logger = WikiLogger.getLogger(WikiUtil.class
      .getName());

  /** webapp context path, initialized from JAMWikiFilter. */
  public static String WEBAPP_CONTEXT_PATH = null;
  private static Pattern INVALID_ROLE_NAME_PATTERN = null;
  private static Pattern INVALID_TOPIC_NAME_PATTERN = null;
  private static Pattern VALID_USER_LOGIN_PATTERN = null;
  public static final String PARAMETER_TOPIC = "topic";
  public static final String PARAMETER_VIRTUAL_WIKI = "virtualWiki";
  public static final String PARAMETER_WATCHLIST = "watchlist";

  static {
    try {
      INVALID_ROLE_NAME_PATTERN = Pattern.compile(Environment
          .getValue(Environment.PROP_PATTERN_INVALID_ROLE_NAME));
      INVALID_TOPIC_NAME_PATTERN = Pattern.compile(Environment
          .getValue(Environment.PROP_PATTERN_INVALID_TOPIC_NAME));
      VALID_USER_LOGIN_PATTERN = Pattern.compile(Environment
          .getValue(Environment.PROP_PATTERN_VALID_USER_LOGIN));
    } catch (PatternSyntaxException e) {
      logger.severe("Unable to compile pattern", e);
    }
  }

  /**
   * Create a pagination object based on parameters found in the current
   * request.
   *
   * @param request
   *          The servlet request object.
   * @return A Pagination object constructed from parameters found in the
   *         request object.
   */
  public static Pagination buildPagination(HttpServletRequest request) {
    int num = Environment.getIntValue(Environment.PROP_RECENT_CHANGES_NUM);
    if (request.getParameter("num") != null) {
      try {
        num = Integer.parseInt(request.getParameter("num"));
      } catch (NumberFormatException e) {
        // invalid number
      }
    }
    int offset = 0;
    if (request.getParameter("offset") != null) {
      try {
        offset = Integer.parseInt(request.getParameter("offset"));
      } catch (NumberFormatException e) {
        // invalid number
      }
    }
    return new Pagination(num, offset);
  }

  /**
   * Utility method to retrieve an instance of the current data handler.
   *
   * @return An instance of the current data handler.
   * @throws IOException
   *           Thrown if a data handler instance can not be instantiated.
   */
  public static DataHandler dataHandlerInstance() throws IOException {
    return new GAEDataHandler();
  }
 
  /**
   * Convert a topic name or other value into a value suitable for use as a
   * file name.  This method replaces spaces with underscores, and then URL
   * encodes the value.
   *
   * @param name The value that is to be encoded for use as a file name.
   * @return The encoded value.
   */
  public static String encodeForFilename(String name) {
    if (StringUtils.isBlank(name)) {
      throw new IllegalArgumentException("File name not specified in encodeForFilename");
    }
    // replace spaces with underscores
    String result = Utilities.encodeTopicName(name);
    // URL encode the rest of the name
    try {
      result = URLEncoder.encode(result, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      // this should never happen
      throw new IllegalStateException("Unsupporting encoding UTF-8");
    }
    return result;
  }
 
  /**
   * Given an article name, return the appropriate comments topic article name.
   * For example, if the article name is "Topic" then the return value is
   * "Comments:Topic".
   *
   * @param name The article name from which a comments article name is to
   *  be constructed.
   * @return The comments article name for the article name.
   */
  public static String extractCommentsLink(String name) {
    if (StringUtils.isBlank(name)) {
      throw new IllegalArgumentException("Topic name must not be empty in extractCommentsLink");
    }
    WikiLink wikiLink = LinkUtil.parseWikiLink(name);
    if (StringUtils.isBlank(wikiLink.getNamespace())) {
      return NamespaceHandler.NAMESPACE_COMMENTS + NamespaceHandler.NAMESPACE_SEPARATOR + name;
    }
    String namespace = wikiLink.getNamespace();
    String commentsNamespace = NamespaceHandler.getCommentsNamespace(namespace);
    return (!StringUtils.isBlank(commentsNamespace)) ? commentsNamespace + NamespaceHandler.NAMESPACE_SEPARATOR + wikiLink.getArticle() : NamespaceHandler.NAMESPACE_COMMENTS + NamespaceHandler.NAMESPACE_SEPARATOR + wikiLink.getArticle();
  }

  /**
   * Given an article name, extract an appropriate topic article name.  For
   * example, if the article name is "Comments:Topic" then the return value
   * is "Topic".
   *
   * @param name The article name from which a topic article name is to be
   *  constructed.
   * @return The topic article name for the article name.
   */
  public static String extractTopicLink(String name) {
    if (StringUtils.isBlank(name)) {
      throw new IllegalArgumentException("Topic name must not be empty in extractTopicLink");
    }
    WikiLink wikiLink = LinkUtil.parseWikiLink(name);
    if (StringUtils.isBlank(wikiLink.getNamespace())) {
      return name;
    }
    String namespace = wikiLink.getNamespace();
    String mainNamespace = NamespaceHandler.getMainNamespace(namespace);
    return (!StringUtils.isBlank(mainNamespace)) ? mainNamespace + NamespaceHandler.NAMESPACE_SEPARATOR + wikiLink.getArticle() : wikiLink.getArticle();
  }
 
  /**
   * Determine the URL for the default virtual wiki topic, not including the application server context.
   */
  public static String findDefaultVirtualWikiUrl(String virtualWikiName) {
    if (StringUtils.isBlank(virtualWikiName)) {
      virtualWikiName = WikiBase.DEFAULT_VWIKI;
    }
    String target = Environment.getValue(Environment.PROP_BASE_DEFAULT_TOPIC);
    try {
      VirtualWiki virtualWiki = WikiBase.getDataHandler().lookupVirtualWiki(virtualWikiName);
      target = virtualWiki.getDefaultTopicName();
    } catch (DataAccessException e) {
      logger.warning("Unable to retrieve default topic for virtual wiki", e);
    }
    return "/" + virtualWikiName + "/" + target;
  }
 
  /**
   * Given a topic type, determine the namespace name.
   *
   * @param topicType The topic type.
   * @return The namespace that matches the topic type.
   */
  public static String findNamespaceForTopicType(int topicType) {
    switch (topicType) {
      case Topic.TYPE_IMAGE:
      case Topic.TYPE_FILE:
        return NamespaceHandler.NAMESPACE_IMAGE;
      case Topic.TYPE_CATEGORY:
        return NamespaceHandler.NAMESPACE_CATEGORY;
      case Topic.TYPE_SYSTEM_FILE:
        return NamespaceHandler.NAMESPACE_JAMWIKI;
      case Topic.TYPE_TEMPLATE:
        return NamespaceHandler.NAMESPACE_TEMPLATE;
      default:
        return "";
    }
  }
 
  /**
   * Given a namespace name, determine the topic type.
   *
   * @param namespace The namespace name.
   * @return The topic type that matches the namespace.
   */
  public static int findTopicTypeForNamespace(String namespace) {
    if (namespace != null) {
      if (namespace.equals(NamespaceHandler.NAMESPACE_CATEGORY)) {
        return Topic.TYPE_CATEGORY;
      }
      if (namespace.equals(NamespaceHandler.NAMESPACE_TEMPLATE)) {
        return Topic.TYPE_TEMPLATE;
      }
      if (namespace.equals(NamespaceHandler.NAMESPACE_JAMWIKI)) {
        return Topic.TYPE_SYSTEM_FILE;
      }
      if (namespace.equals(NamespaceHandler.NAMESPACE_IMAGE)) {
        // FIXME - handle TYPE_FILE
        return Topic.TYPE_IMAGE;
      }
    }
    return Topic.TYPE_ARTICLE;
  }
 
  /**
   * Return the URL of the index page for the wiki.
   *
   * @throws DataAccessException
   *           Thrown if any error occurs while retrieving data.
   */
  public static String getBaseUrl() throws DataAccessException {
    String url = Environment.getValue(Environment.PROP_SERVER_URL);
    url += LinkUtil.buildTopicUrl(WEBAPP_CONTEXT_PATH, WikiBase.DEFAULT_VWIKI,
        Environment.getValue(Environment.PROP_BASE_DEFAULT_TOPIC), true);
    return url;
  }

  /**
   * Retrieve a parameter from the servlet request. This method works around
   * some issues encountered when retrieving non-ASCII values from URL
   * parameters.
   *
   * @param request
   *          The servlet request object.
   * @param name
   *          The parameter name to be retrieved.
   * @param decodeUnderlines
   *          Set to <code>true</code> if underlines should be automatically
   *          converted to spaces.
   * @return The decoded parameter value retrieved from the request.
   */
  public static String getParameterFromRequest(HttpServletRequest request,
      String name, boolean decodeUnderlines) {
    String value = null;
    if (request.getMethod().equalsIgnoreCase("GET")) {
      // parameters passed via the URL are URL encoded, so request.getParameter
      // may
      // not interpret non-ASCII characters properly. This code attempts to work
      // around that issue by manually decoding. yes, this is ugly and it would
      // be
      // great if someone could eventually make it unnecessary.
      String query = request.getQueryString();
      if (StringUtils.isBlank(query)) {
        return null;
      }
      String prefix = name + "=";
      int pos = query.indexOf(prefix);
      if (pos != -1 && (pos + prefix.length()) < query.length()) {
        value = query.substring(pos + prefix.length());
        if (value.indexOf('&') != -1) {
          value = value.substring(0, value.indexOf('&'));
        }
      }
      return Utilities.decodeAndEscapeTopicName(value, decodeUnderlines);
    }
    value = request.getParameter(name);
    if (value == null) {
      value = (String) request.getAttribute(name);
    }
    if (value == null) {
      return null;
    }
    return Utilities.decodeTopicName(value, decodeUnderlines);
  }

  /**
   * Retrieve a topic name from the servlet request. This method will retrieve a
   * request parameter matching the PARAMETER_TOPIC value, and will decode it
   * appropriately.
   *
   * @param request
   *          The servlet request object.
   * @return The decoded topic name retrieved from the request.
   */
  public static String getTopicFromRequest(HttpServletRequest request) {
    return WikiUtil.getParameterFromRequest(request, WikiUtil.PARAMETER_TOPIC,
        true);
  }

  /**
   * Retrieve a topic name from the request URI. This method will retrieve the
   * portion of the URI that follows the virtual wiki and decode it
   * appropriately.
   *
   * @param request
   *          The servlet request object.
   * @return The decoded topic name retrieved from the URI.
   */
  public static String getTopicFromURI(HttpServletRequest request) {
    // skip one directory, which is the virutal wiki
    String topic = retrieveDirectoriesFromURI(request, 1);
    if (topic == null) {
      logger.warning("No topic in URL: " + request.getRequestURI());
      return null;
    }
    int pos = topic.indexOf('#');
    if (pos != -1) {
      // strip everything after and including '#'
      if (pos == 0) {
        logger.warning("No topic in URL: " + request.getRequestURI());
        return null;
      }
      topic = topic.substring(0, pos);
    }
    pos = topic.indexOf('?');
    if (pos != -1) {
      // strip everything after and including '?'
      if (pos == 0) {
        logger.warning("No topic in URL: " + request.getRequestURI());
        return null;
      }
      topic = topic.substring(0, pos);
    }
    pos = topic.indexOf(';');
    if (pos != -1) {
      // some servlet containers return parameters of the form
      // ";jsessionid=1234" when getRequestURI is called.
      if (pos == 0) {
        logger.warning("No topic in URL: " + request.getRequestURI());
        return null;
      }
      topic = topic.substring(0, pos);
    }
    if (!StringUtils.isBlank(topic)) {
      topic = Utilities.decodeAndEscapeTopicName(topic, true);
    }
    return topic;
  }

  /**
   * Retrieve a virtual wiki name from the servlet request. This method will
   * retrieve a request parameter matching the PARAMETER_VIRTUAL_WIKI value, and
   * will decode it appropriately.
   *
   * @param request
   *          The servlet request object.
   * @return The decoded virtual wiki name retrieved from the request.
   */
  public static String getVirtualWikiFromRequest(HttpServletRequest request) {
    String virtualWiki = request.getParameter(WikiUtil.PARAMETER_VIRTUAL_WIKI);
    if (virtualWiki == null) {
      virtualWiki = (String) request
          .getAttribute(WikiUtil.PARAMETER_VIRTUAL_WIKI);
    }
    if (virtualWiki==null || virtualWiki.length()==0) {
      return WikiBase.DEFAULT_VWIKI;
    }
//    if (virtualWiki == null) {
//      return null;
//    }
    return Utilities.decodeTopicName(virtualWiki, true);
  }

  /**
   * Retrieve a virtual wiki name from the request URI. This method will
   * retrieve the portion of the URI that immediately follows the servlet
   * context and decode it appropriately.
   *
   * @param request
   *          The servlet request object.
   * @return The decoded virtual wiki name retrieved from the URI.
   */
  public static String getVirtualWikiFromURI(HttpServletRequest request) {
    String uri = retrieveDirectoriesFromURI(request, 0);
    if (StringUtils.isBlank(uri)) {
      logger.info("No virtual wiki found in URL: " + request.getRequestURI());
      return null;
    }
    // default the virtual wiki to the URI since the user may have accessed a
    // URL of
    // the form /context/virtualwiki with no trailing slash
    String virtualWiki = uri;
    int slashIndex = uri.indexOf('/');
    if (slashIndex != -1) {
      virtualWiki = uri.substring(0, slashIndex);
    }
    return Utilities.decodeAndEscapeTopicName(virtualWiki, true);
  }
 
  /**
   * Given a topic name, determine if that name corresponds to a comments
   * page.
   *
   * @param topicName The topic name (non-null) to examine to determine if it
   *  is a comments page or not.
   * @return <code>true</code> if the page is a comments page, <code>false</code>
   *  otherwise.
   */
  public static boolean isCommentsPage(String topicName) {
    WikiLink wikiLink = LinkUtil.parseWikiLink(topicName);
    if (StringUtils.isBlank(wikiLink.getNamespace())) {
      return false;
    }
    String namespace = wikiLink.getNamespace();
    if (namespace.equals(NamespaceHandler.NAMESPACE_SPECIAL)) {
      return false;
    }
    String commentNamespace = NamespaceHandler.getCommentsNamespace(namespace);
    return namespace.equals(commentNamespace);
  }
 
  /**
   * Determine if the system properties file exists and has been initialized.
   * This method is primarily used to determine whether or not to display
   * the system setup page or not.
   *
   * @return <code>true</code> if the properties file has NOT been initialized,
   *  <code>false</code> otherwise.
   */
  public static boolean isFirstUse() {
    return !Environment.getBooleanValue(Environment.PROP_BASE_INITIALIZED);
  }

  /**
   * Determine if the system code has been upgraded from the configured system
   * version.  Thus if the system is upgraded, this method returns <code>true</code>
   *
   * @return <code>true</code> if the system has been upgraded, <code>false</code>
   *  otherwise.
   */
  public static boolean isUpgrade() {
    if (WikiUtil.isFirstUse()) {
      return false;
    }
    WikiVersion oldVersion = new WikiVersion(Environment.getValue(Environment.PROP_BASE_WIKI_VERSION));
    WikiVersion currentVersion = new WikiVersion(WikiVersion.CURRENT_WIKI_VERSION);
    return false; //oldVersion.before(currentVersion);
  }
 
  /**
   * Utility method for reading special topic values from files and returning
   * the file contents.
   *
   * @param locale The locale for the user viewing the special page.
   * @param pageName The name of the special page being retrieved.
   */
  public static String readSpecialPage(Locale locale, String pageName) throws IOException {
    String contents = null;
    String filename = null;
    String language = null;
    String country = null;
    if (locale != null) {
      language = locale.getLanguage();
      country = locale.getCountry();
    }
    String subdirectory = "";
    if (!StringUtils.isBlank(language) && !StringUtils.isBlank(country)) {
      try {
        subdirectory = new File(WikiBase.SPECIAL_PAGE_DIR, language + "_" + country).getPath();
        filename = new File(subdirectory, WikiUtil.encodeForFilename(pageName) + ".txt").getPath();
        contents = Utilities.readFile(filename);
      } catch (IOException e) {
        logger.info("File " + filename + " does not exist");
      }
    }
    if (contents == null && !StringUtils.isBlank(language)) {
      try {
        subdirectory = new File(WikiBase.SPECIAL_PAGE_DIR, language).getPath();
        filename = new File(subdirectory, WikiUtil.encodeForFilename(pageName) + ".txt").getPath();
        contents = Utilities.readFile(filename);
      } catch (IOException e) {
        logger.info("File " + filename + " does not exist");
      }
    }
    if (contents == null) {
      try {
        subdirectory = new File(WikiBase.SPECIAL_PAGE_DIR).getPath();
        filename = new File(subdirectory, WikiUtil.encodeForFilename(pageName) + ".txt").getPath();
        contents = Utilities.readFile(filename);
      } catch (IOException e) {
        logger.warning("File " + filename + " could not be read", e);
        throw e;
      }
    }
    return contents;
  }
 
  /**
   * Utility method for retrieving values from the URI. This method will attempt
   * to properly convert the URI encoding, and then offers a way to return
   * directories after the initial context directory. For example, if the URI is
   * "/context/first/second/third" and this method is called with a skipCount of
   * 1, the return value is "second/third".
   *
   * @param request
   *          The servlet request object.
   * @param skipCount
   *          The number of directories to skip.
   * @return A UTF-8 encoded portion of the URL that skips the web application
   *         context and skipCount directories, or <code>null</code> if the
   *         number of directories is less than skipCount.
   */
  private static String retrieveDirectoriesFromURI(HttpServletRequest request,
      int skipCount) {
    String uri = request.getRequestURI().trim();
    // FIXME - needs testing on other platforms
    uri = Utilities.convertEncoding(uri, "ISO-8859-1", "UTF-8");
    String contextPath = request.getContextPath().trim();
    if (StringUtils.isBlank(uri) || contextPath == null) {
      return null;
    }
    // make sure there are no instances of "//" in the URL
    uri = uri.replaceAll("(/){2,}", "/");
    if (uri.length() <= contextPath.length()) {
      return null;
    }
    uri = uri.substring(contextPath.length() + 1);
    int i = 0;
    while (i < skipCount) {
      int slashIndex = uri.indexOf('/');
      if (slashIndex == -1) {
        return null;
      }
      uri = uri.substring(slashIndex + 1);
      i++;
    }
    return uri;
  }
 
  /**
   * Utility method for determining if a topic name is valid for use on the Wiki,
   * meaning that it is not empty and does not contain any invalid characters.
   *
   * @param name The topic name to validate.
   * @throws WikiException Thrown if the user name is invalid.
   */
  public static void validateTopicName(String name) throws WikiException {
    if (StringUtils.isBlank(name)) {
      throw new WikiException(new WikiMessage("common.exception.notopic"));
    }
    if (PseudoTopicHandler.isPseudoTopic(name)) {
      throw new WikiException(new WikiMessage("common.exception.pseudotopic", name));
    }
    WikiLink wikiLink = LinkUtil.parseWikiLink(name);
    String namespace = StringUtils.trimToNull(wikiLink.getNamespace());
    String article = StringUtils.trimToNull(wikiLink.getArticle());
    if (StringUtils.startsWith(namespace, "/") || StringUtils.startsWith(article, "/")) {
      throw new WikiException(new WikiMessage("common.exception.name", name));
    }
    if (namespace != null && namespace.toLowerCase().equals(NamespaceHandler.NAMESPACE_SPECIAL.toLowerCase())) {
      throw new WikiException(new WikiMessage("common.exception.name", name));
    }
    Matcher m = WikiUtil.INVALID_TOPIC_NAME_PATTERN.matcher(name);
    if (m.find()) {
      throw new WikiException(new WikiMessage("common.exception.name", name));
    }
  }
 
  /**
   * Utility method for determining if the parameters of a Role are valid
   * or not.
   *
   * @param role The Role to validate.
   * @throws WikiException Thrown if the role is invalid.
   */
  public static void validateRole(Role role) throws WikiException {
    Matcher m = WikiUtil.INVALID_ROLE_NAME_PATTERN.matcher(role.getAuthority());
    if (!m.matches()) {
      throw new WikiException(new WikiMessage("roles.error.name", role.getAuthority()));
    }
    if (!StringUtils.isBlank(role.getDescription()) && role.getDescription().length() > 200) {
      throw new WikiException(new WikiMessage("roles.error.description"));
    }
    // FIXME - throw a user-friendly error if the role name is already in use
  }

  /**
   * Utility method for determining if a password is valid for use on the wiki.
   *
   * @param password The password value.
   * @param confirmPassword Passwords must be entered twice to avoid tying errors.
   *  This field represents the confirmed password entry.
   */
  public static void validatePassword(String password, String confirmPassword) throws WikiException {
    if (StringUtils.isBlank(password)) {
      throw new WikiException(new WikiMessage("error.newpasswordempty"));
    }
    if (StringUtils.isBlank(confirmPassword)) {
      throw new WikiException(new WikiMessage("error.passwordconfirm"));
    }
    if (!password.equals(confirmPassword)) {
      throw new WikiException(new WikiMessage("admin.message.passwordsnomatch"));
    }
  }

  /**
   * Utility method for determining if a username is valid for use on the Wiki,
   * meaning that it is not empty and does not contain any invalid characters.
   *
   * @param name The username to validate.
   * @throws WikiException Thrown if the user name is invalid.
   */
  public static void validateUserName(String name) throws WikiException {
    if (StringUtils.isBlank(name)) {
      throw new WikiException(new WikiMessage("error.loginempty"));
    }
    Matcher m = WikiUtil.VALID_USER_LOGIN_PATTERN.matcher(name);
    if (!m.matches()) {
      throw new WikiException(new WikiMessage("common.exception.name", name));
    }
  }
}
TOP

Related Classes of org.jamwiki.utils.WikiUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.