migrate the profile page to the new format, and refactor the new format's servlets

This commit is contained in:
jrandom
2005-11-12 12:14:23 +00:00
committed by zzz
parent 8c70b8b32a
commit ed285871bf
7 changed files with 972 additions and 572 deletions

View File

@ -55,6 +55,7 @@ public class BlogInfo {
public static final String SIGNATURE = "Signature"; public static final String SIGNATURE = "Signature";
public static final String NAME = "Name"; public static final String NAME = "Name";
public static final String DESCRIPTION = "Description"; public static final String DESCRIPTION = "Description";
public static final String CONTACT_URL = "ContactURL";
public static final String EDITION = "Edition"; public static final String EDITION = "Edition";
public void load(InputStream in) throws IOException { public void load(InputStream in) throws IOException {

View File

@ -400,8 +400,12 @@ public class HTMLRenderer extends EventReceiverImpl {
if (location != null) { if (location != null) {
_bodyBuffer.append(" at "); _bodyBuffer.append(" at ");
SafeURL surl = new SafeURL(locationSchema + "://" + location); SafeURL surl = new SafeURL(locationSchema + "://" + location);
_bodyBuffer.append("<a ").append(getClass("archiveSummaryLink")).append(" href=\"").append(getArchiveURL(null, surl)); if (BlogManager.instance().authorizeRemote(_user)) {
_bodyBuffer.append("\">").append(sanitizeString(surl.toString())).append("</a>"); _bodyBuffer.append("<a ").append(getClass("archiveSummaryLink")).append(" href=\"").append(getArchiveURL(null, surl));
_bodyBuffer.append("\">").append(sanitizeString(surl.toString())).append("</a>");
} else {
_bodyBuffer.append(sanitizeString(surl.getLocation()));
}
if (_user.getAuthenticated()) { if (_user.getAuthenticated()) {
_bodyBuffer.append(" <a ").append(getClass("archiveBookmarkLink")).append(" href=\""); _bodyBuffer.append(" <a ").append(getClass("archiveBookmarkLink")).append(" href=\"");
_bodyBuffer.append(getBookmarkURL(sanitizeString(name), surl.getLocation(), surl.getSchema(), "syndiearchive")); _bodyBuffer.append(getBookmarkURL(sanitizeString(name), surl.getLocation(), surl.getSchema(), "syndiearchive"));
@ -1001,7 +1005,7 @@ public class HTMLRenderer extends EventReceiverImpl {
if (_entry == null) return "unknown"; if (_entry == null) return "unknown";
return getMetadataURL(_entry.getURI().getKeyHash()); return getMetadataURL(_entry.getURI().getKeyHash());
} }
public static String getMetadataURL(Hash blog) { public String getMetadataURL(Hash blog) {
return "viewmetadata.jsp?" + ArchiveViewerBean.PARAM_BLOG + "=" + return "viewmetadata.jsp?" + ArchiveViewerBean.PARAM_BLOG + "=" +
Base64.encode(blog.getData()); Base64.encode(blog.getData());
} }

View File

@ -31,10 +31,19 @@ public class ThreadedHTMLRenderer extends HTMLRenderer {
public static final String PARAM_VISIBLE = "visible"; public static final String PARAM_VISIBLE = "visible";
public static final String PARAM_ADD_TO_GROUP_LOCATION = "addLocation"; public static final String PARAM_ADD_TO_GROUP_LOCATION = "addLocation";
public static final String PARAM_ADD_TO_GROUP_NAME = "addGroup"; public static final String PARAM_ADD_TO_GROUP_NAME = "addGroup";
/** name of the bookmarked entry to remove */
public static final String PARAM_REMOVE_FROM_GROUP_NAME = "removeName";
/** group to remove from the bookmarked entry, or if blank, remove the entry itself */
public static final String PARAM_REMOVE_FROM_GROUP = "removeGroup";
/** index into the nav tree to start displaying */ /** index into the nav tree to start displaying */
public static final String PARAM_OFFSET = "offset"; public static final String PARAM_OFFSET = "offset";
public static final String PARAM_TAGS = "tags"; public static final String PARAM_TAGS = "tags";
public static final String PARAM_AUTHOR = "author"; public static final String PARAM_AUTHOR = "author";
// parameters for editing one's profile
public static final String PARAM_PROFILE_NAME = "profileName";
public static final String PARAM_PROFILE_DESC = "profileDesc";
public static final String PARAM_PROFILE_URL = "profileURL";
public static final String PARAM_PROFILE_OTHER = "profileOther";
public static String getFilterByTagLink(String uri, ThreadNode node, User user, String tag, String author) { public static String getFilterByTagLink(String uri, ThreadNode node, User user, String tag, String author) {
StringBuffer buf = new StringBuffer(64); StringBuffer buf = new StringBuffer(64);
@ -399,6 +408,13 @@ public class ThreadedHTMLRenderer extends HTMLRenderer {
//renderPreBodyCell(); //renderPreBodyCell();
} }
public String getMetadataURL(Hash blog) {
return buildProfileURL(blog);
}
public static String buildProfileURL(Hash blog) {
return "profile.jsp?" + ThreadedHTMLRenderer.PARAM_AUTHOR + "=" +
Base64.encode(blog.getData());
}
protected String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : false); } protected String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : false); }
protected String getEntryURL(boolean showImages) { protected String getEntryURL(boolean showImages) {
if (_entry == null) if (_entry == null)

View File

@ -0,0 +1,716 @@
package net.i2p.syndie.web;
import java.io.*;
import java.util.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.*;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
import net.i2p.syndie.sml.*;
/**
* Base servlet for handling request and rendering the templates
*
*/
public abstract class BaseServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html");
User user = (User)req.getSession().getAttribute("user");
String login = req.getParameter("login");
String pass = req.getParameter("password");
String action = req.getParameter("action");
boolean forceNewIndex = false;
if (req.getParameter("regenerateIndex") != null)
forceNewIndex = true;
if (user == null) {
if ("Login".equals(action)) {
user = BlogManager.instance().login(login, pass); // ignore failures - user will just be unauthorized
if (!user.getAuthenticated())
user = BlogManager.instance().getDefaultUser();
} else {
user = BlogManager.instance().getDefaultUser();
}
forceNewIndex = true;
} else if ("Login".equals(action)) {
user = BlogManager.instance().login(login, pass); // ignore failures - user will just be unauthorized
if (!user.getAuthenticated())
user = BlogManager.instance().getDefaultUser();
forceNewIndex = true;
} else if ("Logout".equals(action)) {
user = BlogManager.instance().getDefaultUser();
forceNewIndex = true;
}
req.getSession().setAttribute("user", user);
forceNewIndex = handleBookmarking(user, req) || forceNewIndex;
handleUpdateProfile(user, req);
FilteredThreadIndex index = (FilteredThreadIndex)req.getSession().getAttribute("threadIndex");
Collection tags = getFilteredTags(req);
Collection filteredAuthors = getFilteredAuthors(req);
if (forceNewIndex || (index == null) || (!index.getFilteredTags().equals(tags)) || (!index.getFilteredAuthors().equals(filteredAuthors))) {
index = new FilteredThreadIndex(user, BlogManager.instance().getArchive(), getFilteredTags(req), filteredAuthors);
req.getSession().setAttribute("threadIndex", index);
}
render(user, req, resp.getWriter(), index);
}
private boolean handleBookmarking(User user, HttpServletRequest req) {
if (!user.getAuthenticated())
return false;
boolean rv = false;
String loc = req.getParameter(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_LOCATION);
String group = req.getParameter(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_NAME);
if ( (loc != null) && (group != null) && (group.trim().length() > 0) ) {
try {
Hash key = new Hash();
key.fromBase64(loc);
PetNameDB db = user.getPetNameDB();
PetName pn = db.getByLocation(loc);
boolean isNew = false;
if (pn == null) {
isNew = true;
BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(key);
String name = null;
if (info != null)
name = info.getProperty(BlogInfo.NAME);
else
name = loc.substring(0,6);
if (db.containsName(name)) {
int i = 0;
while (db.containsName(name + i))
i++;
name = name + i;
}
pn = new PetName(name, "syndie", "syndieblog", loc);
}
pn.addGroup(group);
if (isNew)
db.add(pn);
BlogManager.instance().saveUser(user);
// if we are ignoring someone, we need to recalculate the filters
if (FilteredThreadIndex.GROUP_IGNORE.equals(group))
rv = true;
} catch (DataFormatException dfe) {
// bad loc, ignore
}
}
String name = req.getParameter(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP_NAME);
group = req.getParameter(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP);
if ( (name != null) && (name.trim().length() > 0) ) {
PetNameDB db = user.getPetNameDB();
PetName pn = db.getByName(name);
boolean changed = false;
if (pn != null) {
if ( (group != null) && (group.trim().length() > 0) ) {
// just remove them from the group
changed = pn.isMember(group);
pn.removeGroup(group);
if ( (changed) && (FilteredThreadIndex.GROUP_IGNORE.equals(group)) )
rv = true;
} else {
// remove it completely
if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
rv = true;
db.remove(pn);
changed = true;
}
}
if (changed)
BlogManager.instance().saveUser(user);
}
return rv;
}
protected void handleUpdateProfile(User user, HttpServletRequest req) {
if ( (user == null) || (!user.getAuthenticated()) || (user.getBlog() == null) )
return;
String action = req.getParameter("action");
if ( (action == null) || !("Update profile".equals(action)) )
return;
String name = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_NAME);
String desc = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_DESC);
String url = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_URL);
String other = req.getParameter(ThreadedHTMLRenderer.PARAM_PROFILE_OTHER);
Properties opts = new Properties();
if (!empty(name))
opts.setProperty(BlogInfo.NAME, name.trim());
if (!empty(desc))
opts.setProperty(BlogInfo.DESCRIPTION, desc.trim());
if (!empty(url))
opts.setProperty(BlogInfo.CONTACT_URL, url.trim());
if (!empty(other)) {
StringBuffer key = new StringBuffer();
StringBuffer val = null;
for (int i = 0; i < other.length(); i++) {
char c = other.charAt(i);
if ( (c == ':') || (c == '=') ) {
if (val != null) {
val.append(c);
} else {
val = new StringBuffer();
}
} else if ( (c == '\n') || (c == '\r') ) {
String k = key.toString().trim();
String v = (val != null ? val.toString().trim() : "");
if ( (k.length() > 0) && (v.length() > 0) ) {
opts.setProperty(k, v);
}
key.setLength(0);
val = null;
} else if (val != null) {
val.append(c);
} else {
key.append(c);
}
}
// now finish the last of it
String k = key.toString().trim();
String v = (val != null ? val.toString().trim() : "");
if ( (k.length() > 0) && (v.length() > 0) ) {
opts.setProperty(k, v);
}
}
boolean updated = BlogManager.instance().updateMetadata(user, user.getBlog(), opts);
}
protected void render(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws ServletException, IOException {
Archive archive = BlogManager.instance().getArchive();
int numThreads = 10;
int threadOffset = getOffset(req);
if (threadOffset == -1) {
threadOffset = index.getRootCount() - numThreads;
}
if (threadOffset < 0) {
threadOffset = 0;
}
BlogURI visibleEntry = getVisible(req);
int offset = 0;
if ( empty(req, ThreadedHTMLRenderer.PARAM_OFFSET) && (visibleEntry != null) ) {
// we're on a permalink, so jump the tree to the given thread
threadOffset = index.getRoot(visibleEntry);
if (threadOffset < 0)
threadOffset = 0;
}
renderBegin(user, req, out, index);
renderNavBar(user, req, out, index);
renderControlBar(user, req, out, index);
renderServletDetails(user, req, out, index, threadOffset, visibleEntry, archive);
renderEnd(user, req, out, index);
}
protected void renderBegin(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
out.write(BEGIN_HTML);
}
protected void renderNavBar(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
//out.write("<tr class=\"topNav\"><td class=\"topNav_user\" colspan=\"2\" nowrap=\"true\">\n");
out.write("<tr class=\"topNav\"><td colspan=\"3\" nowrap=\"true\"><span class=\"topNav_user\">\n");
out.write("<!-- nav bar begin -->\n");
if (user.getAuthenticated() && (user.getBlog() != null) ) {
out.write("Logged in as <a href=\"" + getProfileLink(req, user.getBlog()) + "\" title=\"Edit your profile\">");
out.write(user.getUsername());
out.write("</a>\n");
out.write("(<a href=\"switchuser.jsp\" title=\"Log in as another user\">switch</a>)\n");
out.write("<a href=\"post.jsp\" title=\"Post a new thread\">Post a new thread</a>\n");
} else {
out.write("<form action=\"" + req.getRequestURI() + "\" method=\"GET\">\n");
out.write("Login: <input type=\"text\" name=\"login\" />\n");
out.write("Password: <input type=\"password\" name=\"password\" />\n");
out.write("<input type=\"submit\" name=\"action\" value=\"Login\" /></form>\n");
}
//out.write("</td><td class=\"topNav_admin\">\n");
out.write("</span><span class=\"topNav_admin\">\n");
if (user.getAuthenticated() && user.getAllowAccessRemote()) {
out.write("<a href=\"syndicate.jsp\" title=\"Syndicate data between other Syndie nodes\">Syndicate</a>\n");
out.write("<a href=\"importfeed.jsp\" title=\"Import RSS/Atom data\">Import RSS/Atom</a>\n");
out.write("<a href=\"admin.jsp\" title=\"Configure this Syndie node\">Admin</a>\n");
}
out.write("</span><!-- nav bar end -->\n</td></tr>\n");
}
protected static final ArrayList SKIP_TAGS = new ArrayList();
static {
SKIP_TAGS.add("action");
SKIP_TAGS.add("filter");
// post and visible are skipped since we aren't good at filtering by tag when the offset will
// skip around randomly. at least, not yet.
SKIP_TAGS.add("visible");
//SKIP_TAGS.add("post");
//SKIP_TAGS.add("thread");
SKIP_TAGS.add("offset"); // if we are adjusting the filter, ignore the previous offset
SKIP_TAGS.add("login");
SKIP_TAGS.add("password");
}
private static final String CONTROL_TARGET = "threads.jsp";
protected String getControlTarget() { return CONTROL_TARGET; }
protected void renderControlBar(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
out.write("<form action=\"");
//out.write(req.getRequestURI());
out.write(getControlTarget());
out.write("\" method=\"GET\">\n");
String tags = "";
String author = "";
Enumeration params = req.getParameterNames();
while (params.hasMoreElements()) {
String param = (String)params.nextElement();
String val = req.getParameter(param);
if (ThreadedHTMLRenderer.PARAM_TAGS.equals(param)) {
tags = val;
} else if (ThreadedHTMLRenderer.PARAM_AUTHOR.equals(param)) {
author = val;
} else if (SKIP_TAGS.contains(param)) {
// skip
} else if (param.length() <= 0) {
// skip
} else {
out.write("<input type=\"hidden\" name=\"" + param + "\" value=\"" + val + "\" />\n");
}
}
out.write("<tr class=\"controlBar\"><td colspan=\"2\">\n");
out.write("<!-- control bar begin -->\n");
out.write("Filter: <select name=\"" + ThreadedHTMLRenderer.PARAM_AUTHOR + "\">\n");
PetNameDB db = user.getPetNameDB();
TreeSet names = new TreeSet(db.getNames());
out.write("<option value=\"\">Any authors</option>\n");
if (user.getBlog() != null) {
if ( (author != null) && (author.equals(user.getBlog().toBase64())) )
out.write("<option value=\"" + user.getBlog().toBase64() + "\" selected=\"true\">Threads you posted in</option>\n");
else
out.write("<option value=\"" + user.getBlog().toBase64() + "\">Threads you posted in</option>\n");
}
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
PetName pn = db.getByName(name);
if ("syndieblog".equals(pn.getProtocol())) {
if ( (author != null) && (author.equals(pn.getLocation())) )
out.write("<option value=\"" + pn.getLocation() + "\" selected=\"true\">Threads " + name + " posted in</option>\n");
else
out.write("<option value=\"" + pn.getLocation() + "\">Threads " + name + " posted in</option>\n");
}
}
out.write("</select>\n");
out.write("Tags: <input type=\"text\" name=\"" + ThreadedHTMLRenderer.PARAM_TAGS + "\" size=\"10\" value=\"" + tags + "\" />\n");
out.write("<input type=\"submit\" name=\"action\" value=\"Go\" />\n");
out.write("</td><td class=\"controlBarRight\"><a href=\"#threads\" title=\"Jump to the thread navigation\">Threads</a></td>\n");
out.write("<!-- control bar end -->\n");
out.write("</tr>\n");
out.write("</form>\n");
}
protected abstract void renderServletDetails(User user, HttpServletRequest req, PrintWriter out,
ThreadIndex index, int threadOffset, BlogURI visibleEntry,
Archive archive) throws IOException;
protected static final int getOffset(HttpServletRequest req) {
String off = req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET);
try {
return Integer.parseInt(off);
} catch (NumberFormatException nfe) {
return 0;
}
}
protected static final BlogURI getVisible(HttpServletRequest req) {
return getAsBlogURI(req.getParameter(ThreadedHTMLRenderer.PARAM_VISIBLE));
}
protected static final BlogURI getAsBlogURI(String uri) {
if (uri != null) {
int split = uri.indexOf('/');
if ( (split <= 0) || (split + 1 >= uri.length()) )
return null;
String blog = uri.substring(0, split);
String id = uri.substring(split+1);
try {
Hash hash = new Hash();
hash.fromBase64(blog);
long msgId = Long.parseLong(id);
if (msgId > 0)
return new BlogURI(hash, msgId);
} catch (DataFormatException dfe) {
return null;
} catch (NumberFormatException nfe) {
return null;
}
}
return null;
}
protected String trim(String orig, int maxLen) {
if ( (orig == null) || (orig.length() <= maxLen) )
return orig;
return orig.substring(0, maxLen) + "...";
}
protected static final boolean empty(HttpServletRequest req, String param) {
String val = req.getParameter(param);
return (val == null) || (val.trim().length() <= 0);
}
protected static final boolean empty(String val) {
return (val == null) || (val.trim().length() <= 0);
}
protected String getExpandLink(HttpServletRequest req, ThreadNode node) {
return getExpandLink(node, req.getRequestURI(), req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
protected static String getExpandLink(ThreadNode node, String uri, String viewPost, String viewThread,
String offset, String tags, String author) {
StringBuffer buf = new StringBuffer(64);
buf.append(uri);
buf.append('?');
// expand node == let one of node's children be visible
if (node.getChildCount() > 0) {
ThreadNode child = node.getChild(0);
buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
buf.append(child.getEntry().getKeyHash().toBase64()).append('/');
buf.append(child.getEntry().getEntryId()).append('&');
}
if (!empty(viewPost))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
else if (!empty(viewThread))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
if (!empty(offset))
buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
if (!empty(tags))
buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
if (!empty(author))
buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append('&');
return buf.toString();
}
protected String getCollapseLink(HttpServletRequest req, ThreadNode node) {
return getCollapseLink(node, req.getRequestURI(),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
protected String getCollapseLink(ThreadNode node, String uri, String viewPost, String viewThread,
String offset, String tags, String author) {
StringBuffer buf = new StringBuffer(64);
buf.append(uri);
// collapse node == let the node be visible
buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
buf.append(node.getEntry().getEntryId()).append('&');
if (!empty(viewPost))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
else if (!empty(viewThread))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
if (!empty(offset))
buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
if (!empty(tags))
buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
if (!empty(author))
buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append('&');
return buf.toString();
}
protected String getProfileLink(HttpServletRequest req, Hash author) {
return getProfileLink(author);
}
protected String getProfileLink(Hash author) { return ThreadedHTMLRenderer.buildProfileURL(author); }
protected String getAddToGroupLink(HttpServletRequest req, Hash author, User user, String group) {
return getAddToGroupLink(user, author, group, req.getRequestURI(),
req.getParameter(ThreadedHTMLRenderer.PARAM_VISIBLE),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
protected String getAddToGroupLink(User user, Hash author, String group, String uri, String visible,
String viewPost, String viewThread, String offset, String tags, String filteredAuthor) {
StringBuffer buf = new StringBuffer(64);
buf.append(uri);
buf.append('?');
if (!empty(visible))
buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=').append(visible).append('&');
buf.append(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_LOCATION).append('=').append(author.toBase64()).append('&');
buf.append(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_NAME).append('=').append(group).append('&');
if (!empty(viewPost))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
else if (!empty(viewThread))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
if (!empty(offset))
buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
if (!empty(tags))
buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
if (!empty(filteredAuthor))
buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(filteredAuthor).append('&');
return buf.toString();
}
protected String getRemoveFromGroupLink(User user, String name, String group, String uri, String visible,
String viewPost, String viewThread, String offset, String tags, String filteredAuthor) {
StringBuffer buf = new StringBuffer(64);
buf.append(uri);
buf.append('?');
if (!empty(visible))
buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=').append(visible).append('&');
buf.append(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP_NAME).append('=').append(name).append('&');
buf.append(ThreadedHTMLRenderer.PARAM_REMOVE_FROM_GROUP).append('=').append(group).append('&');
if (!empty(viewPost))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
else if (!empty(viewThread))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
if (!empty(offset))
buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
if (!empty(tags))
buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
if (!empty(filteredAuthor))
buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(filteredAuthor).append('&');
return buf.toString();
}
protected String getViewPostLink(HttpServletRequest req, ThreadNode node, User user, boolean isPermalink) {
return ThreadedHTMLRenderer.getViewPostLink(req.getRequestURI(), node, user, isPermalink,
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
protected String getViewThreadLink(HttpServletRequest req, ThreadNode node, User user) {
return getViewThreadLink(req.getRequestURI(), node, user,
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
protected static String getViewThreadLink(String uri, ThreadNode node, User user, String offset,
String tags, String author) {
StringBuffer buf = new StringBuffer(64);
buf.append(uri);
if (node.getChildCount() > 0) {
buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
ThreadNode child = node.getChild(0);
buf.append(child.getEntry().getKeyHash().toBase64()).append('/');
buf.append(child.getEntry().getEntryId()).append('&');
} else {
buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
buf.append(node.getEntry().getEntryId()).append('&');
}
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=');
buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
buf.append(node.getEntry().getEntryId()).append('&');
if (!empty(offset))
buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
if (!empty(tags))
buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
if (!empty(author))
buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append('&');
buf.append("#").append(node.getEntry().toString());
return buf.toString();
}
protected String getFilterByTagLink(HttpServletRequest req, ThreadNode node, User user, String tag, String author) {
return ThreadedHTMLRenderer.getFilterByTagLink(req.getRequestURI(), node, user, tag, author);
}
protected String getNavLink(HttpServletRequest req, int offset) {
return ThreadedHTMLRenderer.getNavLink(req.getRequestURI(),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR),
offset);
}
protected void renderEnd(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
out.write(END_HTML);
}
protected Collection getFilteredTags(HttpServletRequest req) {
String tags = req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS);
if (tags != null) {
StringTokenizer tok = new StringTokenizer(tags, "\n\t ");
ArrayList rv = new ArrayList();
while (tok.hasMoreTokens()) {
String tag = tok.nextToken().trim();
if (tag.length() > 0)
rv.add(tag);
}
return rv;
} else {
return Collections.EMPTY_LIST;
}
}
protected Collection getFilteredAuthors(HttpServletRequest req) {
String authors = req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR);
if (authors != null) {
StringTokenizer tok = new StringTokenizer(authors, "\n\t ");
ArrayList rv = new ArrayList();
while (tok.hasMoreTokens()) {
try {
Hash h = new Hash();
h.fromBase64(tok.nextToken().trim());
rv.add(h);
} catch (DataFormatException dfe) {}
}
return rv;
} else {
return Collections.EMPTY_LIST;
}
}
private static final String BEGIN_HTML = "<html>\n" +
"<head>\n" +
"<title>Syndie</title>\n" +
"<style>\n" +
".overallTable {\n" +
" border-spacing: 0px;\n" +
" border-width: 0px;\n" +
" border: 0px;\n" +
" margin: 0px;\n" +
" padding: 0px;\n" +
"}\n" +
".topNav {\n" +
" background-color: #BBBBBB;\n" +
"}\n" +
".topNav_user {\n" +
" text-align: left;\n" +
" float: left;\n" +
" align: left;\n" +
" display: inline;\n" +
"}\n" +
".topNav_admin {\n" +
" text-align: right;\n" +
" float: right;\n" +
" align: right;\n" +
" display: inline;\n" +
"}\n" +
".controlBar {\n" +
" background-color: #BBBBBB;\n" +
"}\n" +
".controlBarRight {\n" +
" text-align: right;\n" +
"}\n" +
".threadEven {\n" +
" background-color: #FFFFFF;\n" +
" white-space: nowrap;\n" +
"}\n" +
".threadOdd {\n" +
" background-color: #EEEEEE;\n" +
" white-space: nowrap;\n" +
"}\n" +
".threadLeft {\n" +
" text-align: left;\n" +
" align: left;\n" +
"}\n" +
".threadRight {\n" +
" text-align: right;\n" +
"}\n" +
".threadNav {\n" +
" background-color: #BBBBBB;\n" +
"}\n" +
".threadNavRight {\n" +
" text-align: right;\n" +
"}\n" +
".postMeta {\n" +
" background-color: #BBBBFF;\n" +
"}\n" +
".postMetaSubject {\n" +
" text-align: left;\n" +
"}\n" +
".postMetaLink {\n" +
" text-align: right;\n" +
"}\n" +
".postDetails {\n" +
" background-color: #DDDDFF;\n" +
"}\n" +
".postReply {\n" +
" background-color: #BBBBFF;\n" +
"}\n" +
".postReplyText {\n" +
" background-color: #BBBBFF;\n" +
"}\n" +
".postReplyOptions {\n" +
" background-color: #BBBBFF;\n" +
"}\n" +
"</style>\n" +
"<link href=\"style.jsp\" rel=\"stylesheet\" type=\"text/css\" >\n" +
"<link href=\"rss.jsp\" rel=\"alternate\" type=\"application/rss+xml\" >\n" +
"</head>\n" +
"<body>\n" +
"<span style=\"display: none\"><a href=\"#bodySubject\">Jump to the beginning of the first post rendered, if any</a>\n" +
"<a href=\"#threads\">Jump to the thread navigation</a>\n</span>\n" +
"<table border=\"0\" width=\"100%\" class=\"overallTable\">\n";
private static final String END_HTML = "</table>\n" +
"</body>\n";
protected static class TreeRenderState {
private int _rowsWritten;
private int _rowsSkipped;
private List _ignored;
public TreeRenderState(List ignored) {
_rowsWritten = 0;
_rowsSkipped = 0;
_ignored = ignored;
}
public int getRowsWritten() { return _rowsWritten; }
public void incrementRowsWritten() { _rowsWritten++; }
public int getRowsSkipped() { return _rowsSkipped; }
public void incrementRowsSkipped() { _rowsSkipped++; }
public List getIgnoredAuthors() { return _ignored; }
}
}

View File

@ -0,0 +1,209 @@
package net.i2p.syndie.web;
import java.io.*;
import java.util.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.*;
import net.i2p.data.*;
import net.i2p.syndie.*;
import net.i2p.syndie.data.*;
import net.i2p.syndie.sml.*;
/**
* Render the requested profile
*
*/
public class ProfileServlet extends BaseServlet {
protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index,
int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
Hash author = null;
String str = req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR);
if (str != null) {
try {
author = new Hash();
author.fromBase64(str);
} catch (DataFormatException dfe) {
author = null;
}
} else {
author = user.getBlog();
}
String uri = req.getRequestURI();
if (author == null) {
renderInvalidProfile(out);
} else if ( (user.getBlog() != null) && (user.getBlog().equals(author)) ) {
renderMyProfile(user, uri, out, archive);
} else {
renderProfile(user, uri, out, author, archive);
}
}
private void renderInvalidProfile(PrintWriter out) throws IOException {
out.write(INVALID_PROFILE);
}
private void renderMyProfile(User user, String baseURI, PrintWriter out, Archive archive) throws IOException {
BlogInfo info = archive.getBlogInfo(user.getBlog());
if (info == null)
return;
out.write("<!-- " + info.toString() + "-->\n");
out.write("<form action=\"" + baseURI + "\" method=\"POST\">\n");
// now add the form to update
out.write("<tr><td colspan=\"3\">Your profile</td></tr>\n");
out.write("<tr><td colspan=\"3\">Name: <input type=\"text\" name=\""
+ ThreadedHTMLRenderer.PARAM_PROFILE_NAME + "\" value=\""
+ HTMLRenderer.sanitizeTagParam(info.getProperty(BlogInfo.NAME)) + "\"></td></tr>\n");
out.write("<tr><td colspan=\"3\">Account description: <input type=\"text\" name=\""
+ ThreadedHTMLRenderer.PARAM_PROFILE_DESC + "\" value=\""
+ HTMLRenderer.sanitizeTagParam(info.getProperty(BlogInfo.DESCRIPTION)) + "\"></td></tr>\n");
out.write("<tr><td colspan=\"3\">Contact information: <input type=\"text\" name=\""
+ ThreadedHTMLRenderer.PARAM_PROFILE_URL + "\" value=\""
+ HTMLRenderer.sanitizeTagParam(info.getProperty(BlogInfo.CONTACT_URL)) + "\"></td></tr>\n");
out.write("<tr><td colspan=\"3\">Other attributes:<br /><textarea rows=\"3\" name=\""
+ ThreadedHTMLRenderer.PARAM_PROFILE_OTHER + "\" cols=\"60\">");
String props[] = info.getProperties();
if (props != null) {
for (int i = 0; i < props.length; i++) {
if (!BlogInfo.NAME.equals(props[i]) &&
!BlogInfo.DESCRIPTION.equals(props[i]) &&
!BlogInfo.EDITION.equals(props[i]) &&
!BlogInfo.OWNER_KEY.equals(props[i]) &&
!BlogInfo.POSTERS.equals(props[i]) &&
!BlogInfo.SIGNATURE.equals(props[i]) &&
!BlogInfo.CONTACT_URL.equals(props[i])) {
out.write(HTMLRenderer.sanitizeString(props[i], false) + ": "
+ HTMLRenderer.sanitizeString(info.getProperty(props[i]), false) + "\n");
}
}
}
out.write("</textarea></td></tr>\n");
out.write("<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Update profile\" /></td></tr>\n");
out.write("</form>\n");
}
private void renderProfile(User user, String baseURI, PrintWriter out, Hash author, Archive archive) throws IOException {
out.write("<tr><td colspan=\"3\">Profile for <a href=\"" + getControlTarget() + "?"
+ ThreadedHTMLRenderer.PARAM_AUTHOR + '=' + author.toBase64()
+ "\" title=\"View threads by the profiled author\">");
PetName pn = user.getPetNameDB().getByLocation(author.toBase64());
BlogInfo info = archive.getBlogInfo(author);
if (pn != null) {
out.write(pn.getName());
String name = null;
if (info != null)
name = info.getProperty(BlogInfo.NAME);
if ( (name == null) || (name.trim().length() <= 0) )
name = author.toBase64().substring(0, 6);
out.write(" (" + name + ")");
} else {
String name = null;
if (info != null)
name = info.getProperty(BlogInfo.NAME);
if ( (name == null) || (name.trim().length() <= 0) )
name = author.toBase64().substring(0, 6);
out.write(name);
}
out.write("</a>");
if (info != null)
out.write(" [edition " + info.getEdition() + "]");
out.write("</td></tr>\n");
out.write("<tr><td colspan=\"3\"><hr /></td></tr>\n");
if (pn == null) {
out.write("<tr><td colspan=\"3\">Not currently bookmarked. Add them to your ");
String addFav = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_FAVORITE,
baseURI, "", "", "", "", "", author.toBase64());
String addIgnore = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_IGNORE,
baseURI, "", "", "", "", "", author.toBase64());
out.write("<a href=\"" + addFav + "\" title=\"Threads by favorite authors are shown specially\">favorites</a> or ");
out.write("<a href=\"" + addIgnore + "\" title=\"Threads by ignored authors are hidden from view\">ignored</a> ");
out.write("</td></tr>\n");
} else if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE)) {
out.write("<tr><td colspan=\"3\">Currently ignored - threads they create are hidden.</td></tr>\n");
String remIgnore = getRemoveFromGroupLink(user, pn.getName(), FilteredThreadIndex.GROUP_IGNORE,
baseURI, "", "", "", "", "", author.toBase64());
out.write("<tr><td></td><td colspan=\"2\"><a href=\"" + remIgnore + "\">Unignore " + pn.getName() + "</a></td></tr>\n");
String remCompletely = getRemoveFromGroupLink(user, pn.getName(), "",
baseURI, "", "", "", "", "", author.toBase64());
out.write("<tr><td></td><td colspan=\"2\"><a href=\"" + remCompletely + "\">Forget about " + pn.getName() + " entirely</a></td></tr>\n");
} else if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) {
out.write("<tr><td colspan=\"3\">Currently marked as a favorite author - threads they participate in " +
"are highlighted.</td></tr>\n");
String remIgnore = getRemoveFromGroupLink(user, pn.getName(), FilteredThreadIndex.GROUP_FAVORITE,
baseURI, "", "", "", "", "", author.toBase64());
out.write("<tr><td></td><td colspan=\"2\"><a href=\"" + remIgnore + "\">Remove " + pn.getName() + " from the list of favorite authors</a></td></tr>\n");
String addIgnore = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_IGNORE,
baseURI, "", "", "", "", "", author.toBase64());
out.write("<tr><td></td><td colspan=\"2\"><a href=\"" + addIgnore + "\" title=\"Threads by ignored authors are hidden from view\">Ignore the author</a></td></tr>");
String remCompletely = getRemoveFromGroupLink(user, pn.getName(), "",
baseURI, "", "", "", "", "", author.toBase64());
out.write("<tr><td></td><td colspan=\"2\"><a href=\"" + remCompletely + "\">Forget about " + pn.getName() + " entirely</a></td></tr>\n");
} else {
out.write("<tr><td colspan=\"3\">Currently bookmarked. Add them to your ");
String addFav = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_FAVORITE,
baseURI, "", "", "", "", "", author.toBase64());
String addIgnore = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_IGNORE,
baseURI, "", "", "", "", "", author.toBase64());
out.write("<a href=\"" + addFav + "\" title=\"Threads by favorite authors are shown specially\">favorites</a> or ");
out.write("<a href=\"" + addIgnore + "\" title=\"Threads by ignored authors are hidden from view\">ignored</a> list</td></tr>");
String remCompletely = getRemoveFromGroupLink(user, pn.getName(), "",
baseURI, "", "", "", "", "", author.toBase64());
out.write("<tr><td></td><td colspan=\"2\"><a href=\"" + remCompletely + "\">Forget about " + pn.getName() + " entirely</a></td></tr>\n");
}
if (info != null) {
String descr = info.getProperty(BlogInfo.DESCRIPTION);
if ( (descr != null) && (descr.trim().length() > 0) )
out.write("<tr><td colspan=\"3\">Account description: " + HTMLRenderer.sanitizeString(descr) + "</td></tr>\n");
String contactURL = info.getProperty(BlogInfo.CONTACT_URL);
if ( (contactURL != null) && (contactURL.trim().length() > 0) )
out.write("<tr><td colspan=\"3\">Contact information: "
+ HTMLRenderer.sanitizeString(contactURL) + "</td></tr>\n");
String props[] = info.getProperties();
int altCount = 0;
if (props != null)
for (int i = 0; i < props.length; i++)
if (!BlogInfo.NAME.equals(props[i]) &&
!BlogInfo.DESCRIPTION.equals(props[i]) &&
!BlogInfo.EDITION.equals(props[i]) &&
!BlogInfo.OWNER_KEY.equals(props[i]) &&
!BlogInfo.POSTERS.equals(props[i]) &&
!BlogInfo.SIGNATURE.equals(props[i]) &&
!BlogInfo.CONTACT_URL.equals(props[i]))
altCount++;
if (altCount > 0) {
for (int i = 0; i < props.length; i++) {
if (!BlogInfo.NAME.equals(props[i]) &&
!BlogInfo.DESCRIPTION.equals(props[i]) &&
!BlogInfo.EDITION.equals(props[i]) &&
!BlogInfo.OWNER_KEY.equals(props[i]) &&
!BlogInfo.POSTERS.equals(props[i]) &&
!BlogInfo.SIGNATURE.equals(props[i]) &&
!BlogInfo.CONTACT_URL.equals(props[i])) {
out.write("<tr><td colspan=\"3\">");
out.write(HTMLRenderer.sanitizeString(props[i]) + ": "
+ HTMLRenderer.sanitizeString(info.getProperty(props[i])));
out.write("</td></tr>\n");
}
}
}
}
}
private static final String INVALID_PROFILE = "<tr><td colspan=\"3\">The profile requested is invalid</td></tr>\n";
}

