/* A UI subcomponent to display and edit wikitext.
*
* Copyright (C) 2010, 2011 Darrell Karbott
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.0 of the License, or (at your option) any later version.
*
* This library 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: djk@isFiaD04zgAgnrEC5XJt1i4IE7AkNPqhBG5bONi6Yks
*
* This file was developed as component of
* "fniki" (a wiki implementation running over Freenet).
*/
package fniki.wiki.child;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static ys.wikiparser.Utils.*;
import fniki.wiki.ChildContainer;
import fniki.wiki.ChildContainerException;
import fniki.wiki.Query;
import wormarc.FileManifest;
import wormarc.IOUtil;
import fniki.wiki.FreenetWikiTextParser;
import static fniki.wiki.HtmlUtils.*;
import static fniki.wiki.Validations.*;
import fniki.wiki.WikiApp;
import fniki.wiki.WikiContext;
import fniki.wiki.WikiTextStorage;
public class WikiContainer implements ChildContainer {
private final static String ENCODING = "UTF-8";
public WikiContainer() {}
public String handle(WikiContext context) throws ChildContainerException {
try {
String action = context.getAction();
if (action.equals("finished")) {
// Hack: Ignore "finished".
// This happens when the user hits the back button and picks
// a link from a finished task page. e.g. changelog.
action = "view";
}
String title = context.getTitle();
if (!isAlphaNumOrUnder(title)) {
// Titles must be legal page names.
context.raiseAccessDenied("Couldn't work out query.");
}
if (!isLowerCaseAlpha(action)) {
// Illegal action
context.raiseAccessDenied("Couldn't work out query.");
}
Query query = context.getQuery();
if (action.equals("view")) {
return handleView(context, title);
} else if (action.equals("edit")) {
return handleEdit(context, title);
} else if (action.equals("delete")) {
return handleDelete(context, title);
} else if (action.equals("revert")) {
return handleRevert(context, title);
} else if (action.equals("save")) {
return handleSave(context, query);
} else {
context.raiseAccessDenied("Couldn't work out query.");
}
} catch (IOException ioe) {
context.logError("WikiContainer.handle", ioe);
context.raiseServerError("Unexpected Error in WikiContainer.handle. Sorry :-(");
}
return "unreachable code";
}
private String handleView(WikiContext context, String name) throws IOException {
return getPageHtml(context, name);
}
private String handleEdit(WikiContext context, String name) throws IOException {
return getEditorHtml(context, name);
}
private String handleDelete(WikiContext context, String name) throws IOException {
if (context.getStorage().hasPage(name)) {
context.getStorage().deletePage(name);
}
// LATER: do better.
return getPageHtml(context, name);
}
private String handleRevert(WikiContext context, String name) throws ChildContainerException, IOException {
context.getStorage().revertLocalChange(name);
context.raiseRedirect(context.makeLink("/" + name), "Redirecting...");
return "unreachable code";
}
private String handleSave(WikiContext context, Query form) throws ChildContainerException, IOException {
// Name is included in the query data.
System.err.println("handleSave -- ENTERED");
String name = form.get("savepage");
String wikiText = form.get("savetext");
System.err.println("handleSave --got params");
if (name == null || wikiText == null) {
context.raiseAccessDenied("Couldn't parse parameters from POST.");
}
System.err.println("Writing: " + name);
context.getStorage().putPage(name, unescapeHTML(wikiText));
System.err.println("Raising redirect!");
context.raiseRedirect(context.makeLink("/" + name), "Redirecting...");
System.err.println("SOMETHING WENT WRONG!");
return "unreachable code";
}
private String unescapedTitleFromName(String name) {
return name.replace("_", " ");
}
private String getPageHtml(WikiContext context, String name) throws IOException {
StringBuilder buffer = new StringBuilder();
addHeader(name, buffer);
if (context.getStorage().hasPage(name)) {
buffer.append(renderXHTML(context, context.getStorage().getPage(name)));
} else {
if (name.equals(context.getString("default_page", "Front_Page"))) {
buffer.append(renderXHTML(context,
context.getString("default_wikitext",
"Page doesn't exist in the wiki yet.")));
} else if (context.getStorage().hasPage("PageDoesNotExist")) {
buffer.append(renderXHTML(context, context.getStorage().getPage("PageDoesNotExist")));
} else {
buffer.append("Page doesn't exist in the wiki yet.");
}
}
addFooter(context, name, buffer);
return buffer.toString();
}
private void addHeader(String name, StringBuilder buffer) throws IOException {
buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
buffer.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" " +
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
buffer.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
buffer.append("<head><title>\n");
buffer.append(escapeHTML(unescapedTitleFromName(name)));
buffer.append("</title>\n");
buffer.append("<style type=\"text/css\">div.indent{margin-left:20px;} " +
"div.center{text-align:center;} " +
"blockquote{margin-left:20px;background-color:#e0e0e0;} " +
"span.underline{text-decoration:underline;}</style>\n");
buffer.append("</head>\n");
buffer.append("<body>\n");
buffer.append("<h1>\n");
buffer.append(escapeHTML(unescapedTitleFromName(name)));
buffer.append("</h1><hr>\n");
}
private String makeLocalLink(WikiContext context, String name, String action, String label) {
String href = makeHref(context.makeLink("/" + name), action, name, null, null);
return String.format("<a href=\"%s\">%s</a>", href, escapeHTML(label));
}
private void addFooter(WikiContext context, String name, StringBuilder buffer) throws IOException {
buffer.append("<hr>\n");
buffer.append("Parent Version: ");
// DCI: css class to make this smaller.
String version = getVersionHex(context.getString("parent_uri", null));
buffer.append(escapeHTML(version));
buffer.append("<hr>\n");
buffer.append(makeLocalLink(context, name, "edit", "Edit"));
buffer.append(" this page.<br>");
buffer.append(makeLocalLink(context, name, "delete", "Delete"));
buffer.append(" this page without confirmation!<br>");
if (context.getStorage().hasLocalChange(name)) {
buffer.append(makeLocalLink(context, name, "revert", "Revert"));
buffer.append(" local changes to this page without confirmation!<br>");
}
buffer.append(makeLocalLink(context, "fniki/submit", null, "Submit"));
buffer.append(" local changes. <br>");
buffer.append(makeLocalLink(context, "fniki/changelog", "confirm", "Show"));
buffer.append(" change history for this version. <br>");
buffer.append(makeLocalLink(context, "fniki/getversions", "confirm", "Discover"));
buffer.append(String.format(" other recent version of this wiki (wikiname: [%s], FMS group: [%s])<br>",
escapeHTML(context.getString("wikiname", "NOT_SET")),
escapeHTML(context.getString("fms_group", "NOT_SET"))));
buffer.append(makeLocalLink(context, "fniki/config", "view", "View"));
buffer.append(" configuration.<p/>\n");
buffer.append(makeLocalLink(context, "fniki/resettoempty", "view", "Create Wiki!"));
buffer.append(" (<em>careful:</em> This deletes all content and history without confirmation.)<p/>\n");
buffer.append(gotoPageFormHtml(context.makeLink("/" + name),
context.getString("default_page", "Front_Page")));
buffer.append("</body></html>");
}
private String getEditorHtml(WikiContext context, String name) throws IOException {
String template = null;
try {
// IMPORTANT: Only multipart/form-data encoding works in plugins.
// IMPORTANT: Must be multipart/form-data even for standalone because
// the Freenet ContentFilter rewrites the encoding in all forms
// to this value.
template = IOUtil.readUtf8StringAndClose(SettingConfig.class.getResourceAsStream("/edit_form.html"));
} catch (IOException ioe) {
return "Couldn't load edit_form.html template from jar???";
}
String escapedName = escapeHTML(unescapedTitleFromName(name));
String href = makeHref(context.makeLink("/" +name),
"save", null, null, null);
String wikiText = "Page doesn't exist in the wiki yet.";
if (context.getStorage().hasPage(name)) {
wikiText = context.getStorage().getPage(name);
}
return String.format(template,
escapedName,
escapedName,
href,
escapeHTML(name), // i.e. with '_' chars
escapeHTML(wikiText),
// IMPORTANT: Required by Freenet Plugin.
// Doesn't need escaping.
context.getString("form_password", "FORM_PASSWORD_NOT_SET"));
}
public String renderXHTML(WikiContext context, String wikiText) {
return new FreenetWikiTextParser(wikiText, context.getParserDelegate()).toString();
}
}