diff --git a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfoData.java b/apps/syndie/java/src/net/i2p/syndie/data/BlogInfoData.java index b1aeb60e01f387ef6e9b5218d3f53f08d6b4552c..69e9baee4f577894f3b4eb46ed473bca653d3e5b 100644 --- a/apps/syndie/java/src/net/i2p/syndie/data/BlogInfoData.java +++ b/apps/syndie/java/src/net/i2p/syndie/data/BlogInfoData.java @@ -102,6 +102,7 @@ public class BlogInfoData { prevGroup = pn.getGroup(0); } } + line.setLength(0); if (!ok) break; } 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 eeaa9ee8008bfe8afd32854c6001b06b7bc76272..c8684be87ff218da9d10d763bb96d2b3c2f339ff 100644 --- a/apps/syndie/java/src/net/i2p/syndie/data/FilteredThreadIndex.java +++ b/apps/syndie/java/src/net/i2p/syndie/data/FilteredThreadIndex.java @@ -61,6 +61,8 @@ public class FilteredThreadIndex extends ThreadIndex { } private boolean isIgnored(ThreadNode node, List ignoredAuthors, Collection requestedTags, Collection filteredAuthors, boolean filterAuthorsByRoot) { + if (node.getTags().contains(BlogInfoData.TAG)) + return true; // its a fake post, containing some updated metadata for the blog if (filteredAuthors.size() <= 0) { boolean allAuthorsIgnored = true; for (Iterator iter = node.getRecursiveAuthorIterator(); iter.hasNext(); ) { 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 3174f404fd4bef9133cbe5054189778efbf54940..db9ed3b497c46196a07eab4e1ac8942d5684b814 100644 --- a/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java +++ b/apps/syndie/java/src/net/i2p/syndie/sml/HTMLRenderer.java @@ -1102,7 +1102,7 @@ public class HTMLRenderer extends EventReceiverImpl { + "&" + SyndicateServlet.PARAM_SCHEMA + "=" + sanitizeTagParam(archiveLocation.getSchema()) + "&" + SyndicateServlet.PARAM_LOCATION + "=" + sanitizeTagParam(archiveLocation.getLocation()); } - public String getBookmarkURL(String name, String location, String schema, String protocol) { + public static String getBookmarkURL(String name, String location, String schema, String protocol) { return "addresses.jsp?" + AddressesServlet.PARAM_NAME + '=' + sanitizeTagParam(name) + "&" + AddressesServlet.PARAM_NET + '=' + sanitizeTagParam(schema) + "&" + AddressesServlet.PARAM_PROTO + '=' + sanitizeTagParam(protocol) 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 b11e593cf1bb86dea1156e3b9184477b7d2d9523..f82db02eeb214882b638f6c5d8d618e3d55fd2be 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/BaseServlet.java @@ -23,7 +23,7 @@ import net.i2p.util.Log; */ public abstract class BaseServlet extends HttpServlet { protected static final String PARAM_AUTH_ACTION = "syndie.auth"; - private static long _authNonce; + protected static long _authNonce; protected I2PAppContext _context; protected Log _log; @@ -141,6 +141,7 @@ public abstract class BaseServlet extends HttpServlet { forceNewIndex = handleBookmarking(user, req) || forceNewIndex; forceNewIndex = handleManageTags(user, req) || forceNewIndex; handleUpdateProfile(user, req); + req.setAttribute(BaseServlet.class.getName() + ".auth", "true"); } // the 'dataImported' flag is set by successful fetches in the SyndicateServlet/RemoteArchiveBean @@ -176,6 +177,10 @@ public abstract class BaseServlet extends HttpServlet { protected void render(User user, HttpServletRequest req, HttpServletResponse resp, ThreadIndex index) throws IOException, ServletException { render(user, req, resp.getWriter(), index); } + protected boolean isAuthed(HttpServletRequest req) { + String auth = (String)req.getAttribute(BaseServlet.class.getName() + ".auth"); + return (auth != null) && (Boolean.valueOf(auth).booleanValue()); + } private boolean handleBookmarking(User user, HttpServletRequest req) { if (!user.getAuthenticated()) diff --git a/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigBean.java b/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigBean.java index d5ecd1c2ddb5277e5ea00f97bfe668d0569339c9..4ad6c7c8f29c64b42716f76d7ed2e5286720396d 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigBean.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigBean.java @@ -33,6 +33,8 @@ public class BlogConfigBean { _styleOverrides = new Properties(); } + public boolean isUpdated() { return _updated; } + public User getUser() { return _user; } public void setUser(User user) { _user = user; @@ -74,6 +76,8 @@ public class BlogConfigBean { PetName pn = (PetName)grp.get(0); if ( (pn.getGroupCount() == 0) && ( (name == null) || (name.length() <= 0) ) ) return grp; + if (pn.getGroupCount() == 0) + continue; String curGroup = pn.getGroup(0); if (curGroup.equals(name)) return grp; @@ -94,6 +98,7 @@ public class BlogConfigBean { } else { group.add(pn); } + _updated = true; } public void remove(PetName pn) { String groupName = null; @@ -105,6 +110,7 @@ public class BlogConfigBean { if (group.size() <= 0) _groups.remove(group); } + _updated = true; } public void remove(String name) { for (int i = 0; i < getGroupCount(); i++) { @@ -115,6 +121,7 @@ public class BlogConfigBean { group.remove(j); if (group.size() <= 0) _groups.remove(group); + _updated = true; return; } } @@ -140,7 +147,7 @@ public class BlogConfigBean { _logo = logo; _updated = true; } - public boolean hasPendingChanges() { return _updated; } + public boolean hasPendingChanges() { return _loaded && _updated; } private void load() { Archive archive = BlogManager.instance().getArchive(); @@ -175,7 +182,9 @@ public class BlogConfigBean { add(pn); } } - _styleOverrides.putAll(data.getStyleOverrides()); + Properties overrides = data.getStyleOverrides(); + if (overrides != null) + _styleOverrides.putAll(overrides); } catch (IOException ioe) { _log.warn("Unable to load the blog info data from " + uri, ioe); } @@ -183,9 +192,10 @@ public class BlogConfigBean { } } _loaded = true; + _updated = false; } - public boolean publishChanges() throws IOException { + public boolean publishChanges() { FileInputStream logo = null; try { if (_logo != null) @@ -229,6 +239,7 @@ public class BlogConfigBean { if (updated) { // ok great, published locally, though should we push it to others? _log.info("Blog summary updated for " + _user + " in " + uri.toString()); + setUser(_user); return true; } } else { @@ -239,6 +250,8 @@ public class BlogConfigBean { _log.error("Error creating the summary entry"); return false; } + } catch (IOException ioe) { + _log.error("Error publishing", ioe); } finally { if (logo != null) try { logo.close(); } catch (IOException ioe) {} // the other streams are in-memory, drop with the scope diff --git a/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigServlet.java index 117bf9161e32496a6816a2e4f4cfec671e1d5f26..af71fbbc22d9d180ff2a471a28b6c02e28d74427 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigServlet.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/BlogConfigServlet.java @@ -22,8 +22,8 @@ import net.i2p.syndie.sml.*; public class BlogConfigServlet extends BaseServlet { private static final String ATTR_CONFIG_BEAN = "__blogConfigBean"; public static final String PARAM_CONFIG_SCREEN = "screen"; - public static final String SCREEN_GENERAL = "general"; public static final String SCREEN_REFERENCES = "references"; + public static final String SCREEN_IMAGES = "images"; protected void renderServletDetails(User user, HttpServletRequest req, PrintWriter out, ThreadIndex index, int threadOffset, BlogURI visibleEntry, Archive archive) throws IOException { if ( (user == null) || (!user.getAuthenticated() && !BlogManager.instance().isSingleUser())) { @@ -34,6 +34,7 @@ public class BlogConfigServlet extends BaseServlet { if (bean == null) { bean = new BlogConfigBean(); bean.setUser(user); + req.getSession().setAttribute(ATTR_CONFIG_BEAN, bean); } // handle actions here... @@ -41,191 +42,329 @@ public class BlogConfigServlet extends BaseServlet { String screen = req.getParameter(PARAM_CONFIG_SCREEN); if (screen == null) - screen = SCREEN_GENERAL; - out.write("todo: Display screen " + screen); - /* - if (SCREEN_REFERENCES.equals(screen)) { - displayReferencesScreen(req, out, bean); + screen = SCREEN_REFERENCES; + out.write("<tr><td colspan=\"3\">\n"); + showConfigNav(req, out); + + if (isAuthed(req)) { + StringBuffer buf = handleOtherAuthedActions(user, req, bean); + if (buf != null) out.write(buf.toString()); } else { - displayGeneralScreen(req, out, bean); } - */ - } - /* - private void renderMyProfile(User user, String baseURI, PrintWriter out, Archive archive) throws IOException { - BlogInfo info = archive.getBlogInfo(user.getBlog()); - if (info == null) - return; + if (bean.isUpdated()) + showCommitForm(req, out); - out.write("<!-- " + info.toString() + "-->\n"); - out.write("<form action=\"" + baseURI + "\" method=\"POST\">\n"); - writeAuthActionFields(out); - // now add the form to update - out.write("<tr><td colspan=\"3\">Your profile</td></tr>\n"); - out.write("<tr><td colspan=\"3\">Name: <input type=\"text\" name=\"" - + ThreadedHTMLRenderer.PARAM_PROFILE_NAME + "\" value=\"" - + HTMLRenderer.sanitizeTagParam(info.getProperty(BlogInfo.NAME)) + "\"></td></tr>\n"); - out.write("<tr><td colspan=\"3\">Account description: <input type=\"text\" name=\"" - + ThreadedHTMLRenderer.PARAM_PROFILE_DESC + "\" value=\"" - + HTMLRenderer.sanitizeTagParam(info.getProperty(BlogInfo.DESCRIPTION)) + "\"></td></tr>\n"); - out.write("<tr><td colspan=\"3\">Contact information: <input type=\"text\" name=\"" - + ThreadedHTMLRenderer.PARAM_PROFILE_URL + "\" value=\"" - + HTMLRenderer.sanitizeTagParam(info.getProperty(BlogInfo.CONTACT_URL)) + "\"></td></tr>\n"); - out.write("<tr><td colspan=\"3\">Other attributes:<br /><textarea rows=\"3\" name=\"" - + ThreadedHTMLRenderer.PARAM_PROFILE_OTHER + "\" cols=\"60\">"); - String props[] = info.getProperties(); - if (props != null) { - for (int i = 0; i < props.length; i++) { - if (!BlogInfo.NAME.equals(props[i]) && - !BlogInfo.DESCRIPTION.equals(props[i]) && - !BlogInfo.EDITION.equals(props[i]) && - !BlogInfo.OWNER_KEY.equals(props[i]) && - !BlogInfo.POSTERS.equals(props[i]) && - !BlogInfo.SIGNATURE.equals(props[i]) && - !BlogInfo.CONTACT_URL.equals(props[i])) { - out.write(HTMLRenderer.sanitizeString(props[i], false) + ": " - + HTMLRenderer.sanitizeString(info.getProperty(props[i]), false) + "\n"); - } - } + if (SCREEN_REFERENCES.equals(screen)) { + displayReferencesScreen(req, out, user, bean); + } else { + displayUnknownScreen(out, screen); } - out.write("</textarea></td></tr>\n"); - - if (user.getAuthenticated()) { - if ( (user.getUsername() == null) || (user.getUsername().equals(BlogManager.instance().getDefaultLogin())) ) { - // this is the default user, don't let them change the password + out.write("</td></tr>\n"); + } + + private void showCommitForm(HttpServletRequest req, PrintWriter out) throws IOException { + out.write("<form action=\"" + req.getRequestURI() + "\" method=\"GET\">\n"); + writeAuthActionFields(out); + out.write("<i>Note: Uncommitted changes outstanding</a> <input type=\"submit\" name=\"action\" " + + "value=\"Publish blog configuration\" />\n</form>\n"); + } + + private void showConfigNav(HttpServletRequest req, PrintWriter out) throws IOException { + out.write("<span class=\"syndieBlogConfigNav\"><a href=\"" + getScreenURL(req, SCREEN_REFERENCES, false) + + "\" title=\"Configure the blog's references\">References</a> " + + "<a href=\"" + getScreenURL(req, SCREEN_IMAGES, false) + + "\" title=\"Configure the images used on the blog\">Images</a></span><hr />\n"); + } + + private String getScreenURL(HttpServletRequest req, String screen, boolean wantAuth) { + StringBuffer buf = new StringBuffer(128); + buf.append(req.getRequestURI()).append("?").append(PARAM_CONFIG_SCREEN).append("="); + buf.append(screen).append("&"); + if (wantAuth) + buf.append(PARAM_AUTH_ACTION).append('=').append(_authNonce).append("&"); + return buf.toString(); + } + + private void displayUnknownScreen(PrintWriter out, String screen) throws IOException { + out.write("<br /><hr />The screen " + HTMLRenderer.sanitizeString(screen) + " has not yet been implemented"); + } + private void displayReferencesScreen(HttpServletRequest req, PrintWriter out, User user, BlogConfigBean bean) throws IOException { + out.write("<form action=\"" + getScreenURL(req, SCREEN_REFERENCES, false) + "\" method=\"POST\">\n"); + writeAuthActionFields(out); + out.write("<ol class=\"syndieReferenceGroupList\">\n"); + boolean defaultFound = false; + for (int i = 0; i < bean.getGroupCount(); i++) { + List group = bean.getGroup(i); + String groupName = null; + PetName pn = (PetName)group.get(0); + if (pn.getGroupCount() <= 0) { + groupName = ViewBlogServlet.DEFAULT_GROUP_NAME; + defaultFound = true; } else { - out.write("<tr><td colspan=\"3\">Old Password: <input type=\"password\" name=\"oldPassword\" /></td></tr>\n"); - out.write("<tr><td colspan=\"3\">Password: <input type=\"password\" name=\"password\" /></td></tr>\n"); - out.write("<tr><td colspan=\"3\">Password again: <input type=\"password\" name=\"passwordConfirm\" /></td></tr>\n"); + groupName = pn.getGroup(0); } - if (!BlogManager.instance().authorizeRemote(user)) { - out.write("<tr><td colspan=\"3\">To access the remote functionality, please specify the administrative password: <br />\n" + - "<input type=\"password\" name=\"adminPass\" /></td></tr>\n"); + out.write("<li><b>Group:</b> " + HTMLRenderer.sanitizeString(groupName) + "\n"); + if (i > 0) + out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "moveFrom=" + i + + "&moveTo=" + (i-1) + "\" title=\"Move higher\">^</a>"); + if (i + 1 < bean.getGroupCount()) + out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "moveFrom=" + i + + "&moveTo=" + (i+1) + "\" title=\"Move lower\">v</a>"); + out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "delete=" + i + "\" title=\"Delete\">X</a>"); + + out.write("<ol class=\"syndieReferenceGroupElementList\">\n"); + for (int j = 0; j < group.size(); j++) { + out.write("<li>" + ViewBlogServlet.renderLink(user.getBlog(), (PetName)group.get(j))); + if (j > 0) + out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "moveRefFrom=" + i + "." + j + + "&moveRefTo=" + i + "." + (j-1) + "\" title=\"Move higher\">^</a>"); + if (j + 1 < group.size()) + out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "moveRefFrom=" + i + "." + j + + "&moveRefTo=" + i + "." + (j+1) + "\" title=\"Move lower\">v</a>"); + out.write(" <a href=\"" + getScreenURL(req, SCREEN_REFERENCES, true) + "delete=" + i + "." + j + + "\" title=\"Delete\">X</a>"); + out.write("</li>\n"); } + out.write("</ol><!-- end of the syndieReferenceGroupElementList -->\n"); + out.write("</li>\n"); } + out.write("</ol><!-- end of the syndieReferenceGroupList -->\n"); + - out.write("<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Update profile\" /></td></tr>\n"); + out.write("Add a new element: <br />Group: <select name=\"new.group\"><option value=\"\">Select a group...</option>"); + for (int i = 0; i < bean.getGroupCount(); i++) { + List group = bean.getGroup(i); + String groupName = null; + PetName pn = (PetName)group.get(0); + if (pn.getGroupCount() <= 0) + groupName = ViewBlogServlet.DEFAULT_GROUP_NAME; + else + groupName = pn.getGroup(0); + if (groupName != null) + out.write("<option value=\"" + HTMLRenderer.sanitizeTagParam(groupName) + "\">" + + HTMLRenderer.sanitizeString(groupName) + "</option>\n"); + } + if (!defaultFound) + out.write("<option value=\"" + ViewBlogServlet.DEFAULT_GROUP_NAME + "\">" + + ViewBlogServlet.DEFAULT_GROUP_NAME + "</option>\n"); + out.write("</select> or <input type=\"text\" size=\"12\" name=\"new.groupOther\" /><br />" + + "Type: <select name=\"new.type\"><option value=\"blog\">Syndie blog</option>\n" + + "<option value=\"blogpost\">Post within a syndie blog</option>\n" + + "<option value=\"blogpostattachment\">Attachment within a syndie blog</option>\n" + + "<option value=\"eepsite\">Eepsite</option>\n" + + "<option value=\"website\">Website</option>\n</select><br />\n" + + "Name: <input type=\"text\" size=\"20\" name=\"new.name\" /><br /> " + + "Location: <input type=\"text\" name=\"new.location\" size=\"40\" />\n" + + "<ul><li>Blogs should be specified as <code>$base64Key</code></li>\n" + + "<li>Blog posts should be specified as <code>$base64Key/$postEntryId</code></li>\n" + + "<li>Blog post attachments should be specified as <code>$base64Key/$postEntryId/$attachmentNum</code></li>\n" + + "</ul><hr />\n"); + + out.write("<input type=\"submit\" name=\"action\" value=\"Save changes\">\n"); out.write("</form>\n"); } - private void renderProfile(User user, String baseURI, PrintWriter out, Hash author, Archive archive) throws IOException { - out.write("<tr><td colspan=\"3\">Profile for "); - PetName pn = user.getPetNameDB().getByLocation(author.toBase64()); - String name = null; - BlogInfo info = archive.getBlogInfo(author); - if (pn != null) { - out.write(pn.getName()); - name = null; - if (info != null) - name = info.getProperty(BlogInfo.NAME); - - if ( (name == null) || (name.trim().length() <= 0) ) - name = author.toBase64().substring(0, 6); - - out.write(" (" + name + ")"); + private void writePetnameDropdown(PrintWriter out, PetNameDB db) throws IOException { + Set names = db.getNames(); + TreeSet ordered = new TreeSet(names); + for (Iterator iter = ordered.iterator(); iter.hasNext(); ) { + String name = (String)iter.next(); + PetName pn = db.getByName(name); + String proto = pn.getProtocol(); + if ("syndietag".equals(proto)) + continue; + out.write("<option value=\"" + HTMLRenderer.sanitizeTagParam(pn.getName()) + "\">"); + if ("syndieblog".equals(proto)) + out.write("Blog: "); + else if ("syndiearchive".equals(proto)) + out.write("Archive: "); + else if ("eep".equals(proto)) + out.write("Eepsite: "); + else + out.write(HTMLRenderer.sanitizeString(proto) + ": "); + out.write(HTMLRenderer.sanitizeString(pn.getName()) + "</option>\n"); + } + } + + protected StringBuffer handleOtherAuthedActions(User user, HttpServletRequest req, BlogConfigBean bean) { + StringBuffer buf = new StringBuffer(); + req.setAttribute(getClass().getName() + ".output", buf); + String action = req.getParameter("action"); + if ("Publish blog configuration".equals(action)) { + if (bean.publishChanges()) { + buf.append("Changes published<br />\n"); + } else { + buf.append("Changes could not be published (please check the log)<br />\n"); + } } else { - if (info != null) - name = info.getProperty(BlogInfo.NAME); - - if ( (name == null) || (name.trim().length() <= 0) ) - name = author.toBase64().substring(0, 6); - out.write(name); + if ("Save changes".equals(action)) { + String newGroup = req.getParameter("new.group"); + if ( (newGroup == null) || (newGroup.trim().length() <= 0) ) + newGroup = req.getParameter("new.groupOther"); + if ( (newGroup != null) && (newGroup.trim().length() > 0) ) { + addElement(req, user, newGroup, buf, bean); + } else { + } + } else { + } + + handleDelete(req, user, bean, buf); + handleReorderGroup(req, user, bean, buf); + handleReorderRef(req, user, bean, buf); } - out.write("</a>"); - if (info != null) - out.write(" [edition " + info.getEdition() + "]"); - out.write("<br />\n"); - out.write("<a href=\"" + getControlTarget() + "?" + ThreadedHTMLRenderer.PARAM_AUTHOR - + '=' + author.toBase64() + "&" + ThreadedHTMLRenderer.PARAM_THREAD_AUTHOR + "=true&\"" - + " title=\"View '" + HTMLRenderer.sanitizeTagParam(name) + "'s blog\">View their blog</a> or "); - out.write("<a href=\"" + getControlTarget() + "?" + ThreadedHTMLRenderer.PARAM_AUTHOR - + '=' + author.toBase64() + "&\">threads they have participated in</a>\n"); - out.write("</td></tr>\n"); + return buf; + } + + private void addElement(HttpServletRequest req, User user, String newGroup, StringBuffer actionOutputHTML, BlogConfigBean bean) { + String type = req.getParameter("new.type"); + String loc = req.getParameter("new.location"); + String name = req.getParameter("new.name"); - out.write("<tr><td colspan=\"3\"><hr /></td></tr>\n"); - if (pn == null) { - out.write("<tr><td colspan=\"3\">Not currently bookmarked. Add them to your "); - String addFav = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_FAVORITE, - baseURI, "", "", "", "", "", author.toBase64()); - String addIgnore = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_IGNORE, - baseURI, "", "", "", "", "", author.toBase64()); - out.write("<a href=\"" + addFav + "\" title=\"Threads by favorite authors are shown specially\">favorites</a> or "); - out.write("<a href=\"" + addIgnore + "\" title=\"Threads by ignored authors are hidden from view\">ignored</a> "); - out.write("</td></tr>\n"); - } else if (pn.isMember(FilteredThreadIndex.GROUP_IGNORE)) { - out.write("<tr><td colspan=\"3\">Currently ignored - threads they create are hidden.</td></tr>\n"); - String remIgnore = getRemoveFromGroupLink(user, pn.getName(), FilteredThreadIndex.GROUP_IGNORE, - baseURI, "", "", "", "", "", author.toBase64()); - out.write("<tr><td colspan=\"3\"><a href=\"" + remIgnore + "\">Unignore " + pn.getName() + "</a></td></tr>\n"); - String remCompletely = getRemoveFromGroupLink(user, pn.getName(), "", - baseURI, "", "", "", "", "", author.toBase64()); - out.write("<tr><td colspan=\"3\"><a href=\"" + remCompletely + "\">Forget about " + pn.getName() + " entirely</a></td></tr>\n"); - } else if (pn.isMember(FilteredThreadIndex.GROUP_FAVORITE)) { - out.write("<tr><td colspan=\"3\">Currently marked as a favorite author - threads they participate in " + - "are highlighted.</td></tr>\n"); - String remIgnore = getRemoveFromGroupLink(user, pn.getName(), FilteredThreadIndex.GROUP_FAVORITE, - baseURI, "", "", "", "", "", author.toBase64()); - out.write("<tr><td colspan=\"3\"><a href=\"" + remIgnore + "\">Remove " + pn.getName() + " from the list of favorite authors</a></td></tr>\n"); - String addIgnore = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_IGNORE, - baseURI, "", "", "", "", "", author.toBase64()); - out.write("<tr><td colspan=\"3\"><a href=\"" + addIgnore + "\" title=\"Threads by ignored authors are hidden from view\">Ignore the author</a></td></tr>"); - String remCompletely = getRemoveFromGroupLink(user, pn.getName(), "", - baseURI, "", "", "", "", "", author.toBase64()); - out.write("<tr><td colspan=\"3\"><a href=\"" + remCompletely + "\">Forget about " + pn.getName() + " entirely</a></td></tr>\n"); - } else { - out.write("<tr><td colspan=\"3\">Currently bookmarked. Add them to your "); - String addFav = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_FAVORITE, - baseURI, "", "", "", "", "", author.toBase64()); - String addIgnore = getAddToGroupLink(user, author, FilteredThreadIndex.GROUP_IGNORE, - baseURI, "", "", "", "", "", author.toBase64()); - out.write("<a href=\"" + addFav + "\" title=\"Threads by favorite authors are shown specially\">favorites</a> or "); - out.write("<a href=\"" + addIgnore + "\" title=\"Threads by ignored authors are hidden from view\">ignored</a> list</td></tr>"); - String remCompletely = getRemoveFromGroupLink(user, pn.getName(), "", - baseURI, "", "", "", "", "", author.toBase64()); - out.write("<tr><td colspan=\"3\"><a href=\"" + remCompletely + "\">Forget about " + pn.getName() + " entirely</a></td></tr>\n"); + if (empty(type) || empty(loc) || empty(name)) return; + + PetName pn = null; + if ("blog".equals(type)) + pn = new PetName(name, "syndie", "syndieblog", loc); + else if ("blogpost".equals(type)) + pn = new PetName(name, "syndie", "syndieblogpost", loc); + else if ("blogpostattachment".equals(type)) + pn = new PetName(name, "syndie", "syndieblogattachment", loc); + else if ("eepsite".equals(type)) + pn = new PetName(name, "i2p", "http", loc); + else if ("website".equals(type)) + pn = new PetName(name, "web", "http", loc); + else { + // unknown type } - if (info != null) { - String descr = info.getProperty(BlogInfo.DESCRIPTION); - if ( (descr != null) && (descr.trim().length() > 0) ) - out.write("<tr><td colspan=\"3\">Account description: " + HTMLRenderer.sanitizeString(descr) + "</td></tr>\n"); - - String contactURL = info.getProperty(BlogInfo.CONTACT_URL); - if ( (contactURL != null) && (contactURL.trim().length() > 0) ) - out.write("<tr><td colspan=\"3\">Contact information: " - + HTMLRenderer.sanitizeString(contactURL) + "</td></tr>\n"); + if (pn != null) { + if (!ViewBlogServlet.DEFAULT_GROUP_NAME.equals(newGroup)) + pn.addGroup(newGroup); + bean.add(pn); + actionOutputHTML.append("Reference '").append(HTMLRenderer.sanitizeString(name)); + actionOutputHTML.append("' for ").append(HTMLRenderer.sanitizeString(loc)).append(" added to "); + actionOutputHTML.append(HTMLRenderer.sanitizeString(newGroup)).append("<br />\n"); + } + } + + private void handleDelete(HttpServletRequest req, User user, BlogConfigBean bean, StringBuffer actionOutputHTML) { + // control parameters: + // delete=$i removes group # $i + // delete=$i.$j removes element $j in group $i + String del = req.getParameter("delete"); + if (empty(del)) return; + int split = del.indexOf('.'); + int group = -1; + int elem = -1; + if (split <= 0) { + try { group = Integer.parseInt(del); } catch (NumberFormatException nfe) {} + } else { + try { + group = Integer.parseInt(del.substring(0, split)); + elem = Integer.parseInt(del.substring(split+1)); + } catch (NumberFormatException nfe) { + group = -1; + elem = -1; + } + } + if ( (elem >= 0) && (group >= 0) ) { + List l = bean.getGroup(group); + if (elem < l.size()) { + PetName pn = (PetName)l.get(elem); + bean.remove(pn); + actionOutputHTML.append("Reference '").append(HTMLRenderer.sanitizeString(pn.getName())); + actionOutputHTML.append("' for ").append(HTMLRenderer.sanitizeString(pn.getLocation())); + actionOutputHTML.append(" removed<br />\n"); + } + } else if ( (elem == -1) && (group >= 0) ) { + List l = bean.getGroup(group); + for (int i = 0; i < l.size(); i++) { + PetName pn = (PetName)l.get(i); + bean.remove(pn); + } + actionOutputHTML.append("All references in the selected group were removed<br />\n"); + } else { + // noop + } + } + + private void handleReorderGroup(HttpServletRequest req, User user, BlogConfigBean bean, StringBuffer actionOutputHTML) { + // control parameters: + // moveFrom=$i & moveTo=$j moves group $i to position $j + int from = -1; + int to = -1; + try { + String str = req.getParameter("moveFrom"); + if (str != null) + from = Integer.parseInt(str); + str = req.getParameter("moveTo"); + if (str != null) + to = Integer.parseInt(str); - String props[] = info.getProperties(); - int altCount = 0; - if (props != null) - for (int i = 0; i < props.length; i++) - if (!BlogInfo.NAME.equals(props[i]) && - !BlogInfo.DESCRIPTION.equals(props[i]) && - !BlogInfo.EDITION.equals(props[i]) && - !BlogInfo.OWNER_KEY.equals(props[i]) && - !BlogInfo.POSTERS.equals(props[i]) && - !BlogInfo.SIGNATURE.equals(props[i]) && - !BlogInfo.CONTACT_URL.equals(props[i])) - altCount++; - if (altCount > 0) { - for (int i = 0; i < props.length; i++) { - if (!BlogInfo.NAME.equals(props[i]) && - !BlogInfo.DESCRIPTION.equals(props[i]) && - !BlogInfo.EDITION.equals(props[i]) && - !BlogInfo.OWNER_KEY.equals(props[i]) && - !BlogInfo.POSTERS.equals(props[i]) && - !BlogInfo.SIGNATURE.equals(props[i]) && - !BlogInfo.CONTACT_URL.equals(props[i])) { - out.write("<tr><td colspan=\"3\">"); - out.write(HTMLRenderer.sanitizeString(props[i]) + ": " - + HTMLRenderer.sanitizeString(info.getProperty(props[i]))); - out.write("</td></tr>\n"); + if ( (from >= 0) && (to >= 0) ) { + List src = bean.getGroup(from); + List dest = bean.getGroup(to); + List orig = new ArrayList(dest); + dest.clear(); + dest.addAll(src); + src.clear(); + src.addAll(orig); + bean.groupsUpdated(); + actionOutputHTML.append("Reference group moved<br />\n"); + } + } catch (NumberFormatException nfe) { + // ignore + } + } + + private void handleReorderRef(HttpServletRequest req, User user, BlogConfigBean bean, StringBuffer actionOutputHTML) { + // control parameters: + // moveRefFrom=$i.$j & moveRefTo=$k.$l moves element $j in group $i to position $l in group l + // (i == k) + int from = -1; + int fromElem = -1; + int to = -1; // ignored + int toElem = -1; + try { + String str = req.getParameter("moveRefFrom"); + if (str != null) { + int split = str.indexOf('.'); + if (split > 0) { + try { + from = Integer.parseInt(str.substring(0, split)); + fromElem = Integer.parseInt(str.substring(split+1)); + } catch (NumberFormatException nfe) { + from = -1; + fromElem = -1; } } } + str = req.getParameter("moveRefTo"); + if (str != null) { + int split = str.indexOf('.'); + if (split > 0) { + try { + to = Integer.parseInt(str.substring(0, split)); + toElem = Integer.parseInt(str.substring(split+1)); + } catch (NumberFormatException nfe) { + to = -1; + toElem = -1; + } + } + } + + if ( (from >= 0) && (fromElem >= 0) && (toElem >= 0) ) { + List src = bean.getGroup(from); + PetName pn = (PetName)src.remove(fromElem); + src.add(toElem, pn); + bean.groupsUpdated(); + actionOutputHTML.append("Reference element moved<br />\n"); + } + } catch (NumberFormatException nfe) { + // ignore } } - */ - + + protected String getTitle() { return "Syndie :: Configure blog"; } } diff --git a/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java b/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java index ebebc4914be71ce7300f62fd0f639fa3001673ed..f4ee59228b03e1f5395053775035d6bea9c94ac0 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/ProfileServlet.java @@ -104,6 +104,7 @@ public class ProfileServlet extends BaseServlet { out.write("<tr><td colspan=\"3\"><input type=\"submit\" name=\"action\" value=\"Update profile\" /></td></tr>\n"); out.write("</form>\n"); + out.write("<tr><td colspan=\"3\"><a href=\"configblog.jsp\">Configure your blog</a></td></tr>\n"); } private void renderProfile(User user, String baseURI, PrintWriter out, Hash author, Archive archive) throws IOException { 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 5d5d780c79544af52b248c46f0c33c0ee46aebf9..f4c93a39296b325542d9ed639cc3092099c80e94 100644 --- a/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogServlet.java +++ b/apps/syndie/java/src/net/i2p/syndie/web/ViewBlogServlet.java @@ -79,6 +79,20 @@ public class ViewBlogServlet extends BaseServlet { private List getPosts(User user, Archive archive, BlogInfo info, HttpServletRequest req, ThreadIndex index) { List rv = new ArrayList(1); if (info == null) return rv; + + String entrySelected = req.getParameter(PARAM_ENTRY); + if (entrySelected != null) { + // $blogKey/$entryId + BlogURI uri = null; + if (entrySelected.startsWith("blog://")) + uri = new BlogURI(entrySelected); + else + uri = new BlogURI("blog://" + entrySelected.trim()); + if (uri.getEntryId() >= 0) { + rv.add(uri); + return rv; + } + } ArchiveIndex aindex = archive.getIndex(); @@ -188,7 +202,7 @@ public class ViewBlogServlet extends BaseServlet { out.write("</div>\n"); } - private static final String DEFAULT_GROUP_NAME = "References"; + public static final String DEFAULT_GROUP_NAME = "References"; private void renderReferences(PrintWriter out, BlogInfo info, BlogInfoData data) throws IOException { out.write("<div class=\"syndieBlogLinks\">\n"); if (data != null) { @@ -207,23 +221,118 @@ public class ViewBlogServlet extends BaseServlet { out.write("<ul>\n"); for (int j = 0; j < group.size(); j++) { pn = (PetName)group.get(j); - out.write("<li>" + renderLink(pn) + "</li>\n"); + out.write("<li>" + renderLink(info.getKey().calculateHash(), pn) + "</li>\n"); } out.write("</ul>\n</div>\n<!-- end " + name + " -->\n"); } } - out.write("<div class=\"syndieBlogLinkGroup\">\n"); - out.write("<span class=\"syndieBlogLinkGroupName\">Custom links</span>\n"); - out.write("<ul><li><a href=\"\">are not yet implemented</a></li><li><a href=\"\">but are coming soon</a></li></ul>\n"); - out.write("</div><!-- end fake group -->"); + //out.write("<div class=\"syndieBlogLinkGroup\">\n"); + //out.write("<span class=\"syndieBlogLinkGroupName\">Custom links</span>\n"); + //out.write("<ul><li><a href=\"\">are not yet implemented</a></li><li><a href=\"\">but are coming soon</a></li></ul>\n"); + //out.write("</div><!-- end fake group -->"); out.write("<div class=\"syndieBlogMeta\">"); out.write("Secured by <a href=\"http://syndie.i2p.net/\">Syndie</a>"); out.write("</div>\n"); out.write("</div><!-- end syndieBlogLinks -->\n\n"); } - private String renderLink(PetName pn) { - return "<a href=\"\" title=\"go somewhere\">" + HTMLRenderer.sanitizeString(pn.getName()) + "</a>"; + /** 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); + String type = pn.getProtocol(); + if ("syndieblog".equals(type)) { + String loc = pn.getLocation(); + if (loc != null) { + buf.append("<a href=\"blog.jsp?").append(PARAM_BLOG).append("="); + buf.append(HTMLRenderer.sanitizeTagParam(pn.getLocation())); + buf.append("\" title=\"View ").append(HTMLRenderer.sanitizeTagParam(pn.getName())).append("\">"); + } + buf.append(HTMLRenderer.sanitizeString(pn.getName())); + if (loc != null) { + buf.append("</a>"); + //buf.append(" <a href=\"").append(HTMLRenderer.getBookmarkURL(pn.getName(), pn.getLocation(), "syndie", "syndieblog")); + //buf.append("\" title=\"Bookmark ").append(HTMLRenderer.sanitizeTagParam(pn.getName())).append("\"><image src=\"images/addToFavorites.png\" alt=\"\" /></a>\n"); + } + } else if ("syndieblogpost".equals(type)) { + String loc = pn.getLocation(); + if (loc != null) { + buf.append("<a href=\"blog.jsp?").append(PARAM_BLOG).append("="); + buf.append(blogFrom.toBase64()).append("&"); + buf.append(PARAM_ENTRY).append("=").append(HTMLRenderer.sanitizeTagParam(pn.getLocation())); + buf.append("\" title=\"View the specified post\">"); + } + buf.append(HTMLRenderer.sanitizeString(pn.getName())); + if (loc != null) { + buf.append("</a>"); + } + } else if ("syndieblogattachment".equals(type)) { + String loc = pn.getLocation(); + if (loc != null) { + int split = loc.lastIndexOf('/'); + try { + int attachmentId = -1; + if (split > 0) + attachmentId = Integer.parseInt(loc.substring(split+1)); + + if (attachmentId < 0) { + loc = null; + } else { + BlogURI post = null; + if (loc.startsWith("blog://")) + post = new BlogURI(loc.substring(0, split)); + else + post = new BlogURI("blog://" + loc.substring(0, split)); + + EntryContainer entry = BlogManager.instance().getArchive().getEntry(post); + if (entry != null) { + Attachment attachments[] = entry.getAttachments(); + if (attachmentId < attachments.length) { + buf.append("<a href=\"blog.jsp?").append(PARAM_BLOG).append("="); + buf.append(blogFrom.toBase64()).append("&"); + buf.append(PARAM_ATTACHMENT).append("=").append(HTMLRenderer.sanitizeTagParam(loc)); + buf.append("\" title=\""); + buf.append("'"); + buf.append(HTMLRenderer.sanitizeTagParam(attachments[attachmentId].getName())); + buf.append("', "); + buf.append(attachments[attachmentId].getDataLength()/1024).append("KB, "); + buf.append("of type ").append(HTMLRenderer.sanitizeTagParam(attachments[attachmentId].getMimeType())); + buf.append("\">"); + buf.append(HTMLRenderer.sanitizeString(pn.getName())); + buf.append("</a>"); + } else { + loc = null; + } + } else { + loc = null; + } + } + } catch (Exception e) { + e.printStackTrace(); + loc = null; + } + } + if (loc == null) + buf.append(HTMLRenderer.sanitizeString(pn.getName())); + } else if ( ("eepsite".equals(type)) || ("i2p".equals(type)) || + ("website".equals(type)) || ("http".equals(type)) || ("web".equals(type)) ) { + String loc = pn.getLocation(); + if (loc != null) { + buf.append("<a href=\"externallink.jsp?"); + if (pn.getNetwork() != null) + buf.append("schema=").append(Base64.encode(pn.getNetwork())).append("&"); + if (pn.getLocation() != null) + buf.append("location=").append(Base64.encode(pn.getLocation())).append("&"); + buf.append("\" title=\"View ").append(HTMLRenderer.sanitizeTagParam(pn.getLocation())).append("\">"); + } + buf.append(HTMLRenderer.sanitizeString(pn.getName())); + if (loc != null) { + buf.append("</a>"); + } + } else { + buf.append("<a href=\"\" title=\"go somewhere? ").append(HTMLRenderer.sanitizeString(pn.toString())).append("\">"); + buf.append(HTMLRenderer.sanitizeString(pn.getName())).append("</a>"); + } + return buf.toString(); } private static final int POSTS_PER_PAGE = 5; @@ -410,7 +519,7 @@ public class ViewBlogServlet extends BaseServlet { "}\n" + ".syndieBlogLinkGroup li a {\n" + " display: block;\n" + -" width: 100%;\n" + +//" width: 100%;\n" + "}\n" + ".syndieBlogLinkGroupName {\n" + " font-size: 80%;\n" + diff --git a/history.txt b/history.txt index c2d67c55320580eb79c61e7d6ff3bad7acacb100..be1277259b51e2802072e0aeab2d9a5f5bcdd474 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,9 @@ -$Id: history.txt,v 1.378 2006/01/04 21:48:17 jrandom Exp $ +$Id: history.txt,v 1.379 2006/01/08 15:54:39 jrandom Exp $ + +2005-01-09 jrandom + * Bugfix for a rare SSU error (thanks cervantes!) + * More progress on the blog interface, allowing customizable blog-wide + links. 2006-01-08 jrandom * First pass of the new blog interface, though without much of the useful diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 78f51c017c7a5c58f02f75fb32a34f2afd5bd58e..639c3d3fcb3d17718375d7bff848c4cb01a73c8e 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.324 $ $Date: 2006/01/01 12:23:29 $"; + public final static String ID = "$Revision: 1.325 $ $Date: 2006/01/04 21:48:17 $"; public final static String VERSION = "0.6.1.8"; - public final static long BUILD = 8; + public final static long BUILD = 9; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java b/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java index ff2dd703e4252974fc81aaa39f1bdeab01443d32..e4148ea58388139cdf132fc8fb933fd3ff60c9c8 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java @@ -473,6 +473,7 @@ public class OutboundMessageFragments { // ok, simplest possible thing is to always tack on the bitfields if List msgIds = peer.getCurrentFullACKs(); + if (msgIds == null) msgIds = new ArrayList(); List partialACKBitfields = new ArrayList(); peer.fetchPartialACKs(partialACKBitfields); int piggybackedPartialACK = partialACKBitfields.size(); @@ -482,6 +483,10 @@ public class OutboundMessageFragments { for (int i = 0; i < fragments; i++) { if (state.needsSending(i)) { rv[i] = _builder.buildPacket(state, i, peer, remaining, partialACKBitfields); + if (rv[i] == null) { + sparseCount++; + continue; + } rv[i].setFragmentCount(fragments); OutNetMessage msg = state.getMessage(); if (msg != null)