View File

@ -6,7 +6,6 @@ import java.util.*;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.client.naming.*; import net.i2p.client.naming.*;
@ -16,225 +15,20 @@ import net.i2p.syndie.data.*;
import net.i2p.syndie.sml.*; import net.i2p.syndie.sml.*;
/** /**
* Render the appropriate posts and the thread tree
* *
*/ */
public class ViewThreadedServlet extends HttpServlet { public class ViewThreadedServlet extends BaseServlet {
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index,
req.setCharacterEncoding("UTF-8"); int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html");
User user = (User)req.getSession().getAttribute("user");
String login = req.getParameter("login");
String pass = req.getParameter("password");
String action = req.getParameter("action");
boolean forceNewIndex = false;
if (req.getParameter("regenerateIndex") != null)
forceNewIndex = true;
if (user == null) {
if ("Login".equals(action)) {
user = BlogManager.instance().login(login, pass); // ignore failures - user will just be unauthorized
if (!user.getAuthenticated())
user.invalidate();
} else {
user = new User();
}
forceNewIndex = true;
} else if ("Login".equals(action)) {
user = BlogManager.instance().login(login, pass); // ignore failures - user will just be unauthorized
forceNewIndex = true;
} else if ("Logout".equals(action)) {
user = new User();
forceNewIndex = true;
}
req.getSession().setAttribute("user", user);
if (user.getAuthenticated()) {
String loc = req.getParameter(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_LOCATION);
String group = req.getParameter(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_NAME);
if ( (loc != null) && (group != null) && (group.trim().length() > 0) ) {
try {
Hash key = new Hash();
key.fromBase64(loc);
PetNameDB db = user.getPetNameDB();
PetName pn = db.getByLocation(loc);
boolean isNew = false;
if (pn == null) {
isNew = true;
BlogInfo info = BlogManager.instance().getArchive().getBlogInfo(key);
String name = null;
if (info != null)
name = info.getProperty(BlogInfo.NAME);
else
name = loc.substring(0,6);
if (db.containsName(name)) {
int i = 0;
while (db.containsName(name + i))
i++;
name = name + i;
}
pn = new PetName(name, "syndie", "syndieblog", loc);
}
pn.addGroup(group);
if (isNew)
db.add(pn);
BlogManager.instance().saveUser(user);
// if we are ignoring someone, we need to recalculate the filters
if (FilteredThreadIndex.GROUP_IGNORE.equals(group))
forceNewIndex = true;
} catch (DataFormatException dfe) {
// bad loc, ignore
}
}
}
FilteredThreadIndex index = (FilteredThreadIndex)req.getSession().getAttribute("threadIndex");
Collection tags = getFilteredTags(req);
Collection filteredAuthors = getFilteredAuthors(req);
if (forceNewIndex || (index == null) || (!index.getFilteredTags().equals(tags)) || (!index.getFilteredAuthors().equals(filteredAuthors))) {
index = new FilteredThreadIndex(user, BlogManager.instance().getArchive(), getFilteredTags(req), filteredAuthors);
req.getSession().setAttribute("threadIndex", index);
}
render(user, req, resp.getWriter(), index);
}
private void render(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws ServletException, IOException {
Archive archive = BlogManager.instance().getArchive();
int numThreads = 10;
int threadOffset = getOffset(req);
if (threadOffset == -1) {
threadOffset = index.getRootCount() - numThreads;
}
if (threadOffset < 0) {
threadOffset = 0;
}
BlogURI visibleEntry = getVisible(req);
int offset = 0;
if ( empty(req, ThreadedHTMLRenderer.PARAM_OFFSET) && (visibleEntry != null) ) {
// we're on a permalink, so jump the tree to the given thread
threadOffset = index.getRoot(visibleEntry);
if (threadOffset < 0)
threadOffset = 0;
}
renderBegin(user, req, out, index);
renderNavBar(user, req, out, index);
renderControlBar(user, req, out, index);
renderBody(user, req, out, index); renderBody(user, req, out, index);
renderThreadNav(user, req, out, threadOffset, index); renderThreadNav(user, req, out, threadOffset, index);
renderThreadTree(user, req, out, threadOffset, visibleEntry, archive, index); renderThreadTree(user, req, out, threadOffset, visibleEntry, archive, index);
renderThreadNav(user, req, out, threadOffset, index); renderThreadNav(user, req, out, threadOffset, index);
renderEnd(user, req, out, index);
} }
private void renderBegin(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException { private void renderBody(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
out.write(BEGIN_HTML);
}
private void renderNavBar(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
//out.write("<tr class=\"topNav\"><td class=\"topNav_user\" colspan=\"2\" nowrap=\"true\">\n");
out.write("<tr class=\"topNav\"><td colspan=\"3\" nowrap=\"true\"><span class=\"topNav_user\">\n");
out.write("<!-- nav bar begin -->\n");
if (user.getAuthenticated()) {
out.write("Logged in as <a href=\"" + getProfileLink(req, user.getBlog()) + "\" title=\"Edit your profile\">");
out.write(user.getUsername());
out.write("</a>\n");
out.write("(<a href=\"switchuser.jsp\" title=\"Log in as another user\">switch</a>)\n");
out.write("<a href=\"post.jsp\" title=\"Post a new thread\">Post a new thread</a>\n");
} else {
out.write("<form action=\"" + req.getRequestURI() + "\" method=\"GET\">\n");
out.write("Login: <input type=\"text\" name=\"login\" />\n");
out.write("Password: <input type=\"password\" name=\"password\" />\n");
out.write("<input type=\"submit\" name=\"action\" value=\"Login\" /></form>\n");
}
//out.write("</td><td class=\"topNav_admin\">\n");
out.write("</span><span class=\"topNav_admin\">\n");
if (user.getAuthenticated() && user.getAllowAccessRemote()) {
out.write("<a href=\"syndicate.jsp\" title=\"Syndicate data between other Syndie nodes\">Syndicate</a>\n");
out.write("<a href=\"importfeed.jsp\" title=\"Import RSS/Atom data\">Import RSS/Atom</a>\n");
out.write("<a href=\"admin.jsp\" title=\"Configure this Syndie node\">Admin</a>\n");
}
out.write("</span><!-- nav bar end -->\n</td></tr>\n");
}
private static final ArrayList SKIP_TAGS = new ArrayList();
static {
SKIP_TAGS.add("action");
SKIP_TAGS.add("filter");
// post and visible are skipped since we aren't good at filtering by tag when the offset will
// skip around randomly. at least, not yet.
SKIP_TAGS.add("visible");
//SKIP_TAGS.add("post");
//SKIP_TAGS.add("thread");
SKIP_TAGS.add("offset"); // if we are adjusting the filter, ignore the previous offset
SKIP_TAGS.add("login");
SKIP_TAGS.add("password");
}
private void renderControlBar(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
out.write("<form action=\"");
out.write(req.getRequestURI());
out.write("\" method=\"GET\">\n");
String tags = "";
String author = "";
Enumeration params = req.getParameterNames();
while (params.hasMoreElements()) {
String param = (String)params.nextElement();
String val = req.getParameter(param);
if (ThreadedHTMLRenderer.PARAM_TAGS.equals(param)) {
tags = val;
} else if (ThreadedHTMLRenderer.PARAM_AUTHOR.equals(param)) {
author = val;
} else if (SKIP_TAGS.contains(param)) {
// skip
} else if (param.length() <= 0) {
// skip
} else {
out.write("<input type=\"hidden\" name=\"" + param + "\" value=\"" + val + "\" />\n");
}
}
out.write("<tr class=\"controlBar\"><td colspan=\"2\">\n");
out.write("<!-- control bar begin -->\n");
out.write("Filter: <select name=\"" + ThreadedHTMLRenderer.PARAM_AUTHOR + "\">\n");
PetNameDB db = user.getPetNameDB();
TreeSet names = new TreeSet(db.getNames());
out.write("<option value=\"\">Any authors</option>\n");
if (author.equals(user.getBlog().toBase64()))
out.write("<option value=\"" + user.getBlog().toBase64() + "\" selected=\"true\">Threads you posted in</option>\n");
else
out.write("<option value=\"" + user.getBlog().toBase64() + "\">Threads you posted in</option>\n");
for (Iterator iter = names.iterator(); iter.hasNext(); ) {
String name = (String) iter.next();
PetName pn = db.getByName(name);
if ("syndieblog".equals(pn.getProtocol())) {
if (author.equals(pn.getLocation()))
out.write("<option value=\"" + pn.getLocation() + "\" selected=\"true\">Threads " + name + " posted in</option>\n");
else
out.write("<option value=\"" + pn.getLocation() + "\">Threads " + name + " posted in</option>\n");
}
}
out.write("</select>\n");
out.write("Tags: <input type=\"text\" name=\"" + ThreadedHTMLRenderer.PARAM_TAGS + "\" size=\"10\" value=\"" + tags + "\" />\n");
out.write("<input type=\"submit\" name=\"action\" value=\"Go\" />\n");
out.write("</td><td class=\"controlBarRight\"><a href=\"#threads\" title=\"Jump to the thread navigation\">Threads</a></td>\n");
out.write("<!-- control bar end -->\n");
out.write("</tr>\n");
out.write("</form>\n");
}
private void renderBody(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException, ServletException {
ThreadedHTMLRenderer renderer = new ThreadedHTMLRenderer(I2PAppContext.getGlobalContext()); ThreadedHTMLRenderer renderer = new ThreadedHTMLRenderer(I2PAppContext.getGlobalContext());
Archive archive = BlogManager.instance().getArchive(); Archive archive = BlogManager.instance().getArchive();
List posts = getPosts(archive, req, index); List posts = getPosts(archive, req, index);
@ -324,73 +118,6 @@ public class ViewThreadedServlet extends HttpServlet {
renderThreadTree(user, out, index, archive, req, threadOffset, numThreads, visibleEntry); renderThreadTree(user, out, index, archive, req, threadOffset, numThreads, visibleEntry);
} }
private static final int getOffset(HttpServletRequest req) {
String off = req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET);
try {
return Integer.parseInt(off);
} catch (NumberFormatException nfe) {
return 0;
}
}
private static final BlogURI getVisible(HttpServletRequest req) {
return getAsBlogURI(req.getParameter(ThreadedHTMLRenderer.PARAM_VISIBLE));
}
private static final BlogURI getAsBlogURI(String uri) {
if (uri != null) {
int split = uri.indexOf('/');
if ( (split <= 0) || (split + 1 >= uri.length()) )
return null;
String blog = uri.substring(0, split);
String id = uri.substring(split+1);
try {
Hash hash = new Hash();
hash.fromBase64(blog);
long msgId = Long.parseLong(id);
if (msgId > 0)
return new BlogURI(hash, msgId);
} catch (DataFormatException dfe) {
return null;
} catch (NumberFormatException nfe) {
return null;
}
}
return null;
}
private Collection getFilteredTags(HttpServletRequest req) {
String tags = req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS);
if (tags != null) {
StringTokenizer tok = new StringTokenizer(tags, "\n\t ");
ArrayList rv = new ArrayList();
while (tok.hasMoreTokens()) {
String tag = tok.nextToken().trim();
if (tag.length() > 0)
rv.add(tag);
}
return rv;
} else {
return Collections.EMPTY_LIST;
}
}
private Collection getFilteredAuthors(HttpServletRequest req) {
String authors = req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR);
if (authors != null) {
StringTokenizer tok = new StringTokenizer(authors, "\n\t ");
ArrayList rv = new ArrayList();
while (tok.hasMoreTokens()) {
try {
Hash h = new Hash();
h.fromBase64(tok.nextToken().trim());
rv.add(h);
} catch (DataFormatException dfe) {}
}
return rv;
} else {
return Collections.EMPTY_LIST;
}
}
private void renderThreadTree(User user, PrintWriter out, ThreadIndex index, Archive archive, HttpServletRequest req, private void renderThreadTree(User user, PrintWriter out, ThreadIndex index, Archive archive, HttpServletRequest req,
int threadOffset, int numThreads, BlogURI visibleEntry) { int threadOffset, int numThreads, BlogURI visibleEntry) {
@ -421,12 +148,10 @@ public class ViewThreadedServlet extends HttpServlet {
out.write("<!-- threads end -->\n"); out.write("<!-- threads end -->\n");
} }
/**
* @return true if some post in the thread has been written
*/
private boolean renderThread(User user, PrintWriter out, ThreadIndex index, Archive archive, HttpServletRequest req, private boolean renderThread(User user, PrintWriter out, ThreadIndex index, Archive archive, HttpServletRequest req,
ThreadNode node, int depth, BlogURI visibleEntry, TreeRenderState state) { ThreadNode node, int depth, BlogURI visibleEntry, TreeRenderState state) {
boolean isFavorite = false; boolean isFavorite = false;
boolean ignored = false;
HTMLRenderer rend = new HTMLRenderer(I2PAppContext.getGlobalContext()); HTMLRenderer rend = new HTMLRenderer(I2PAppContext.getGlobalContext());
SMLParser parser = new SMLParser(I2PAppContext.getGlobalContext()); SMLParser parser = new SMLParser(I2PAppContext.getGlobalContext());
@ -436,6 +161,8 @@ public class ViewThreadedServlet extends HttpServlet {
if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) { if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) {
isFavorite = true; isFavorite = true;
} }
if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
ignored = true;
} }
state.incrementRowsWritten(); state.incrementRowsWritten();
@ -498,10 +225,12 @@ public class ViewThreadedServlet extends HttpServlet {
} }
out.write("</a>\n"); out.write("</a>\n");
if (node.getEntry().getKeyHash().equals(user.getBlog())) { if ( (user.getBlog() != null) && (node.getEntry().getKeyHash().equals(user.getBlog())) ) {
out.write("<img src=\"images/self.png\" alt=\"You wrote this\" border=\"0\" />\n"); out.write("<img src=\"images/self.png\" alt=\"You wrote this\" border=\"0\" />\n");
} else if (isFavorite) { } else if (isFavorite) {
out.write("<img src=\"images/favorites.png\" alt=\"favorites\" border=\"0\" />\n"); out.write("<img src=\"images/favorites.png\" alt=\"favorites\" border=\"0\" />\n");
} else if (ignored) {
out.write("<img src=\"images/addToIgnored.png\" alt=\"ignored\" border=\"0\" />\n");
} else { } else {
if (user.getAuthenticated()) { if (user.getAuthenticated()) {
// give them a link to bookmark or ignore the peer // give them a link to bookmark or ignore the peer
@ -547,14 +276,8 @@ public class ViewThreadedServlet extends HttpServlet {
return rendered; return rendered;
} }
private String trim(String orig, int maxLen) {
if ( (orig == null) || (orig.length() <= maxLen) )
return orig;
return orig.substring(0, maxLen) + "...";
}
private String getFlagHTML(User user, ThreadNode node) { private String getFlagHTML(User user, ThreadNode node) {
if (node.containsAuthor(user.getBlog())) if ( (user.getBlog() != null) && (node.containsAuthor(user.getBlog())) )
return "<img src=\"images/self.png\" border=\"0\" alt=\"You have posted in the thread\" />"; return "<img src=\"images/self.png\" border=\"0\" alt=\"You have posted in the thread\" />";
// grab all of the peers in the user's favorites group and check to see if // grab all of the peers in the user's favorites group and check to see if
@ -579,282 +302,4 @@ public class ViewThreadedServlet extends HttpServlet {
return "&nbsp;"; return "&nbsp;";
} }
private static final boolean empty(HttpServletRequest req, String param) {
String val = req.getParameter(param);
return (val == null) || (val.trim().length() <= 0);
}
private static final boolean empty(String val) {
return (val == null) || (val.trim().length() <= 0);
}
private String getExpandLink(HttpServletRequest req, ThreadNode node) {
return getExpandLink(node, req.getRequestURI(), req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
private static String getExpandLink(ThreadNode node, String uri, String viewPost, String viewThread,
String offset, String tags, String author) {
StringBuffer buf = new StringBuffer(64);
buf.append(uri);
buf.append('?');
// expand node == let one of node's children be visible
if (node.getChildCount() > 0) {
ThreadNode child = node.getChild(0);
buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
buf.append(child.getEntry().getKeyHash().toBase64()).append('/');
buf.append(child.getEntry().getEntryId()).append('&');
}
if (!empty(viewPost))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
else if (!empty(viewThread))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
if (!empty(offset))
buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
if (!empty(tags))
buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
if (!empty(author))
buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append('&');
return buf.toString();
}
private String getCollapseLink(HttpServletRequest req, ThreadNode node) {
return getCollapseLink(node, req.getRequestURI(),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
private String getCollapseLink(ThreadNode node, String uri, String viewPost, String viewThread,
String offset, String tags, String author) {
StringBuffer buf = new StringBuffer(64);
buf.append(uri);
// collapse node == let the node be visible
buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
buf.append(node.getEntry().getEntryId()).append('&');
if (!empty(viewPost))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
else if (!empty(viewThread))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
if (!empty(offset))
buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
if (!empty(tags))
buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
if (!empty(author))
buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append('&');
return buf.toString();
}
private String getProfileLink(HttpServletRequest req, Hash author) {
return getProfileLink(author);
}
private static String getProfileLink(Hash author) { return HTMLRenderer.getMetadataURL(author); }
private String getAddToGroupLink(HttpServletRequest req, Hash author, User user, String group) {
return getAddToGroupLink(user, author, group, req.getRequestURI(),
req.getParameter(ThreadedHTMLRenderer.PARAM_VISIBLE),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
private String getAddToGroupLink(User user, Hash author, String group, String uri, String visible,
String viewPost, String viewThread, String offset, String tags, String filteredAuthor) {
StringBuffer buf = new StringBuffer(64);
buf.append(uri);
buf.append('?');
if (!empty(visible))
buf.append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=').append(visible).append('&');
buf.append(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_LOCATION).append('=').append(author.toBase64()).append('&');
buf.append(ThreadedHTMLRenderer.PARAM_ADD_TO_GROUP_NAME).append('=').append(group).append('&');
if (!empty(viewPost))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_POST).append('=').append(viewPost).append('&');
else if (!empty(viewThread))
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
if (!empty(offset))
buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
if (!empty(tags))
buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
if (!empty(filteredAuthor))
buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(filteredAuthor).append('&');
return buf.toString();
}
private String getViewPostLink(HttpServletRequest req, ThreadNode node, User user, boolean isPermalink) {
return ThreadedHTMLRenderer.getViewPostLink(req.getRequestURI(), node, user, isPermalink,
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
private String getViewThreadLink(HttpServletRequest req, ThreadNode node, User user) {
return getViewThreadLink(req.getRequestURI(), node, user,
req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR));
}
private static String getViewThreadLink(String uri, ThreadNode node, User user, String offset,
String tags, String author) {
StringBuffer buf = new StringBuffer(64);
buf.append(uri);
if (node.getChildCount() > 0) {
buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
ThreadNode child = node.getChild(0);
buf.append(child.getEntry().getKeyHash().toBase64()).append('/');
buf.append(child.getEntry().getEntryId()).append('&');
} else {
buf.append('?').append(ThreadedHTMLRenderer.PARAM_VISIBLE).append('=');
buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
buf.append(node.getEntry().getEntryId()).append('&');
}
buf.append(ThreadedHTMLRenderer.PARAM_VIEW_THREAD).append('=');
buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
buf.append(node.getEntry().getEntryId()).append('&');
if (!empty(offset))
buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(offset).append('&');
if (!empty(tags))
buf.append(ThreadedHTMLRenderer.PARAM_TAGS).append('=').append(tags).append('&');
if (!empty(author))
buf.append(ThreadedHTMLRenderer.PARAM_AUTHOR).append('=').append(author).append('&');
buf.append("#").append(node.getEntry().toString());
return buf.toString();
}
private String getFilterByTagLink(HttpServletRequest req, ThreadNode node, User user, String tag, String author) {
return ThreadedHTMLRenderer.getFilterByTagLink(req.getRequestURI(), node, user, tag, author);
}
private String getNavLink(HttpServletRequest req, int offset) {
return ThreadedHTMLRenderer.getNavLink(req.getRequestURI(),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_POST),
req.getParameter(ThreadedHTMLRenderer.PARAM_VIEW_THREAD),
req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS),
req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR),
offset);
}
private void renderEnd(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException {
out.write(END_HTML);
}
private static final String BEGIN_HTML = "<html>\n" +
"<head>\n" +
"<title>Syndie</title>\n" +
"<style>\n" +
".overallTable {\n" +
" border-spacing: 0px;\n" +
" border-width: 0px;\n" +
" border: 0px;\n" +
" margin: 0px;\n" +
" padding: 0px;\n" +
"}\n" +
".topNav {\n" +
" background-color: #BBBBBB;\n" +
"}\n" +
".topNav_user {\n" +
" text-align: left;\n" +
" float: left;\n" +
" align: left;\n" +
" display: inline;\n" +
"}\n" +
".topNav_admin {\n" +
" text-align: right;\n" +
" float: right;\n" +
" align: right;\n" +
" display: inline;\n" +
"}\n" +
".controlBar {\n" +
" background-color: #BBBBBB;\n" +
"}\n" +
".controlBarRight {\n" +
" text-align: right;\n" +
"}\n" +
".threadEven {\n" +
" background-color: #FFFFFF;\n" +
" white-space: nowrap;\n" +
"}\n" +
".threadOdd {\n" +
" background-color: #EEEEEE;\n" +
" white-space: nowrap;\n" +
"}\n" +
".threadLeft {\n" +
" text-align: left;\n" +
" align: left;\n" +
"}\n" +
".threadRight {\n" +
" text-align: right;\n" +
"}\n" +
".threadNav {\n" +
" background-color: #BBBBBB;\n" +
"}\n" +
".threadNavRight {\n" +
" text-align: right;\n" +
"}\n" +
".postMeta {\n" +
" background-color: #BBBBFF;\n" +
"}\n" +
".postMetaSubject {\n" +
" text-align: left;\n" +
"}\n" +
".postMetaLink {\n" +
" text-align: right;\n" +
"}\n" +
".postDetails {\n" +
" background-color: #DDDDFF;\n" +
"}\n" +
".postReply {\n" +
" background-color: #BBBBFF;\n" +
"}\n" +
".postReplyText {\n" +
" background-color: #BBBBFF;\n" +
"}\n" +
".postReplyOptions {\n" +
" background-color: #BBBBFF;\n" +
"}\n" +
"</style>\n" +
"<link href=\"style.jsp\" rel=\"stylesheet\" type=\"text/css\" >\n" +
"<link href=\"rss.jsp\" rel=\"alternate\" type=\"application/rss+xml\" >\n" +
"</head>\n" +
"<body>\n" +
"<span style=\"display: none\"><a href=\"#bodySubject\">Jump to the beginning of the first post rendered, if any</a>\n" +
"<a href=\"#threads\">Jump to the thread navigation</a>\n</span>\n" +
"<table border=\"0\" width=\"100%\" class=\"overallTable\">\n";
private static final String END_HTML = "</table>\n" +
"</body>\n";
private static class TreeRenderState {
private int _rowsWritten;
private int _rowsSkipped;
private List _ignored;
public TreeRenderState(List ignored) {
_rowsWritten = 0;
_rowsSkipped = 0;
_ignored = ignored;
}
public int getRowsWritten() { return _rowsWritten; }
public void incrementRowsWritten() { _rowsWritten++; }
public int getRowsSkipped() { return _rowsSkipped; }
public void incrementRowsSkipped() { _rowsSkipped++; }
public List getIgnoredAuthors() { return _ignored; }
}
} }

View File

@ -19,6 +19,11 @@
<servlet-class>net.i2p.syndie.web.ViewThreadedServlet</servlet-class> <servlet-class>net.i2p.syndie.web.ViewThreadedServlet</servlet-class>
</servlet> </servlet>
<servlet>
<servlet-name>net.i2p.syndie.web.ProfileServlet</servlet-name>
<servlet-class>net.i2p.syndie.web.ProfileServlet</servlet-class>
</servlet>
<servlet> <servlet>
<servlet-name>net.i2p.syndie.UpdaterServlet</servlet-name> <servlet-name>net.i2p.syndie.UpdaterServlet</servlet-name>
<servlet-class>net.i2p.syndie.UpdaterServlet</servlet-class> <servlet-class>net.i2p.syndie.UpdaterServlet</servlet-class>
@ -45,6 +50,10 @@
<servlet-name>net.i2p.syndie.web.ViewThreadedServlet</servlet-name> <servlet-name>net.i2p.syndie.web.ViewThreadedServlet</servlet-name>
<url-pattern>/threads.jsp</url-pattern> <url-pattern>/threads.jsp</url-pattern>
</servlet-mapping> </servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.syndie.web.ProfileServlet</servlet-name>
<url-pattern>/profile.jsp</url-pattern>
</servlet-mapping>
<session-config> <session-config>
<session-timeout> <session-timeout>