Package de.zib.scalaris.examples.wikipedia.bliki

Source Code of de.zib.scalaris.examples.wikipedia.bliki.WikiServlet

/**
*  Copyright 2007-2011 Zuse Institute Berlin
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*/
package de.zib.scalaris.examples.wikipedia.bliki;

import info.bliki.api.Connector;
import info.bliki.api.User;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;

import de.zib.scalaris.examples.wikipedia.CircularByteArrayOutputStream;
import de.zib.scalaris.examples.wikipedia.NamespaceUtils;
import de.zib.scalaris.examples.wikipedia.Options;
import de.zib.scalaris.examples.wikipedia.PageHistoryResult;
import de.zib.scalaris.examples.wikipedia.RevisionResult;
import de.zib.scalaris.examples.wikipedia.SavePageResult;
import de.zib.scalaris.examples.wikipedia.ValueResult;
import de.zib.scalaris.examples.wikipedia.WikiServletContext;
import de.zib.scalaris.examples.wikipedia.bliki.WikiPageListBean.FormType;
import de.zib.scalaris.examples.wikipedia.data.Contributor;
import de.zib.scalaris.examples.wikipedia.data.Page;
import de.zib.scalaris.examples.wikipedia.data.Revision;
import de.zib.scalaris.examples.wikipedia.data.SiteInfo;
import de.zib.scalaris.examples.wikipedia.data.xml.WikiDump;
import de.zib.scalaris.examples.wikipedia.plugin.PluginClassLoader;
import de.zib.scalaris.examples.wikipedia.plugin.WikiEventHandler;
import de.zib.scalaris.examples.wikipedia.plugin.WikiPlugin;

