diff --git a/apps/syndie/java/build.xml b/apps/syndie/java/build.xml index 45fc5b6aaf..fcf14a2418 100644 --- a/apps/syndie/java/build.xml +++ b/apps/syndie/java/build.xml @@ -9,6 +9,7 @@ + + + @@ -157,6 +160,7 @@ + diff --git a/apps/syndie/java/src/net/i2p/syndie/sml/BlogPostInfoRenderer.java b/apps/syndie/java/src/net/i2p/syndie/sml/BlogPostInfoRenderer.java new file mode 100644 index 0000000000..c47aabb49e --- /dev/null +++ b/apps/syndie/java/src/net/i2p/syndie/sml/BlogPostInfoRenderer.java @@ -0,0 +1,480 @@ +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.client.naming.PetNameDB; +import net.i2p.data.*; +import net.i2p.syndie.*; +import net.i2p.syndie.data.*; +import net.i2p.syndie.web.*; +import net.i2p.util.Log; + +/** + * render the metadata of a post for display in the left nav of the blog view + * (showing the attachments, etc). Most of this is just duplicated from the HTMLRenderer + * + */ +public class BlogPostInfoRenderer extends EventReceiverImpl { + private Log _log; + protected SMLParser _parser; + protected Writer _out; + protected User _user; + protected Archive _archive; + protected EntryContainer _entry; + protected int _lastNewlineAt; + protected Map _headers; + protected List _addresses; + protected List _links; + protected List _blogs; + protected List _archives; + protected StringBuffer _bodyBuffer; + + public BlogPostInfoRenderer(I2PAppContext ctx) { + super(ctx); + _log = ctx.logManager().getLog(getClass()); + _parser = new SMLParser(ctx); + } + + public void render(User user, Archive archive, EntryContainer entry, Writer out) throws IOException { + if (entry == null) + return; + render(user, archive, entry, entry.getEntry().getText(), out); + } + public void render(User user, Archive archive, EntryContainer entry, String rawSML, Writer out) throws IOException { + prepare(user, archive, entry, rawSML, out); + _out.write(_bodyBuffer.toString()); + } + protected void prepare(User user, Archive archive, EntryContainer entry, String rawSML, Writer out) throws IOException { + _user = user; + _archive = archive; + _entry = entry; + _out = out; + _headers = new HashMap(); + _bodyBuffer = new StringBuffer(1024); + _addresses = new ArrayList(); + _links = new ArrayList(); + _blogs = new ArrayList(); + _archives = new ArrayList(); + _parser.parse(rawSML, this); + } + + public void receiveEnd() { + BlogURI postURI = null; + Attachment attachments[] = null; + if (_entry != null) { + attachments = _entry.getAttachments(); + postURI = _entry.getURI(); + } + renderAttachments(postURI, attachments); + renderBlogs(postURI); + renderLinks(postURI); + renderAddresses(postURI); + renderArchives(postURI); + } + + private void renderAttachments(BlogURI postURI, Attachment attachments[]) { + if ( (attachments != null) && (attachments.length > 0) ) { + _bodyBuffer.append("
\n"); + _bodyBuffer.append("Attachments\n
    "); + + // for each attachment: + //
  1. $name\n($size of type $type)
  2. + for (int i = 0; i < attachments.length; i++) { + _bodyBuffer.append("
  3. "); + String name = attachments[i].getName(); + if ( (name == null) && (name.trim().length() <= 0) ) + name = "Attachment " + i; + + if (postURI != null) { + _bodyBuffer.append(""); + } + _bodyBuffer.append(HTMLRenderer.sanitizeString(name, 40)); + if (postURI != null) + _bodyBuffer.append(""); + + _bodyBuffer.append("\n("); + int bytes = attachments[i].getDataLength(); + if (bytes > 10*1024*1024) + _bodyBuffer.append(bytes/(1024*1024)).append("MBytes"); + else if (bytes > 10*1024) + _bodyBuffer.append(bytes/(10*1024)).append("KBytes"); + else + _bodyBuffer.append(bytes).append("Bytes"); + + String type = attachments[i].getMimeType(); + if (type != null) { + if ("application/octet-stream".equals(type)) { + _bodyBuffer.append(", binary"); + } else { + int split = type.lastIndexOf('/'); + if (split > 0) + _bodyBuffer.append(", ").append(HTMLRenderer.sanitizeString(type.substring(split+1), 30)); + else + _bodyBuffer.append(", ").append(HTMLRenderer.sanitizeString(type, 30)); + } + } + + _bodyBuffer.append(")"); + + String desc = attachments[i].getDescription(); + if ( (desc != null) && (desc.trim().length() > 0) ) + _bodyBuffer.append("
    \n").append(HTMLRenderer.sanitizeString(desc, 120)); + + _bodyBuffer.append("
  4. \n"); + } + _bodyBuffer.append("
\n"); + _bodyBuffer.append("
\n"); + } + } + + private void renderBlogs(BlogURI postURI) { + if ( (_blogs != null) && (_blogs.size() > 0) ) { + _bodyBuffer.append("
\n"); + _bodyBuffer.append("Blogs\n
    "); + + // for each blog ref: + //
  1. $name\n ? :) :(
  2. + for (int i = 0; i < _blogs.size(); i++) { + _bodyBuffer.append("
  3. "); + Blog blog = (Blog)_blogs.get(i); + PetNameDB db = _user.getPetNameDB(); + PetName pn = db.getByLocation(blog.hash); + + if ( (blog.entryId > 0) && (blog.hash != null) ) { + // view a specific post in their blog (jumping to their blog, rather than keeping the + // current blog's formatting... is that the right thing to do?) + _bodyBuffer.append(""); + if (pn != null) + _bodyBuffer.append(HTMLRenderer.sanitizeString(pn.getName())); + else + _bodyBuffer.append(HTMLRenderer.sanitizeString(blog.name)); + _bodyBuffer.append(" on ").append(getEntryDate(blog.entryId)); + _bodyBuffer.append(""); + } else if (blog.hash != null) { + // view their full blog + _bodyBuffer.append(""); + + if (pn != null) { + // we already have a petname for this user + _bodyBuffer.append(pn.getName()).append(""); + /* "); + _bodyBuffer.append("?"); + */ + } else { + // this name is already in the addressbook with another location, + // generate a new nym + while ( (pn = db.getByName(blog.name)) != null) + blog.name = blog.name + "."; + _bodyBuffer.append(HTMLRenderer.sanitizeString(blog.name)).append(""); + /* "); + _bodyBuffer.append("?"); + */ + // should probably add on some inline-bookmarking support, but we'd need requestURL for that + } + } + _bodyBuffer.append("
  4. \n"); + } + _bodyBuffer.append("
\n"); + } + } + + private final SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy/MM/dd", Locale.UK); + private final String getEntryDate(long when) { + synchronized (_dateFormat) { + try { + String str = _dateFormat.format(new Date(when)); + long dayBegin = _dateFormat.parse(str).getTime(); + return str + " [" + (when - dayBegin) + "]"; + } catch (ParseException pe) { + // wtf + return "unknown"; + } + } + } + + private void renderLinks(BlogURI postURI) { + if ( (_links != null) && (_links.size() > 0) ) { + _bodyBuffer.append("
\n"); + _bodyBuffer.append("Links\n
    "); + + // for each link: + //
  1. $location
  2. + for (int i = 0; i < _links.size(); i++) { + _bodyBuffer.append("
  3. "); + + Link l = (Link)_links.get(i); + String schema = l.schema; + _bodyBuffer.append("").append(HTMLRenderer.sanitizeString(l.location, 40)).append(" ("); + _bodyBuffer.append(HTMLRenderer.sanitizeString(l.schema, 10)).append(")"); + + _bodyBuffer.append("
  4. \n"); + } + + _bodyBuffer.append("
\n"); + } + } + + private void renderAddresses(BlogURI postURI) { + if ( (_addresses != null) && (_addresses.size() > 0) ) { + _bodyBuffer.append("
\n"); + _bodyBuffer.append("Addresses\n
    "); + + // for each address: + //
  1. $name
  2. + for (int i = 0; i < _addresses.size(); i++) { + _bodyBuffer.append("
  3. "); + Address a = (Address)_addresses.get(i); + importAddress(a); + PetName pn = null; + if (_user != null) + pn = _user.getPetNameDB().getByLocation(a.location); + if (pn != null) { + _bodyBuffer.append(HTMLRenderer.sanitizeString(pn.getName())); + } else { + _bodyBuffer.append("").append(HTMLRenderer.sanitizeString(a.name)).append(""); + } + _bodyBuffer.append("
  4. \n"); + } + + _bodyBuffer.append("
\n"); + } + } + + public void importAddress(Address a) { + if (_user != null && _user.getImportAddresses() && !_user.getPetNameDB().containsName(a.name)) { + PetName pn = new PetName(a.name, a.schema, a.protocol, a.location); + _user.getPetNameDB().add(pn); + try { + _user.getPetNameDB().store(_user.getAddressbookLocation()); + } catch (IOException ioe) { + //ignore + } + } + if (BlogManager.instance().getImportAddresses() + && I2PAppContext.getGlobalContext().namingService().lookup(a.name) == null + && a.schema.equalsIgnoreCase("i2p")) { + PetName pn = new PetName(a.name, a.schema, a.protocol, a.location); + I2PAppContext.getGlobalContext().petnameDb().add(pn); + try { + I2PAppContext.getGlobalContext().petnameDb().store(); + } catch (IOException ioe) { + //ignore + } + } + } + + private void renderArchives(BlogURI postURI) { + if ( (_archives != null) && (_archives.size() > 0) ) { + _bodyBuffer.append("
\n"); + _bodyBuffer.append("Archives\n
    "); + + // for each archive: + //
  1. $name :)
    $description
  2. + for (int i = 0; i < _archives.size(); i++) { + _bodyBuffer.append("
  3. "); + ArchiveRef a = (ArchiveRef)_archives.get(i); + boolean authRemote = BlogManager.instance().authorizeRemote(_user); + if (authRemote) { + _bodyBuffer.append(""); + } + + _bodyBuffer.append(HTMLRenderer.sanitizeString(a.name)); + + if (authRemote) { + _bodyBuffer.append(""); + } + + if ( (a.description != null) && (a.description.trim().length() > 0) ) + _bodyBuffer.append(" ").append(HTMLRenderer.sanitizeString(a.description, 64)); + + _bodyBuffer.append(" bookmark it"); + + _bodyBuffer.append("
  4. \n"); + } + + _bodyBuffer.append("
\n"); + } + } + + public void receiveHeader(String header, String value) { + //System.err.println("Receive header [" + header + "] = [" + value + "]"); + if (HTMLRenderer.HEADER_PETNAME.equals(header)) { + StringTokenizer tok = new StringTokenizer(value, "\t\n"); + if (tok.countTokens() != 4) + return; + String name = tok.nextToken(); + String net = tok.nextToken(); + String proto = tok.nextToken(); + String loc = tok.nextToken(); + Address a = new Address(); + a.name = HTMLRenderer.sanitizeString(name, false); + a.schema = HTMLRenderer.sanitizeString(net, false); + a.protocol = HTMLRenderer.sanitizeString(proto, false); + a.location = HTMLRenderer.sanitizeString(loc, false); + _addresses.add(a); + } + } + + public void receiveHeaderEnd() { } + + public void receivePlain(String text) { } + + public void receiveBold(String text) { } + public void receiveItalic(String text) { } + public void receiveUnderline(String text) { } + public void receiveHR() { } + public void receiveH1(String body) { } + public void receiveH2(String body) { } + public void receiveH3(String body) { } + public void receiveH4(String body) { } + public void receiveH5(String body) { } + public void receivePre(String body) { } + public void receiveQuote(String text, String whoQuoted, String quoteLocationSchema, String quoteLocation) { } + public void receiveCode(String text, String codeLocationSchema, String codeLocation) { } + public void receiveImage(String alternateText, int attachmentId) { } + public void receiveCut(String summaryText) { } + public void receiveNewline() { } + public void receiveLT() { } + public void receiveGT() { } + public void receiveBegin() {} + public void receiveLeftBracket() { } + public void receiveRightBracket() { } + + protected static class Blog { + public String name; + public String hash; + public String tag; + public long entryId; + public List locations; + public int hashCode() { return -1; } + public boolean equals(Object o) { + Blog b = (Blog)o; + return DataHelper.eq(hash, b.hash) && DataHelper.eq(tag, b.tag) && DataHelper.eq(name, b.name) + && DataHelper.eq(entryId, b.entryId) && DataHelper.eq(locations, b.locations); + } + } + /** + * when we see a link to a blog, we may want to: + * = view the blog entry + * = view all entries in that blog + * = view all entries in that blog with the given tag + * = view the blog's metadata + * = [fetch the blog from other locations] + * = [add the blog's locations to our list of known locations] + * = [shitlist the blog] + * = [add the blog to one of our groups] + * + * [blah] implies *later*. + */ + public void receiveBlog(String name, String hash, String tag, long entryId, List locations, String description) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Receiving the blog: " + name + "/" + hash + "/" + tag + "/" + entryId +"/" + locations + ": "+ description); + byte blogData[] = Base64.decode(hash); + if ( (blogData == null) || (blogData.length != Hash.HASH_LENGTH) ) + return; + + Blog b = new Blog(); + b.name = name; + b.hash = hash; + b.tag = tag; + b.entryId = entryId; + b.locations = locations; + if (!_blogs.contains(b)) + _blogs.add(b); + } + + protected static class ArchiveRef { + public String name; + public String description; + public String locationSchema; + public String location; + public int hashCode() { return -1; } + public boolean equals(Object o) { + ArchiveRef a = (ArchiveRef)o; + return DataHelper.eq(name, a.name) && DataHelper.eq(description, a.description) + && DataHelper.eq(locationSchema, a.locationSchema) + && DataHelper.eq(location, a.location); + } + } + public void receiveArchive(String name, String description, String locationSchema, String location, + String postingKey, String anchorText) { + ArchiveRef a = new ArchiveRef(); + a.name = name; + a.description = description; + a.locationSchema = locationSchema; + a.location = location; + if (!_archives.contains(a)) + _archives.add(a); + } + + protected static class Link { + public String schema; + public String location; + public int hashCode() { return -1; } + public boolean equals(Object o) { + Link l = (Link)o; + return DataHelper.eq(schema, l.schema) && DataHelper.eq(location, l.location); + } + } + public void receiveLink(String schema, String location, String text) { + Link l = new Link(); + l.schema = schema; + l.location = location; + if (!_links.contains(l)) + _links.add(l); + } + + protected static class Address { + public String name; + public String schema; + public String location; + public String protocol; + public int hashCode() { return -1; } + public boolean equals(Object o) { + Address a = (Address)o; + return DataHelper.eq(schema, a.schema) && DataHelper.eq(location, a.location) && DataHelper.eq(protocol, a.protocol) && DataHelper.eq(name, a.name); + } + } + public void receiveAddress(String name, String schema, String protocol, String location, String anchorText) { + Address a = new Address(); + a.name = name; + a.schema = schema; + a.location = location; + a.protocol = protocol; + if (!_addresses.contains(a)) + _addresses.add(a); + } + + public void receiveAttachment(int id, int thumb, String anchorText) { } +} 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 a7235fa13d..f308cbf918 100644 --- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java +++ b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java @@ -1096,7 +1096,7 @@ public class HTMLRenderer extends EventReceiverImpl { buf.append(ThreadedHTMLRenderer.PARAM_OFFSET).append('=').append(pageNum*numPerPage).append('&'); return buf.toString(); } - public String getArchiveURL(Hash blog, SafeURL archiveLocation) { + public static String getArchiveURL(Hash blog, SafeURL archiveLocation) { return "syndicate.jsp?" //+ "action=Continue..." // should this be the case? + "&" + SyndicateServlet.PARAM_SCHEMA + "=" + sanitizeTagParam(archiveLocation.getSchema()) diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogServlet.java index 1e0bc704f4..4e70dd86ac 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogServlet.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogServlet.java @@ -164,7 +164,7 @@ public class ViewBlogServlet extends BaseServlet { renderStyle(out, info, data, req); out.write(""); renderHeader(user, req, out, info, data, title, desc); - renderReferences(out, info, data); + renderReferences(user, out, info, data, req, posts); renderBody(user, out, info, data, posts, offset, archive, req); out.write("\n"); } @@ -209,12 +209,15 @@ public class ViewBlogServlet extends BaseServlet { String url = "blog.jsp?" + (info != null ? PARAM_BLOG + "=" + info.getKey().calculateHash().toBase64() : ""); out.write("" + HTMLRenderer.sanitizeString(name) + ""); + out.write("
profile threads"); } out.write("\n"); } public static final String DEFAULT_GROUP_NAME = "References"; - private void renderReferences(PrintWriter out, BlogInfo info, BlogInfoData data) throws IOException { + private void renderReferences(User user, PrintWriter out, BlogInfo info, BlogInfoData data, HttpServletRequest req, List posts) throws IOException { out.write("
\n"); if (data != null) { for (int i = 0; i < data.getReferenceGroupCount(); i++) { @@ -241,12 +244,31 @@ public class ViewBlogServlet extends BaseServlet { //out.write("Custom links\n"); //out.write("\n"); //out.write("
"); + + renderPostReferences(user, req, out, posts); + out.write("
"); out.write("Secured by Syndie"); out.write("
\n"); out.write("\n\n"); } + private void renderPostReferences(User user, HttpServletRequest req, PrintWriter out, List posts) throws IOException { + if (!empty(req, PARAM_ENTRY) && (posts.size() == 1)) { + BlogURI uri = (BlogURI)posts.get(0); + Archive archive = BlogManager.instance().getArchive(); + EntryContainer entry = archive.getEntry(uri); + if (entry != null) { + out.write("
\n"); + + BlogPostInfoRenderer renderer = new BlogPostInfoRenderer(_context); + renderer.render(user, archive, entry, out); + + out.write("
\n"); + } + } + } + /** generate a link for the given petname within the scope of the given blog */ public static String renderLink(Hash blogFrom, PetName pn) { StringBuffer buf = new StringBuffer(64); @@ -669,6 +691,33 @@ public class ViewBlogServlet extends BaseServlet { " font-size: 80%;\n" + " font-weight: bold;\n" + "}\n" + +".syndieBlogPostInfoGroup {\n" + +" text-align: left;\n" + +" font-size: 80%;\n" + +" background-color: #FFEA9F;\n" + +" border: solid;\n" + +" border-width: 1px 1px 1px 1px;\n" + +" border-color: #000;\n" + +" margin-top: 5px;\n" + +" margin-right: 5px;\n" + +"}\n" + +".syndieBlogPostInfoGroup ol {\n" + +" list-style: none;\n" + +" margin-left: 0;\n" + +" margin-top: 0;\n" + +" margin-bottom: 0;\n" + +" padding-left: 0;\n" + +"}\n" + +".syndieBlogPostInfoGroup li {\n" + +" margin: 0;\n" + +"}\n" + +".syndieBlogPostInfoGroup li a {\n" + +" display: block;\n" + +"}\n" + +".syndieBlogPostInfoGroupName {\n" + +" font-size: 80%;\n" + +" font-weight: bold;\n" + +"}\n" + ".syndieBlogMeta {\n" + " text-align: left;\n" + " font-size: 80%;\n" + diff --git a/history.txt b/history.txt index 2f212f9d97..bf1e12be5c 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,13 @@ -$Id: history.txt,v 1.380 2006/01/09 01:33:29 jrandom Exp $ +$Id: history.txt,v 1.381 2006/01/09 17:22:43 jrandom Exp $ + +2005-01-10 jrandom + * Added the per-post list of attachments/blogs/etc to the blog view in + Syndie (though this does not yet include comments or some further + refinements) + * Have the I2P shortcut launch i2p.exe instead of i2psvc.exe on windows, + removing the dox box (though also removes the restart functionality...) + * Give the i2p.exe the correct java.library.path to support the systray + dll (thanks Bobcat, Sugadude, anon!) 2005-01-09 jrandom * Removed a longstanding bug that had caused unnecessary router identity diff --git a/installer/i2pstandalone.xml b/installer/i2pstandalone.xml index c2b554f4e8..cc24b9b49f 100644 --- a/installer/i2pstandalone.xml +++ b/installer/i2pstandalone.xml @@ -12,7 +12,7 @@ 64 64 --> - -Djava.library.path=. -DloggerFilenameOverride=logs/log-router-@.txt -Dorg.mortbay.http.Version.paranoid=true -Dorg.mortbay.util.FileResource.checkAliases=false + -Djava.library.path=.;lib -DloggerFilenameOverride=logs/log-router-@.txt -Dorg.mortbay.http.Version.paranoid=true -Dorg.mortbay.util.FileResource.checkAliases=false resources/i2plogo.bmp diff --git a/installer/resources/shortcutSpec.xml b/installer/resources/shortcutSpec.xml index b17a7cfc07..3b1605d70d 100644 --- a/installer/resources/shortcutSpec.xml +++ b/installer/resources/shortcutSpec.xml @@ -2,8 +2,8 @@