From 19bba048f2d2c87362713a7d33d478809b583845 Mon Sep 17 00:00:00 2001
From: jrandom <jrandom>
Date: Mon, 5 Dec 2005 06:14:15 +0000
Subject: [PATCH] 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.

---
 .../src/net/i2p/syndie/sml/HTMLRenderer.java  |   7 +
 .../src/net/i2p/syndie/web/BaseServlet.java   |   3 +
 .../net/i2p/syndie/web/ThreadNavServlet.java  | 144 ++++++++++++++++++
 apps/syndie/jsp/web.xml                       |   9 ++
 history.txt                                   |   8 +-
 5 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 apps/syndie/java/src/net/i2p/syndie/web/ThreadNavServlet.java

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("&", "&amp;");
+        orig = orig.replaceAll("<", "&lt;");
+        orig = orig.replaceAll(">", "&gt;");
+        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("<?xml version=\"1.0\" ?>\n");
+        if (FORMAT_XML.equals(format)) {
+            out.write("<threadTree>");
+        } else {
+            out.write("<rdf:rdf xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" " +
+                      "         xmlns:syndie=\"http://syndie.i2p.net/syndie.ns\">\n");
+        }
+    }
+    private void renderEnd(PrintWriter out, String format) throws IOException {
+        if (FORMAT_XML.equals(format)) {
+            out.write("</threadTree>");
+        } else {
+            out.write("</rdf:rdf>\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("<rdf:Description rdf:about=\"entry://" + blog + "/" + node.getEntry().getEntryId() + "\">");
+        PetName pn = user.getPetNameDB().getByLocation(blog);
+        String name = null;
+        if (pn != null) {
+            if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE))
+                out.write("<syndie:favoriteauthor />\n");
+            if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE))
+                out.write("<syndie:ignoredauthor />\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("<syndie:author syndie:blog=\"" + blog + "\" syndie:name=\"" + HTMLRenderer.sanitizeStrippedXML(name) + "\" />\n");
+        if ( (user.getBlog() != null) && (node.containsAuthor(user.getBlog())) )
+            out.write("<syndie:threadself />\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("<syndie:subject>" + HTMLRenderer.sanitizeStrippedXML(subject) + "</syndie: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("<syndie:age>" + daysAgo + "</syndie:age>\n");
+        
+        out.write("<syndie:children>");
+        for (int i = 0; i < node.getChildCount(); i++)
+            render(user, node.getChild(i), out);
+        out.write("</syndie:children>\n");
+        
+        out.write("</rdf:Description>\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 @@
      <servlet-class>net.i2p.syndie.web.ExternalLinkServlet</servlet-class>
     </servlet>
      
+    <servlet>
+     <servlet-name>net.i2p.syndie.web.ThreadNavServlet</servlet-name>
+     <servlet-class>net.i2p.syndie.web.ThreadNavServlet</servlet-class>
+    </servlet>
+     
     <servlet>
 	 <servlet-name>net.i2p.syndie.UpdaterServlet</servlet-name>
 	 <servlet-class>net.i2p.syndie.UpdaterServlet</servlet-class>
@@ -117,6 +122,10 @@
       <servlet-name>net.i2p.syndie.web.ExternalLinkServlet</servlet-name>
       <url-pattern>/externallink.jsp</url-pattern>
     </servlet-mapping>
+    <servlet-mapping> 
+      <servlet-name>net.i2p.syndie.web.ThreadNavServlet</servlet-name>
+      <url-pattern>/threadnav/*</url-pattern>
+    </servlet-mapping>
     
     <session-config>
         <session-timeout>
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!)
-- 
GitLab