/**
* Servlet for handling wiki page display and editing.
*
* @param <Connection> connection to a DB
*
* @author Nico Kruber, kruber@zib.de
*/
public abstract class WikiServlet<Connection> extends HttpServlet implements
        Servlet, WikiServletContext, WikiServletDataHandler<Connection> {
    protected static final String MAIN_PAGE = "Main Page";
    protected static final int IMPORT_REDIRECT_EVERY = 5; // seconds

    private static final long serialVersionUID = 1L;
   
    /**
     * Version of the "Wikipedia on Scalaris" example implementation.
     */
    public static final String version = "0.4.1";

    protected SiteInfo siteinfo = null;
    protected MyNamespace namespace = null;
   
    protected boolean initialized = false;

    /**
     * Name under which the servlet is available.
     */
    public static final String wikiBaseURL = "wiki";
    /**
     * URL for page links
     */
    public static final String linkBaseURL = wikiBaseURL + "?title=${title}";
    /**
     * URL for image links
     */
    public static final String imageBaseURL = wikiBaseURL + "?get_image=${image}";

    protected static final Pattern MATCH_WIKI_IMPORT_FILE = Pattern.compile(".*((\\.xml(\\.gz|\\.bz2)?)|\\.db)$");
    protected static final Pattern MATCH_WIKI_IMAGE_PX = Pattern.compile("^[0-9]*px-");
    protected static final Pattern MATCH_WIKI_IMAGE_SVG_PNG = Pattern.compile("\\.svg\\.png$");
    /*
     * http://simple.wiktionary.org/wiki/Main_Page
     * http://bar.wikipedia.org/wiki/Hauptseitn
     * https://secure.wikimedia.org/wikipedia/en/wiki/Main_Page
     */
    protected static final Pattern MATCH_WIKI_SITE_BASE = Pattern.compile("^(http[s]?://.+)(/wiki/.*)$");
   
    protected String currentImport = "";

    protected static CircularByteArrayOutputStream importLog = null;
    protected WikiDump importHandler = null;
   
    protected List<WikiEventHandler> eventHandlers = new LinkedList<WikiEventHandler>();

    /**
     * Creates the servlet.
     */
    public WikiServlet() {
        super();
    }

    /**
     * Servlet initialisation: creates the connection to the erlang node and
     * imports site information.
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        final String WIKI_USE_NEW_SCALARIS_OPS = config.getInitParameter("WIKI_USE_NEW_SCALARIS_OPS");
        if (WIKI_USE_NEW_SCALARIS_OPS != null) {
            Options.WIKI_USE_NEW_SCALARIS_OPS = Boolean.parseBoolean(WIKI_USE_NEW_SCALARIS_OPS);
        }
        final String WIKI_USE_BACKLINKS = config.getInitParameter("WIKI_USE_BACKLINKS");
        if (WIKI_USE_BACKLINKS != null) {
            Options.WIKI_USE_BACKLINKS = Boolean.parseBoolean(WIKI_USE_BACKLINKS);
        }
        final String WIKI_SAVEPAGE_RETRIES = config.getInitParameter("WIKI_SAVEPAGE_RETRIES");
        if (WIKI_SAVEPAGE_RETRIES != null) {
            Options.WIKI_SAVEPAGE_RETRIES = parseInt(WIKI_SAVEPAGE_RETRIES, Options.WIKI_SAVEPAGE_RETRIES);
        }
        final String WIKI_SAVEPAGE_RETRY_DELAY = config.getInitParameter("WIKI_SAVEPAGE_RETRY_DELAY");
        if (WIKI_SAVEPAGE_RETRY_DELAY != null) {
            Options.WIKI_SAVEPAGE_RETRY_DELAY = parseInt(WIKI_SAVEPAGE_RETRY_DELAY, Options.WIKI_SAVEPAGE_RETRY_DELAY);
        }
    }
   
    /**
     * Loads the siteinfo object.
     *
     * @return <tt>true</tt> on success,
     *         <tt>false</tt> if not found or no connection available
     */
    abstract protected boolean loadSiteInfo();
   
    /**
     * Load all plugins from the plugin directory
     * <tt>&lt;ServletContextDir&gt;/WEB-INF/plugins</tt>.
     *
     * @return <tt>true</tt> on success, <tt>false</tt> otherwise
     */
    @SuppressWarnings("unchecked")
    protected synchronized boolean loadPlugins() {
        final String pluginDir = getServletContext().getRealPath("/WEB-INF/plugins");
        try {
            PluginClassLoader pcl = new PluginClassLoader(pluginDir, new Class[] {WikiPlugin.class});
            List<Class<?>> plugins = pcl.getClasses(WikiPlugin.class);
            if (plugins != null) {
                for (Class<?> clazz: plugins) {
                    WikiPlugin plugin;
                    try {
                        plugin = ((Class<WikiPlugin>) clazz).newInstance();
                        plugin.init(this);
                    } catch (Exception e) {
                        System.err.println("failed to load plugin " + clazz.getCanonicalName());
                        e.printStackTrace();
                        continue;
                    }
                }
            }
        } catch (IOException e) {
            System.err.println("failed to load plugins");
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * Sets up the connection to the DB server.
     *
     * @param request
     *            the request to the servlet
     */
    abstract protected Connection getConnection(HttpServletRequest request);

    /**
     * Releases the connection to the DB server, e.g. closes it.
     *
     * @param request
     *            the request to the servlet
     * @param conn
     *            the connection to release
     */
    abstract protected void releaseConnection(HttpServletRequest request, Connection conn);

    @Override
    public void destroy() {
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest request,
     *      HttpServletResponse response)
     */
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        String image = request.getParameter("get_image");
        if (image != null) {
            showImage(request, response, image);
            return;
        }

        Connection connection = getConnection(request);
        if (connection == null) {
            showEmptyPage(request, response, new WikiPageBean()); // should forward to another page
            return; // return just in case
        }
        try {
            if (!initialized && !loadSiteInfo() || !currentImport.isEmpty()) {
                showImportPage(request, response, connection); // should forward to another page
                return; // return just in case
            }
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");

            // show empty page for testing purposes if a parameter called "test" exists:
            if (request.getParameter("test") != null) {
                showEmptyPage(request, response, new WikiPageBean());
                return;
            }

            // get parameters:
            String req_title = request.getParameter("title");
            if (req_title == null) {
                req_title = MAIN_PAGE;
            }
           
            // if the "search" parameter exists, show the search
            String req_search = request.getParameter("search");
            if (req_search != null) {
                handleSearch(request, response, req_title, req_search, connection, new WikiPageListBean());
                return;
            }

            String req_action = request.getParameter("action");

            if (req_title.equals("Special:Random")) {
                handleViewRandomPage(request, response, req_title, connection, new WikiPageBean());
            } else if (req_title.startsWith("Special:AllPages") || req_title.startsWith("Special:Allpages")) {
                handleSpecialAllPages(request, response, req_title, connection, new WikiPageListBean());
            } else if (req_title.startsWith("Special:PrefixIndex")) {
                handleSpecialPrefix(request, response, req_title, connection, new WikiPageListBean());
            } else if (req_title.startsWith("Special:Search")) {
                handleSearch(request, response, req_title, req_search, connection, new WikiPageListBean());
            } else if (req_title.startsWith("Special:WhatLinksHere")) {
                handleSpecialWhatLinksHere(request, response, req_title, connection, new WikiPageListBean());
            } else if (req_title.equals("Special:SpecialPages")) {
                handleViewSpecialPages(request, response, connection, new WikiPageListBean());
            } else if (req_title.equals("Special:Statistics")) {
                handleViewSpecialStatistics(request, response, connection, new WikiPageListBean());
            } else if (req_title.equals("Special:Version")) {
                handleViewSpecialVersion(request, response, connection, new WikiPageListBean());
            } else if (req_action == null || req_action.equals("view")) {
                handleViewPage(request, response, req_title, connection, new WikiPageBean());
            } else if (req_action.equals("history")) {
                handleViewPageHistory(request, response, req_title, connection, new WikiPageBean());
            } else if (req_action.equals("edit")) {
                handleEditPage(request, response, req_title, connection, new WikiPageEditBean());
            } else {
                // default: show page
                handleViewPage(request, response, req_title, connection, new WikiPageBean());
            }

            // if the request has not been forwarded, print a general error
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            out.write("An unknown error occured, please contact your administrator. A server restart may be required.");
            out.close();
        } finally {
            releaseConnection(request, connection);
        }
    }

    /**
     * Shows the page search for the given search string.
     *
     * @param request
     *            the HTTP request
     * @param response
     *            the response object
     * @param title
     *            the requested title, e.g. "Special:Search"
     * @param req_search
     *            search string
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws ServletException
     * @throws IOException
     */
    private void handleSearch(HttpServletRequest request,
            HttpServletResponse response, String title, String req_search,
            Connection connection, WikiPageListBean page)
            throws ServletException, IOException {
        // note: req_search can only be null if Special:Search is shown
        if (req_search == null) {
            int slashIndex = title.indexOf('/');
            if (slashIndex != (-1)) {
                req_search = title.substring(slashIndex + 1);
            }
        }
        // use default namespace (id 0) for invalid values
        int nsId = parseInt(request.getParameter("namespace"), 0);
        page.setPageHeading("Search");
        page.setFormTitle("Search results");
        page.setFormType(FormType.PageSearchForm);
        ValueResult<List<String>> result;
        if (req_search == null) {
            page.setTitle("Special:Search&namespace=" + nsId);
            page.setSearch("");
            result = new ValueResult<List<String>>(new ArrayList<String>(0));
        } else {
            page.setTitle("Special:Search&search=" + req_search + "&namespace=" + nsId);
            page.setSearch(req_search);
            if (nsId == 0) {
                result = getArticleList(connection);
            } else {
                result = getPageList(connection);
            }
        }
        page.setNamespaceId(nsId);
        page.addStats(result.stats);
        handleViewSpecialPageList(request, response, result, connection, page);
    }

    /**
     * @param request
     *            the HTTP request
     * @param response
     *            the response object
     * @param title
     *            the requested title, e.g. "Special:Search"
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws ServletException
     * @throws IOException
     */
    private void handleSpecialAllPages(HttpServletRequest request,
            HttpServletResponse response, String title,
            Connection connection, WikiPageListBean page) throws ServletException, IOException {
        String req_from = request.getParameter("from");
        if (req_from == null) {
            int slashIndex = title.indexOf('/');
            if (slashIndex != (-1)) {
                req_from = title.substring(slashIndex + 1);
            }
        }
        String req_to = request.getParameter("to");
        // use default namespace (id 0) for invalid values
        int nsId = parseInt(request.getParameter("namespace"), 0);
        page.setPageHeading("All pages");
        page.setFormTitle("All pages");
        page.setFormType(FormType.FromToForm);
        ValueResult<List<String>> result;
        if (req_from == null && req_to == null) {
            page.setTitle("Special:AllPages&namespace=" + nsId);
            page.setFromPage("");
            page.setToPage("");
            result = new ValueResult<List<String>>(new ArrayList<String>(0));
        } else {
            if (req_from == null) {
                req_from = ""; // start with first page
            }
            if (req_to == null) {
                req_to = ""; // stop at last page
            }
            page.setTitle("Special:AllPages&from=" + req_from + "&to=" + req_to + "&namespace=" + nsId);
            page.setFromPage(req_from);
            page.setToPage(req_to);
            if (nsId == 0) {
                result = getArticleList(connection);
            } else {
                result = getPageList(connection);
            }
        }
        page.addStats(result.stats);
        page.setNamespaceId(nsId);
        handleViewSpecialPageList(request, response, result, connection, page);
    }

    /**
     * @param request
     *            the HTTP request
     * @param response
     *            the response object
     * @param title
     *            the requested title, e.g. "Special:Search"
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws ServletException
     * @throws IOException
     */
    private void handleSpecialPrefix(HttpServletRequest request,
            HttpServletResponse response, String title,
            Connection connection, WikiPageListBean page) throws ServletException, IOException {
        String req_prefix = request.getParameter("prefix");
        if (req_prefix == null) {
            int slashIndex = title.indexOf('/');
            if (slashIndex != (-1)) {
                req_prefix = title.substring(slashIndex + 1);
            }
        }
        // use default namespace (id 0) for invalid values
        int nsId = parseInt(request.getParameter("namespace"), 0);
        page.setPageHeading("All pages");
        page.setFormTitle("All pages");
        page.setFormType(FormType.PagePrefixForm);
        ValueResult<List<String>> result;
        if (req_prefix == null) {
            page.setTitle("Special:PrefixIndex&namespace=" + nsId);
            page.setPrefix("");
            result = new ValueResult<List<String>>(new ArrayList<String>(0));
        } else {
            page.setTitle("Special:PrefixIndex&prefix=" + req_prefix + "&namespace=" + nsId);
            page.setPrefix(req_prefix);
            if (nsId == 0) {
                result = getArticleList(connection);
            } else {
                result = getPageList(connection);
            }
        }
        page.addStats(result.stats);
        page.setNamespaceId(nsId);
        handleViewSpecialPageList(request, response, result, connection, page);
    }

    /**
     * @param request
     *            the HTTP request
     * @param response
     *            the response object
     * @param title
     *            the requested title, e.g. "Special:Search"
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws ServletException
     * @throws IOException
     */
    private void handleSpecialWhatLinksHere(HttpServletRequest request,
            HttpServletResponse response, String title, Connection connection,
            WikiPageListBean page) throws ServletException, IOException {
        String req_target = request.getParameter("target");
        if (req_target == null) {
            // maybe we got the name separated with a '/' in the title:
            int slashIndex = title.indexOf('/');
            if (slashIndex != (-1)) {
                req_target = title.substring(slashIndex + 1);
            }
        }
        page.setFormTitle("What links here");
        page.setFormType(FormType.TargetPageForm);
        ValueResult<List<String>> result;
        if (req_target == null) {
            page.setPageHeading("Pages that link to a selected page");
            page.setTitle("Special:WhatLinksHere");
            page.setTarget("");
            result = new ValueResult<List<String>>(new ArrayList<String>(0));
        } else {
            page.setPageHeading("Pages that link to \"" + req_target + "\"");
            page.setTitle("Special:WhatLinksHere&target=" + req_target);
            page.setTarget(req_target);
            result = getPagesLinkingTo(connection, req_target, namespace);
        }
        page.addStats(result.stats);
        handleViewSpecialPageList(request, response, result, connection, page);
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest request,
     *      HttpServletResponse response)
     */
    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        Connection connection = getConnection(request);
        if (connection == null) {
            showEmptyPage(request, response, new WikiPageBean()); // should forward to another page
            return; // return just in case
        }
        try {
            if (!initialized && !loadSiteInfo() || !currentImport.isEmpty()) {
                showImportPage(request, response, connection); // should forward to another page
                return; // return just in case
            }
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");

            handleEditPageSubmitted(request, response,
                    request.getParameter("title"), connection,
                    new WikiPageEditBean());

            // if the request has not been forwarded, print a general error
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            out.write("An unknown error occured, please contact your administrator. A server restart may be required.");
            out.close();
        } finally {
            releaseConnection(request, connection);
        }
    }

    /**
     * Gets a random page and forwards the user to the site with this page.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param title
     *            the requested title (mostly "Special:Random")
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws IOException
     *             if the forward fails
     * @throws ServletException
     */
    private void handleViewRandomPage(HttpServletRequest request,
            HttpServletResponse response, String title, Connection connection,
            WikiPageBean page) throws IOException, ServletException {
        ValueResult<String> result = getRandomArticle(connection, new Random());
        page.addStats(result.stats);
        if (result.success) {
            ArrayList<Long> times = new ArrayList<Long>();
            for (List<Long> time : result.stats.values()) {
                times.addAll(time);
            }
            StringBuilder redirectUrl = new StringBuilder(256);
            redirectUrl.append("?title=");
            redirectUrl.append(URLEncoder.encode(MyWikiModel.denormalisePageTitle(result.value, namespace), "UTF-8"));
            redirectUrl.append("&random_times=" + StringUtils.join(times, "%2C"));
            response.sendRedirect(response.encodeRedirectURL(redirectUrl.toString()));
        } else if (result.connect_failed) {
            setParam_error(request, "ERROR: DB connection failed");
            showEmptyPage(request, response, page);
        } else {
            response.sendRedirect(response.encodeRedirectURL("?title=Main Page&notice=error: can not view random page: <pre>" + result.message + "</pre>"));
        }
    }

    /**
     * Shows the contents of the page page with the given <tt>title</tt>.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param title
     *            the title of the page to show
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws IOException
     * @throws ServletException
     */
    private void handleViewPage(HttpServletRequest request,
            HttpServletResponse response, String title, Connection connection,
            WikiPageBean page) throws ServletException, IOException {
        // get renderer
        int render = getParam_renderer(request);
        // get revision id to load:
        int req_oldid = getParam_oldid(request);

        RevisionResult result = getRevision(connection, title, req_oldid, namespace);
        page.addStats(result.stats);
       
        if (result.connect_failed) {
            setParam_error(request, "ERROR: DB connection failed");
            showEmptyPage(request, response, page);
            return;
        } else if (result.page_not_existing) {
            handleViewPageNotExisting(request, response, title, connection, page);
            return;
        } else if (result.rev_not_existing) {
            result = getRevision(connection, title, namespace);
            page.addStats(result.stats);
            addToParam_notice(request, "revision " + req_oldid + " not found - loaded current revision instead");
        }
       
        if (result.success) {
            renderRevision(result.page.getTitle(), result.revision, render, request, connection, page);
           
            if (!result.page.checkEditAllowed("")) {
                page.setEditRestricted(true);
            }

            // forward the request and the bean to the jsp:
            request.setAttribute("pageBean", page);
            RequestDispatcher dispatcher = request.getRequestDispatcher("page.jsp");
            dispatcher.forward(request, response);
        } else {
            setParam_error(request, "ERROR: revision unavailable");
            addToParam_notice(request, "error: unknown error getting page " + title + ":" + req_oldid + ": <pre>" + result.message + "</pre>");
            showEmptyPage(request, response, page);
        }
    }
   
    /**
     * Extracts the full base URL from a requested URL.
     *
     * @param requestUrl
     *            the URL from the request
     *
     * @return the first part of the URL preceding '/' + {@link #wikiBaseURL}
     */
    private static String extractFullUrl(String requestUrl) {
        int colonIndex = requestUrl.indexOf('/' + wikiBaseURL);
        if (colonIndex != (-1)) {
            return requestUrl.substring(0, colonIndex + 1) + linkBaseURL;
        }
        return null;
       
    }
   
    /**
     * Creates a {@link WikiPageBean} object with the rendered content of a
     * given revision.
     *
     * @param title
     *            the title of the article to render
     * @param revision
     *            the revision to render
     * @param renderer
     *            the renderer to use (0=plain text, 1=Bliki)
     * @param request
     *            the request object
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page (the rendered content will be added to
     *            this object)
     */
    private void renderRevision(String title, Revision revision,
            int renderer, HttpServletRequest request, Connection connection,
            WikiPageBean page) {
        // set the page's contents according to the renderer used
        // (categories are included in the content string, so they only
        // need special handling the wiki renderer is used)
        MyWikiModel wikiModel = getWikiModel(connection);
        String fullUrl = extractFullUrl(request.getRequestURL().toString());
        if (fullUrl != null) {
            wikiModel.setLinkBaseFullURL(fullUrl);
        }
        wikiModel.setPageName(title);
        if (renderer > 0) {
            String mainText = wikiModel.render(revision.unpackedText());
            if (wikiModel.isCategoryNamespace(wikiModel.getNamespace(title))) {
                ValueResult<List<String>> catPagesResult = getPagesInCategory(connection, title, namespace);
                page.addStats(catPagesResult.stats);
                if (catPagesResult.success) {
                    TreeSet<String> subCategories = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
                    TreeSet<String> categoryPages = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
                    final List<String> catPageList = new ArrayList<String>(catPagesResult.value.size());
                    wikiModel.denormalisePageTitles(catPagesResult.value, catPageList);
                    for (String pageInCat: catPageList) {
                        String pageNamespace = wikiModel.getNamespace(pageInCat);
                        if (wikiModel.isCategoryNamespace(pageNamespace)) {
                            subCategories.add(wikiModel.getTitleName(pageInCat));
                        } else if (wikiModel.isTemplateNamespace(pageNamespace)) {
                            // all pages using a template are in the category, too
                            ValueResult<List<String>> tplResult = getPagesInTemplate(connection, pageInCat, namespace);
                            page.addStats(tplResult.stats);
                            if (tplResult.success) {
                                final List<String> tplPageList = new ArrayList<String>(tplResult.value.size());
                                wikiModel.denormalisePageTitles(tplResult.value, tplPageList);
                                categoryPages.addAll(tplPageList);
                            } else {
                                if (tplResult.connect_failed) {
                                    setParam_error(request, "ERROR: DB connection failed");
                                } else {
                                    setParam_error(request, "ERROR: template page list unavailable");
                                }
                                addToParam_notice(request, "error getting pages using template: " + tplResult.message);
                            }
                        } else {
                            categoryPages.add(pageInCat);
                        }
                    }
                    page.setSubCategories(subCategories);
                    page.setCategoryPages(categoryPages);
                } else {
                    if (catPagesResult.connect_failed) {
                        setParam_error(request, "ERROR: DB connection failed");
                    } else {
                        setParam_error(request, "ERROR: category page list unavailable");
                    }
                    addToParam_notice(request, "error getting category pages: " + catPagesResult.message);
                }
            }
            String redirectedPageName = wikiModel.getRedirectLink();
            if (redirectedPageName != null) {
                page.setRedirectedTo(redirectedPageName);
                // add the content from the page directed to:
                wikiModel.tearDown();
                wikiModel.setUp();
                mainText = wikiModel.render(wikiModel.getRedirectContent(redirectedPageName));
            }
            page.setPage(mainText);
            page.setCategories(wikiModel.getCategories().keySet());
            page.addStats(wikiModel.getStats());
        } else if (renderer == 0) {
            // for debugging, show all parameters:
            StringBuilder sb = new StringBuilder();
            for (Enumeration<?> req_pars = request.getParameterNames(); req_pars.hasMoreElements();) {
                String element = (String) req_pars.nextElement();
                sb.append(element + " = ");
                sb.append(request.getParameter(element) + "\n");
            }
            sb.append("\n\n");
            for (Enumeration<?> headers = request.getHeaderNames(); headers.hasMoreElements();) {
                String element = (String) headers.nextElement();
                sb.append(element + " = ");
                sb.append(request.getHeader(element) + "\n");
            }
            page.setPage("<p>WikiText:<pre>"
                    + StringEscapeUtils.escapeHtml(revision.unpackedText()) + "</pre></p>" +
                    "<p>Version:<pre>"
                    + StringEscapeUtils.escapeHtml(String.valueOf(revision.getId())) + "</pre></p>" +
                    "<p>Last change:<pre>"
                    + StringEscapeUtils.escapeHtml(revision.getTimestamp()) + "</pre></p>" +
                    "<p>Request Parameters:<pre>"
                    + StringEscapeUtils.escapeHtml(sb.toString()) + "</pre></p>");
        }

        page.setNotice(getParam_notice(request));
        final String[] pageSaveTimes = getParam(request, "save_times").split(",");
        for (String pageSaveTime : pageSaveTimes) {
            final int pageSaveTimeInt = parseInt(pageSaveTime, -1);
            if (pageSaveTimeInt >= 0) {
                page.addStat("saving " + title, pageSaveTimeInt);
            }
        }
        final String[] pageRandomTimes = getParam(request, "random_times").split(",");
        for (String pageRandomTime : pageRandomTimes) {
            final int pageRandomTimeInt = parseInt(pageRandomTime, -1);
            if (pageRandomTimeInt >= 0) {
                page.addStat("random page", pageRandomTimeInt);
            }
        }
        page.setError(getParam_error(request));
        page.setTitle(title);
        page.setVersion(revision.getId());
        page.setWikiTitle(siteinfo.getSitename());
        page.setWikiNamespace(namespace);

        // extract the date:
        page.setDate(Revision.stringToCalendar(revision.getTimestamp()));
    }
   
    /**
     * Shows the "Page not available" message the wiki returns in case a page
     * does not exist.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param title
     *            the original title of the page that does not exist
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws IOException
     * @throws ServletException
     */
    private void handleViewPageNotExisting(HttpServletRequest request,
            HttpServletResponse response, String title, Connection connection,
            WikiPageBean page) throws ServletException, IOException {
        // get renderer
        int render = getParam_renderer(request);
        String notExistingTitle = "MediaWiki:Noarticletext";

        RevisionResult result = getRevision(connection, notExistingTitle, namespace);
        page.addStats(result.stats);
       
        if (result.success) {
            renderRevision(title, result.revision, render, request, connection, page);
        } else if (result.connect_failed) {
            setParam_error(request, "ERROR: DB connection failed");
            showEmptyPage(request, response, page);
            return;
        } else {
//            addToParam_notice(request, "error: unknown error getting page " + notExistingTitle + ": <pre>" + result.message + "</pre>");
            page.setPage("Page not available.");
            page.setError(getParam_error(request));
            page.setTitle(title);
        }
        // re-set version (we are only showing this page due to a non-existing page)
        page.setVersion(-1);
        page.setNotAvailable(true);
        page.setNotice(getParam_notice(request));
        page.setWikiTitle(siteinfo.getSitename());
        page.setWikiNamespace(namespace);

        // forward the request and the bean to the jsp:
        request.setAttribute("pageBean", page);
        RequestDispatcher dispatcher = request.getRequestDispatcher("page.jsp");
        dispatcher.forward(request, response);
    }

    /**
     * Shows the page containing the history information of an article with the
     * given <tt>title</tt>.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param title
     *            the title of the page
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws IOException
     * @throws ServletException
     */
    private void handleViewPageHistory(HttpServletRequest request,
            HttpServletResponse response, String title, Connection connection,
            WikiPageBean page) throws ServletException, IOException {
        PageHistoryResult result = getPageHistory(connection, title, namespace);
        page.addStats(result.stats);
        if (result.connect_failed) {
            setParam_error(request, "ERROR: DB connection failed");
            showEmptyPage(request, response, page);
            return;
        } else if (result.not_existing) {
            handleViewPageNotExisting(request, response, title, connection, page);
            return;
        }
       
        if (result.success) {
            page.setNotice(getParam_notice(request));
            page.setRevisions(result.revisions);
            if (!result.page.checkEditAllowed("")) {
                page.setEditRestricted(true);
            }

            page.setError(getParam_error(request));
            page.setTitle(title);
            if (!result.revisions.isEmpty()) {
                page.setVersion(result.revisions.get(0).getId());
            }
            page.setWikiTitle(siteinfo.getSitename());
            page.setWikiNamespace(namespace);
           
            // forward the request and the bean to the jsp:
            request.setAttribute("pageBean", page);
            RequestDispatcher dispatcher = request
                    .getRequestDispatcher("pageHistory.jsp");
            dispatcher.forward(request, response);
        } else {
            setParam_error(request, "ERROR: revision list unavailable");
            addToParam_notice(request, "error: unknown error getting revision list for page " + title + ": <pre>" + result.message + "</pre>");
            showEmptyPage(request, response, page);
        }
    }

    /**
     * Shows a page containing a list of article names.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param result
     *            result from reading the page list
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws IOException
     * @throws ServletException
     */
    private void handleViewSpecialPageList(HttpServletRequest request,
            HttpServletResponse response, ValueResult<List<String>> result,
            Connection connection, WikiPageListBean page)
            throws ServletException, IOException {
        if (result.success) {
            final TreeSet<String> pageList = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
            MyWikiModel.denormalisePageTitles(result.value, namespace, pageList);
            page.setNotice(getParam_notice(request));
            String nsPrefix = namespace.getNamespaceByNumber(page.getNamespaceId());
            if (!nsPrefix.isEmpty()) {
                nsPrefix += ":";
            }
            String prefix = nsPrefix + page.getPrefix();
            String from = page.getFromPage();
            String fullFrom = nsPrefix + page.getFromPage();
            String to = page.getToPage();
            String fullTo = nsPrefix + page.getToPage();
            String search = page.getSearch().toLowerCase();
            if (!prefix.isEmpty() || !from.isEmpty() || !to.isEmpty() || !search.isEmpty()) {
                // only show pages with this prefix:
                for (Iterator<String> it = pageList.iterator(); it.hasNext(); ) {
                    String cur = it.next();
                    // case-insensitive "startsWith" check:
                    if (!cur.regionMatches(true, 0, prefix, 0, prefix.length())) {
                        it.remove();
                    } else if (!from.isEmpty() && cur.compareToIgnoreCase(fullFrom) <= 0) {
                        it.remove();
                    } else if (!to.isEmpty() && cur.compareToIgnoreCase(fullTo) > 0) {
                        it.remove();
                    } else if (!search.isEmpty() && !cur.toLowerCase().contains(search)) {
                        it.remove();
                    }
                }
            }
            page.setPages(pageList);
           
            page.setWikiTitle(siteinfo.getSitename());
            page.setWikiNamespace(namespace);
           
            // forward the request and the bean to the jsp:
            request.setAttribute("pageBean", page);
            RequestDispatcher dispatcher = request
                    .getRequestDispatcher("pageSpecial_pagelist.jsp");
            dispatcher.forward(request, response);
        } else {
            if (result.connect_failed) {
                setParam_error(request, "ERROR: DB connection failed");
            } else {
                setParam_error(request, "ERROR: page list unavailable");
                addToParam_notice(request, "error: unknown error getting page list for " + page.getTitle() + ": <pre>" + result.message + "</pre>");
            }
            showEmptyPage(request, response, page);
            return;
        }
        page.setError(getParam_error(request));
        page.setTitle(page.getTitle());
    }

    /**
     * Shows the overview of all available special pages.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws ServletException
     * @throws IOException
     */
    private void handleViewSpecialPages(HttpServletRequest request,
            HttpServletResponse response, Connection connection,
            WikiPageListBean page) throws ServletException, IOException {
        final String title = "Special:SpecialPages";
        page.setPageHeading("Special pages");
        page.setTitle(title);
       
        Map<String /*group*/, Map<String /*title*/, String /*description*/>> specialPages = new LinkedHashMap<String, Map<String, String>>();
        Map<String, String> curSpecialPages;
        // Lists of pages
        curSpecialPages = new LinkedHashMap<String, String>();
        curSpecialPages.put("Special:AllPages", "All pages");
        curSpecialPages.put("Special:PrefixIndex", "All pages with prefix");
        specialPages.put("Lists of pages", curSpecialPages);
        // Wiki data and tools
        curSpecialPages = new LinkedHashMap<String, String>();
        curSpecialPages.put("Special:Statistics", "Statistics");
        curSpecialPages.put("Special:Version", "Version");
        specialPages.put("Wiki data and tools", curSpecialPages);
        // Redirecting special pages
        curSpecialPages = new LinkedHashMap<String, String>();
        curSpecialPages.put("Special:Search", "Search");
        curSpecialPages.put("Special:Random", "Show any page");
        specialPages.put("Redirecting special pages", curSpecialPages);
        // Page tools
        curSpecialPages = new LinkedHashMap<String, String>();
        if (Options.WIKI_USE_BACKLINKS) {
            curSpecialPages.put("Special:WhatLinksHere", "What links here");
        }
        specialPages.put("Page tools", curSpecialPages);

        StringBuilder content = new StringBuilder();
        for (Entry<String, Map<String, String>> specialPagesInGroup: specialPages.entrySet()) {
            String groupName = specialPagesInGroup.getKey();
            int i = 0;
            Iterator<Entry<String, String>> it = specialPagesInGroup.getValue().entrySet().iterator();
           
            content.append("<h4 class=\"mw-specialpagesgroup\"> <span class=\"mw-headline\">" + groupName + "</span></h4>\n");
            content.append("<table style=\"width: 100%;\" class=\"mw-specialpages-table\">\n");
            content.append(" <tbody>\n");
            content.append("  <tr>\n");
            content.append("   <td style=\"width: 30%; vertical-align: top;\">\n");
            content.append("    <ul>\n");
            int pagesInFirst = specialPagesInGroup.getValue().size() / 2 + specialPagesInGroup.getValue().size() % 2;
            for (; i < pagesInFirst; ++i) {
                Entry<String, String> pageInFirst = it.next();
                content.append("<li><a href=\"wiki?title=" + pageInFirst.getKey() + "\" title=\"" + pageInFirst.getKey() + "\">" + pageInFirst.getValue() + "</a></li>\n");
            }
            content.append("    </ul>\n");
            content.append("   </td>\n");
            content.append("   <td style=\"width: 10%;\"></td>\n");
            content.append("   <td style=\"width: 30%;\">\n");
            content.append("    <ul>\n");
            while(it.hasNext()) {
                Entry<String, String> pageInSecond = it.next();
                content.append("<li><a href=\"wiki?title=" + pageInSecond.getKey() + "\" title=\"" + pageInSecond.getKey() + "\">" + pageInSecond.getValue() + "</a></li>\n");
            }
            content.append("    </ul>\n");
            content.append("   </td>\n");
            content.append("   <td style=\"width: 30%;\"></td>\n");
            content.append("  </tr>\n");
            content.append(" </tbody>\n");
            content.append("</table>\n");
        }
       
        page.setPage(content.toString());
        // abuse #handleViewSpecialPageList here:
        ValueResult<List<String>> result = new ValueResult<List<String>>(new LinkedList<String>());
        handleViewSpecialPageList(request, response, result, connection, page);
    }

    /**
     * Shows several statistics about the running Wiki instance.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws ServletException
     * @throws IOException
     */
    private void handleViewSpecialStatistics(HttpServletRequest request,
            HttpServletResponse response, Connection connection,
            WikiPageListBean page) throws ServletException, IOException {
        MyWikiModel wikiModel = getWikiModel(connection);
        final String title = "Special:Statistics";
        page.setPageHeading("Statistics");
        page.setTitle(title);

        String articleCountStr;
        do {
            ValueResult<BigInteger> articleCount = getArticleCount(connection);
            page.addStats(articleCount.stats);
            if (articleCount.success) {
                articleCountStr = wikiModel.formatStatisticNumber(false, articleCount.value);
            } else {
                articleCountStr = "n/a";
            }
        } while(false);
       
        String pageCountStr;
        String pageEditsStr;
        String pageEditsPerPageStr;
        do {
            ValueResult<BigInteger> pageCount = getPageCount(connection);
            page.addStats(pageCount.stats);
            if (pageCount.success) {
                pageCountStr = wikiModel.formatStatisticNumber(false, pageCount.value);
               
                ValueResult<BigInteger> pageEdits = getStatsPageEdits(connection);
                page.addStats(pageEdits.stats);
                if (pageEdits.success) {
                    pageEditsStr = wikiModel.formatStatisticNumber(false, pageEdits.value);
                    if (pageCount.value.equals(BigInteger.valueOf(0))) {
                        pageEditsPerPageStr = wikiModel.formatStatisticNumber(false, 0.0);
                    } else {
                        pageEditsPerPageStr = wikiModel.formatStatisticNumber(false, pageEdits.value.doubleValue() / pageCount.value.doubleValue());
                    }
                } else {
                    pageEditsStr = "n/a";
                    pageEditsPerPageStr = "n/a";
                }
            } else {
                pageCountStr = "n/a";
                pageEditsStr = "n/a";
                pageEditsPerPageStr = "n/a";
            }
        } while (false);
        BigInteger uploadedFiles = BigInteger.valueOf(0); // currently not supported      
       
        Map<String /*group*/, Map<String /*name*/, String /*value*/>> specialPages = new LinkedHashMap<String, Map<String, String>>();
        Map<String, String> curStats;
        // Page statistics
        curStats = new LinkedHashMap<String, String>();
        curStats.put("Content pages", articleCountStr);
        curStats.put("Pages<br><small class=\"mw-statistic-desc\"> (All pages in the wiki, including talk pages, redirects, etc.)</small>",
                pageCountStr);
        curStats.put("Uploaded files", wikiModel.formatStatisticNumber(false, uploadedFiles));
        specialPages.put("Page statistics", curStats);
        // Edit statistics
        curStats = new LinkedHashMap<String, String>();
        curStats.put("Page edits since Wikipedia was set up", pageEditsStr);
        curStats.put("Average changes per page", pageEditsPerPageStr);
        specialPages.put("Edit statistics", curStats);
        // User statistics
        curStats = new LinkedHashMap<String, String>();
        specialPages.put("User statistics", curStats);

        StringBuilder content = new StringBuilder();
        content.append("<table class=\"wikitable mw-statistics-table\">\n");
        content.append(" <tbody>\n");
        for (Entry<String, Map<String, String>> specialPagesInGroup: specialPages.entrySet()) {
            String groupName = specialPagesInGroup.getKey();
            Iterator<Entry<String, String>> it = specialPagesInGroup.getValue().entrySet().iterator();
           
            content.append("  <tr>\n");
            content.append("   <th colspan=\"2\">" + groupName + "</th>\n");
            content.append("  </tr>\n");
           
            while(it.hasNext()) {
                Entry<String, String> stat = it.next();
                content.append("  <tr class=\"mw-statistics\">\n");
                content.append("   <td>" + stat.getKey() + "</td>\n");
                content.append("   <td class=\"mw-statistics-numbers\">" + stat.getValue() + "</td>\n");
                content.append("  </tr>\n");
            }
        }
        content.append(" </tbody>\n");
        content.append("</table>\n");
       
        page.setPage(content.toString());
        page.addStats(wikiModel.getStats());
        // abuse #handleViewSpecialPageList here:
        ValueResult<List<String>> result = new ValueResult<List<String>>(new LinkedList<String>());
        handleViewSpecialPageList(request, response, result, connection, page);
    }

    /**
     * Shows version information about the running Wiki instance.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param connection
     *            connection to the database
     * @param page
     *            the bean for the page
     *
     * @throws ServletException
     * @throws IOException
     */
    private void handleViewSpecialVersion(HttpServletRequest request,
            HttpServletResponse response, Connection connection,
            WikiPageListBean page) throws ServletException, IOException {
        final String title = "Special:Version";
        page.setPageHeading("Version");
        page.setTitle(title);
        String dbVersionStr;
        do {
            ValueResult<String> dbVersion = getDbVersion(connection);
            if (dbVersion.success) {
                dbVersionStr = dbVersion.value;
            } else {
                dbVersionStr = "n/a";
            }
        } while(false);

        StringBuilder content = new StringBuilder();
        content.append("<h2 id=\"mw-version-license\"> <span class=\"mw-headline\" id=\"License\">License</span></h2>\n");
        content.append("<div>\n");
        content.append("<p>This wiki is powered by <b><a href=\"http://code.google.com/p/scalaris/\" class=\"external text\" rel=\"nofollow\">Scalaris</a></b>, copyright © 2011 Zuse Institute Berlin</p>\n");
        content.append("<p>\n");
        content.append(" Licensed under the Apache License, Version 2.0 (the \"License\");</br>\n");
        content.append(" you may not use this software except in compliance with the License.</br>\n");
        content.append(" &nbsp;</br>\n");
        content.append(" You may obtain a copy of the License at</br>\n");
        content.append(" <a href=\"http://www.apache.org/licenses/LICENSE-2.0\" class=\"external text\" rel=\"nofollow\">http://www.apache.org/licenses/LICENSE-2.0</a>\n");
        content.append("</p>\n");
        content.append("<p>\n");
        content.append(" Unless required by applicable law or agreed to in writing, software</br>\n");
        content.append(" distributed under the License is distributed on an \"AS IS\" BASIS,</br>\n");
        content.append(" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</br>\n");
        content.append(" See the License for the specific language governing permissions and</br>\n");
        content.append(" limitations under the License.\n");
        content.append("</p>\n");
        content.append("</div>");
       
        content.append("<h2 id=\"mw-version-software\"> <span class=\"mw-headline\" id=\"Installed_software\">Installed software</span></h2>\n");
        content.append("<table class=\"wikitable\" id=\"sv-software\">\n");
        content.append(" <tbody>\n");
        content.append("  <tr>\n");
        content.append("   <th>Product</th>\n");
        content.append("   <th>Version</th>\n");
        content.append("  </tr>\n");
        content.append("  <tr>\n");
        content.append("   <td><a href=\"http://code.google.com/p/scalaris/\" class=\"external text\" rel=\"nofollow\">Scalaris Wiki Example</a></td>\n");
        content.append("   <td>" + version + "</td>\n");
        content.append("  </tr>\n");
        content.append("  <tr>\n");
        content.append("   <td><a href=\"http://code.google.com/p/scalaris/\" class=\"external text\" rel=\"nofollow\">Scalaris</a></td>\n");
        content.append("   <td>" + dbVersionStr + "</td>\n");
        content.append("  </tr>\n");
        content.append("  <tr>\n");
        content.append("   <td>Server</td>\n");
        content.append("   <td>" + getServletContext().getServerInfo() + "</td>\n");
        content.append("  </tr>\n");
        content.append(" </tbody>\n");
        content.append("</table>\n");
       
        page.setPage(content.toString());
        // abuse #handleViewSpecialPageList here:
        ValueResult<List<String>> result = new ValueResult<List<String>>(new LinkedList<String>());
        handleViewSpecialPageList(request, response, result, connection, page);
    }
   
    /**
     * Shows an empty page for testing purposes.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param page
     *            the bean for the page
     *
     * @throws IOException
     * @throws ServletException
     */
    private void showEmptyPage(HttpServletRequest request,
            HttpServletResponse response, WikiPageBeanBase page)
            throws ServletException, IOException {
        page.setNotice(getParam_notice(request));
        page.setError(getParam_error(request));
        request.setAttribute("pageBean", new WikiPageBean(page));
        RequestDispatcher dispatcher = request.getRequestDispatcher("page.jsp");
        dispatcher.forward(request, response);
    }
   
    /**
     * Shows a page for importing a DB dump (if implemented by the sub-class).
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     *
     * @throws IOException
     * @throws ServletException
     */
    protected synchronized void showImportPage(HttpServletRequest request,
            HttpServletResponse response, Connection connection)
            throws ServletException, IOException {
    }

    /**
     * Shows the edit page form for an article with the given <tt>title</tt>.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param title
     *            the title of the article to show
     * @param page
     *            the bean for the page
     *           
     * @throws IOException
     * @throws ServletException
     */
    private void handleEditPage(HttpServletRequest request,
            HttpServletResponse response, String title, Connection connection,
            WikiPageEditBean page) throws ServletException, IOException {
        // get revision id to load:
        int req_oldid = getParam_oldid(request);

        RevisionResult result = getRevision(connection, title, req_oldid, namespace);
        page.addStats(result.stats);
        if (result.connect_failed) {
            setParam_error(request, "ERROR: DB connection failed");
            showEmptyPage(request, response, page);
            return;
        } else if (result.rev_not_existing) {
            result = getRevision(connection, title, namespace);
            page.addStats(result.stats);
            addToParam_notice(request, "revision " + req_oldid + " not found - loaded current revision instead");
        }

        if (result.page_not_existing) {
            page.setVersion(-1);
            page.setNewPage(true);
        } else if (result.rev_not_existing) {
            // DB corrupt
            setParam_error(request, "ERROR: revision unavailable");
            addToParam_notice(request, "error: unknown error getting current revision of page \"" + title + "\": <pre>" + result.message + "</pre>");
            showEmptyPage(request, response, page);
            return;
        }
        if (result.success) {
            if (!result.page.checkEditAllowed("")) {
                page.setEditRestricted(true);
            }
            page.setPage(StringEscapeUtils.escapeHtml(result.revision.unpackedText()));
            page.setVersion(result.revision.getId());
        } else if (!page.isNewPage()) {
            page.setEditRestricted(true);
        }

        // set the textarea's contents:
        page.setNotice(getParam_notice(request));
        page.setError(getParam_error(request));
        page.setTitle(title);
        page.setWikiTitle(siteinfo.getSitename());
        page.setWikiNamespace(namespace);

        // forward the request and the bean to the jsp:
        request.setAttribute("pageBean", page);
        RequestDispatcher dispatcher = request.getRequestDispatcher("pageEdit.jsp");
        dispatcher.forward(request, response);
    }
   
    /**
     * Shows a preview of the edit operation submitted or saves the page with
     * the given <tt>title</tt> depending on what button the user clicked.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param title
     *            the title of the article to show
     * @param page
     *            the bean for the page
     *
     * @throws IOException
     * @throws UnsupportedEncodingException
     * @throws ServletException
     */
    private void handleEditPageSubmitted(HttpServletRequest request,
            HttpServletResponse response, String title, Connection connection,
            WikiPageEditBean page) throws UnsupportedEncodingException,
            IOException, ServletException {
        String content = request.getParameter("wpTextbox1");
        String summary = request.getParameter("wpSummary");
        int oldVersion = parseInt(request.getParameter("oldVersion"), -1);
        boolean minorChange = Boolean.parseBoolean(request.getParameter("minor"));

        // save page or preview+edit page?
        if (request.getParameter("wpSave") != null) {
            // save page
            Contributor contributor = new Contributor();
            contributor.setIp(request.getRemoteAddr());
            String timestamp = Revision.calendarToString(Calendar.getInstance(TimeZone.getTimeZone("UTC")));
            int newRevId = (oldVersion == -1) ? 1 : oldVersion + 1;
            Revision newRev = new Revision(newRevId, timestamp, minorChange, contributor, summary);
            newRev.setUnpackedText(content);

            SavePageResult result;
            int retries = 0;
            while (true) {
                result = savePage(connection, title, newRev, oldVersion, null, siteinfo, "", namespace);
                page.addStats(result.stats);
                if (!result.success && retries < Options.WIKI_SAVEPAGE_RETRIES) {
                    // check for conflicting edit on same page, do not retry in this case
                    final Page oldPage = result.oldPage;
                    if (oldPage != null && oldPage.getCurRev().getId() != oldVersion) {
                        break;
                    }
                    try {
                        Thread.sleep(Options.WIKI_SAVEPAGE_RETRY_DELAY);
                    } catch (InterruptedException e) {
                    }
                    ++retries;
                } else {
                    break;
                }
            }
            for (WikiEventHandler handler: eventHandlers) {
                handler.onPageSaved(result);
            }
            if (result.success) {
                // successfully saved -> show page with a notice of the successful operation
                ArrayList<Long> times = new ArrayList<Long>();
                for (List<Long> time : result.stats.values()) {
                    times.addAll(time);
                }
                // do not include the UTF-8-title directly into encodeRedirectURL since that's not
                // encoding umlauts (maybe other special chars as well) correctly, e.g. ä -> %E4 instead of %C3%A4
                StringBuilder redirectUrl = new StringBuilder(256);
                redirectUrl.append("?title=");
                redirectUrl.append(URLEncoder.encode(title, "UTF-8"));
                redirectUrl.append("&notice=successfully%20saved%20page");
                redirectUrl.append("&save_times=" + StringUtils.join(times, "%2C"));
                response.sendRedirect(response.encodeRedirectURL(redirectUrl.toString()));
                return;
            } else {
                // set error message and show the edit page again (see below)
                if (result.connect_failed) {
                    setParam_error(request, "ERROR: DB connection failed");
                } else {
                    setParam_error(request, "ERROR: conflicting edit");
                }
                addToParam_notice(request, "error: could not save page: <pre>" + result.message + "</pre>");
            }
        }

        // preview+edit page

        page.setNotice(getParam_notice(request));
        // set the textarea's contents:
        page.setPage(StringEscapeUtils.escapeHtml(content));

        MyWikiModel wikiModel = getWikiModel(connection);
        wikiModel.setPageName(title);
        String fullUrl = extractFullUrl(request.getRequestURL().toString());
        if (fullUrl != null) {
            wikiModel.setLinkBaseFullURL(fullUrl);
        }
        page.setPreview(wikiModel.render(content));
        page.addStats(wikiModel.getStats());
        page.setPage(content);
        page.setVersion(oldVersion);
        page.setError(getParam_error(request));
        page.setTitle(title);
        page.setSummary(request.getParameter("wpSummary"));
        page.setWikiTitle(siteinfo.getSitename());
        page.setWikiNamespace(namespace);

        // forward the request and the bean to the jsp:
        request.setAttribute("pageBean", page);
        RequestDispatcher dispatcher = request.getRequestDispatcher("pageEdit.jsp");
        dispatcher.forward(request, response);
    }
   
    /**
     * Shows a preview of the edit operation submitted or saves the page with
     * the given <tt>title</tt> depending on what button the user clicked.
     *
     * @param request
     *            the request of the current operation
     * @param response
     *            the response of the current operation
     * @param title
     *            the title of the article to show
     *
     * @throws IOException
     * @throws UnsupportedEncodingException
     * @throws ServletException
     */
    private void showImage(HttpServletRequest request,
            HttpServletResponse response, String image)
            throws UnsupportedEncodingException, IOException, ServletException {
        // we need to fix the image title first, e.g. a prefix with the desired size may exist
        image = MATCH_WIKI_IMAGE_PX.matcher(image).replaceFirst("");
        String realImageUrl = getWikiImageUrl(image);
        if (realImageUrl != null) {
            response.sendRedirect(realImageUrl);
        } else {
            // bliki may have created ".svg.png" from an original ".svg" image:
            String new_image = MATCH_WIKI_IMAGE_SVG_PNG.matcher(image).replaceFirst(".svg");
            if (!image.equals(new_image)
                    && (realImageUrl = getWikiImageUrl(new_image)) != null) {
                response.sendRedirect(realImageUrl);
            } else {
                response.sendRedirect(response.encodeRedirectURL("images/image.png"));
            }
        }
    }

    /**
     * Retrieves the URL of an image from the Wikipedia related to the base URL
     * of this wiki.
     *
     * @param image
     *            the name of the image as created by the bliki engine
     */
    protected String getWikiImageUrl(String image) {
        // add namespace - "Image" is a default alias for "File" in any language
        image = new String("Image:" + image);
        String fullBaseUrl = siteinfo.getBase();
        String baseUrl = "http://en.wikipedia.org";
        Matcher matcher = MATCH_WIKI_SITE_BASE.matcher(fullBaseUrl);
        if (matcher.matches()) {
            baseUrl = matcher.group(1);
        }
        User user = new User("", "", baseUrl + "/w/api.php");
        Connector connector = new Connector();
        user = connector.login(user);

        // set image width thumb size to 200px
        List<info.bliki.api.Page> pages = user.queryImageinfo(new String[] { image }, 200);
        if (pages.size() == 1) {
            info.bliki.api.Page imagePage = pages.get(0);
//            System.out.println("IMG-THUMB-URL: " + imagePage.getImageThumbUrl());
//            System.out.println("IMG-URL: " + imagePage.getImageUrl());

            if (imagePage.getImageThumbUrl() != null && !imagePage.getImageThumbUrl().isEmpty()) {
                return imagePage.getImageThumbUrl();
            }
        }
        return null;
    }
   
    abstract protected MyWikiModel getWikiModel(Connection connection);

    /**
     * Adds the given notice to the notice attribute.
     *
     * @param request
     *            the http request
     * @param notice
     *            the notice to add
     */
    public static void addToParam_notice(HttpServletRequest request, String notice) {
        String req_notice = getParam_notice(request);
        String new_notice = req_notice.isEmpty() ? notice : req_notice + "<br />" + notice;
        request.setAttribute("notice", new_notice);
    }

    /**
     * Returns the notice parameter.
     *
     * @param request
     *            the http request
     *
     * @return the notice parameter or ""
     *
     * @see #getParam(HttpServletRequest, String)
     */
    public static String getParam_notice(HttpServletRequest request) {
        return getParam(request, "notice");
    }

    /**
     * Returns the given parameter (or attribute if the parameter does not
     * exist, or an empty string if both are not present).
     *
     * @param request
     *            the http request
     * @param name
     *            the name of the parameter
     *
     * @return the notice parameter or ""
     */
    public static String getParam(HttpServletRequest request, String name) {
        String parValue = request.getParameter(name);
        if (parValue == null) {
            Object temp = request.getAttribute(name);
            if (temp instanceof String) {
                parValue = (String) temp;
            }
        }
        if (parValue == null) {
            return new String("");
        } else {
            return parValue;
        }
    }

    /**
     * Returns the error parameter.
     *
     * @param request
     *            the http request
     *
     * @return the error parameter or ""
     *
     * @see #getParam(HttpServletRequest, String)
     */
    public static String getParam_error(HttpServletRequest request) {
        return getParam(request, "error");
    }

    /**
     * Sets the error attribute. Once set, it cannot be changed with this
     * method.
     *
     * @param request
     *            the http request
     * @param error
     *            the error to set
     */
    public static void setParam_error(HttpServletRequest request, String error) {
        String req_error = getParam(request, "error");
        if (req_error.isEmpty()) {
            request.setAttribute("error", error + " - ");
        }
    }

    /**
     * Get the revision id to load from the request object
     *
     * @param request
     *            the http request
     * @return the revision id or -1 on failure to parse
     */
    private static int getParam_oldid(HttpServletRequest request) {
        String req_oldid = request.getParameter("oldid");
        return parseInt(req_oldid, -1);
    }

    /**
     * Determines which renderer should be used by evaluating the render
     * parameter of the request
     *
     * @param request
     *            the http request
     * @return the renderer id
     */
    private static int getParam_renderer(HttpServletRequest request) {
        int render = 1;
        String req_render = request.getParameter("render");
        if (req_render == null) {
            // already set to 1, so the default renderer is used
        } else if (req_render.equals("0")) {
            render = 0;
        } else if (req_render.equals("-1")) {
            render = -1;
        }
        return render;
    }
   
    protected final static int parseInt(String value, int def) {
        if (value == null) {
            return def;
        }
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return def;
        }
    }
   
    protected final static Calendar parseDate(String value, Calendar def) {
        if (value == null) {
            return def;
        }
        try {
            return Revision.stringToCalendar(value);
        } catch (IllegalArgumentException e) {
            return def;
        }
    }

    /* (non-Javadoc)
     * @see de.zib.scalaris.examples.wikipedia.bliki.WikiServletInterface#getNamespace()
     */
    @Override
    public final NamespaceUtils getNamespace() {
        return namespace;
    }

    /* (non-Javadoc)
     * @see de.zib.scalaris.examples.wikipedia.bliki.WikiServletInterface#getSiteinfo()
     */
    @Override
    public SiteInfo getSiteinfo() {
        return siteinfo;
    }

    /* (non-Javadoc)
     * @see de.zib.scalaris.examples.wikipedia.bliki.WikiServletInterface#getVersion()
     */
    @Override
    public String getVersion() {
        return version;
    }

    /* (non-Javadoc)
     * @see de.zib.scalaris.examples.wikipedia.bliki.WikiServletInterface#getWikibaseurl()
     */
    @Override
    public String getWikibaseurl() {
        return wikiBaseURL;
    }

    /* (non-Javadoc)
     * @see de.zib.scalaris.examples.wikipedia.bliki.WikiServletInterface#getLinkbaseurl()
     */
    @Override
    public String getLinkbaseurl() {
        return linkBaseURL;
    }

    /* (non-Javadoc)
     * @see de.zib.scalaris.examples.wikipedia.bliki.WikiServletInterface#getImagebaseurl()
     */
    @Override
    public String getImagebaseurl() {
        return imageBaseURL;
    }

    /* (non-Javadoc)
     * @see de.zib.scalaris.examples.wikipedia.bliki.WikiServletInterface#getImagebaseurl()
     */
    @Override
    public synchronized void registerEventHandler(WikiEventHandler handler) {
        eventHandlers.add(handler);
    }
}
TOP

Related Classes of de.zib.scalaris.examples.wikipedia.bliki.WikiServlet

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.