package net.pterodactylus.sone.notify;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.FluentIterable.from;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import javax.annotation.Nonnull;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.PostReply;
-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 com.google.common.base.Optional;
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
+@Singleton
public class ListNotificationFilters {
+ private final PostVisibilityFilter postVisibilityFilter;
+ private final ReplyVisibilityFilter replyVisibilityFilter;
+
+ @Inject
+ public ListNotificationFilters(@Nonnull PostVisibilityFilter postVisibilityFilter, @Nonnull ReplyVisibilityFilter replyVisibilityFilter) {
+ this.postVisibilityFilter = postVisibilityFilter;
+ this.replyVisibilityFilter = replyVisibilityFilter;
+ }
+
/**
* Filters new-post and new-reply notifications in the given list of
* notifications. If {@code currentSone} is <code>null</code>, new-post and
* itself will be retained in the notifications.
*
* @param notifications
- * The notifications to filter
+ * The notifications to filter
* @param currentSone
- * The current Sone, or {@code null} if not logged in
+ * The current Sone, or {@code null} if not logged in
* @return The filtered notifications
*/
@SuppressWarnings("unchecked")
- public static List<Notification> filterNotifications(Collection<? extends Notification> notifications, Sone currentSone) {
+ public List<Notification> filterNotifications(Collection<? extends Notification> notifications, Sone currentSone) {
List<Notification> filteredNotifications = new ArrayList<Notification>();
for (Notification notification : notifications) {
if (notification.getId().equals("new-sone-notification")) {
}
filteredNotifications.add(notification);
} else if (notification.getId().equals("new-post-notification")) {
- if ((currentSone != null) && !currentSone.getOptions().isShowNewPostNotifications()) {
+ if (currentSone == null) {
continue;
}
- Optional<ListNotification<Post>> filteredNotification = filterNewPostNotification((ListNotification<Post>) notification, currentSone, true);
+ if (!currentSone.getOptions().isShowNewPostNotifications()) {
+ continue;
+ }
+ Optional<ListNotification<Post>> filteredNotification = filterNewPostNotification((ListNotification<Post>) notification, currentSone);
if (filteredNotification.isPresent()) {
filteredNotifications.add(filteredNotification.get());
}
} else if (notification.getId().equals("new-reply-notification")) {
- if ((currentSone != null) && !currentSone.getOptions().isShowNewReplyNotifications()) {
+ if (currentSone == null) {
continue;
}
- Optional<ListNotification<PostReply>> filteredNotification = filterNewReplyNotification((ListNotification<PostReply>) notification, currentSone);
+ if (!currentSone.getOptions().isShowNewReplyNotifications()) {
+ continue;
+ }
+ Optional<ListNotification<PostReply>> filteredNotification =
+ filterNewReplyNotification((ListNotification<PostReply>) notification, currentSone);
if (filteredNotification.isPresent()) {
filteredNotifications.add(filteredNotification.get());
}
} else if (notification.getId().equals("mention-notification")) {
- Optional<ListNotification<Post>> filteredNotification = filterNewPostNotification((ListNotification<Post>) notification, null, false);
+ Optional<ListNotification<Post>> filteredNotification = filterNewPostNotification((ListNotification<Post>) notification, null);
if (filteredNotification.isPresent()) {
filteredNotifications.add(filteredNotification.get());
}
* other posts are removed.
*
* @param newPostNotification
- * The new-post notification
+ * The new-post notification
* @param currentSone
- * The current Sone, or {@code null} if not logged in
- * @param soneRequired
- * Whether a non-{@code null} Sone in {@code currentSone} is
- * required
+ * The current Sone, or {@code null} if not logged in
* @return The filtered new-post notification, or {@code null} if the
- * notification should be removed
+ * notification should be removed
*/
- private static Optional<ListNotification<Post>> filterNewPostNotification(ListNotification<Post> newPostNotification, Sone currentSone, boolean soneRequired) {
- if (soneRequired && (currentSone == null)) {
- return Optional.absent();
- }
- List<Post> newPosts = new ArrayList<Post>();
- for (Post post : newPostNotification.getElements()) {
- if (isPostVisible(currentSone, post)) {
- newPosts.add(post);
- }
- }
+ @Nonnull
+ private Optional<ListNotification<Post>> filterNewPostNotification(@Nonnull ListNotification<Post> newPostNotification,
+ @Nonnull Sone currentSone) {
+ List<Post> newPosts = from(newPostNotification.getElements()).filter(postVisibilityFilter.isVisible(currentSone)).toList();
if (newPosts.isEmpty()) {
return Optional.absent();
}
* replies are removed.
*
* @param newReplyNotification
- * The new-reply notification
+ * The new-reply notification
* @param currentSone
- * The current Sone, or {@code null} if not logged in
+ * The current Sone, or {@code null} if not logged in
* @return The filtered new-reply notification, or {@code null} if the
- * notification should be removed
+ * notification should be removed
*/
- private static Optional<ListNotification<PostReply>> filterNewReplyNotification(ListNotification<PostReply> newReplyNotification, Sone currentSone) {
- if (currentSone == null) {
- return Optional.absent();
- }
- List<PostReply> newReplies = new ArrayList<PostReply>();
- for (PostReply reply : newReplyNotification.getElements()) {
- if (isReplyVisible(currentSone, reply)) {
- newReplies.add(reply);
- }
- }
+ private Optional<ListNotification<PostReply>> filterNewReplyNotification(ListNotification<PostReply> newReplyNotification,
+ @Nonnull Sone currentSone) {
+ List<PostReply> newReplies = from(newReplyNotification.getElements()).filter(replyVisibilityFilter.isVisible(currentSone)).toList();
if (newReplies.isEmpty()) {
return Optional.absent();
}
return Optional.of(filteredNotification);
}
- /**
- * Filters the given posts, using {@link #isPostVisible(Sone, Post)} to
- * decide whether a post should be contained in the returned list. If
- * {@code currentSone} is not {@code null} it is used to filter out posts
- * that are from Sones that are not followed or not trusted by the given
- * Sone.
- *
- * @param posts
- * The posts to filter
- * @param currentSone
- * The current Sone (may be {@code null})
- * @return The filtered posts
- */
- public static List<Post> filterPosts(Collection<Post> posts, Sone currentSone) {
- List<Post> filteredPosts = new ArrayList<Post>();
- for (Post post : posts) {
- if (isPostVisible(currentSone, post)) {
- filteredPosts.add(post);
- }
- }
- return filteredPosts;
- }
-
- /**
- * 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 post’s {@link Post#getTime() time} is in the future.</li>
- * </ul>
- * <p>
- * If {@code post} is not {@code null} more checks are performed, and the
- * post will be invisible if:
- * </p>
- * <ul>
- * <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 (may be
- * {@code null} to skip Sone-specific checks, such as trust)
- * @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) {
- checkNotNull(post, "post must not be null");
- if (!post.isLoaded()) {
- return false;
- }
- Sone postSone = post.getSone();
- if (sone != null) {
- 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 {
- /*
- * a null trust means that the trust updater has not yet
- * received a trust value for this relation. if we return false,
- * the post feed will stay empty until the trust updater has
- * received trust values. to prevent this we simply assume that
- * posts are visible if there is no trust.
- */
- }
- if ((!postSone.equals(sone)) && !sone.hasFriend(postSone.getId()) && !sone.getId().equals(post.getRecipientId().orNull())) {
- return false;
- }
- }
- return post.getTime() <= System.currentTimeMillis();
- }
-
- /**
- * Checks whether a reply is visible to the given Sone. A reply is not
- * considered visible if one of the following statements is true:
- * <ul>
- * <li>The reply does not have a post.</li>
- * <li>The reply’s post does not have a Sone.</li>
- * <li>The Sone of the reply’s post is not the given Sone, the given Sone
- * does not follow the reply’s post’s Sone, and the given Sone is not the
- * recipient of the reply’s 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
- * reply’s post’s Sone but the implicit trust is negative.</li>
- * <li>The reply’s post’s {@link Post#getTime() time} is in the future.</li>
- * <li>The reply’s {@link Reply#getTime() time} is in the future.</li>
- * </ul>
- * If none of these statements is true the reply is considered visible.
- *
- * @param sone
- * The Sone that checks for a post’s visibility (may be
- * {@code null} to skip Sone-specific checks, such as trust)
- * @param reply
- * The reply to check for visibility
- * @return {@code true} if the reply is considered visible, {@code false}
- * otherwise
- */
- public static boolean isReplyVisible(Sone sone, PostReply reply) {
- checkNotNull(reply, "reply must not be null");
- Optional<Post> post = reply.getPost();
- if (!post.isPresent()) {
- return false;
- }
- if (!isPostVisible(sone, post.get())) {
- return false;
- }
- return reply.getTime() <= System.currentTimeMillis();
- }
-
}
--- /dev/null
+package net.pterodactylus.sone.notify;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.inject.Singleton;
+
+import net.pterodactylus.sone.data.Post;
+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 com.google.common.base.Predicate;
+
+/**
+ * Filters {@link Notification}s involving {@link Post}s.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+@Singleton
+public class PostVisibilityFilter {
+
+ /**
+ * 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 post’s {@link Post#getTime() time} is in the future.</li>
+ * </ul>
+ * <p>
+ * If {@code post} is not {@code null} more checks are performed, and the
+ * post will be invisible if:
+ * </p>
+ * <ul>
+ * <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 (may be
+ * {@code null} to skip Sone-specific checks, such as trust)
+ * @param post
+ * The post to check for visibility
+ * @return {@code true} if the post is considered visible, {@code false}
+ * otherwise
+ */
+ boolean isPostVisible(@Nullable Sone sone, @Nonnull Post post) {
+ checkNotNull(post, "post must not be null");
+ if (!post.isLoaded()) {
+ return false;
+ }
+ Sone postSone = post.getSone();
+ if (sone != null) {
+ 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 {
+ /*
+ * a null trust means that the trust updater has not yet
+ * received a trust value for this relation. if we return false,
+ * the post feed will stay empty until the trust updater has
+ * received trust values. to prevent this we simply assume that
+ * posts are visible if there is no trust.
+ */
+ }
+ if ((!postSone.equals(sone)) && !sone.hasFriend(postSone.getId()) && !sone.getId().equals(post.getRecipientId().orNull())) {
+ return false;
+ }
+ }
+ return post.getTime() <= System.currentTimeMillis();
+ }
+
+ @Nonnull
+ public Predicate<Post> isVisible(@Nullable final Sone currentSone) {
+ return new Predicate<Post>() {
+ @Nonnull
+ @Override
+ public boolean apply(@Nullable Post post) {
+ return isPostVisible(currentSone, post);
+ }
+ };
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.notify;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Sone;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+
+/**
+ * Filter that checks a {@link PostReply} for visibility.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+@Singleton
+public class ReplyVisibilityFilter {
+
+ private final PostVisibilityFilter postVisibilityFilter;
+
+ @Inject
+ public ReplyVisibilityFilter(@Nonnull PostVisibilityFilter postVisibilityFilter) {
+ this.postVisibilityFilter = postVisibilityFilter;
+ }
+
+ /**
+ * Checks whether a reply is visible to the given Sone. A reply is not
+ * considered visible if one of the following statements is true:
+ * <ul>
+ * <li>The reply does not have a post.</li>
+ * <li>The reply’s post {@link PostVisibilityFilter#isPostVisible(Sone, Post) is not visible}.</li>
+ * <li>The reply’s {@link PostReply#getTime() time} is in the future.</li>
+ * </ul>
+ * If none of these statements is true the reply is considered visible.
+ *
+ * @param sone
+ * The Sone that checks for a post’s visibility (may be
+ * {@code null} to skip Sone-specific checks, such as trust)
+ * @param reply
+ * The reply to check for visibility
+ * @return {@code true} if the reply is considered visible, {@code false}
+ * otherwise
+ */
+ boolean isReplyVisible(@Nullable Sone sone, @Nonnull PostReply reply) {
+ checkNotNull(reply, "reply must not be null");
+ Optional<Post> post = reply.getPost();
+ if (!post.isPresent()) {
+ return false;
+ }
+ if (!postVisibilityFilter.isPostVisible(sone, post.get())) {
+ return false;
+ }
+ return reply.getTime() <= System.currentTimeMillis();
+ }
+
+ @Nonnull
+ public Predicate<PostReply> isVisible(@Nullable final Sone currentSone) {
+ return new Predicate<PostReply>() {
+ @Nonnull
+ @Override
+ public boolean apply(@Nullable PostReply postReply) {
+ return isReplyVisible(currentSone, postReply);
+ }
+ };
+ }
+
+}
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
+import com.google.common.base.Optional;
+
/**
* Page that lets the user dismiss a notification.
*
protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
super.processTemplate(request, templateContext);
String notificationId = request.getHttpRequest().getPartAsStringFailsafe("notification", 36);
- Notification notification = webInterface.getNotifications().getNotification(notificationId);
- if ((notification != null) && notification.isDismissable()) {
- notification.dismiss();
+ Optional<Notification> notification = webInterface.getNotification(notificationId);
+ if (notification.isPresent() && notification.get().isDismissable()) {
+ notification.get().dismiss();
}
String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
throw new RedirectException(returnPage);
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.notify.ListNotificationFilters;
+import net.pterodactylus.sone.notify.PostVisibilityFilter;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.collection.Pagination;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
/**
*/
public class IndexPage extends SoneTemplatePage {
- /**
- * @param template
- * The template to render
- * @param webInterface
- * The Sone web interface
- */
- public IndexPage(Template template, WebInterface webInterface) {
+ private final PostVisibilityFilter postVisibilityFilter;
+
+ public IndexPage(Template template, WebInterface webInterface, PostVisibilityFilter postVisibilityFilter) {
super("index.html", template, "Page.Index.Title", webInterface, true);
+ this.postVisibilityFilter = postVisibilityFilter;
}
//
}
}
}
- allPosts = Collections2.filter(allPosts, new Predicate<Post>() {
-
- @Override
- public boolean apply(Post post) {
- return ListNotificationFilters.isPostVisible(currentSone, post);
- }
- });
- allPosts = Collections2.filter(allPosts, Post.FUTURE_POSTS_FILTER);
+ allPosts = Collections2.filter(allPosts, postVisibilityFilter.isVisible(currentSone));
List<Post> sortedPosts = new ArrayList<Post>(allPosts);
Collections.sort(sortedPosts, Post.TIME_COMPARATOR);
Pagination<Post> pagination = new Pagination<Post>(sortedPosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(parseInt(request.getHttpRequest().getParam("page"), 0));
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
-
-import com.google.common.collect.Collections2;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.notify.ListNotificationFilters;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.notify.PostVisibilityFilter;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.collection.Pagination;
import net.pterodactylus.util.template.Template;
/**
* Page that displays all new posts and replies. The posts are filtered using
- * {@link ListNotificationFilters#filterPosts(java.util.Collection, net.pterodactylus.sone.data.Sone)}
- * and sorted by time.
+ * {@link PostVisibilityFilter#isPostVisible(Sone, Post)} and sorted by time.
*
* @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
*/
super.processTemplate(request, templateContext);
/* collect new elements from notifications. */
- Set<Post> posts = new HashSet<Post>(webInterface.getNewPosts());
- for (PostReply reply : Collections2.filter(webInterface.getNewReplies(), PostReply.HAS_POST_FILTER)) {
+ List<Post> posts = new ArrayList<Post>(webInterface.getNewPosts(getCurrentSone(request.getToadletContext(), false)));
+ for (PostReply reply : webInterface.getNewReplies(getCurrentSone(request.getToadletContext(), false))) {
posts.add(reply.getPost().get());
}
/* filter and sort them. */
- List<Post> sortedPosts = ListNotificationFilters.filterPosts(posts, webInterface.getCurrentSone(request.getToadletContext(), false));
- Collections.sort(sortedPosts, Post.TIME_COMPARATOR);
+ Collections.sort(posts, Post.TIME_COMPARATOR);
/* paginate them. */
- Pagination<Post> pagination = new Pagination<Post>(sortedPosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(parseInt(request.getHttpRequest().getParam("page"), 0));
+ Pagination<Post> pagination = new Pagination<Post>(posts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(parseInt(request.getHttpRequest().getParam("page"), 0));
templateContext.set("pagination", pagination);
templateContext.set("posts", pagination.getItems());
}
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.main.SonePlugin;
-import net.pterodactylus.sone.notify.ListNotificationFilters;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.sone.web.page.FreenetTemplatePage;
import net.pterodactylus.util.notify.Notification;
import net.pterodactylus.util.template.Template;
import net.pterodactylus.util.template.TemplateContext;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
import freenet.clients.http.SessionManager.Session;
import freenet.clients.http.ToadletContext;
import freenet.support.api.HTTPRequest;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
/**
* Base page for the Sone web interface.
*
* The path of the page
* @param template
* The template to render
- * @param webInterface
- * The Sone web interface
- */
- public SoneTemplatePage(String path, Template template, WebInterface webInterface) {
- this(path, template, null, webInterface, false);
- }
-
- /**
- * Creates a new template page for Sone that does not require the user to be
- * logged in.
- *
- * @param path
- * The path of the page
- * @param template
- * The template to render
* @param pageTitleKey
* The l10n key of the page title
* @param webInterface
templateContext.set("latestEdition", webInterface.getCore().getUpdateChecker().getLatestEdition());
templateContext.set("latestVersion", webInterface.getCore().getUpdateChecker().getLatestVersion());
templateContext.set("latestVersionTime", webInterface.getCore().getUpdateChecker().getLatestVersionDate());
- List<Notification> notifications = ListNotificationFilters.filterNotifications(webInterface.getNotifications().getNotifications(), currentSone);
+ List<Notification> notifications = new ArrayList<Notification>(webInterface.getNotifications(currentSone));
Collections.sort(notifications, Notification.CREATED_TIME_SORTER);
templateContext.set("notifications", notifications);
templateContext.set("notificationHash", notifications.hashCode());
package net.pterodactylus.sone.web;
+import static com.google.common.collect.FluentIterable.from;
import static java.util.logging.Logger.getLogger;
import static net.pterodactylus.util.template.TemplateParser.parse;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import net.pterodactylus.sone.core.Core;
import net.pterodactylus.sone.core.event.ImageInsertAbortedEvent;
import net.pterodactylus.sone.main.ReparseFilter;
import net.pterodactylus.sone.main.SonePlugin;
import net.pterodactylus.sone.notify.ListNotification;
+import net.pterodactylus.sone.notify.ListNotificationFilters;
+import net.pterodactylus.sone.notify.PostVisibilityFilter;
+import net.pterodactylus.sone.notify.ReplyVisibilityFilter;
import net.pterodactylus.sone.template.AlbumAccessor;
import net.pterodactylus.sone.template.CollectionAccessor;
import net.pterodactylus.sone.template.CssClassNameFilter;
import net.pterodactylus.util.web.RedirectPage;
import net.pterodactylus.util.web.TemplatePage;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.eventbus.Subscribe;
-import com.google.inject.Inject;
-
import freenet.clients.http.SessionManager;
import freenet.clients.http.SessionManager.Session;
import freenet.clients.http.ToadletContainer;
import freenet.l10n.BaseL10n;
import freenet.support.api.HTTPRequest;
+import com.google.common.base.Optional;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.eventbus.Subscribe;
+import com.google.inject.Inject;
+
/**
* Bundles functionality that a web interface of a Freenet plugin needs, e.g.
* references to l10n helpers.
/** The parser filter. */
private final ParserFilter parserFilter;
+ private final ListNotificationFilters listNotificationFilters;
+ private final PostVisibilityFilter postVisibilityFilter;
+ private final ReplyVisibilityFilter replyVisibilityFilter;
+
/** The “new Sone” notification. */
private final ListNotification<Sone> newSoneNotification;
* The Sone plugin
*/
@Inject
- public WebInterface(SonePlugin sonePlugin, Loaders loaders) {
+ public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilters listNotificationFilters, PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter) {
this.sonePlugin = sonePlugin;
this.loaders = loaders;
+ this.listNotificationFilters = listNotificationFilters;
+ this.postVisibilityFilter = postVisibilityFilter;
+ this.replyVisibilityFilter = replyVisibilityFilter;
formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
soneTextParser = new SoneTextParser(getCore(), getCore());
return notificationManager;
}
+ @Nonnull
+ public Optional<Notification> getNotification(@Nonnull String notificationId) {
+ return Optional.fromNullable(notificationManager.getNotification(notificationId));
+ }
+
+ @Nonnull
+ public Collection<Notification> getNotifications(@Nullable Sone currentSone) {
+ return listNotificationFilters.filterNotifications(notificationManager.getNotifications(), currentSone);
+ }
+
/**
* Returns the l10n helper of the node.
*
return ImmutableSet.<Post> builder().addAll(newPostNotification.getElements()).addAll(localPostNotification.getElements()).build();
}
+ @Nonnull
+ public Collection<Post> getNewPosts(@Nullable Sone currentSone) {
+ Set<Post> allNewPosts = ImmutableSet.<Post> builder()
+ .addAll(newPostNotification.getElements())
+ .addAll(localPostNotification.getElements())
+ .build();
+ return from(allNewPosts).filter(postVisibilityFilter.isVisible(currentSone)).toSet();
+ }
+
/**
* Returns the replies that have been announced as new in the
* {@link #newReplyNotification}.
return ImmutableSet.<PostReply> builder().addAll(newReplyNotification.getElements()).addAll(localReplyNotification.getElements()).build();
}
+ @Nonnull
+ public Collection<PostReply> getNewReplies(@Nullable Sone currentSone) {
+ Set<PostReply> allNewReplies = ImmutableSet.<PostReply>builder()
+ .addAll(newReplyNotification.getElements())
+ .addAll(localReplyNotification.getElements())
+ .build();
+ return from(allNewReplies).filter(replyVisibilityFilter.isVisible(currentSone)).toSet();
+ }
+
/**
* Sets whether the current start of the plugin is the first start. It is
* considered a first start if the configuration file does not exist.
PageToadletFactory pageToadletFactory = new PageToadletFactory(sonePlugin.pluginRespirator().getHLSimpleClient(), "/Sone/");
pageToadlets.add(pageToadletFactory.createPageToadlet(new RedirectPage<FreenetRequest>("", "index.html")));
- pageToadlets.add(pageToadletFactory.createPageToadlet(new IndexPage(indexTemplate, this), "Index"));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new IndexPage(indexTemplate, this, postVisibilityFilter), "Index"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new NewPage(newTemplate, this), "New"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new CreateSonePage(createSoneTemplate, this), "CreateSone"));
pageToadlets.add(pageToadletFactory.createPageToadlet(new KnownSonesPage(knownSonesTemplate, this), "KnownSones"));
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.notify.Notification;
+import com.google.common.base.Optional;
+
/**
* AJAX page that lets the user dismiss a notification.
*
@Override
protected JsonReturnObject createJsonObject(FreenetRequest request) {
String notificationId = request.getHttpRequest().getParam("notification");
- Notification notification = webInterface.getNotifications().getNotification(notificationId);
- if (notification == null) {
+ Optional<Notification> notification = webInterface.getNotification(notificationId);
+ if (!notification.isPresent()) {
return createErrorJsonObject("invalid-notification-id");
}
- if (!notification.isDismissable()) {
+ if (!notification.get().isDismissable()) {
return createErrorJsonObject("not-dismissable");
}
- notification.dismiss();
+ notification.get().dismiss();
return createSuccessJsonObject();
}
import java.io.IOException;
import java.io.StringWriter;
-import java.util.Collection;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.main.SonePlugin;
-import net.pterodactylus.sone.notify.ListNotificationFilters;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
import net.pterodactylus.util.notify.Notification;
@Override
protected JsonReturnObject createJsonObject(FreenetRequest request) {
Sone currentSone = getCurrentSone(request.getToadletContext(), false);
- Collection<Notification> notifications = webInterface.getNotifications().getNotifications();
- List<Notification> filteredNotifications = ListNotificationFilters.filterNotifications(notifications, currentSone);
- Collections.sort(filteredNotifications, Notification.CREATED_TIME_SORTER);
+ List<Notification> notifications = new ArrayList<Notification>(webInterface.getNotifications(currentSone));
+ Collections.sort(notifications, Notification.CREATED_TIME_SORTER);
ArrayNode jsonNotifications = new ArrayNode(instance);
- for (Notification notification : filteredNotifications) {
+ for (Notification notification : notifications) {
jsonNotifications.add(createJsonNotification(request, notification));
}
- return createSuccessJsonObject().put("notificationHash", filteredNotifications.hashCode()).put("notifications", jsonNotifications).put("options", createJsonOptions(currentSone));
+ return createSuccessJsonObject().put("notificationHash", notifications.hashCode()).put("notifications", jsonNotifications).put("options", createJsonOptions(currentSone));
}
//
import java.text.DateFormat;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.notify.ListNotificationFilters;
+import net.pterodactylus.sone.notify.PostVisibilityFilter;
+import net.pterodactylus.sone.notify.ReplyVisibilityFilter;
import net.pterodactylus.sone.template.SoneAccessor;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.FreenetRequest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
/**
* The “get status” AJAX handler returns all information that is necessary to
jsonSones.add(createJsonSone(sone));
}
/* load notifications. */
- List<Notification> notifications = ListNotificationFilters.filterNotifications(webInterface.getNotifications().getNotifications(), currentSone);
+ List<Notification> notifications = new ArrayList<Notification>(webInterface.getNotifications(currentSone));
Collections.sort(notifications, Notification.CREATED_TIME_SORTER);
/* load new posts. */
- Collection<Post> newPosts = webInterface.getNewPosts();
- if (currentSone != null) {
- newPosts = Collections2.filter(newPosts, new Predicate<Post>() {
-
- @Override
- public boolean apply(Post post) {
- return ListNotificationFilters.isPostVisible(currentSone, post);
- }
+ Collection<Post> newPosts = webInterface.getNewPosts(getCurrentSone(request.getToadletContext(), false));
- });
- }
ArrayNode jsonPosts = new ArrayNode(instance);
for (Post post : newPosts) {
ObjectNode jsonPost = new ObjectNode(instance);
jsonPosts.add(jsonPost);
}
/* load new replies. */
- Collection<PostReply> newReplies = webInterface.getNewReplies();
- if (currentSone != null) {
- newReplies = Collections2.filter(newReplies, new Predicate<PostReply>() {
-
- @Override
- public boolean apply(PostReply reply) {
- return ListNotificationFilters.isReplyVisible(currentSone, reply);
- }
+ Collection<PostReply> newReplies = webInterface.getNewReplies(getCurrentSone(request.getToadletContext(), false));
- });
- }
- /* remove replies to unknown posts. */
- newReplies = Collections2.filter(newReplies, PostReply.HAS_POST_FILTER);
ArrayNode jsonReplies = new ArrayNode(instance);
for (PostReply reply : newReplies) {
ObjectNode jsonReply = new ObjectNode(instance);
--- /dev/null
+package net.pterodactylus.sone.notify;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.emptyIterable;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.data.SoneOptions;
+import net.pterodactylus.sone.freenet.wot.OwnIdentity;
+import net.pterodactylus.util.notify.Notification;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.hamcrest.Matchers;
+import org.junit.Test;
+
+/**
+ * Unit test for {@link ListNotificationFiltersTest}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ListNotificationFiltersTest {
+
+ private static final String LOCAL_ID = "local-id";
+
+ private final PostVisibilityFilter postVisibilityFilter = mock(PostVisibilityFilter.class);
+ private final ReplyVisibilityFilter replyVisibilityFilter = mock(ReplyVisibilityFilter.class);
+ private final ListNotificationFilters listNotificationFilters = new ListNotificationFilters(postVisibilityFilter, replyVisibilityFilter);
+
+ private final Sone localSone = mock(Sone.class);
+ private final SoneOptions soneOptions = mock(SoneOptions.class);
+ private final OwnIdentity localIdentity = mock(OwnIdentity.class);
+ private final List<ListNotification<Post>> newPostNotifications = Arrays.asList(createNewPostNotification());
+ private final List<ListNotification<PostReply>> newReplyNotifications = Arrays.asList(createNewReplyNotification());
+ private final List<ListNotification<Post>> mentionNotifications = Arrays.asList(createMentionNotification());
+
+ public ListNotificationFiltersTest() {
+ when(localSone.getId()).thenReturn(LOCAL_ID);
+ when(localSone.isLocal()).thenReturn(true);
+ when(localSone.getIdentity()).thenReturn(localIdentity);
+ when(localIdentity.getId()).thenReturn(LOCAL_ID);
+ when(localSone.getOptions()).thenReturn(soneOptions);
+ }
+
+ @Test
+ public void filterIsOnlyCreatedOnce() {
+ Injector injector = Guice.createInjector();
+ ListNotificationFilters firstFilter = injector.getInstance(ListNotificationFilters.class);
+ ListNotificationFilters secondFilter = injector.getInstance(ListNotificationFilters.class);
+ assertThat(firstFilter, sameInstance(secondFilter));
+ }
+
+ @Test
+ public void newSoneNotificationsAreNotRemovedIfNotLoggedIn() {
+ List<Notification> notifications = Arrays.asList(createNewSoneNotification());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(notifications, null);
+ assertThat(filteredNotifications, contains(notifications.get(0)));
+ }
+
+ private Notification createNewSoneNotification() {
+ ListNotification<Sone> newSoneNotification = mock(ListNotification.class);
+ when(newSoneNotification.getId()).thenReturn("new-sone-notification");
+ return newSoneNotification;
+ }
+
+ @Test
+ public void newSoneNotificationsAreRemovedIfLoggedInAndNewSonesShouldNotBeShown() {
+ List<Notification> notifications = Arrays.asList(createNewSoneNotification());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(notifications, localSone);
+ assertThat(filteredNotifications, emptyIterable());
+ }
+
+ @Test
+ public void newSoneNotificationsAreNotRemovedIfLoggedInAndNewSonesShouldBeShown() {
+ List<Notification> notifications = Arrays.asList(createNewSoneNotification());
+ when(soneOptions.isShowNewSoneNotifications()).thenReturn(true);
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(notifications, localSone);
+ assertThat(filteredNotifications, contains(notifications.get(0)));
+ }
+
+ private ListNotification<Post> createNewPostNotification() {
+ ListNotification<Post> newSoneNotification = mock(ListNotification.class);
+ when(newSoneNotification.getElements()).thenReturn(new ArrayList<Post>());
+ when(newSoneNotification.getId()).thenReturn("new-post-notification");
+ return newSoneNotification;
+ }
+
+ @Test
+ public void newPostNotificationIsNotShownIfOptionsSetAccordingly() {
+ List<ListNotification<Post>> notifications = Arrays.asList(createNewPostNotification());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(notifications, localSone);
+ assertThat(filteredNotifications, hasSize(0));
+ }
+
+ private void activateNewPostNotifications() {
+ when(soneOptions.isShowNewPostNotifications()).thenReturn(true);
+ }
+
+ private boolean addPostToPostNotification(List<ListNotification<Post>> notifications) {
+ return notifications.get(0).getElements().add(mock(Post.class));
+ }
+
+ private void setPostVisibilityPredicate(Predicate<Post> value) {
+ when(postVisibilityFilter.isVisible(any(Sone.class))).thenReturn(value);
+ }
+
+ @Test
+ public void newPostNotificationIsNotShownIfNoNewPostsAreVisible() {
+ activateNewPostNotifications();
+ addPostToPostNotification(newPostNotifications);
+ setPostVisibilityPredicate(Predicates.<Post>alwaysFalse());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(newPostNotifications, localSone);
+ assertThat(filteredNotifications, hasSize(0));
+ }
+
+ @Test
+ public void newPostNotificationIsShownIfNewPostsAreVisible() {
+ activateNewPostNotifications();
+ addPostToPostNotification(newPostNotifications);
+ setPostVisibilityPredicate(Predicates.<Post>alwaysTrue());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(newPostNotifications, localSone);
+ assertThat(filteredNotifications, contains((Notification) newPostNotifications.get(0)));
+ }
+
+ @Test
+ public void newPostNotificationIsNotShownIfNewPostsAreVisibleButLocalSoneIsNull() {
+ activateNewPostNotifications();
+ addPostToPostNotification(newPostNotifications);
+ setPostVisibilityPredicate(Predicates.<Post>alwaysTrue());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(newPostNotifications, null);
+ assertThat(filteredNotifications, Matchers.<Notification>emptyIterable());
+ }
+
+ @Test
+ public void newPostNotificationContainsOnlyVisiblePosts() {
+ activateNewPostNotifications();
+ addPostToPostNotification(newPostNotifications);
+ addPostToPostNotification(newPostNotifications);
+ setPostVisibilityPredicate(new Predicate<Post>() {
+ @Override
+ public boolean apply(@Nullable Post post) {
+ return post.equals(newPostNotifications.get(0).getElements().get(1));
+ }
+ });
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(newPostNotifications, localSone);
+ assertThat(filteredNotifications, hasSize(1));
+ assertThat(((ListNotification<Post>) filteredNotifications.get(0)).getElements().get(0), is(newPostNotifications.get(0).getElements().get(1)));
+ }
+
+ private ListNotification<PostReply> createNewReplyNotification() {
+ ListNotification<PostReply> newReplyNotifications = mock(ListNotification.class);
+ when(newReplyNotifications.getElements()).thenReturn(new ArrayList<PostReply>());
+ when(newReplyNotifications.getId()).thenReturn("new-reply-notification");
+ return newReplyNotifications;
+ }
+
+ private void activateNewReplyNotifications() {
+ when(soneOptions.isShowNewReplyNotifications()).thenReturn(true);
+ }
+
+ private void addReplyToNewReplyNotification(List<ListNotification<PostReply>> notifications) {
+ notifications.get(0).getElements().add(mock(PostReply.class));
+ }
+
+ private void setReplyVisibilityPredicate(Predicate<PostReply> value) {
+ when(replyVisibilityFilter.isVisible(any(Sone.class))).thenReturn(value);
+ }
+
+ @Test
+ public void newReplyNotificationContainsOnlyVisibleReplies() {
+ activateNewReplyNotifications();
+ addReplyToNewReplyNotification(newReplyNotifications);
+ addReplyToNewReplyNotification(newReplyNotifications);
+ setReplyVisibilityPredicate(new Predicate<PostReply>() {
+ @Override
+ public boolean apply(@Nullable PostReply postReply) {
+ return postReply.equals(newReplyNotifications.get(0).getElements().get(1));
+ }
+ });
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(newReplyNotifications, localSone);
+ assertThat(filteredNotifications, hasSize(1));
+ assertThat(((ListNotification<PostReply>) filteredNotifications.get(0)).getElements().get(0), is(newReplyNotifications.get(0).getElements().get(1)));
+ }
+
+ @Test
+ public void newReplyNotificationIsNotModifiedIfAllRepliesAreVisible() {
+ activateNewReplyNotifications();
+ addReplyToNewReplyNotification(newReplyNotifications);
+ addReplyToNewReplyNotification(newReplyNotifications);
+ setReplyVisibilityPredicate(Predicates.<PostReply>alwaysTrue());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(newReplyNotifications, localSone);
+ assertThat(filteredNotifications, hasSize(1));
+ assertThat(filteredNotifications.get(0), is((Notification) newReplyNotifications.get(0)));
+ assertThat(((ListNotification<PostReply>) filteredNotifications.get(0)).getElements(), hasSize(2));
+ }
+
+ @Test
+ public void newReplyNotificationIsNotShownIfNoRepliesAreVisible() {
+ activateNewReplyNotifications();
+ addReplyToNewReplyNotification(newReplyNotifications);
+ addReplyToNewReplyNotification(newReplyNotifications);
+ setReplyVisibilityPredicate(Predicates.<PostReply>alwaysFalse());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(newReplyNotifications, localSone);
+ assertThat(filteredNotifications, hasSize(0));
+ }
+
+ @Test
+ public void newReplyNotificationIsNotShownIfDeactivatedInOptions() {
+ addReplyToNewReplyNotification(newReplyNotifications);
+ addReplyToNewReplyNotification(newReplyNotifications);
+ setReplyVisibilityPredicate(Predicates.<PostReply>alwaysTrue());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(newReplyNotifications, localSone);
+ assertThat(filteredNotifications, hasSize(0));
+ }
+
+ @Test
+ public void newReplyNotificationIsNotShownIfCurrentSoneIsNull() {
+ addReplyToNewReplyNotification(newReplyNotifications);
+ addReplyToNewReplyNotification(newReplyNotifications);
+ setReplyVisibilityPredicate(Predicates.<PostReply>alwaysTrue());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(newReplyNotifications, null);
+ assertThat(filteredNotifications, hasSize(0));
+ }
+
+ private ListNotification<Post> createMentionNotification() {
+ ListNotification<Post> newSoneNotification = mock(ListNotification.class);
+ when(newSoneNotification.getElements()).thenReturn(new ArrayList<Post>());
+ when(newSoneNotification.getId()).thenReturn("mention-notification");
+ return newSoneNotification;
+ }
+
+ @Test
+ public void mentionNotificationContainsOnlyVisiblePosts() {
+ addPostToPostNotification(mentionNotifications);
+ addPostToPostNotification(mentionNotifications);
+ setPostVisibilityPredicate(new Predicate<Post>() {
+ @Override
+ public boolean apply(@Nullable Post post) {
+ return post.equals(mentionNotifications.get(0).getElements().get(1));
+ }
+ });
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(mentionNotifications, localSone);
+ assertThat(filteredNotifications, hasSize(1));
+ assertThat(((ListNotification<Post>) filteredNotifications.get(0)).getElements().get(0), is(mentionNotifications.get(0).getElements().get(1)));
+ }
+
+ @Test
+ public void mentionNotificationIsNotShownIfNoPostsAreVisible() {
+ addPostToPostNotification(mentionNotifications);
+ addPostToPostNotification(mentionNotifications);
+ setPostVisibilityPredicate(Predicates.<Post>alwaysFalse());
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(mentionNotifications, localSone);
+ assertThat(filteredNotifications, hasSize(0));
+ }
+
+ @Test
+ public void unfilterableNotificationIsNotFiltered() {
+ Notification notification = mock(Notification.class);
+ when(notification.getId()).thenReturn("random-notification");
+ List<Notification> notifications = Arrays.asList(notification);
+ List<Notification> filteredNotifications = listNotificationFilters.filterNotifications(notifications, null);
+ assertThat(filteredNotifications, contains(notification));
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.notify;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.wot.Identity;
+import net.pterodactylus.sone.freenet.wot.OwnIdentity;
+import net.pterodactylus.sone.freenet.wot.Trust;
+
+import com.google.common.base.Optional;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.junit.Test;
+
+/**
+ * Unit test for {@link PostVisibilityFilterTest}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PostVisibilityFilterTest {
+
+ private static final String LOCAL_ID = "local-id";
+ private static final String REMOTE_ID = "remote-id";
+
+ private final PostVisibilityFilter postVisibilityFilter = new PostVisibilityFilter();
+
+ private final Sone localSone = mock(Sone.class);
+ private final OwnIdentity localIdentity = mock(OwnIdentity.class);
+ private final Post post = mock(Post.class);
+ private final Sone remoteSone = mock(Sone.class);
+ private final Identity remoteIdentity = mock(Identity.class);
+
+ public PostVisibilityFilterTest() {
+ when(localSone.getId()).thenReturn(LOCAL_ID);
+ when(localSone.isLocal()).thenReturn(true);
+ when(localSone.getIdentity()).thenReturn(localIdentity);
+ when(localIdentity.getId()).thenReturn(LOCAL_ID);
+ when(remoteSone.getId()).thenReturn(REMOTE_ID);
+ when(remoteSone.getIdentity()).thenReturn(remoteIdentity);
+ when(remoteIdentity.getId()).thenReturn(REMOTE_ID);
+ when(post.getRecipientId()).thenReturn(Optional.<String>absent());
+ }
+
+ @Test
+ public void postVisibilityFilterIsOnlyCreatedOnce() {
+ Injector injector = Guice.createInjector();
+ PostVisibilityFilter firstFilter = injector.getInstance(PostVisibilityFilter.class);
+ PostVisibilityFilter secondFilter = injector.getInstance(PostVisibilityFilter.class);
+ assertThat(firstFilter, sameInstance(secondFilter));
+ }
+
+ @Test
+ public void postIsNotVisibleIfItIsNotLoaded() {
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(false));
+ }
+
+ private static void makePostLoaded(Post post) {
+ when(post.isLoaded()).thenReturn(true);
+ }
+
+ @Test
+ public void loadedPostIsVisibleWithoutSone() {
+ makePostLoaded(post);
+ assertThat(postVisibilityFilter.isPostVisible(null, post), is(true));
+ }
+
+ private void makePostComeFromTheFuture() {
+ when(post.getTime()).thenReturn(System.currentTimeMillis() + 1000);
+ }
+
+ @Test
+ public void loadedPostFromTheFutureIsNotVisible() {
+ makePostLoaded(post);
+ makePostComeFromTheFuture();
+ assertThat(postVisibilityFilter.isPostVisible(null, post), is(false));
+ }
+
+ private void makePostFromRemoteSone() {
+ when(post.getSone()).thenReturn(remoteSone);
+ }
+
+ private void giveRemoteIdentityNegativeExplicitTrust() {
+ when(remoteIdentity.getTrust(localIdentity)).thenReturn(new Trust(-1, null, null));
+ }
+
+ @Test
+ public void loadedPostFromExplicitelyNotTrustedSoneIsNotVisible() {
+ makePostLoaded(post);
+ makePostFromRemoteSone();
+ giveRemoteIdentityNegativeExplicitTrust();
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(false));
+ }
+
+ private void giveRemoteIdentityNegativeImplicitTrust() {
+ when(remoteIdentity.getTrust(localIdentity)).thenReturn(new Trust(null, -1, null));
+ }
+
+ @Test
+ public void loadedPostFromImplicitelyUntrustedSoneIsNotVisible() {
+ makePostLoaded(post);
+ makePostFromRemoteSone();
+ giveRemoteIdentityNegativeImplicitTrust();
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(false));
+ }
+
+ private void makeLocalSoneFollowRemoteSone() {
+ when(localSone.hasFriend(REMOTE_ID)).thenReturn(true);
+ }
+
+ private void giveRemoteIdentityPositiveExplicitTrustButNegativeImplicitTrust() {
+ when(remoteIdentity.getTrust(localIdentity)).thenReturn(new Trust(1, -1, null));
+ }
+
+ @Test
+ public void loadedPostFromExplicitelyTrustedButImplicitelyUntrustedSoneIsVisible() {
+ makePostLoaded(post);
+ makePostFromRemoteSone();
+ makeLocalSoneFollowRemoteSone();
+ giveRemoteIdentityPositiveExplicitTrustButNegativeImplicitTrust();
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(true));
+ }
+
+ private void giveTheRemoteIdentityPositiveImplicitTrust() {
+ when(remoteIdentity.getTrust(localIdentity)).thenReturn(new Trust(null, 1, null));
+ }
+
+ @Test
+ public void loadedPostFromImplicitelyTrustedSoneIsVisible() {
+ makePostLoaded(post);
+ makePostFromRemoteSone();
+ makeLocalSoneFollowRemoteSone();
+ giveTheRemoteIdentityPositiveImplicitTrust();
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(true));
+ }
+
+ private void giveTheRemoteIdentityUnknownTrust() {
+ when(remoteIdentity.getTrust(localIdentity)).thenReturn(new Trust(null, null, null));
+ }
+
+ @Test
+ public void loadedPostFromSoneWithUnknownTrustIsVisible() {
+ makePostLoaded(post);
+ makePostFromRemoteSone();
+ makeLocalSoneFollowRemoteSone();
+ giveTheRemoteIdentityUnknownTrust();
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(true));
+ }
+
+ @Test
+ public void loadedPostFromUnfollowedRemoteSoneThatIsNotDirectedAtLocalSoneIsNotVisible() {
+ makePostLoaded(post);
+ makePostFromRemoteSone();
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(false));
+ }
+
+ private void makePostFromLocalSone() {
+ makePostLoaded(post);
+ when(post.getSone()).thenReturn(localSone);
+ }
+
+ @Test
+ public void loadedPostFromLocalSoneIsVisible() {
+ makePostFromLocalSone();
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(true));
+ }
+
+ @Test
+ public void loadedPostFromFollowedRemoteSoneThatIsNotDirectedAtLocalSoneIsVisible() {
+ makePostLoaded(post);
+ makePostFromRemoteSone();
+ makeLocalSoneFollowRemoteSone();
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(true));
+ }
+
+ private void makePostDirectedAtLocalId() {
+ when(post.getRecipientId()).thenReturn(Optional.of(LOCAL_ID));
+ }
+
+ @Test
+ public void loadedPostFromRemoteSoneThatIsDirectedAtLocalSoneIsVisible() {
+ makePostLoaded(post);
+ makePostFromRemoteSone();
+ makePostDirectedAtLocalId();
+ assertThat(postVisibilityFilter.isPostVisible(localSone, post), is(true));
+ }
+
+ @Test
+ public void predicateWillCorrectlyRecognizeVisiblePost() {
+ makePostFromLocalSone();
+ assertThat(postVisibilityFilter.isVisible(null).apply(post), is(true));
+ }
+
+ @Test
+ public void predicateWillCorrectlyRecognizeNotVisiblePost() {
+ assertThat(postVisibilityFilter.isVisible(null).apply(post), is(false));
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.notify;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.PostReply;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.wot.OwnIdentity;
+
+import com.google.common.base.Optional;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.junit.Test;
+
+/**
+ * Unit test for {@link ReplyVisibilityFilterTest}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ReplyVisibilityFilterTest {
+
+ private static final String LOCAL_ID = "local-id";
+
+ private final PostVisibilityFilter postVisibilityFilter = mock(PostVisibilityFilter.class);
+ private final ReplyVisibilityFilter replyVisibilityFilter = new ReplyVisibilityFilter(postVisibilityFilter);
+
+ private final Sone localSone = mock(Sone.class);
+ private final OwnIdentity localIdentity = mock(OwnIdentity.class);
+ private final Post post = mock(Post.class);
+ private final PostReply postReply = mock(PostReply.class);
+
+ public ReplyVisibilityFilterTest() {
+ when(localSone.getId()).thenReturn(LOCAL_ID);
+ when(localSone.isLocal()).thenReturn(true);
+ when(localSone.getIdentity()).thenReturn(localIdentity);
+ when(post.getRecipientId()).thenReturn(Optional.<String>absent());
+ }
+
+ @Test
+ public void replyVisibilityFilterIsOnlyCreatedOnce() {
+ Injector injector = Guice.createInjector();
+ ReplyVisibilityFilter firstFilter = injector.getInstance(ReplyVisibilityFilter.class);
+ ReplyVisibilityFilter secondFilter = injector.getInstance(ReplyVisibilityFilter.class);
+ assertThat(firstFilter, sameInstance(secondFilter));
+ }
+
+ private void makePostPresent() {
+ when(postReply.getPost()).thenReturn(Optional.of(post));
+ }
+
+ @Test
+ public void replyIsNotVisibleIfPostIsNotVisible() {
+ makePostPresent();
+ assertThat(replyVisibilityFilter.isReplyVisible(localSone, postReply), is(false));
+ }
+
+ private void makePostAbsent() {
+ when(postReply.getPost()).thenReturn(Optional.<Post>absent());
+ }
+
+ @Test
+ public void replyIsNotVisibleIfPostIsNotPresent() {
+ makePostAbsent();
+ assertThat(replyVisibilityFilter.isReplyVisible(localSone, postReply), is(false));
+ }
+
+ private void makePostPresentAndVisible() {
+ makePostPresent();
+ when(postVisibilityFilter.isPostVisible(localSone, post)).thenReturn(true);
+ }
+
+ private void makeReplyComeFromFuture() {
+ when(postReply.getTime()).thenReturn(System.currentTimeMillis() + 1000);
+ }
+
+ @Test
+ public void replyIsNotVisibleIfItIsFromTheFuture() {
+ makePostPresentAndVisible();
+ makeReplyComeFromFuture();
+ assertThat(replyVisibilityFilter.isReplyVisible(localSone, postReply), is(false));
+ }
+
+ @Test
+ public void replyIsVisibleIfItIsNotFromTheFuture() {
+ makePostPresentAndVisible();
+ assertThat(replyVisibilityFilter.isReplyVisible(localSone, postReply), is(true));
+ }
+
+ @Test
+ public void predicateCorrectlyRecognizesVisibleReply() {
+ makePostPresentAndVisible();
+ assertThat(replyVisibilityFilter.isVisible(localSone).apply(postReply), is(true));
+ }
+
+ @Test
+ public void predicateCorrectlyRecognizesNotVisibleReply() {
+ makePostPresentAndVisible();
+ makeReplyComeFromFuture();
+ assertThat(replyVisibilityFilter.isVisible(localSone).apply(postReply), is(false));
+ }
+
+}