From 14134694d7b08b46953d35466682e2ae846b1a78 Mon Sep 17 00:00:00 2001 From: jrandom Date: Sat, 12 Nov 2005 05:03:51 +0000 Subject: [PATCH] 2005-11-11 jrandom * Add filtering threads by author to Syndie, populated with authors in the user's addressbook * When creating the default user, add "http://syndiemedia.i2p/archive/archive.txt" to their addressbook, configured to automatically pull updates. (what other archives should be included?) * Tiny servlet to help dole out the new routerconsole themes, and bundle the installer/resources/themes/** into ./docs/themes/** on both install and update. --- apps/routerconsole/jsp/viewtheme.jsp | 14 ++ apps/routerconsole/jsp/web.xml | 7 + .../java/src/net/i2p/syndie/BlogManager.java | 27 ++++ .../i2p/syndie/data/FilteredThreadIndex.java | 48 ++++-- .../i2p/syndie/sml/ThreadedHTMLRenderer.java | 39 +++-- .../i2p/syndie/web/ViewThreadedServlet.java | 144 +++++++++++++----- build.xml | 10 ++ core/java/src/net/i2p/util/FileUtil.java | 33 ++++ history.txt | 51 ++++--- 9 files changed, 284 insertions(+), 89 deletions(-) create mode 100644 apps/routerconsole/jsp/viewtheme.jsp diff --git a/apps/routerconsole/jsp/viewtheme.jsp b/apps/routerconsole/jsp/viewtheme.jsp new file mode 100644 index 000000000..3c344be39 --- /dev/null +++ b/apps/routerconsole/jsp/viewtheme.jsp @@ -0,0 +1,14 @@ +<% +String uri = request.getRequestURI(); +if (uri.endsWith(".css")) { + response.setContentType("text/css"); +} else if (uri.endsWith(".png")) { + response.setContentType("image/png"); +} else if (uri.endsWith(".gif")) { + response.setContentType("image/gif"); +} else if (uri.endsWith(".jpg")) { + response.setContentType("image/jpeg"); +} + +net.i2p.util.FileUtil.readFile(uri, "./docs", response.getOutputStream()); +%> \ No newline at end of file diff --git a/apps/routerconsole/jsp/web.xml b/apps/routerconsole/jsp/web.xml index a814e9a87..7a8e431ce 100644 --- a/apps/routerconsole/jsp/web.xml +++ b/apps/routerconsole/jsp/web.xml @@ -5,6 +5,13 @@ + + + + net.i2p.router.web.jsp.viewtheme_jsp + /themes/* + + 30 diff --git a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java index bdab2cae6..63028668d 100644 --- a/apps/syndie/java/src/net/i2p/syndie/BlogManager.java +++ b/apps/syndie/java/src/net/i2p/syndie/BlogManager.java @@ -447,6 +447,15 @@ public class BlogManager { pass = DEFAULT_PASS; return pass; } + + /** + * If we are a single user instance, when we create the default user, give them + * addressbook entries for each of the following, *and* schedule them for syndication + * + */ + private static final String DEFAULT_SINGLE_USER_ARCHIVES[] = new String[] { + "http://syndiemedia.i2p/archive/archive.txt" + }; public User getDefaultUser() { User user = new User(_context); @@ -479,6 +488,10 @@ public class BlogManager { String ok = register(user, getDefaultLogin(), getDefaultPass(), "", "default", "Default Syndie blog", ""); if (User.LOGIN_OK.equals(ok)) { _log.info("Default user created: " + user); + for (int i = 0; i < DEFAULT_SINGLE_USER_ARCHIVES.length; i++) + user.getPetNameDB().add(new PetName("DefaultArchive" + i, "syndie", "syndiearchive", DEFAULT_SINGLE_USER_ARCHIVES[i])); + scheduleSyndication(DEFAULT_SINGLE_USER_ARCHIVES); + saveUser(user); return; } else { user.invalidate(); @@ -906,6 +919,20 @@ public class BlogManager { System.setProperty("syndie.updateArchives", buf.toString()); Updater.wakeup(); } + public void scheduleSyndication(String locations[]) { + String archives[] = getUpdateArchives(); + HashSet locs = new HashSet(); + for (int i = 0; (archives != null) && (i < archives.length); i++) + locs.add(archives[i]); + for (int i = 0; (locations != null) && (i < locations.length); i++) + locs.add(locations[i]); + + StringBuffer buf = new StringBuffer(64); + for (Iterator iter = locs.iterator(); iter.hasNext(); ) + buf.append(iter.next().toString().trim()).append(','); + System.setProperty("syndie.updateArchives", buf.toString()); + Updater.wakeup(); + } public void unscheduleSyndication(String location) { String archives[] = getUpdateArchives(); if ( (archives != null) && (archives.length > 0) ) { diff --git a/apps/syndie/java/src/net/i2p/syndie/data/FilteredThreadIndex.java b/apps/syndie/java/src/net/i2p/syndie/data/FilteredThreadIndex.java index c5da4a485..fb9dde971 100644 --- a/apps/syndie/java/src/net/i2p/syndie/data/FilteredThreadIndex.java +++ b/apps/syndie/java/src/net/i2p/syndie/data/FilteredThreadIndex.java @@ -15,11 +15,12 @@ public class FilteredThreadIndex extends ThreadIndex { private Collection _filteredTags; private List _roots; private List _ignoredAuthors; + private Collection _filteredAuthors; public static final String GROUP_FAVORITE = "Favorite"; public static final String GROUP_IGNORE = "Ignore"; - public FilteredThreadIndex(User user, Archive archive, Collection tags) { + public FilteredThreadIndex(User user, Archive archive, Collection tags, Collection authors) { super(); _user = user; _archive = archive; @@ -27,6 +28,9 @@ public class FilteredThreadIndex extends ThreadIndex { _filteredTags = tags; if (_filteredTags == null) _filteredTags = Collections.EMPTY_SET; + _filteredAuthors = authors; + if (_filteredAuthors == null) + _filteredAuthors = Collections.EMPTY_SET; _ignoredAuthors = new ArrayList(); for (Iterator iter = user.getPetNameDB().iterator(); iter.hasNext(); ) { @@ -49,27 +53,44 @@ public class FilteredThreadIndex extends ThreadIndex { _roots = new ArrayList(_baseIndex.getRootCount()); for (int i = 0; i < _baseIndex.getRootCount(); i++) { ThreadNode node = _baseIndex.getRoot(i); - if (!isIgnored(node, _ignoredAuthors, _filteredTags)) + if (!isIgnored(node, _ignoredAuthors, _filteredTags, _filteredAuthors)) _roots.add(node); } } - - private boolean isIgnored(ThreadNode node, List ignoredAuthors, Collection requestedTags) { - boolean allAuthorsIgnored = true; - for (Iterator iter = node.getRecursiveAuthorIterator(); iter.hasNext(); ) { - Hash author = (Hash)iter.next(); - if (!ignoredAuthors.contains(author)) { - allAuthorsIgnored = false; - break; + private boolean isIgnored(ThreadNode node, List ignoredAuthors, Collection requestedTags, Collection filteredAuthors) { + if (filteredAuthors.size() <= 0) { + boolean allAuthorsIgnored = true; + for (Iterator iter = node.getRecursiveAuthorIterator(); iter.hasNext(); ) { + Hash author = (Hash)iter.next(); + if (!ignoredAuthors.contains(author)) { + allAuthorsIgnored = false; + break; + } } + + if ( (allAuthorsIgnored) && (ignoredAuthors.size() > 0) ) + return true; + } else { + boolean filteredAuthorMatches = false; + for (Iterator iter = filteredAuthors.iterator(); iter.hasNext(); ) { + Hash author = (Hash)iter.next(); + if (node.containsAuthor(author)) { + filteredAuthorMatches = true; + break; + } + } + if (!filteredAuthorMatches) + return true; } - if ( (allAuthorsIgnored) && (ignoredAuthors.size() > 0) ) - return true; + // ok, author checking passed, so only ignore the thread if tags were specified and the + // thread doesn't contain that tag + if (requestedTags.size() > 0) { + Collection nodeTags = node.getRecursiveTags(); for (Iterator iter = requestedTags.iterator(); iter.hasNext(); ) - if (node.getRecursiveTags().contains(iter.next())) + if (nodeTags.contains(iter.next())) return false; // authors we aren't ignoring have posted in the thread, but the user is filtering // posts by tags, and this thread doesn't include any of those tags @@ -85,4 +106,5 @@ public class FilteredThreadIndex extends ThreadIndex { public ThreadNode getRoot(int index) { return (ThreadNode)_roots.get(index); } public ThreadNode getNode(BlogURI uri) { return _baseIndex.getNode(uri); } public Collection getFilteredTags() { return _filteredTags; } + public Collection getFilteredAuthors() { return _filteredAuthors; } } 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 31274d997..a225008a3 100644 --- a/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java +++ b/apps/syndie/java/src/net/i2p/syndie/sml/ThreadedHTMLRenderer.java @@ -34,8 +34,9 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { /** 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"; - public static String getFilterByTagLink(String uri, ThreadNode node, User user, String tag) { + public static String getFilterByTagLink(String uri, ThreadNode node, User user, String tag, String author) { StringBuffer buf = new StringBuffer(64); buf.append(uri).append('?'); if (node != null) { @@ -44,12 +45,16 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { buf.append(node.getEntry().getEntryId()).append('&'); } - if ( (tag != null) && (tag.trim().length() > 0) ) - buf.append(PARAM_TAGS).append('=').append(tag); + if (!empty(tag)) + buf.append(PARAM_TAGS).append('=').append(tag).append('&'); + + if (!empty(author)) + buf.append(PARAM_AUTHOR).append('=').append(author).append('&'); + return buf.toString(); } - public static String getNavLink(String uri, String viewPost, String viewThread, String tags, int offset) { + public static String getNavLink(String uri, String viewPost, String viewThread, String tags, String author, int offset) { StringBuffer buf = new StringBuffer(64); buf.append(uri); buf.append('?'); @@ -61,13 +66,16 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { if (!empty(tags)) buf.append(PARAM_TAGS).append('=').append(tags).append('&'); + if (!empty(author)) + buf.append(PARAM_AUTHOR).append('=').append(author).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) { + String offset, String tags, String author) { StringBuffer buf = new StringBuffer(64); buf.append(uri); if (node.getChildCount() > 0) { @@ -84,11 +92,14 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { 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('&'); + if (!isPermalink) { + if (!empty(offset)) + buf.append(PARAM_OFFSET).append('=').append(offset).append('&'); + if (!empty(tags)) + buf.append(PARAM_TAGS).append('=').append(tags).append('&'); + if (!empty(author)) + buf.append(PARAM_AUTHOR).append('=').append(author).append('&'); + } return buf.toString(); } @@ -98,7 +109,7 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { public void render(User user, Writer out, Archive archive, BlogURI post, boolean inlineReply, ThreadIndex index, String baseURI, - String offset, String requestTags) throws IOException { + String offset, String requestTags, String filteredAuthor) throws IOException { EntryContainer entry = archive.getEntry(post); if (entry == null) return; _entry = entry; @@ -126,7 +137,7 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { String subject = (String)_headers.get(HTMLRenderer.HEADER_SUBJECT); if (subject == null) subject = ""; - out.write(" "); + out.write(" "); out.write(subject); out.write("\n"); out.write("\n"); @@ -158,7 +169,7 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { for (Iterator tagIter = tags.iterator(); tagIter.hasNext(); ) { String tag = (String)tagIter.next(); out.write(""); @@ -168,7 +179,7 @@ public class ThreadedHTMLRenderer extends HTMLRenderer { } out.write("\npermalink\n"); out.write("\n\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 50875ae76..45bd08c5f 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/ViewThreadedServlet.java @@ -96,8 +96,9 @@ public class ViewThreadedServlet extends HttpServlet { FilteredThreadIndex index = (FilteredThreadIndex)req.getSession().getAttribute("threadIndex"); Collection tags = getFilteredTags(req); - if (forceNewIndex || (index == null) || (!index.getFilteredTags().equals(tags)) ) { - index = new FilteredThreadIndex(user, BlogManager.instance().getArchive(), 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); } @@ -129,6 +130,7 @@ public class ViewThreadedServlet extends HttpServlet { renderNavBar(user, req, out, index); renderControlBar(user, req, out, index); 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); @@ -183,12 +185,15 @@ public class ViewThreadedServlet extends HttpServlet { 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) { @@ -199,12 +204,30 @@ public class ViewThreadedServlet extends HttpServlet { } out.write("\n"); out.write("\n"); - out.write("Filter: \n"); - out.write("Tags: \n"); + out.write("Filter: \n"); + + out.write("Tags: \n"); + out.write("\n"); out.write("Threads\n"); out.write("\n"); @@ -215,13 +238,15 @@ public class ViewThreadedServlet extends HttpServlet { ThreadedHTMLRenderer renderer = new ThreadedHTMLRenderer(I2PAppContext.getGlobalContext()); Archive archive = BlogManager.instance().getArchive(); List posts = getPosts(archive, req, index); + String uri = req.getRequestURI(); String off = req.getParameter(ThreadedHTMLRenderer.PARAM_OFFSET); String tags = req.getParameter(ThreadedHTMLRenderer.PARAM_TAGS); - + String author = req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR); + for (int i = 0; i < posts.size(); i++) { BlogURI post = (BlogURI)posts.get(i); - renderer.render(user, out, archive, post, posts.size() == 1, index, uri, off, tags); + renderer.render(user, out, archive, post, posts.size() == 1, index, uri, off, tags, author); } } @@ -261,9 +286,13 @@ public class ViewThreadedServlet extends HttpServlet { private void renderThreadNav(User user, HttpServletRequest req, PrintWriter out, int threadOffset, ThreadIndex index) throws IOException { out.write("\n"); out.write("\n"); - out.write("<< First Page "); + if (threadOffset == 0) { + out.write("<< First Page "); + } else { + out.write("<< First Page "); + } if (threadOffset > 0) { out.write("\n"); renderThread(user, out, index, archive, req, node, 0, visibleEntry, state); out.write("\n"); + written++; } - out.write("\n"); + + if (written <= 0) + out.write("No matching threads\n"); + + out.write("\n"); } /** @@ -418,11 +471,11 @@ public class ViewThreadedServlet extends HttpServlet { if (allowCollapse) { out.write("\"-\"\n"); + out.write("\" title=\"collapse thread\">\"collapse\"\n"); } else { out.write("\"+\"\n"); + out.write("\" title=\"expand thread\">\"expand\"\n"); } } else { out.write("\"\"\n"); @@ -539,10 +592,11 @@ public class ViewThreadedServlet extends HttpServlet { 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_TAGS), + req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR)); } private static String getExpandLink(ThreadNode node, String uri, String viewPost, String viewThread, - String offset, String tags) { + String offset, String tags, String author) { StringBuffer buf = new StringBuffer(64); buf.append(uri); buf.append('?'); @@ -565,6 +619,9 @@ public class ViewThreadedServlet extends HttpServlet { 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) { @@ -572,11 +629,12 @@ public class ViewThreadedServlet extends HttpServlet { 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_TAGS), + req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR)); } private String getCollapseLink(ThreadNode node, String uri, String viewPost, String viewThread, - String offset, String tags) { + String offset, String tags, String author) { StringBuffer buf = new StringBuffer(64); buf.append(uri); // collapse node == let the node be visible @@ -595,6 +653,9 @@ public class ViewThreadedServlet extends HttpServlet { 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) { @@ -608,10 +669,11 @@ public class ViewThreadedServlet extends HttpServlet { 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_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 viewPost, String viewThread, String offset, String tags, String filteredAuthor) { StringBuffer buf = new StringBuffer(64); buf.append(uri); buf.append('?'); @@ -631,19 +693,25 @@ public class ViewThreadedServlet extends HttpServlet { 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_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_TAGS), + req.getParameter(ThreadedHTMLRenderer.PARAM_AUTHOR)); } - private static String getViewThreadLink(String uri, ThreadNode node, User user, String offset, String tags) { + 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) { @@ -666,17 +734,21 @@ public class ViewThreadedServlet extends HttpServlet { 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) { - return ThreadedHTMLRenderer.getFilterByTagLink(req.getRequestURI(), node, user, tag); + 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(), + 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); } @@ -763,22 +835,10 @@ public class ViewThreadedServlet extends HttpServlet { "\n" + "\n" + "\n" + +"Jump to the beginning of the first post rendered, if any\n" + +"Jump to the thread navigation\n\n" + "\n"; - private static final String CONTROL_BAR_WITHOUT_TAGS = "\n" + -"\n" + -"\n" + -"\n" + -"\n"; - private static final String END_HTML = "
\n" + -"\n" + -"Filter: \n" + -"\n" + -"Threads
\n" + "\n"; diff --git a/build.xml b/build.xml index 40a3155f3..0c9943926 100644 --- a/build.xml +++ b/build.xml @@ -301,6 +301,10 @@ + + + + @@ -376,6 +380,12 @@ + + + + + + diff --git a/core/java/src/net/i2p/util/FileUtil.java b/core/java/src/net/i2p/util/FileUtil.java index 0c54250f0..e168ccbb5 100644 --- a/core/java/src/net/i2p/util/FileUtil.java +++ b/core/java/src/net/i2p/util/FileUtil.java @@ -4,8 +4,10 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Enumeration; @@ -163,6 +165,37 @@ public class FileUtil { } } + /** + * Dump the contents of the given path (relative to the root) to the output + * stream. The path must not go above the root, either - if it does, it will + * throw a FileNotFoundException + */ + public static void readFile(String path, String root, OutputStream out) throws IOException { + File rootDir = new File(root); + while (path.startsWith("/") && (path.length() > 0) ) + path = path.substring(1); + if (path.length() <= 0) throw new FileNotFoundException("Not serving up the root dir"); + File target = new File(rootDir, path); + if (!target.exists()) throw new FileNotFoundException("Requested file does not exist: " + path); + String targetStr = target.getCanonicalPath(); + String rootDirStr = rootDir.getCanonicalPath(); + if (!targetStr.startsWith(rootDirStr)) throw new FileNotFoundException("Requested file is outside the root dir: " + path); + + byte buf[] = new byte[1024]; + FileInputStream in = null; + try { + in = new FileInputStream(target); + int read = 0; + while ( (read = in.read(buf)) != -1) + out.write(buf, 0, read); + out.close(); + } finally { + if (in != null) + in.close(); + } + } + + /** return true if it was copied successfully */ public static boolean copy(String source, String dest, boolean overwriteExisting) { File src = new File(source); diff --git a/history.txt b/history.txt index 083e6e331..75d856a3c 100644 --- a/history.txt +++ b/history.txt @@ -1,25 +1,36 @@ -$Id: history.txt,v 1.318 2005/11/11 06:29:16 jrandom Exp $ +$Id: history.txt,v 1.319 2005/11/11 21:38:56 cervantes Exp $ + +2005-11-11 jrandom + * Add filtering threads by author to Syndie, populated with authors in the + user's addressbook + * When creating the default user, add + "http://syndiemedia.i2p/archive/archive.txt" to their addressbook, + configured to automatically pull updates. (what other archives should + be included?) + * Tiny servlet to help dole out the new routerconsole themes, and bundle + the installer/resources/themes/** into ./docs/themes/** on both install + and update. 2005-11-11 cervantes - * Initial pass of the routerconsole revamp, starting with I2PTunnel and - being progressively rolled out to other sections at later dates. - Featuring abstracted W3C strict XHTML1.0 markup, with CSS providing - layout and styling. - * Implemented console themes. Users can create their own themes by - creating css files in: {i2pdir}/docs/themes/console/{themename}/ - and activating it using the routerconsole.theme={themename} advanced - config property. Look at the example incomplete "defCon1" theme. - Note: This is very much a work in progress. Folks might want to hold-off - creating their own skins until the markup has solidified. - * Added "routerconsole.javascript.disabled=true" to disable console - client-side scripting and "routerconsole.css.disabled=true" to remove - css styling (only rolled out in the i2ptunnel interface currently) - * Fixed long standing bug with i2ptunnel client and server edit screens - where tunnel count and depth properties would fail to save. Added - backup quantity and variance configuration options. - * Added basic accessibility support (key shortcuts, linear markup, alt and - title information and form labels). - * So far only tested on IE6, Firefox 1.0.6, Opera 8 and lynx. + * Initial pass of the routerconsole revamp, starting with I2PTunnel and + being progressively rolled out to other sections at later dates. + Featuring abstracted W3C strict XHTML1.0 markup, with CSS providing + layout and styling. + * Implemented console themes. Users can create their own themes by + creating css files in: {i2pdir}/docs/themes/console/{themename}/ + and activating it using the routerconsole.theme={themename} advanced + config property. Look at the example incomplete "defCon1" theme. + Note: This is very much a work in progress. Folks might want to hold-off + creating their own skins until the markup has solidified. + * Added "routerconsole.javascript.disabled=true" to disable console + client-side scripting and "routerconsole.css.disabled=true" to remove + css styling (only rolled out in the i2ptunnel interface currently) + * Fixed long standing bug with i2ptunnel client and server edit screens + where tunnel count and depth properties would fail to save. Added + backup quantity and variance configuration options. + * Added basic accessibility support (key shortcuts, linear markup, alt and + title information and form labels). + * So far only tested on IE6, Firefox 1.0.6, Opera 8 and lynx. 2005-11-11 jrandom * Default Syndie to single user mode, and automatically log into a default