diff --git a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java b/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java index 0d6d858c7145dada26a6a3b9bd2586681d69d930..6ad973cd06068f6a51df0416820ec36e66713c8e 100644 --- a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java +++ b/apps/syndie/java/src/net/i2p/syndie/data/BlogInfo.java @@ -55,6 +55,7 @@ public class BlogInfo { public static final String SIGNATURE = "Signature"; public static final String NAME = "Name"; public static final String DESCRIPTION = "Description"; + public static final String CONTACT_URL = "ContactURL"; public static final String EDITION = "Edition"; public void load(InputStream in) throws IOException { diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java index 3496690efed389f6c509e95865780b7516591222..847947b5e071b6fd8a0ab8967779124a331d3d93 100644 --- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java +++ b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java @@ -400,8 +400,12 @@ public class HTMLRenderer extends EventReceiverImpl { if (location != null) { _bodyBuffer.append(" at "); SafeURL surl = new SafeURL(locationSchema + "://" + location); - _bodyBuffer.append("<a ").append(getClass("archiveSummaryLink")).append(" href=\"").append(getArchiveURL(null, surl)); - _bodyBuffer.append("\">").append(sanitizeString(surl.toString())).append("</a>"); + if (BlogManager.instance().authorizeRemote(_user)) { + _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()) { _bodyBuffer.append(" <a ").append(getClass("archiveBookmarkLink")).append(" href=\""); _bodyBuffer.append(getBookmarkURL(sanitizeString(name), surl.getLocation(), surl.getSchema(), "syndiearchive")); @@ -1001,7 +1005,7 @@ public class HTMLRenderer extends EventReceiverImpl { if (_entry == null) return "unknown"; return getMetadataURL(_entry.getURI().getKeyHash()); } - public static String getMetadataURL(Hash blog) { + public String getMetadataURL(Hash blog) { return "viewmetadata.jsp?" + ArchiveViewerBean.PARAM_BLOG + "=" + Base64.encode(blog.getData()); } diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java index a225008a3ffd460aa7d62cac177cc96861062d3d..77edf48f90126b2fa77e5a8ef9a3617561a65acb 100644 --- a/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java +++ b/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java @@ -31,10 +31,19 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { 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_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 */ public static final String PARAM_OFFSET = "offset"; public static final String PARAM_TAGS = "tags"; 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) { StringBuffer buf = new StringBuffer(64); @@ -399,6 +408,13 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { //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(boolean showImages) { if (_entry == null) diff --git a/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..29bde6acdbd4e880c2dccc570f85f5b3dcc1847e --- /dev/null +++ b/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java @@ -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; } + } +} diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..7ddbfc9b3987b53356f100347e4b12cd1c9cfbc3 --- /dev/null +++ b/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java @@ -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"; +} diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java index 45bd08c5f00d22df3792c8a757797133e44016bf..da805d42f38ea31f4b08c9f422544b5c34fc5af8 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java @@ -6,7 +6,6 @@ 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.*; @@ -16,225 +15,20 @@ import net.i2p.syndie.data.*; import net.i2p.syndie.sml.*; /** + * Render the appropriate posts and the thread tree * */ -public class ViewThreadedServlet 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.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); +public class ViewThreadedServlet extends BaseServlet { + protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, + int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException { renderBody(user, req, out, index); renderThreadNav(user, req, out, threadOffset, index); renderThreadTree(user, req, out, threadOffset, visibleEntry, archive, 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 { - 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 { + private void renderBody(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index) throws IOException { ThreadedHTMLRenderer renderer = new ThreadedHTMLRenderer(I2PAppContext.getGlobalContext()); Archive archive = BlogManager.instance().getArchive(); List posts = getPosts(archive, req, index); @@ -323,74 +117,7 @@ public class ViewThreadedServlet extends HttpServlet { int numThreads = 10; 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, int threadOffset, int numThreads, BlogURI visibleEntry) { @@ -421,12 +148,10 @@ public class ViewThreadedServlet extends HttpServlet { 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, ThreadNode node, int depth, BlogURI visibleEntry, TreeRenderState state) { boolean isFavorite = false; + boolean ignored = false; HTMLRenderer rend = new HTMLRenderer(I2PAppContext.getGlobalContext()); SMLParser parser = new SMLParser(I2PAppContext.getGlobalContext()); @@ -436,6 +161,8 @@ public class ViewThreadedServlet extends HttpServlet { if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) { isFavorite = true; } + if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE)) + ignored = true; } state.incrementRowsWritten(); @@ -498,10 +225,12 @@ public class ViewThreadedServlet extends HttpServlet { } 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"); } else if (isFavorite) { 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 { if (user.getAuthenticated()) { // give them a link to bookmark or ignore the peer @@ -547,14 +276,8 @@ public class ViewThreadedServlet extends HttpServlet { 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) { - 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\" />"; // 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 " "; } - 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; } - } } diff --git a/apps/syndie/jsp/web.xml b/apps/syndie/jsp/web.xml index acdb2e131bc24d0e4a6944319c17309004758301..52c3a854d3689457da1885e7c1abbbb26c663f64 100644 --- a/apps/syndie/jsp/web.xml +++ b/apps/syndie/jsp/web.xml @@ -18,6 +18,11 @@ <servlet-name>net.i2p.syndie.web.ViewThreadedServlet</servlet-name> <servlet-class>net.i2p.syndie.web.ViewThreadedServlet</servlet-class> </servlet> + + <servlet> + <servlet-name>net.i2p.syndie.web.ProfileServlet</servlet-name> + <servlet-class>net.i2p.syndie.web.ProfileServlet</servlet-class> + </servlet> <servlet> <servlet-name>net.i2p.syndie.UpdaterServlet</servlet-name> @@ -45,6 +50,10 @@ <servlet-name>net.i2p.syndie.web.ViewThreadedServlet</servlet-name> <url-pattern>/threads.jsp</url-pattern> </servlet-mapping> + <servlet-mapping> + <servlet-name>net.i2p.syndie.web.ProfileServlet</servlet-name> + <url-pattern>/profile.jsp</url-pattern> + </servlet-mapping> <session-config> <session-timeout>