Merge branch 'release-0.6.3' 0.6.3
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 28 Apr 2011 19:38:55 +0000 (21:38 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 28 Apr 2011 19:38:55 +0000 (21:38 +0200)
21 files changed:
pom.xml
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/SoneInserter.java
src/main/java/net/pterodactylus/sone/core/UpdateChecker.java
src/main/java/net/pterodactylus/sone/main/SonePlugin.java
src/main/java/net/pterodactylus/sone/notify/ListNotificationFilters.java
src/main/java/net/pterodactylus/sone/template/ParserFilter.java
src/main/java/net/pterodactylus/sone/text/FreenetLinkParser.java
src/main/java/net/pterodactylus/sone/text/FreenetLinkParserContext.java
src/main/java/net/pterodactylus/sone/web/IndexPage.java
src/main/java/net/pterodactylus/sone/web/SearchPage.java
src/main/java/net/pterodactylus/sone/web/ajax/GetPostAjaxPage.java
src/main/java/net/pterodactylus/sone/web/ajax/GetReplyAjaxPage.java
src/main/java/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.java
src/main/resources/i18n/sone.en.properties
src/main/resources/templates/insert/index.html
src/main/resources/templates/notify/newPostNotification.html
src/main/resources/templates/notify/newReplyNotification.html
src/main/resources/templates/notify/newSoneNotification.html
src/main/resources/templates/viewSone.html
src/test/java/net/pterodactylus/sone/text/FreenetLinkParserTest.java

diff --git a/pom.xml b/pom.xml
index daacd28..d7cd60c 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -2,12 +2,12 @@
        <modelVersion>4.0.0</modelVersion>
        <groupId>net.pterodactylus</groupId>
        <artifactId>sone</artifactId>
-       <version>0.6.2</version>
+       <version>0.6.3</version>
        <dependencies>
                <dependency>
                        <groupId>net.pterodactylus</groupId>
                        <artifactId>utils</artifactId>
-                       <version>0.9.4</version>
+                       <version>0.9.5</version>
                </dependency>
                <dependency>
                        <groupId>junit</groupId>
index fcbc889..55e3b65 100644 (file)
@@ -25,6 +25,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Map.Entry;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -877,6 +878,7 @@ public class Core implements IdentityListener, UpdateListener {
                }
                Sone sone = addLocalSone(ownIdentity);
                sone.getOptions().addBooleanOption("AutoFollow", new DefaultOption<Boolean>(false));
+               sone.addFriend("nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI");
                saveSone(sone);
                return sone;
        }
@@ -922,7 +924,7 @@ public class Core implements IdentityListener, UpdateListener {
                                @Override
                                @SuppressWarnings("synthetic-access")
                                public void run() {
-                                       soneDownloader.fetchSone(sone);
+                                       soneDownloader.fetchSone(sone, sone.getRequestUri());
                                }
 
                        }, "Sone Downloader").start();
