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 dba3f28332..55a012ed81 100644
--- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
+++ b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java
@@ -981,6 +981,13 @@ public class HTMLRenderer extends EventReceiverImpl {
else
return orig.toString();
}
+ public static final String sanitizeStrippedXML(String orig) {
+ if (orig == null) return "";
+ orig = orig.replaceAll("&", "&");
+ orig = orig.replaceAll("<", "<");
+ orig = orig.replaceAll(">", ">");
+ return orig;
+ }
private static final String STYLE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
public static String sanitizeStyle(String style) {
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java
index c63a0deca6..c432d08f65 100644
--- a/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java
+++ b/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java
@@ -163,6 +163,9 @@ public abstract class BaseServlet extends HttpServlet {
_log.info("New filtered index created (forced? " + forceNewIndex + ", tagsChanged? " + tagsChanged + ", authorsChanged? " + authorsChanged + ")");
}
+ render(user, req, resp, index);
+ }
+ protected void render(User user, HttpServletRequest req, HttpServletResponse resp, ThreadIndex index) throws IOException, ServletException {
render(user, req, resp.getWriter(), index);
}
diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ThreadNavServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ThreadNavServlet.java
new file mode 100644
index 0000000000..e750ceee95
--- /dev/null
+++ b/apps/syndie/java/src/net/i2p/syndie/web/ThreadNavServlet.java
@@ -0,0 +1,144 @@
+package net.i2p.syndie.web;
+
+import java.io.*;
+import java.util.*;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+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.*;
+
+/**
+ * Export the thread nav as either RDF or XML
+ *
+ */
+public class ThreadNavServlet extends BaseServlet {
+ public static final String PARAM_COUNT = "count";
+ public static final String PARAM_OFFSET = "offset";
+ public static final String PARAM_FORMAT = "format";
+
+ public static final String FORMAT_RDF = "rdf";
+ public static final String FORMAT_XML = "xml";
+
+ protected void render(User user, HttpServletRequest req, HttpServletResponse resp, ThreadIndex index) throws ServletException, IOException {
+ int threadCount = empty(req, PARAM_COUNT) ? index.getRootCount() : getInt(req, PARAM_COUNT);
+ int offset = getInt(req, PARAM_OFFSET);
+ String uri = req.getRequestURI();
+ if (uri.endsWith(FORMAT_XML)) {
+ resp.setContentType("text/xml; charset=UTF-8");
+ render(user, index, resp.getWriter(), threadCount, offset, FORMAT_XML);
+ } else {
+ resp.setContentType("application/rdf+xml; charset=UTF-8");
+ render(user, index, resp.getWriter(), threadCount, offset, FORMAT_RDF);
+ }
+ }
+
+ private int getInt(HttpServletRequest req, String param) {
+ String val = req.getParameter(param);
+ if (val != null) {
+ try {
+ return Integer.parseInt(val);
+ } catch (NumberFormatException nfe) {
+ // ignore
+ }
+ }
+ return -1;
+ }
+
+ private static final int DEFAULT_THREADCOUNT = 10;
+ private static final int DEFAULT_THREADOFFSET = 0;
+
+ private void render(User user, ThreadIndex index, PrintWriter out, int threadCount, int offset, String format) throws IOException {
+ int startRoot = DEFAULT_THREADOFFSET;
+ if (offset >= 0)
+ startRoot = offset;
+ renderStart(out, format);
+
+ int endRoot = startRoot + (threadCount > 0 ? threadCount : DEFAULT_THREADCOUNT);
+ if (endRoot >= index.getRootCount())
+ endRoot = index.getRootCount() - 1;
+ for (int i = startRoot; i <= endRoot; i++) {
+ ThreadNode node = index.getRoot(i);
+ if (FORMAT_XML.equals(format))
+ out.write(node.toString());
+ else
+ render(user, node, out);
+ }
+ renderEnd(out, format);
+ }
+ private void renderStart(PrintWriter out, String format) throws IOException {
+ out.write("\n");
+ if (FORMAT_XML.equals(format)) {
+ out.write("");
+ } else {
+ out.write("\n");
+ }
+ }
+ private void renderEnd(PrintWriter out, String format) throws IOException {
+ if (FORMAT_XML.equals(format)) {
+ out.write("");
+ } else {
+ out.write("\n");
+ }
+ }
+ private void render(User user, ThreadNode node, PrintWriter out) throws IOException {
+ Archive archive = BlogManager.instance().getArchive();
+ String blog = node.getEntry().getKeyHash().toBase64();
+ out.write("");
+ PetName pn = user.getPetNameDB().getByLocation(blog);
+ String name = null;
+ if (pn != null) {
+ if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE))
+ out.write("\n");
+ if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
+ out.write("\n");
+ name = pn.getName();
+ } else {
+ BlogInfo info = archive.getBlogInfo(node.getEntry().getKeyHash());
+ if (info != null)
+ name = info.getProperty(BlogInfo.NAME);
+ if ( (name == null) || (name.trim().length() <= 0) )
+ name = node.getEntry().getKeyHash().toBase64().substring(0,6);
+ }
+ out.write("\n");
+ if ( (user.getBlog() != null) && (node.containsAuthor(user.getBlog())) )
+ out.write("\n");
+
+ EntryContainer entry = archive.getEntry(node.getEntry());
+ if (entry == null) throw new RuntimeException("Unable to fetch the entry " + node.getEntry());
+
+ SMLParser parser = new SMLParser(I2PAppContext.getGlobalContext());
+ HeaderReceiver rec = new HeaderReceiver();
+ parser.parse(entry.getEntry().getText(), rec);
+ String subject = rec.getHeader(HTMLRenderer.HEADER_SUBJECT);
+ if ( (subject == null) || (subject.trim().length() <= 0) )
+ subject = "(no subject)";
+
+ out.write("" + HTMLRenderer.sanitizeStrippedXML(subject) + "\n");
+
+ long dayBegin = BlogManager.instance().getDayBegin();
+ long postId = node.getEntry().getEntryId();
+ int daysAgo = (int)((dayBegin - postId + 24*60*60*1000-1)/(24*60*60*1000));
+ out.write("" + daysAgo + "\n");
+
+ out.write("");
+ for (int i = 0; i < node.getChildCount(); i++)
+ render(user, node.getChild(i), out);
+ out.write("\n");
+
+ out.write("\n");
+ }
+
+ protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index,
+ int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException {
+ throw new UnsupportedOperationException("Not relvent...");
+ }
+}
diff --git a/apps/syndie/jsp/web.xml b/apps/syndie/jsp/web.xml
index 83bba5f1a0..d4d7ea68e9 100644
--- a/apps/syndie/jsp/web.xml
+++ b/apps/syndie/jsp/web.xml
@@ -59,6 +59,11 @@
net.i2p.syndie.web.ExternalLinkServlet
+
+ net.i2p.syndie.web.ThreadNavServlet
+ net.i2p.syndie.web.ThreadNavServlet
+
+
net.i2p.syndie.UpdaterServlet
net.i2p.syndie.UpdaterServlet
@@ -117,6 +122,10 @@
net.i2p.syndie.web.ExternalLinkServlet
/externallink.jsp
+
+ net.i2p.syndie.web.ThreadNavServlet
+ /threadnav/*
+
diff --git a/history.txt b/history.txt
index 5c8e79f424..ea6f31ffe0 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,10 @@
-$Id: history.txt,v 1.346 2005/12/04 15:02:25 jrandom Exp $
+$Id: history.txt,v 1.347 2005/12/04 15:12:19 jrandom Exp $
+
+2005-12-05 jrandom
+ * Added an RDF and XML thread export to Syndie, reachable at
+ .../threadnav/rdf or .../threadnav/xml, accepting the parameters
+ count=$numThreads and offset=$threadIndex. If the $numThreads is -1, it
+ displays all threads.
2005-12-04 TLorD
* Patch for the C SAM library to null terminate strings on copy (thanks!)