diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java
new file mode 100644
index 000000000..68426eb29
--- /dev/null
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java
@@ -0,0 +1,427 @@
+package net.i2p.syndie.sml;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import net.i2p.I2PAppContext;
+import net.i2p.client.naming.PetName;
+import net.i2p.data.*;
+import net.i2p.syndie.*;
+import net.i2p.syndie.data.*;
+import net.i2p.syndie.web.*;
+import net.i2p.util.Log;
+
+/**
+ *
+ */
+public class ThreadedHTMLRenderer extends HTMLRenderer {
+ private Log _log;
+ private String _baseURI;
+
+ public ThreadedHTMLRenderer(I2PAppContext ctx) {
+ super(ctx);
+ _log = ctx.logManager().getLog(ThreadedHTMLRenderer.class);
+ }
+
+ /** what, if any, post should be rendered */
+ public static final String PARAM_VIEW_POST = "post";
+ /** what, if any, thread should be rendered in its entirety */
+ public static final String PARAM_VIEW_THREAD = "thread";
+ /** what post should be visible in the nav tree */
+ 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";
+ /** index into the nav tree to start displaying */
+ public static final String PARAM_OFFSET = "offset";
+ public static final String PARAM_TAGS = "tags";
+
+ public static String getFilterByTagLink(String uri, ThreadNode node, User user, String tag) {
+ StringBuffer buf = new StringBuffer(64);
+ buf.append(uri).append('?');
+ if (node != null) {
+ buf.append(PARAM_VIEW_POST).append('=');
+ buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
+ buf.append(node.getEntry().getEntryId()).append('&');
+ }
+
+ if ( (tag != null) && (tag.trim().length() > 0) )
+ buf.append(PARAM_TAGS).append('=').append(tag);
+ return buf.toString();
+ }
+
+ public static String getNavLink(String uri, String viewPost, String viewThread, String tags, int offset) {
+ StringBuffer buf = new StringBuffer(64);
+ buf.append(uri);
+ buf.append('?');
+ if (!empty(viewPost))
+ buf.append(PARAM_VIEW_POST).append('=').append(viewPost).append('&');
+ else if (!empty(viewThread))
+ buf.append(PARAM_VIEW_THREAD).append('=').append(viewThread).append('&');
+
+ if (!empty(tags))
+ buf.append(PARAM_TAGS).append('=').append(tags).append('&');
+
+ buf.append(PARAM_OFFSET).append('=').append(offset).append('&');
+
+ return buf.toString();
+ }
+
+ public static String getViewPostLink(String uri, ThreadNode node, User user, boolean isPermalink,
+ String offset, String tags) {
+ StringBuffer buf = new StringBuffer(64);
+ buf.append(uri);
+ if (node.getChildCount() > 0) {
+ buf.append('?').append(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(PARAM_VISIBLE).append('=');
+ buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
+ buf.append(node.getEntry().getEntryId()).append('&');
+ }
+ buf.append(PARAM_VIEW_POST).append('=');
+ buf.append(node.getEntry().getKeyHash().toBase64()).append('/');
+ buf.append(node.getEntry().getEntryId()).append('&');
+
+ if ( (!isPermalink) && (!empty(offset)) )
+ buf.append(PARAM_OFFSET).append('=').append(offset).append('&');
+
+ if ( (!isPermalink) && (!empty(tags)) )
+ buf.append(PARAM_TAGS).append('=').append(tags).append('&');
+
+ return buf.toString();
+ }
+
+
+ private static final boolean empty(String val) { return (val == null) || (val.trim().length() <= 0); }
+
+ public void render(User user, Writer out, Archive archive, BlogURI post,
+ boolean inlineReply, ThreadIndex index, String baseURI,
+ String offset, String requestTags) throws IOException {
+ EntryContainer entry = archive.getEntry(post);
+ if (entry == null) return;
+ _entry = entry;
+
+ _baseURI = baseURI;
+ _user = user;
+ _out = out;
+ _archive = archive;
+ _cutBody = false;
+ _showImages = true;
+ _headers = new HashMap();
+ _bodyBuffer = new StringBuffer(1024);
+ _postBodyBuffer = new StringBuffer(1024);
+ _addresses = new ArrayList();
+ _links = new ArrayList();
+ _blogs = new ArrayList();
+ _archives = new ArrayList();
+
+ _parser.parse(entry.getEntry().getText(), this);
+
+ out.write("\n");
+ out.write("\n");
+ out.write("
\n");
+
+ String subject = (String)_headers.get(HTMLRenderer.HEADER_SUBJECT);
+ if (subject == null)
+ subject = "";
+ out.write(" | ");
+ out.write(subject);
+ out.write(" |
\n");
+ out.write("| \n");
+ out.write("");
+
+ String author = null;
+ PetName pn = user.getPetNameDB().getByLocation(post.getKeyHash().toBase64());
+ if (pn == null) {
+ BlogInfo info = archive.getBlogInfo(post.getKeyHash());
+ if (info != null)
+ author = info.getProperty(BlogInfo.NAME);
+ } else {
+ author = pn.getName();
+ }
+ if ( (author == null) || (author.trim().length() <= 0) )
+ author = post.getKeyHash().toBase64().substring(0,6);
+
+ ThreadNode node = index.getNode(post);
+
+ out.write(author);
+ out.write(" @ ");
+ out.write(getEntryDate(post.getEntryId()));
+
+ Collection tags = node.getTags();
+ if ( (tags != null) && (tags.size() > 0) ) {
+ out.write("\nTags: \n");
+ for (Iterator tagIter = tags.iterator(); tagIter.hasNext(); ) {
+ String tag = (String)tagIter.next();
+ out.write("");
+ out.write(" " + tag);
+ out.write("\n");
+ }
+ }
+
+ out.write("\npermalink\n");
+
+ out.write(" | \n
\n");
+ out.write("\n");
+ out.write("\n");
+ out.write("\n");
+ out.write("| \n");
+ out.write(_bodyBuffer.toString());
+ out.write(" | \n
\n");
+ out.write("\n");
+ out.write("\n");
+ out.write(_postBodyBuffer.toString());
+/*
+"\n" +
+" \n" +
+"
\n" +
+ */
+ out.write("\n");
+ if (inlineReply && user.getAuthenticated() ) {
+ String refuseReplies = (String)_headers.get(HTMLRenderer.HEADER_REFUSE_REPLIES);
+ // show the reply form if we are the author or replies have not been explicitly rejected
+ if ( (user.getBlog().equals(post.getKeyHash())) ||
+ (refuseReplies == null) || (!Boolean.valueOf(refuseReplies).booleanValue()) ) {
+ out.write("\n");
+ out.write("\n");
+ out.write("\n");
+ }
+ }
+ out.write("\n");
+ }
+
+ public void receiveEnd() {
+ _postBodyBuffer.append("\n");
+ _postBodyBuffer.append(" \n");
+ _postBodyBuffer.append("
\n");
+ }
+
+ public void receiveHeaderEnd() {
+ //_preBodyBuffer.append("\n");
+ //renderSubjectCell();
+ //renderMetaCell();
+ //renderPreBodyCell();
+ }
+
+ protected String getEntryURL() { return getEntryURL(_user != null ? _user.getShowImages() : false); }
+ protected String getEntryURL(boolean showImages) {
+ if (_entry == null)
+ return _baseURI;
+ else
+ return _baseURI + '?' + PARAM_VIEW_POST + '=' +
+ Base64.encode(_entry.getURI().getKeyHash().getData()) + '/'
+ + _entry.getURI().getEntryId() + '&';
+ }
+
+ public String getPageURL(User user, String selector, int numPerPage, int pageNum) { return _baseURI; }
+
+ public String getPageURL(Hash blog, String tag, long entryId, String group, int numPerPage, int pageNum, boolean expandEntries, boolean showImages) {
+ StringBuffer buf = new StringBuffer(128);
+ buf.append(_baseURI).append('?');
+ if ( (blog != null) && (entryId > 0) ) {
+ buf.append(PARAM_VIEW_POST).append('=').append(Base64.encode(blog.getData())).append('/').append(entryId).append('&');
+ buf.append(PARAM_VISIBLE).append('=').append(Base64.encode(blog.getData())).append('/').append(entryId).append('&');
+ }
+ if (tag != null)
+ buf.append(PARAM_TAGS).append('=').append(sanitizeTagParam(tag)).append('&');
+ return buf.toString();
+ }
+ public String getArchiveURL(Hash blog, SafeURL archiveLocation) {
+ return "remote.jsp?"
+ //+ "action=Continue..." // should this be the case?
+ + "&schema=" + sanitizeTagParam(archiveLocation.getSchema())
+ + "&location=" + sanitizeTagParam(archiveLocation.getLocation());
+ }
+ public String getBookmarkURL(String name, String location, String schema, String protocol) {
+ return "addresses.jsp?name=" + sanitizeTagParam(name)
+ + "&network=" + sanitizeTagParam(schema)
+ + "&protocol=" + sanitizeTagParam(protocol)
+ + "&location=" + sanitizeTagParam(location);
+
+ }
+}