@@ -1918,6 +1920,48 @@ public class Core implements IdentityListener, UpdateListener {
        @Override
        public void identityRemoved(OwnIdentity ownIdentity, Identity identity) {
                trustedIdentities.get(ownIdentity).remove(identity);
+               boolean foundIdentity = false;
+               for (Entry<OwnIdentity, Set<Identity>> trustedIdentity : trustedIdentities.entrySet()) {
+                       if (trustedIdentity.getKey().equals(ownIdentity)) {
+                               continue;
+                       }
+                       if (trustedIdentity.getValue().contains(identity)) {
+                               foundIdentity = true;
+                       }
+               }
+               if (foundIdentity) {
+                       /* some local identity still trusts this identity, don’t remove. */
+                       return;
+               }
+               Sone sone = getSone(identity.getId(), false);
+               if (sone == null) {
+                       /* TODO - we don’t have the Sone anymore. should this happen? */
+                       return;
+               }
+               synchronized (posts) {
+                       synchronized (newPosts) {
+                               for (Post post : sone.getPosts()) {
+                                       posts.remove(post.getId());
+                                       newPosts.remove(post.getId());
+                                       coreListenerManager.firePostRemoved(post);
+                               }
+                       }
+               }
+               synchronized (replies) {
+                       synchronized (newReplies) {
+                               for (Reply reply : sone.getReplies()) {
+                                       replies.remove(reply.getId());
+                                       newReplies.remove(reply.getId());
+                                       coreListenerManager.fireReplyRemoved(reply);
+                               }
+                       }
+               }
+               synchronized (remoteSones) {
+                       remoteSones.remove(identity.getId());
+               }
+               synchronized (newSones) {
+                       newSones.remove(identity.getId());
+               }
        }
 
        //
index 3936975..0abebc3 100644 (file)
@@ -229,7 +229,6 @@ public class SoneInserter extends AbstractService {
                                        synchronized (sone) {
                                                if (lastInsertFingerprint.equals(sone.getFingerprint())) {
                                                        logger.log(Level.FINE, "Sone “%s” was not modified further, resetting counter…", new Object[] { sone });
-                                                       core.saveSone(sone);
                                                        lastModificationTime = 0;
                                                        modified = false;
                                                }
@@ -248,7 +247,7 @@ public class SoneInserter extends AbstractService {
         *
         * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
         */
-       private static class InsertInformation {
+       private class InsertInformation {
 
                /** All properties of the Sone, copied for thread safety. */
                private final Map<String, Object> soneProperties = new HashMap<String, Object>();
@@ -347,6 +346,7 @@ public class SoneInserter extends AbstractService {
 
                        TemplateContext templateContext = templateContextFactory.createTemplateContext();
                        templateContext.set("currentSone", soneProperties);
+                       templateContext.set("currentEdition", core.getUpdateChecker().getLatestEdition());
                        templateContext.set("version", SonePlugin.VERSION);
                        StringWriter writer = new StringWriter();
                        StringBucket bucket = null;
index e07e160..7f03b3a 100644 (file)
@@ -49,7 +49,7 @@ public class UpdateChecker {
        private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/";
 
        /** The current latest known edition. */
-       private static final int LATEST_EDITION = 25;
+       private static final int LATEST_EDITION = 35;
 
        /** The Freenet interface. */
        private final FreenetInterface freenetInterface;
index 7ada162..ac7da00 100644 (file)
@@ -78,7 +78,7 @@ public class SonePlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10
        }
 
        /** The version. */
-       public static final Version VERSION = new Version(0, 6, 2);
+       public static final Version VERSION = new Version(0, 6, 3);
 
        /** The logger. */
        private static final Logger logger = Logging.getLogger(SonePlugin.class);
index fe024d6..207f4cc 100644 (file)
@@ -24,7 +24,10 @@ import java.util.List;
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.wot.OwnIdentity;
+import net.pterodactylus.sone.freenet.wot.Trust;
 import net.pterodactylus.util.notify.Notification;
+import net.pterodactylus.util.validation.Validation;
 
 /**
  * Filter for {@link ListNotification}s.
@@ -90,7 +93,7 @@ public class ListNotificationFilters {
                }
                List<Post> newPosts = new ArrayList<Post>();
                for (Post post : newPostNotification.getElements()) {
-                       if (currentSone.hasFriend(post.getSone().getId()) || currentSone.equals(post.getSone()) || currentSone.equals(post.getRecipient())) {
+                       if (isPostVisible(currentSone, post)) {
                                newPosts.add(post);
                        }
                }
@@ -125,7 +128,7 @@ public class ListNotificationFilters {
                }
                List<Reply> newReplies = new ArrayList<Reply>();
                for (Reply reply : newReplyNotification.getElements()) {
-                       if (((reply.getPost().getSone() != null) && currentSone.hasFriend(reply.getPost().getSone().getId())) || currentSone.equals(reply.getPost().getSone()) || currentSone.equals(reply.getPost().getRecipient())) {
+                       if (isPostVisible(currentSone, reply.getPost())) {
                                newReplies.add(reply);
                        }
                }
@@ -166,4 +169,50 @@ public class ListNotificationFilters {
                return null;
        }
 
+       /**
+        * Checks whether a post is visible to the given Sone. A post is not
+        * considered visible if one of the following statements is true:
+        * <ul>
+        * <li>The post does not have a Sone.</li>
+        * <li>The Sone of the post is not the given Sone, the given Sone does not
+        * follow the post’s Sone, and the given Sone is not the recipient of the
+        * post.</li>
+        * <li>The trust relationship between the two Sones can not be retrieved.</li>
+        * <li>The given Sone has explicitely assigned negative trust to the post’s
+        * Sone.</li>
+        * <li>The given Sone has not explicitely assigned negative trust to the
+        * post’s Sone but the implicit trust is negative.</li>
+        * </ul>
+        * If none of these statements is true the post is considered visible.
+        *
+        * @param sone
+        *            The Sone that checks for a post’s visibility
+        * @param post
+        *            The post to check for visibility
+        * @return {@code true} if the post is considered visible, {@code false}
+        *         otherwise
+        */
+       public static boolean isPostVisible(Sone sone, Post post) {
+               Validation.begin().isNotNull("Sone", sone).isNotNull("Post", post).check().isNotNull("Sone’s Identity", sone.getIdentity()).check().isInstanceOf("Sone’s Identity", sone.getIdentity(), OwnIdentity.class).check();
+               Sone postSone = post.getSone();
+               if (postSone == null) {
+                       return false;
+               }
+               Trust trust = postSone.getIdentity().getTrust((OwnIdentity) sone.getIdentity());
+               if (trust != null) {
+                       if ((trust.getExplicit() != null) && (trust.getExplicit() < 0)) {
+                               return false;
+                       }
+                       if ((trust.getExplicit() == null) && (trust.getImplicit() != null) && (trust.getImplicit() < 0)) {
+                               return false;
+                       }
+               } else {
+                       return false;
+               }
+               if ((!postSone.equals(sone)) && !sone.hasFriend(postSone.getId()) && !sone.equals(post.getRecipient())) {
+                       return false;
+               }
+               return true;
+       }
+
 }
index 477e972..db945b5 100644 (file)
@@ -25,6 +25,7 @@ import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.text.FreenetLinkParser;
 import net.pterodactylus.sone.text.FreenetLinkParserContext;
+import net.pterodactylus.sone.web.page.Page.Request;
 import net.pterodactylus.util.template.Filter;
 import net.pterodactylus.util.template.TemplateContext;
 import net.pterodactylus.util.template.TemplateContextFactory;
@@ -70,7 +71,7 @@ public class ParserFilter implements Filter {
                if (sone == null) {
                        sone = core.getSone(soneKey, false);
                }
-               FreenetLinkParserContext context = new FreenetLinkParserContext(sone);
+               FreenetLinkParserContext context = new FreenetLinkParserContext((Request) templateContext.get("request"), sone);
                try {
                        return linkParser.parse(context, new StringReader(text));
                } catch (IOException ioe1) {
index cea4452..4b88acc 100644 (file)
@@ -127,6 +127,18 @@ public class FreenetLinkParser implements Parser<FreenetLinkParserContext> {
                        }
                        emptyLines = 0;
                        boolean lineComplete = true;
+
+                       /* filter http(s) links to own node. */
+                       String hostHeader = (context.getRequest() != null) ? context.getRequest().getHttpRequest().getHeader("host") : null;
+                       logger.log(Level.FINEST, "hostHeader: %s", hostHeader);
+                       if (hostHeader != null) {
+                               for (String toRemove : new String[] { "http://" + hostHeader + "/", "https://" + hostHeader + "/", "http://" + hostHeader, "https://" + hostHeader }) {
+                                       while (line.indexOf(toRemove) != -1) {
+                                               line = line.replace(toRemove, "");
+                                       }
+                               }
+                       }
+
                        while (line.length() > 0) {
                                int nextKsk = line.indexOf("KSK@");
                                int nextChk = line.indexOf("CHK@");
@@ -253,7 +265,7 @@ public class FreenetLinkParser implements Parser<FreenetLinkParserContext> {
                                        } else if (linkType == LinkType.POST) {
                                                String postId = link.substring(7);
                                                Post post = core.getPost(postId, false);
-                                               if (post != null) {
+                                               if ((post != null) && (post.getSone() != null)) {
                                                        String postText = post.getText();
                                                        postText = postText.substring(0, Math.min(postText.length(), 20)) + "…";
                                                        Sone postSone = post.getSone();
index e524d91..0712fc0 100644 (file)
@@ -18,6 +18,7 @@
 package net.pterodactylus.sone.text;
 
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.web.page.Page.Request;
 
 /**
  * {@link ParserContext} implementation for the {@link FreenetLinkParser}. It
@@ -28,20 +29,35 @@ import net.pterodactylus.sone.data.Sone;
  */
 public class FreenetLinkParserContext implements ParserContext {
 
+       /** The request being processed. */
+       private final Request request;
+
        /** The posting Sone. */
        private final Sone postingSone;
 
        /**
         * Creates a new link parser context.
         *
+        * @param request
+        *            The request being processed
         * @param postingSone
         *            The posting Sone
         */
-       public FreenetLinkParserContext(Sone postingSone) {
+       public FreenetLinkParserContext(Request request, Sone postingSone) {
+               this.request = request;
                this.postingSone = postingSone;
        }
 
        /**
+        * Returns the request that is currently being processed.
+        *
+        * @return The request being processed
+        */
+       public Request getRequest() {
+               return request;
+       }
+
+       /**
         * Returns the Sone that provided the text that is being parsed.
         *
         * @return The posting Sone
index 922d5ef..35b88a9 100644 (file)
@@ -23,7 +23,9 @@ import java.util.List;
 
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.notify.ListNotificationFilters;
 import net.pterodactylus.util.collection.Pagination;
+import net.pterodactylus.util.filter.Filter;
 import net.pterodactylus.util.filter.Filters;
 import net.pterodactylus.util.number.Numbers;
 import net.pterodactylus.util.template.Template;
@@ -57,7 +59,7 @@ public class IndexPage extends SoneTemplatePage {
        @Override
        protected void processTemplate(Request request, TemplateContext templateContext) throws RedirectException {
                super.processTemplate(request, templateContext);
-               Sone currentSone = getCurrentSone(request.getToadletContext());
+               final Sone currentSone = getCurrentSone(request.getToadletContext());
                List<Post> allPosts = new ArrayList<Post>();
                allPosts.addAll(currentSone.getPosts());
                for (String friendSoneId : currentSone.getFriends()) {
@@ -73,6 +75,13 @@ public class IndexPage extends SoneTemplatePage {
                                }
                        }
                }
+               allPosts = Filters.filteredList(allPosts, new Filter<Post>() {
+
+                       @Override
+                       public boolean filterObject(Post post) {
+                               return ListNotificationFilters.isPostVisible(currentSone, post);
+                       }
+               });
                allPosts = Filters.filteredList(allPosts, Post.FUTURE_POSTS_FILTER);
                Collections.sort(allPosts, Post.TIME_COMPARATOR);
                Pagination<Post> pagination = new Pagination<Post>(allPosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("page"), 0));
index 74bfd10..6f5e001 100644 (file)
@@ -32,8 +32,8 @@ import net.pterodactylus.sone.data.Profile;
 import net.pterodactylus.sone.data.Profile.Field;
 import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.util.collection.Converter;
-import net.pterodactylus.util.collection.Converters;
+import net.pterodactylus.util.collection.Mapper;
+import net.pterodactylus.util.collection.Mappers;
 import net.pterodactylus.util.collection.Pagination;
 import net.pterodactylus.util.filter.Filter;
 import net.pterodactylus.util.filter.Filters;
@@ -105,8 +105,8 @@ public class SearchPage extends SoneTemplatePage {
                Collections.sort(sortedPostHits, Hit.DESCENDING_COMPARATOR);
 
                /* extract Sones and posts. */
-               List<Sone> resultSones = Converters.convertList(sortedSoneHits, new HitConverter<Sone>());
-               List<Post> resultPosts = Converters.convertList(sortedPostHits, new HitConverter<Post>());
+               List<Sone> resultSones = Mappers.mappedList(sortedSoneHits, new HitMapper<Sone>());
+               List<Post> resultPosts = Mappers.mappedList(sortedPostHits, new HitMapper<Post>());
 
                /* pagination. */
                Pagination<Sone> sonePagination = new Pagination<Sone>(resultSones, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(Numbers.safeParseInteger(request.getHttpRequest().getParam("sonePage"), 0));
@@ -480,13 +480,13 @@ public class SearchPage extends SoneTemplatePage {
         *            The type of the object to extract
         * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
         */
-       public static class HitConverter<T> implements Converter<Hit<T>, T> {
+       public static class HitMapper<T> implements Mapper<Hit<T>, T> {
 
                /**
                 * {@inheritDoc}
                 */
                @Override
-               public T convert(Hit<T> input) {
+               public T map(Hit<T> input) {
                        return input.getObject();
                }
 
index 4e5f1b8..f12f603 100644 (file)
@@ -62,7 +62,7 @@ public class GetPostAjaxPage extends JsonPage {
                if (post == null) {
                        return createErrorJsonObject("invalid-post-id");
                }
-               return createSuccessJsonObject().put("post", createJsonPost(post, getCurrentSone(request.getToadletContext())));
+               return createSuccessJsonObject().put("post", createJsonPost(request, post, getCurrentSone(request.getToadletContext())));
        }
 
        /**
@@ -87,7 +87,7 @@ public class GetPostAjaxPage extends JsonPage {
         *            The currently logged in Sone (to store in the template)
         * @return The JSON representation of the post
         */
-       private JsonObject createJsonPost(Post post, Sone currentSone) {
+       private JsonObject createJsonPost(Request request, Post post, Sone currentSone) {
                JsonObject jsonPost = new JsonObject();
                jsonPost.put("id", post.getId());
                jsonPost.put("sone", post.getSone().getId());
@@ -95,6 +95,7 @@ public class GetPostAjaxPage extends JsonPage {
                jsonPost.put("time", post.getTime());
                StringWriter stringWriter = new StringWriter();
                TemplateContext templateContext = webInterface.getTemplateContextFactory().createTemplateContext();
+               templateContext.set("request", request);
                templateContext.set("post", post);
                templateContext.set("currentSone", currentSone);
                templateContext.set("localSones", webInterface.getCore().getLocalSones());
index c19bcba..82226e3 100644 (file)
@@ -65,7 +65,7 @@ public class GetReplyAjaxPage extends JsonPage {
                if ((reply == null) || (reply.getSone() == null)) {
                        return createErrorJsonObject("invalid-reply-id");
                }
-               return createSuccessJsonObject().put("reply", createJsonReply(reply, getCurrentSone(request.getToadletContext())));
+               return createSuccessJsonObject().put("reply", createJsonReply(request, reply, getCurrentSone(request.getToadletContext())));
        }
 
        /**
@@ -89,7 +89,7 @@ public class GetReplyAjaxPage extends JsonPage {
         *            The currently logged in Sone (to store in the template)
         * @return The JSON representation of the reply
         */
-       private JsonObject createJsonReply(Reply reply, Sone currentSone) {
+       private JsonObject createJsonReply(Request request, Reply reply, Sone currentSone) {
                JsonObject jsonReply = new JsonObject();
                jsonReply.put("id", reply.getId());
                jsonReply.put("postId", reply.getPost().getId());
@@ -97,6 +97,7 @@ public class GetReplyAjaxPage extends JsonPage {
                jsonReply.put("time", reply.getTime());
                StringWriter stringWriter = new StringWriter();
                TemplateContext templateContext = webInterface.getTemplateContextFactory().createTemplateContext();
+               templateContext.set("request", request);
                templateContext.set("reply", reply);
                templateContext.set("currentSone", currentSone);
                try {
index 7d581d7..9531a6d 100644 (file)
@@ -114,7 +114,7 @@ public class GetStatusAjaxPage extends JsonPage {
 
                                @Override
                                public boolean filterObject(Reply reply) {
-                                       return currentSone.hasFriend(reply.getPost().getSone().getId()) || currentSone.equals(reply.getPost().getSone()) || currentSone.equals(reply.getPost().getRecipient());
+                                       return (reply.getPost() != null) && (reply.getPost().getSone() != null) && (currentSone.hasFriend(reply.getPost().getSone().getId()) || currentSone.equals(reply.getPost().getSone()) || currentSone.equals(reply.getPost().getRecipient()));
                                }
 
                        });
index 06d036a..737785a 100644 (file)
@@ -34,7 +34,7 @@ Page.Options.Page.Description=These options influence the runtime behaviour of t
 Page.Options.Section.SoneSpecificOptions.Title=Sone-specific Options
 Page.Options.Section.SoneSpecificOptions.NotLoggedIn=These options are only available if you are {link}logged in{/link}.
 Page.Options.Section.SoneSpecificOptions.LoggedIn=These options are only available while you are logged in and they are only valid for the Sone you are logged in as.
-Page.Options.Option.AutoFollow.Description=If a new Sone is discovered, follow it automatically.
+Page.Options.Option.AutoFollow.Description=If a new Sone is discovered, follow it automatically. Note that this will only follow Sones that are discovered after you activate this option!
 Page.Options.Section.RuntimeOptions.Title=Runtime Behaviour
 Page.Options.Option.InsertionDelay.Description=The number of seconds the Sone inserter waits after a modification of a Sone before it is being inserted.
 Page.Options.Option.PostsPerPage.Description=The number of posts to display on a page before pagination controls are being shown.
@@ -134,6 +134,7 @@ Page.ViewSone.PostList.Title=Posts by {sone}
 Page.ViewSone.PostList.Text.NoPostYet=This Sone has not yet posted anything.
 Page.ViewSone.Profile.Title=Profile
 Page.ViewSone.Profile.Label.Name=Name
+Page.ViewSone.Profile.Name.WoTLink=Web of trust profile
 Page.ViewSone.Replies.Title=Posts {sone} has replied to
 
 Page.ViewPost.Title=View Post - Sone
index 73e35a8..7295361 100644 (file)
@@ -6,7 +6,7 @@
                <h1>Sone “<% currentSone.name|html>”</h1>
                <p>
                        This page should not be viewed directly; it is merely a reminder
-                       that you should install the <i>Sone</i> plugin.
+                       that you should install the <a href="/USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/<% currentEdition>/">Sone</a> plugin.
                </p>
        </body>
 </html>
index 54008dc..0cd71ff 100644 (file)
@@ -1,15 +1,15 @@
+<form class="mark-as-read" action="markAsKnown.html" method="post">
+       <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+       <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+       <input type="hidden" name="type" value="post" />
+       <input type="hidden" name="id" value="<%foreach posts post><% post.id|html><%notlast> <%/notlast><%/foreach>" />
+       <button type="submit" name="mark-read" value="true"><%= Notification.NewPost.Button.MarkRead|l10n|html></button>
+</form>
 <div class="short-text hidden">
        <%= Notification.NewPost.ShortText|l10n|html>
        <a class="link" onclick="showNotificationDetails('<%notification.id|html>'); return false;"><%= Notification.ClickHereToRead|l10n|html></a>
 </div>
 <div class="text">
-       <form class="mark-as-read" action="markAsKnown.html" method="post">
-               <input type="hidden" name="formPassword" value="<% formPassword|html>" />
-               <input type="hidden" name="returnPage" value="<% request.uri|html>" />
-               <input type="hidden" name="type" value="post" />
-               <input type="hidden" name="id" value="<%foreach posts post><% post.id|html><%notlast> <%/notlast><%/foreach>" />
-               <button type="submit" name="mark-read" value="true"><%= Notification.NewPost.Button.MarkRead|l10n|html></button>
-       </form>
        <%= Notification.NewPost.Text|l10n|html>
        <%foreach posts post>
                <div class="hidden post-id"><%post.id|html></div>
index ee8c410..a4588e5 100644 (file)
@@ -1,15 +1,15 @@
+<form class="mark-as-read" action="markAsKnown.html" method="post">
+       <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+       <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+       <input type="hidden" name="type" value="reply" />
+       <input type="hidden" name="id" value="<%foreach replies reply><% reply.id|html><%notlast> <%/notlast><%/foreach>" />
+       <button type="submit" name="mark-read" value="true"><%= Notification.NewPost.Button.MarkRead|l10n|html></button>
+</form>
 <div class="short-text hidden">
        <%= Notification.NewReply.ShortText|l10n|html>
        <a class="link" onclick="showNotificationDetails('<%notification.id|html>'); return false;"><%= Notification.ClickHereToRead|l10n|html></a>
 </div>
 <div class="text">
-       <form class="mark-as-read" action="markAsKnown.html" method="post">
-               <input type="hidden" name="formPassword" value="<% formPassword|html>" />
-               <input type="hidden" name="returnPage" value="<% request.uri|html>" />
-               <input type="hidden" name="type" value="reply" />
-               <input type="hidden" name="id" value="<%foreach replies reply><% reply.id|html><%notlast> <%/notlast><%/foreach>" />
-               <button type="submit" name="mark-read" value="true"><%= Notification.NewPost.Button.MarkRead|l10n|html></button>
-       </form>
        <%foreach replies reply><div class="hidden reply-id"><%reply.id|html></div><%/foreach>
        <%= Notification.NewReply.Text|l10n|html>
        <%foreach replies postGroup|replyGroup>
index aaa0d14..03cacf2 100644 (file)
@@ -1,15 +1,15 @@
+<form class="mark-as-read" action="markAsKnown.html" method="post">
+       <input type="hidden" name="formPassword" value="<% formPassword|html>" />
+       <input type="hidden" name="returnPage" value="<% request.uri|html>" />
+       <input type="hidden" name="type" value="sone" />
+       <input type="hidden" name="id" value="<%foreach sones sone><% sone.id|html><%notlast> <%/notlast><%/foreach>" />
+       <button type="submit" name="mark-read" value="true"><%= Notification.NewPost.Button.MarkRead|l10n|html></button>
+</form>
 <div class="short-text hidden">
        <%= Notification.NewSone.ShortText|l10n|html>
        <a class="link" onclick="showNotificationDetails('<%notification.id|html>'); return false;"><%= Notification.ClickHereToRead|l10n|html></a>
 </div>
 <div class="text">
-       <form class="mark-as-read" action="markAsKnown.html" method="post">
-               <input type="hidden" name="formPassword" value="<% formPassword|html>" />
-               <input type="hidden" name="returnPage" value="<% request.uri|html>" />
-               <input type="hidden" name="type" value="sone" />
-               <input type="hidden" name="id" value="<%foreach sones sone><% sone.id|html><%notlast> <%/notlast><%/foreach>" />
-               <button type="submit" name="mark-read" value="true"><%= Notification.NewPost.Button.MarkRead|l10n|html></button>
-       </form>
        <%= Notification.NewSone.Text|l10n|html>
        <%foreach sones sone>
                <div class="hidden sone-id"><% sone.id|html></div>
index 27b156d..f915628 100644 (file)
@@ -25,7 +25,7 @@
 
                        <div class="profile-field">
                                <div class="name"><%= Page.ViewSone.Profile.Label.Name|l10n|html></div>
-                               <div class="value"><a href="/WebOfTrust/ShowIdentity?id=<% sone.id|html>"><% sone.niceName|html></a></div>
+                               <div class="value"><% sone.niceName|html> (<a href="/WebOfTrust/ShowIdentity?id=<% sone.id|html>"><%= Page.ViewSone.Profile.Name.WoTLink|l10n|html></a>)</div>
                        </div>
 
                        <%foreach sone.profile.fields field>
index 7e05fd5..e667aeb 100644 (file)
@@ -41,7 +41,7 @@ public class FreenetLinkParserTest extends TestCase {
                TemplateContextFactory templateContextFactory = new TemplateContextFactory();
                templateContextFactory.addFilter("html", new HtmlFilter());
                FreenetLinkParser parser = new FreenetLinkParser(null, templateContextFactory);
-               FreenetLinkParserContext context = new FreenetLinkParserContext(null);
+               FreenetLinkParserContext context = new FreenetLinkParserContext(null, null);
                Part part;
 
                part = parser.parse(context, new StringReader("Text."));