From c7ff1ba974ad153d6ef28c740ee30ce3a047ff34 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 10 Apr 2017 12:15:48 -0300 Subject: [PATCH 1/8] Store RSS feeds in a separate dedicated blog A fake LocalAuthor is created for this new blog and stored in the feed's metadata. --- .../briar/android/blog/FeedFragment.java | 1 - .../android/blog/RssFeedImportActivity.java | 13 +- .../android/blog/RssFeedManageActivity.java | 1 - .../org/briarproject/briar/api/feed/Feed.java | 82 +++++-------- .../briar/api/feed/FeedConstants.java | 4 +- .../briar/api/feed/FeedManager.java | 5 +- .../briarproject/briar/feed/FeedFactory.java | 34 ++++++ .../briar/feed/FeedFactoryImpl.java | 111 ++++++++++++++++++ .../briar/feed/FeedManagerImpl.java | 105 ++++++++++------- .../briarproject/briar/feed/FeedModule.java | 5 + 10 files changed, 245 insertions(+), 116 deletions(-) create mode 100644 briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java index 82a432091..c7a64659a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java @@ -179,7 +179,6 @@ public class FeedFragment extends BaseFragment implements case R.id.action_rss_feeds_import: Intent i2 = new Intent(getActivity(), RssFeedImportActivity.class); - i2.putExtra(GROUP_ID, personalBlog.getId().getBytes()); startActivity(i2); return true; case R.id.action_rss_feeds_manage: diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java index 7e2085818..1d09ad2c4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java @@ -1,7 +1,6 @@ package org.briarproject.briar.android.blog; import android.content.DialogInterface; -import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AlertDialog; import android.text.Editable; @@ -15,7 +14,6 @@ import android.widget.ProgressBar; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; @@ -44,9 +42,6 @@ public class RssFeedImportActivity extends BriarActivity { @IoExecutor Executor ioExecutor; - // Fields that are accessed from background threads must be volatile - private volatile GroupId groupId = null; - @Inject @SuppressWarnings("WeakerAccess") volatile FeedManager feedManager; @@ -55,12 +50,6 @@ public class RssFeedImportActivity extends BriarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // GroupId from Intent - Intent i = getIntent(); - byte[] b = i.getByteArrayExtra(GROUP_ID); - if (b == null) throw new IllegalStateException("No Group in intent."); - groupId = new GroupId(b); - setContentView(R.layout.activity_rss_feed_import); urlInput = (EditText) findViewById(R.id.urlInput); @@ -128,7 +117,7 @@ public class RssFeedImportActivity extends BriarActivity { @Override public void run() { try { - feedManager.addFeed(url, groupId); + feedManager.addFeed(url); feedImported(); } catch (DbException | IOException e) { if (LOG.isLoggable(WARNING)) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java index 10e37d6f2..8e6067729 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java @@ -87,7 +87,6 @@ public class RssFeedManageActivity extends BriarActivity return true; case R.id.action_rss_feeds_import: Intent i = new Intent(this, RssFeedImportActivity.class); - i.putExtra(GROUP_ID, groupId.getBytes()); startActivity(i); return true; default: diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java index b36a8a70b..51f53f10c 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java @@ -1,39 +1,32 @@ package org.briarproject.briar.api.feed; -import org.briarproject.bramble.api.FormatException; -import org.briarproject.bramble.api.data.BdfDictionary; -import org.briarproject.bramble.api.data.BdfEntry; +import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.briar.api.blog.Blog; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_BLOG_GROUP_ID; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_ADDED; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_AUTHOR; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_DESC; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_LAST_ENTRY; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_TITLE; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_UPDATED; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_URL; - @Immutable @NotNullByDefault public class Feed { private final String url; - private final GroupId blogId; + private final Blog blog; + private final LocalAuthor localAuthor; @Nullable private final String title, description, author; private final long added, updated, lastEntryTime; - public Feed(String url, GroupId blogId, @Nullable String title, - @Nullable String description, @Nullable String author, - long added, long updated, long lastEntryTime) { + public Feed(String url, Blog blog, LocalAuthor localAuthor, + @Nullable String title, @Nullable String description, + @Nullable String author, long added, long updated, + long lastEntryTime) { this.url = url; - this.blogId = blogId; + this.blog = blog; + this.localAuthor = localAuthor; this.title = title; this.description = description; this.author = author; @@ -42,13 +35,14 @@ public class Feed { this.lastEntryTime = lastEntryTime; } - public Feed(String url, GroupId blogId, @Nullable String title, - @Nullable String description, @Nullable String author, long added) { - this(url, blogId, title, description, author, added, 0L, 0L); + public Feed(String url, Blog blog, LocalAuthor localAuthor, + @Nullable String title, @Nullable String description, + @Nullable String author, long added) { + this(url, blog, localAuthor, title, description, author, added, 0L, 0L); } - public Feed(String url, GroupId blogId, long added) { - this(url, blogId, null, null, null, added, 0L, 0L); + public Feed(String url, Blog blog, LocalAuthor localAuthor, long added) { + this(url, blog, localAuthor, null, null, null, added, 0L, 0L); } public String getUrl() { @@ -56,34 +50,15 @@ public class Feed { } public GroupId getBlogId() { - return blogId; + return blog.getId(); } - public BdfDictionary toBdfDictionary() { - BdfDictionary d = BdfDictionary.of( - new BdfEntry(KEY_FEED_URL, url), - new BdfEntry(KEY_BLOG_GROUP_ID, blogId.getBytes()), - new BdfEntry(KEY_FEED_ADDED, added), - new BdfEntry(KEY_FEED_UPDATED, updated), - new BdfEntry(KEY_FEED_LAST_ENTRY, lastEntryTime) - ); - if (title != null) d.put(KEY_FEED_TITLE, title); - if (description != null) d.put(KEY_FEED_DESC, description); - if (author != null) d.put(KEY_FEED_AUTHOR, author); - return d; + public Blog getBlog() { + return blog; } - public static Feed from(BdfDictionary d) throws FormatException { - String url = d.getString(KEY_FEED_URL); - GroupId blogId = new GroupId(d.getRaw(KEY_BLOG_GROUP_ID)); - String title = d.getOptionalString(KEY_FEED_TITLE); - String desc = d.getOptionalString(KEY_FEED_DESC); - String author = d.getOptionalString(KEY_FEED_AUTHOR); - long added = d.getLong(KEY_FEED_ADDED, 0L); - long updated = d.getLong(KEY_FEED_UPDATED, 0L); - long lastEntryTime = d.getLong(KEY_FEED_LAST_ENTRY, 0L); - return new Feed(url, blogId, title, desc, author, added, updated, - lastEntryTime); + public LocalAuthor getLocalAuthor() { + return localAuthor; } @Nullable @@ -118,13 +93,13 @@ public class Feed { if (this == o) return true; if (o instanceof Feed) { Feed f = (Feed) o; - return url.equals(f.url) && blogId.equals(f.getBlogId()) && - equalsWithNull(title, f.getTitle()) && - equalsWithNull(description, f.getDescription()) && - equalsWithNull(author, f.getAuthor()) && - added == f.getAdded() && - updated == f.getUpdated() && - lastEntryTime == f.getLastEntryTime(); + return url.equals(f.url) && blog.equals(f.blog) && + equalsWithNull(title, f.title) && + equalsWithNull(description, f.description) && + equalsWithNull(author, f.author) && + added == f.added && + updated == f.updated && + lastEntryTime == f.lastEntryTime; } return false; } @@ -134,4 +109,5 @@ public class Feed { if (a == null || b == null) return false; return a.equals(b); } + } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java index 8be05d568..9dc944585 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java @@ -18,7 +18,9 @@ public interface FeedConstants { // group metadata keys String KEY_FEEDS = "feeds"; String KEY_FEED_URL = "feedURL"; - String KEY_BLOG_GROUP_ID = "blogGroupId"; + String KEY_BLOG_TITLE = "blogTitle"; + String KEY_PUBLIC_KEY = "publicKey"; + String KEY_PRIVATE_KEY = "privateKey"; String KEY_FEED_TITLE = "feedTitle"; String KEY_FEED_DESC = "feedDesc"; String KEY_FEED_AUTHOR = "feedAuthor"; diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java index 5d31b448a..74a762eaf 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java @@ -3,7 +3,6 @@ package org.briarproject.briar.api.feed; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.ClientId; -import org.briarproject.bramble.api.sync.GroupId; import java.io.IOException; import java.util.List; @@ -17,9 +16,9 @@ public interface FeedManager { ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed"); /** - * Adds an RSS feed. + * Adds an RSS feed as a new dedicated blog. */ - void addFeed(String url, GroupId g) throws DbException, IOException; + void addFeed(String url) throws DbException, IOException; /** * Removes an RSS feed. diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java new file mode 100644 index 000000000..192c4855e --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java @@ -0,0 +1,34 @@ +package org.briarproject.briar.feed; + +import com.rometools.rome.feed.synd.SyndFeed; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.briar.api.feed.Feed; + +interface FeedFactory { + + /** + * Create a new feed based on the feed url + * and the metadata of an existing {@link SyndFeed}. + */ + Feed createFeed(String url, SyndFeed feed); + + /** + * Creates a new updated feed, based on the given existing feed, + * new metadata from the given {@link SyndFeed} + * and the time of the last feed entry. + */ + Feed createFeed(Feed feed, SyndFeed f, long lastEntryTime); + + /** + * De-serializes a {@link BdfDictionary} into a {@link Feed}. + */ + Feed createFeed(BdfDictionary d) throws FormatException; + + /** + * Serializes a {@link Feed} into a {@link BdfDictionary}. + */ + BdfDictionary feedToBdfDictionary(Feed feed); + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java new file mode 100644 index 000000000..531f169ef --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java @@ -0,0 +1,111 @@ +package org.briarproject.briar.feed; + +import com.rometools.rome.feed.synd.SyndFeed; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyPair; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfEntry; +import org.briarproject.bramble.api.identity.AuthorFactory; +import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.briar.api.blog.Blog; +import org.briarproject.briar.api.blog.BlogFactory; +import org.briarproject.briar.api.feed.Feed; + +import javax.inject.Inject; + +import static org.briarproject.briar.api.feed.FeedConstants.KEY_BLOG_TITLE; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_ADDED; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_AUTHOR; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_DESC; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_LAST_ENTRY; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_TITLE; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_UPDATED; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_URL; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_PRIVATE_KEY; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_PUBLIC_KEY; + +class FeedFactoryImpl implements FeedFactory { + + private final CryptoComponent cryptoComponent; + private final AuthorFactory authorFactory; + private final BlogFactory blogFactory; + private final Clock clock; + + @Inject + FeedFactoryImpl(CryptoComponent cryptoComponent, + AuthorFactory authorFactory, BlogFactory blogFactory, Clock clock) { + this.cryptoComponent = cryptoComponent; + this.authorFactory = authorFactory; + this.blogFactory = blogFactory; + this.clock = clock; + } + + @Override + public Feed createFeed(String url, SyndFeed syndFeed) { + if (syndFeed.getTitle() == null) syndFeed.setTitle("RSS feed"); + + KeyPair keyPair = cryptoComponent.generateSignatureKeyPair(); + LocalAuthor localAuthor = authorFactory + .createLocalAuthor(syndFeed.getTitle(), + keyPair.getPublic().getEncoded(), + keyPair.getPrivate().getEncoded()); + Blog blog = blogFactory.createBlog(localAuthor); + long added = clock.currentTimeMillis(); + + return new Feed(url, blog, localAuthor, added); + } + + @Override + public Feed createFeed(Feed feed, SyndFeed f, long lastEntryTime) { + long updated = clock.currentTimeMillis(); + return new Feed(feed.getUrl(), feed.getBlog(), feed.getLocalAuthor(), + f.getTitle(), f.getDescription(), f.getAuthor(), + feed.getAdded(), updated, lastEntryTime); + } + + @Override + public Feed createFeed(BdfDictionary d) throws FormatException { + String url = d.getString(KEY_FEED_URL); + + String blogTitle = d.getString(KEY_BLOG_TITLE); + byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY); + byte[] privateKey = d.getRaw(KEY_PRIVATE_KEY); + LocalAuthor localAuthor = authorFactory + .createLocalAuthor(blogTitle, publicKey, privateKey); + Blog blog = blogFactory.createBlog(localAuthor); + + String title = d.getOptionalString(KEY_FEED_TITLE); + String desc = d.getOptionalString(KEY_FEED_DESC); + String author = d.getOptionalString(KEY_FEED_AUTHOR); + long added = d.getLong(KEY_FEED_ADDED, 0L); + long updated = d.getLong(KEY_FEED_UPDATED, 0L); + long lastEntryTime = d.getLong(KEY_FEED_LAST_ENTRY, 0L); + + return new Feed(url, blog, localAuthor, title, desc, author, added, + updated, lastEntryTime); + } + + @Override + public BdfDictionary feedToBdfDictionary(Feed feed) { + BdfDictionary d = BdfDictionary.of( + new BdfEntry(KEY_FEED_URL, feed.getUrl()), + new BdfEntry(KEY_BLOG_TITLE, feed.getLocalAuthor().getName()), + new BdfEntry(KEY_PUBLIC_KEY, + feed.getLocalAuthor().getPublicKey()), + new BdfEntry(KEY_PRIVATE_KEY, + feed.getLocalAuthor().getPrivateKey()), + new BdfEntry(KEY_FEED_ADDED, feed.getAdded()), + new BdfEntry(KEY_FEED_UPDATED, feed.getUpdated()), + new BdfEntry(KEY_FEED_LAST_ENTRY, feed.getLastEntryTime()) + ); + if (feed.getTitle() != null) d.put(KEY_FEED_TITLE, feed.getTitle()); + if (feed.getDescription() != null) + d.put(KEY_FEED_DESC, feed.getDescription()); + if (feed.getAuthor() != null) d.put(KEY_FEED_AUTHOR, feed.getAuthor()); + return d; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java index d92df5001..8168d58b1 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java @@ -18,7 +18,6 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventListener; -import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -88,9 +87,9 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { private final DatabaseComponent db; private final ContactGroupFactory contactGroupFactory; private final ClientHelper clientHelper; - private final IdentityManager identityManager; private final BlogManager blogManager; private final BlogPostFactory blogPostFactory; + private final FeedFactory feedFactory; private final SocketFactory torSocketFactory; private final Clock clock; private final AtomicBoolean fetcherStarted = new AtomicBoolean(false); @@ -99,8 +98,8 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { FeedManagerImpl(@Scheduler ScheduledExecutorService scheduler, @IoExecutor Executor ioExecutor, DatabaseComponent db, ContactGroupFactory contactGroupFactory, ClientHelper clientHelper, - IdentityManager identityManager, BlogManager blogManager, - BlogPostFactory blogPostFactory, SocketFactory torSocketFactory, + BlogManager blogManager, BlogPostFactory blogPostFactory, + FeedFactory feedFactory, SocketFactory torSocketFactory, Clock clock) { this.scheduler = scheduler; @@ -108,9 +107,9 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { this.db = db; this.contactGroupFactory = contactGroupFactory; this.clientHelper = clientHelper; - this.identityManager = identityManager; this.blogManager = blogManager; this.blogPostFactory = blogPostFactory; + this.feedFactory = feedFactory; this.torSocketFactory = torSocketFactory; this.clock = clock; } @@ -158,21 +157,22 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { } @Override - public void addFeed(String url, GroupId g) throws DbException, IOException { - LOG.info("Adding new RSS feed..."); - + public void addFeed(String url) throws DbException, IOException { // TODO check for existing feed? - // fetch feed to get its metadata - Feed feed = new Feed(url, g, clock.currentTimeMillis()); + // fetch syndication feed to get its metadata + SyndFeed f; try { - feed = fetchFeed(feed, false); + f = fetchSyndFeed(url); } catch (FeedException e) { throw new IOException(e); } - // store feed + Feed feed = feedFactory.createFeed(url, f); + + // store feed and new blog Transaction txn = db.startTransaction(false); try { + blogManager.addBlog(txn, feed.getBlog()); List feeds = getFeeds(txn); feeds.add(feed); storeFeeds(txn, feeds); @@ -181,10 +181,10 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { db.endTransaction(txn); } - // fetch feed again, post entries this time + // fetch feed again and post entries Feed updatedFeed; try { - updatedFeed = fetchFeed(feed, true); + updatedFeed = fetchFeed(feed); } catch (FeedException e) { throw new IOException(e); } @@ -208,16 +208,17 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { Transaction txn = db.startTransaction(false); try { List feeds = getFeeds(txn); - boolean found = false; - for (Feed feed : feeds) { - if (feed.getUrl().equals(url)) { - found = true; - feeds.remove(feed); + Feed feed = null; + for (Feed f : feeds) { + if (f.getUrl().equals(url)) { + feed = f; + feeds.remove(f); break; } } - if (!found) throw new DbException(); + if (feed == null) throw new DbException(); storeFeeds(txn, feeds); + // TODO blogManager.removeBlog(txn, feed.getBlog()); db.commitTransaction(txn); } finally { db.endTransaction(txn); @@ -246,7 +247,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { for (Object object : d.getList(KEY_FEEDS)) { if (!(object instanceof BdfDictionary)) throw new FormatException(); - feeds.add(Feed.from((BdfDictionary) object)); + feeds.add(feedFactory.createFeed((BdfDictionary) object)); } } catch (FormatException e) { throw new DbException(e); @@ -259,7 +260,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { BdfList feedList = new BdfList(); for (Feed feed : feeds) { - feedList.add(feed.toBdfDictionary()); + feedList.add(feedFactory.feedToBdfDictionary(feed)); } BdfDictionary gm = BdfDictionary.of(new BdfEntry(KEY_FEEDS, feedList)); try { @@ -300,7 +301,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { List newFeeds = new ArrayList(feeds.size()); for (Feed feed : feeds) { try { - newFeeds.add(fetchFeed(feed, true)); + newFeeds.add(fetchFeed(feed)); } catch (FeedException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -323,31 +324,45 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { LOG.info("Done updating RSS feeds"); } - private Feed fetchFeed(Feed feed, boolean post) - throws FeedException, IOException, DbException { - String title, description, author; - long updated = clock.currentTimeMillis(); - long lastEntryTime = feed.getLastEntryTime(); - - SyndFeed f = getSyndFeed(getFeedInputStream(feed.getUrl())); - title = StringUtils.isNullOrEmpty(f.getTitle()) ? null : f.getTitle(); - if (title != null) title = clean(title, STRIP_ALL); - description = StringUtils.isNullOrEmpty(f.getDescription()) ? null : - f.getDescription(); - if (description != null) description = clean(description, STRIP_ALL); - author = - StringUtils.isNullOrEmpty(f.getAuthor()) ? null : f.getAuthor(); - if (author != null) author = clean(author, STRIP_ALL); + private SyndFeed fetchSyndFeed(String url) + throws FeedException, IOException { + // fetch feed + SyndFeed f = getSyndFeed(getFeedInputStream(url)); if (f.getEntries().size() == 0) throw new FeedException("Feed has no entries"); + // clean title + String title = + StringUtils.isNullOrEmpty(f.getTitle()) ? null : f.getTitle(); + if (title != null) title = clean(title, STRIP_ALL); + f.setTitle(title); + + // clean description + String description = + StringUtils.isNullOrEmpty(f.getDescription()) ? null : + f.getDescription(); + if (description != null) description = clean(description, STRIP_ALL); + f.setDescription(description); + + // clean author + String author = + StringUtils.isNullOrEmpty(f.getAuthor()) ? null : f.getAuthor(); + if (author != null) author = clean(author, STRIP_ALL); + f.setAuthor(author); + + return f; + } + + private Feed fetchFeed(Feed feed) + throws FeedException, IOException, DbException { + // fetch and clean feed + SyndFeed f = fetchSyndFeed(feed.getUrl()); + // sort and add new entries - if (post) { - lastEntryTime = postFeedEntries(feed, f.getEntries()); - } - return new Feed(feed.getUrl(), feed.getBlogId(), title, description, - author, feed.getAdded(), updated, lastEntryTime); + long lastEntryTime = postFeedEntries(feed, f.getEntries()); + + return feedFactory.createFeed(feed, f, lastEntryTime); } private InputStream getFeedInputStream(String url) throws IOException { @@ -461,9 +476,9 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { String body = getPostBody(b.toString()); try { // create and store post - LocalAuthor author = identityManager.getLocalAuthor(txn); + LocalAuthor localAuthor = feed.getLocalAuthor(); BlogPost post = blogPostFactory - .createBlogPost(groupId, time, null, author, body); + .createBlogPost(groupId, time, null, localAuthor, body); blogManager.addLocalPost(txn, post); } catch (DbException e) { if (LOG.isLoggable(WARNING)) diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java index d47a088b8..ab8dfbc29 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java @@ -28,4 +28,9 @@ public class FeedModule { return feedManager; } + @Provides + FeedFactory provideFeedFactory(FeedFactoryImpl feedFactory) { + return feedFactory; + } + } From 17de785c12f2edd7947f7c3c2ee93f3cb7d7abbb Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 10 Apr 2017 13:39:28 -0300 Subject: [PATCH 2/8] Remove blog as well when removing RSS feed This also adds a confirmation dialog to the removal process. --- .../android/blog/RssFeedManageActivity.java | 55 ++++++++++++------- briar-android/src/main/res/values/strings.xml | 3 + .../briar/api/blog/BlogManager.java | 5 ++ .../briar/api/feed/FeedManager.java | 2 +- .../briar/blog/BlogManagerImpl.java | 5 ++ .../briar/feed/FeedManagerImpl.java | 8 +-- 6 files changed, 51 insertions(+), 27 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java index 8e6067729..56f948f4b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java @@ -1,15 +1,16 @@ package org.briarproject.briar.android.blog; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.support.design.widget.Snackbar; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import org.briarproject.bramble.api.db.DbException; -import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; @@ -34,7 +35,6 @@ public class RssFeedManageActivity extends BriarActivity private BriarRecyclerView list; private RssFeedAdapter adapter; - private GroupId groupId; @Inject @SuppressWarnings("WeakerAccess") @@ -44,12 +44,6 @@ public class RssFeedManageActivity extends BriarActivity public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // GroupId from Intent - Intent i = getIntent(); - byte[] b = i.getByteArrayExtra(GROUP_ID); - if (b == null) throw new IllegalStateException("No Group in intent."); - groupId = new GroupId(b); - setContentView(R.layout.activity_rss_feed_manage); adapter = new RssFeedAdapter(this, this); @@ -101,19 +95,22 @@ public class RssFeedManageActivity extends BriarActivity @Override public void onDeleteClick(final Feed feed) { - runOnDbThread(new Runnable() { - @Override - public void run() { - try { - feedManager.removeFeed(feed.getUrl()); - onFeedDeleted(feed); - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - onDeleteError(); - } - } - }); + DialogInterface.OnClickListener okListener = + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + deleteFeed(feed); + } + }; + AlertDialog.Builder builder = new AlertDialog.Builder(this, + R.style.BriarDialogTheme); + builder.setTitle(getString(R.string.blogs_rss_remove_feed)); + builder.setMessage( + getString(R.string.blogs_rss_remove_feed_dialog_message)); + builder.setPositiveButton(R.string.cancel, null); + builder.setNegativeButton(R.string.blogs_rss_remove_feed_ok, + okListener); + builder.show(); } private void loadFeeds() { @@ -148,6 +145,22 @@ public class RssFeedManageActivity extends BriarActivity }); } + private void deleteFeed(final Feed feed) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + feedManager.removeFeed(feed); + onFeedDeleted(feed); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + onDeleteError(); + } + } + }); + } + private void onLoadError() { runOnUiThreadUnlessDestroyed(new Runnable() { @Override diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 99e86b60e..5103e596b 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -304,6 +304,9 @@ Imported: Author: Last Updated: + Remove Feed + Are you sure that you want to remove this feed and all its posts?\nNote that this will not remove the feed\'s blog from other people\'s devices. + Remove Feed The feed could not be deleted! You haven\'t imported any RSS feeds.\n\nWhy don\'t you click the plus in the top right screen corner to add your first? There was a problem loading your feeds. Please try again later. diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java index 20ffb991c..041d474a4 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java @@ -41,6 +41,11 @@ public interface BlogManager { */ void removeBlog(Blog b) throws DbException; + /** + * Removes and deletes a blog with the given {@link Transaction}. + */ + void removeBlog(Transaction txn, Blog b) throws DbException; + /** * Stores a local blog post. */ diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java index 74a762eaf..7d83b22c1 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java @@ -23,7 +23,7 @@ public interface FeedManager { /** * Removes an RSS feed. */ - void removeFeed(String url) throws DbException; + void removeFeed(Feed feed) throws DbException; /** * Returns a list of all added RSS feeds diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java index 496b308fb..22b800916 100644 --- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java @@ -224,6 +224,11 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, } } + @Override + public void removeBlog(Transaction txn, Blog b) throws DbException { + removeBlog(txn, b, false); + } + private void removeBlog(Transaction txn, Blog b, boolean forced) throws DbException { if (!forced && !canBeRemoved(txn, b.getId())) diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java index 8168d58b1..8db542e8b 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java @@ -203,22 +203,20 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { } @Override - public void removeFeed(String url) throws DbException { + public void removeFeed(Feed feed) throws DbException { LOG.info("Removing RSS feed..."); Transaction txn = db.startTransaction(false); try { List feeds = getFeeds(txn); - Feed feed = null; for (Feed f : feeds) { - if (f.getUrl().equals(url)) { + if (f.getBlogId().equals(feed.getBlogId())) { feed = f; feeds.remove(f); break; } } - if (feed == null) throw new DbException(); storeFeeds(txn, feeds); - // TODO blogManager.removeBlog(txn, feed.getBlog()); + blogManager.removeBlog(txn, feed.getBlog()); db.commitTransaction(txn); } finally { db.endTransaction(txn); From 58b9efb24c7bdd41d9aaa044674c4b3da9a9f73d Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 10 Apr 2017 13:53:40 -0300 Subject: [PATCH 3/8] Open feed's blog when clicking it in 'manage activity' --- .../briar/android/blog/RssFeedAdapter.java | 17 ++++++++++++++--- .../android/blog/RssFeedManageActivity.java | 9 +++++++++ .../src/main/res/layout/list_item_rss_feed.xml | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java index 3052e3694..e55dcef30 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java @@ -6,7 +6,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.ImageView; +import android.widget.ImageButton; import android.widget.TextView; import org.briarproject.briar.R; @@ -75,6 +75,14 @@ class RssFeedAdapter extends BriarAdapter { } else { ui.description.setVisibility(GONE); } + + // Open feed's blog when clicked + ui.layout.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + listener.onFeedClick(item); + } + }); } @Override @@ -99,8 +107,9 @@ class RssFeedAdapter extends BriarAdapter { } static class FeedViewHolder extends RecyclerView.ViewHolder { + private final View layout; private final TextView title; - private final ImageView delete; + private final ImageButton delete; private final TextView imported; private final TextView updated; private final TextView author; @@ -110,8 +119,9 @@ class RssFeedAdapter extends BriarAdapter { private FeedViewHolder(View v) { super(v); + layout = v; title = (TextView) v.findViewById(R.id.titleView); - delete = (ImageView) v.findViewById(R.id.deleteButton); + delete = (ImageButton) v.findViewById(R.id.deleteButton); imported = (TextView) v.findViewById(R.id.importedView); updated = (TextView) v.findViewById(R.id.updatedView); author = (TextView) v.findViewById(R.id.authorView); @@ -121,6 +131,7 @@ class RssFeedAdapter extends BriarAdapter { } interface RssFeedListener { + void onFeedClick(Feed feed); void onDeleteClick(Feed feed); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java index 56f948f4b..8525f1f65 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java @@ -24,6 +24,7 @@ import java.util.logging.Logger; import javax.inject.Inject; +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.support.design.widget.Snackbar.LENGTH_LONG; import static java.util.logging.Level.WARNING; @@ -93,6 +94,14 @@ public class RssFeedManageActivity extends BriarActivity component.inject(this); } + @Override + public void onFeedClick(Feed feed) { + Intent i = new Intent(this, BlogActivity.class); + i.putExtra(GROUP_ID, feed.getBlogId().getBytes()); + i.setFlags(FLAG_ACTIVITY_CLEAR_TOP); + startActivity(i); + } + @Override public void onDeleteClick(final Feed feed) { DialogInterface.OnClickListener okListener = diff --git a/briar-android/src/main/res/layout/list_item_rss_feed.xml b/briar-android/src/main/res/layout/list_item_rss_feed.xml index 76e0978a7..183dfc3b0 100644 --- a/briar-android/src/main/res/layout/list_item_rss_feed.xml +++ b/briar-android/src/main/res/layout/list_item_rss_feed.xml @@ -19,7 +19,7 @@ android:textSize="@dimen/text_size_medium" tools:text="This is a name of a RSS Feed"/> - Date: Mon, 10 Apr 2017 18:57:14 -0300 Subject: [PATCH 4/8] Change blog descriptor format to include RSS feed flag This now also handles the case where an RSS blog is deleted via the blog deletion option and not the feed management. --- .../briarproject/bramble/db/JdbcDatabase.java | 4 +-- .../org/briarproject/briar/api/blog/Blog.java | 8 ++++- .../briar/api/blog/BlogFactory.java | 5 +++ .../org/briarproject/briar/api/feed/Feed.java | 14 +------- .../briar/blog/BlogFactoryImpl.java | 16 ++++++++-- .../briar/feed/FeedFactoryImpl.java | 4 +-- .../briar/feed/FeedManagerImpl.java | 32 ++++++++++++------- .../briarproject/briar/feed/FeedModule.java | 5 ++- .../briar/sharing/BlogSharingValidator.java | 10 ++++-- .../briar/blog/BlogManagerImplTest.java | 2 +- .../briar/blog/BlogPostValidatorTest.java | 2 +- .../sharing/BlogSharingValidatorTest.java | 23 ++++++------- 12 files changed, 77 insertions(+), 48 deletions(-) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java index 54238266b..7ae01fe47 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java @@ -68,8 +68,8 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry; @NotNullByDefault abstract class JdbcDatabase implements Database { - private static final int SCHEMA_VERSION = 29; - private static final int MIN_SCHEMA_VERSION = 29; + private static final int SCHEMA_VERSION = 30; + private static final int MIN_SCHEMA_VERSION = 30; private static final String CREATE_SETTINGS = "CREATE TABLE settings" diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java index b7e1d240f..a0e4fff02 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java @@ -13,16 +13,22 @@ import javax.annotation.concurrent.Immutable; public class Blog extends BaseGroup implements Shareable { private final Author author; + private final boolean rssFeed; - public Blog(Group group, Author author) { + public Blog(Group group, Author author, boolean rssFeed) { super(group); this.author = author; + this.rssFeed = rssFeed; } public Author getAuthor() { return author; } + public boolean isRssFeed() { + return rssFeed; + } + @Override public boolean equals(Object o) { return o instanceof Blog && super.equals(o); diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java index 4b0a89cf3..a9d057501 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java @@ -13,6 +13,11 @@ public interface BlogFactory { */ Blog createBlog(Author author); + /** + * Creates a RSS feed blog for a given author. + */ + Blog createFeedBlog(Author author); + /** * Parses a blog with the given Group */ diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java index 51f53f10c..4932ecab7 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java @@ -93,21 +93,9 @@ public class Feed { if (this == o) return true; if (o instanceof Feed) { Feed f = (Feed) o; - return url.equals(f.url) && blog.equals(f.blog) && - equalsWithNull(title, f.title) && - equalsWithNull(description, f.description) && - equalsWithNull(author, f.author) && - added == f.added && - updated == f.updated && - lastEntryTime == f.lastEntryTime; + return blog.equals(f.blog); } return false; } - private boolean equalsWithNull(@Nullable Object a, @Nullable Object b) { - if (a == b) return true; - if (a == null || b == null) return false; - return a.equals(b); - } - } diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java index 26c1eb108..deaae2cfe 100644 --- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java @@ -33,15 +33,25 @@ class BlogFactoryImpl implements BlogFactory { @Override public Blog createBlog(Author a) { + return createBlog(a, false); + } + + @Override + public Blog createFeedBlog(Author a) { + return createBlog(a, true); + } + + private Blog createBlog(Author a, boolean rssFeed) { try { BdfList blog = BdfList.of( a.getName(), - a.getPublicKey() + a.getPublicKey(), + rssFeed ); byte[] descriptor = clientHelper.toByteArray(blog); Group g = groupFactory .createGroup(BlogManagerImpl.CLIENT_ID, descriptor); - return new Blog(g, a); + return new Blog(g, a, rssFeed); } catch (FormatException e) { throw new RuntimeException(e); } @@ -54,7 +64,7 @@ class BlogFactoryImpl implements BlogFactory { BdfList blog = clientHelper.toList(descriptor); Author a = authorFactory.createAuthor(blog.getString(0), blog.getRaw(1)); - return new Blog(g, a); + return new Blog(g, a, blog.getBoolean(2)); } } diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java index 531f169ef..14aca1559 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java @@ -52,7 +52,7 @@ class FeedFactoryImpl implements FeedFactory { .createLocalAuthor(syndFeed.getTitle(), keyPair.getPublic().getEncoded(), keyPair.getPrivate().getEncoded()); - Blog blog = blogFactory.createBlog(localAuthor); + Blog blog = blogFactory.createFeedBlog(localAuthor); long added = clock.currentTimeMillis(); return new Feed(url, blog, localAuthor, added); @@ -75,7 +75,7 @@ class FeedFactoryImpl implements FeedFactory { byte[] privateKey = d.getRaw(KEY_PRIVATE_KEY); LocalAuthor localAuthor = authorFactory .createLocalAuthor(blogTitle, publicKey, privateKey); - Blog blog = blogFactory.createBlog(localAuthor); + Blog blog = blogFactory.createFeedBlog(localAuthor); String title = d.getOptionalString(KEY_FEED_TITLE); String desc = d.getOptionalString(KEY_FEED_DESC); diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java index 8db542e8b..969de52a3 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java @@ -30,6 +30,7 @@ import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.util.StringUtils; +import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogPost; import org.briarproject.briar.api.blog.BlogPostFactory; @@ -74,7 +75,8 @@ import static org.briarproject.briar.util.HtmlUtils.clean; @ThreadSafe @NotNullByDefault -class FeedManagerImpl implements FeedManager, Client, EventListener { +class FeedManagerImpl implements FeedManager, Client, EventListener, + BlogManager.RemoveBlogHook { private static final Logger LOG = Logger.getLogger(FeedManagerImpl.class.getName()); @@ -158,7 +160,6 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { @Override public void addFeed(String url) throws DbException, IOException { - // TODO check for existing feed? // fetch syndication feed to get its metadata SyndFeed f; try { @@ -207,15 +208,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { LOG.info("Removing RSS feed..."); Transaction txn = db.startTransaction(false); try { - List feeds = getFeeds(txn); - for (Feed f : feeds) { - if (f.getBlogId().equals(feed.getBlogId())) { - feed = f; - feeds.remove(f); - break; - } - } - storeFeeds(txn, feeds); + // this will call removingBlog() where the feed itself gets removed blogManager.removeBlog(txn, feed.getBlog()); db.commitTransaction(txn); } finally { @@ -223,6 +216,23 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { } } + @Override + public void removingBlog(Transaction txn, Blog b) throws DbException { + if (!b.isRssFeed()) return; + + // delete blog's RSS feed if we have it + boolean found = false; + List feeds = getFeeds(txn); + for (Feed f : feeds) { + if (f.getBlogId().equals(b.getId())) { + found = true; + feeds.remove(f); + break; + } + } + if (found) storeFeeds(txn, feeds); + } + @Override public List getFeeds() throws DbException { List feeds; diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java index ab8dfbc29..c4b567c18 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java @@ -2,6 +2,7 @@ package org.briarproject.briar.feed; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.feed.FeedManager; import javax.inject.Inject; @@ -21,10 +22,12 @@ public class FeedModule { @Provides @Singleton FeedManager provideFeedManager(FeedManagerImpl feedManager, - LifecycleManager lifecycleManager, EventBus eventBus) { + LifecycleManager lifecycleManager, EventBus eventBus, + BlogManager blogManager) { lifecycleManager.registerClient(feedManager); eventBus.addListener(feedManager); + blogManager.registerRemoveBlogHook(feedManager); return feedManager; } diff --git a/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java b/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java index 6f0df8e47..f9b378ef7 100644 --- a/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java +++ b/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java @@ -39,14 +39,20 @@ class BlogSharingValidator extends SharingValidator { @Override protected GroupId validateDescriptor(BdfList descriptor) throws FormatException { - checkSize(descriptor, 2); + checkSize(descriptor, 3); String name = descriptor.getString(0); checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH); byte[] publicKey = descriptor.getRaw(1); checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH); + boolean rssFeed = descriptor.getBoolean(2); Author author = authorFactory.createAuthor(name, publicKey); - Blog blog = blogFactory.createBlog(author); + Blog blog; + if (rssFeed) { + blog = blogFactory.createFeedBlog(author); + } else { + blog = blogFactory.createBlog(author); + } return blog.getId(); } diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java index e2b5e8ef5..4ff1bb75c 100644 --- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java @@ -297,7 +297,7 @@ public class BlogManagerImplTest extends BriarTestCase { final LocalAuthor localAuthor = new LocalAuthor(authorId, "Author", publicKey, privateKey, created); - return new Blog(group, localAuthor); + return new Blog(group, localAuthor, false); } private BdfDictionary authorToBdfDictionary(Author a) { diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java index 1ac77a550..d3731d19d 100644 --- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java @@ -79,7 +79,7 @@ public class BlogPostValidatorTest extends BriarTestCase { new BdfEntry(KEY_AUTHOR_NAME, author.getName()), new BdfEntry(KEY_PUBLIC_KEY, author.getPublicKey()) ); - blog = new Blog(group, author); + blog = new Blog(group, author, false); MessageId messageId = new MessageId(TestUtils.getRandomId()); long timestamp = System.currentTimeMillis(); diff --git a/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java index c67360d64..7c4c96ab2 100644 --- a/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java @@ -23,8 +23,8 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { private final byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH); private final Author author = new Author(authorId, authorName, publicKey); - private final Blog blog = new Blog(group, author); - private final BdfList descriptor = BdfList.of(authorName, publicKey); + private final Blog blog = new Blog(group, author, false); + private final BdfList descriptor = BdfList.of(authorName, publicKey, false); private final String content = TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH); @@ -64,7 +64,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsNullBlogName() throws Exception { - BdfList invalidDescriptor = BdfList.of(null, publicKey); + BdfList invalidDescriptor = BdfList.of(null, publicKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -72,7 +72,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsNonStringBlogName() throws Exception { - BdfList invalidDescriptor = BdfList.of(123, publicKey); + BdfList invalidDescriptor = BdfList.of(123, publicKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -80,7 +80,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsTooShortBlogName() throws Exception { - BdfList invalidDescriptor = BdfList.of("", publicKey); + BdfList invalidDescriptor = BdfList.of("", publicKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -89,7 +89,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test public void testAcceptsMinLengthBlogName() throws Exception { String shortBlogName = TestUtils.getRandomString(1); - BdfList validDescriptor = BdfList.of(shortBlogName, publicKey); + BdfList validDescriptor = BdfList.of(shortBlogName, publicKey, false); expectCreateBlog(shortBlogName, publicKey); expectEncodeMetadata(INVITE); BdfMessageContext messageContext = v.validateMessage(message, group, @@ -102,7 +102,8 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { public void testRejectsTooLongBlogName() throws Exception { String invalidBlogName = TestUtils.getRandomString(MAX_BLOG_NAME_LENGTH + 1); - BdfList invalidDescriptor = BdfList.of(invalidBlogName, publicKey); + BdfList invalidDescriptor = + BdfList.of(invalidBlogName, publicKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -110,7 +111,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsNullPublicKey() throws Exception { - BdfList invalidDescriptor = BdfList.of(authorName, null); + BdfList invalidDescriptor = BdfList.of(authorName, null, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -118,7 +119,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsNonRawPublicKey() throws Exception { - BdfList invalidDescriptor = BdfList.of(authorName, 123); + BdfList invalidDescriptor = BdfList.of(authorName, 123, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -127,7 +128,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsTooLongPublicKey() throws Exception { byte[] invalidKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1); - BdfList invalidDescriptor = BdfList.of(authorName, invalidKey); + BdfList invalidDescriptor = BdfList.of(authorName, invalidKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -136,7 +137,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test public void testAcceptsMinLengthPublicKey() throws Exception { byte[] key = TestUtils.getRandomBytes(1); - BdfList validDescriptor = BdfList.of(authorName, key); + BdfList validDescriptor = BdfList.of(authorName, key, false); expectCreateBlog(authorName, key); expectEncodeMetadata(INVITE); From b0b4a85d15a1f62755229721484845eaebc10c1e Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 10 Apr 2017 18:58:46 -0300 Subject: [PATCH 5/8] Add integration test for FeedManager Attention: This factors out a DnsModule to be able to make actual non-Tor DNS lookups for testing. --- .../bramble/test/TestSocksModule.java | 16 +++ .../briarproject/briar/BriarCoreModule.java | 2 + .../briarproject/briar/feed/DnsModule.java | 18 +++ .../briar/feed/FeedManagerImpl.java | 20 +-- .../org/briarproject/briar/feed/NoDns.java | 28 +++++ .../feed/FeedManagerIntegrationTest.java | 119 ++++++++++++++++++ .../FeedManagerIntegrationTestComponent.java | 79 ++++++++++++ .../briar/test/TestDnsModule.java | 15 +++ 8 files changed, 281 insertions(+), 16 deletions(-) create mode 100644 bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java create mode 100644 briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java create mode 100644 briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java create mode 100644 briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java new file mode 100644 index 000000000..e142a2479 --- /dev/null +++ b/bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java @@ -0,0 +1,16 @@ +package org.briarproject.bramble.test; + +import javax.net.SocketFactory; + +import dagger.Module; +import dagger.Provides; + +@Module +public class TestSocksModule { + + @Provides + SocketFactory provideSocketFactory() { + return SocketFactory.getDefault(); + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java index a9a7bfc50..99d5879aa 100644 --- a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java @@ -2,6 +2,7 @@ package org.briarproject.briar; import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.client.BriarClientModule; +import org.briarproject.briar.feed.DnsModule; import org.briarproject.briar.feed.FeedModule; import org.briarproject.briar.forum.ForumModule; import org.briarproject.briar.introduction.IntroductionModule; @@ -16,6 +17,7 @@ import dagger.Module; BlogModule.class, BriarClientModule.class, FeedModule.class, + DnsModule.class, ForumModule.class, GroupInvitationModule.class, IntroductionModule.class, diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java b/briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java new file mode 100644 index 000000000..dc6cd8a88 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java @@ -0,0 +1,18 @@ +package org.briarproject.briar.feed; + +import dagger.Module; +import dagger.Provides; +import okhttp3.Dns; + +/** + * This is a dedicated module, so it can be replaced for testing. + */ +@Module +public class DnsModule { + + @Provides + Dns provideDns(NoDns noDns) { + return noDns; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java index 969de52a3..75d8977ea 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java @@ -39,8 +39,6 @@ import org.briarproject.briar.api.feed.FeedManager; import java.io.IOException; import java.io.InputStream; -import java.net.InetAddress; -import java.net.UnknownHostException; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Collections; @@ -81,7 +79,6 @@ class FeedManagerImpl implements FeedManager, Client, EventListener, private static final Logger LOG = Logger.getLogger(FeedManagerImpl.class.getName()); - private static final byte[] UNSPECIFIED_ADDRESS = new byte[4]; private static final int CONNECT_TIMEOUT = 60 * 1000; // Milliseconds private final ScheduledExecutorService scheduler; @@ -94,6 +91,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener, private final FeedFactory feedFactory; private final SocketFactory torSocketFactory; private final Clock clock; + private final Dns noDnsLookups; private final AtomicBoolean fetcherStarted = new AtomicBoolean(false); @Inject @@ -102,7 +100,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener, ContactGroupFactory contactGroupFactory, ClientHelper clientHelper, BlogManager blogManager, BlogPostFactory blogPostFactory, FeedFactory feedFactory, SocketFactory torSocketFactory, - Clock clock) { + Clock clock, Dns noDnsLookups) { this.scheduler = scheduler; this.ioExecutor = ioExecutor; @@ -114,6 +112,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener, this.feedFactory = feedFactory; this.torSocketFactory = torSocketFactory; this.clock = clock; + this.noDnsLookups = noDnsLookups; } @Override @@ -374,21 +373,10 @@ class FeedManagerImpl implements FeedManager, Client, EventListener, } private InputStream getFeedInputStream(String url) throws IOException { - // Don't make local DNS lookups - Dns noLookups = new Dns() { - @Override - public List lookup(String hostname) - throws UnknownHostException { - InetAddress unspecified = - InetAddress.getByAddress(hostname, UNSPECIFIED_ADDRESS); - return Collections.singletonList(unspecified); - } - }; - // Build HTTP Client OkHttpClient client = new OkHttpClient.Builder() .socketFactory(torSocketFactory) - .dns(noLookups) + .dns(noDnsLookups) // Don't make local DNS lookups .connectTimeout(CONNECT_TIMEOUT, MILLISECONDS) .build(); diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java b/briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java new file mode 100644 index 000000000..5f5cc8c44 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java @@ -0,0 +1,28 @@ +package org.briarproject.briar.feed; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collections; +import java.util.List; + +import javax.inject.Inject; + +import okhttp3.Dns; + +class NoDns implements Dns { + + private static final byte[] UNSPECIFIED_ADDRESS = new byte[4]; + + @Inject + public NoDns() { + } + + @Override + public List lookup(String hostname) + throws UnknownHostException { + InetAddress unspecified = + InetAddress.getByAddress(hostname, UNSPECIFIED_ADDRESS); + return Collections.singletonList(unspecified); + } + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java new file mode 100644 index 000000000..25d4992c4 --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java @@ -0,0 +1,119 @@ +package org.briarproject.briar.feed; + +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.contact.ContactModule; +import org.briarproject.bramble.crypto.CryptoModule; +import org.briarproject.bramble.identity.IdentityModule; +import org.briarproject.bramble.lifecycle.LifecycleModule; +import org.briarproject.bramble.sync.SyncModule; +import org.briarproject.bramble.system.SystemModule; +import org.briarproject.bramble.test.TestDatabaseModule; +import org.briarproject.bramble.test.TestUtils; +import org.briarproject.bramble.transport.TransportModule; +import org.briarproject.briar.api.blog.Blog; +import org.briarproject.briar.api.blog.BlogManager; +import org.briarproject.briar.api.feed.Feed; +import org.briarproject.briar.api.feed.FeedManager; +import org.briarproject.briar.blog.BlogModule; +import org.briarproject.briar.test.BriarTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class FeedManagerIntegrationTest extends BriarTestCase { + + private LifecycleManager lifecycleManager; + private FeedManager feedManager; + private BlogManager blogManager; + private final File testDir = TestUtils.getTestDirectory(); + private final File testFile = new File(testDir, "feedTest"); + + @Before + public void setUp() throws Exception { + assertTrue(testDir.mkdirs()); + FeedManagerIntegrationTestComponent component = + DaggerFeedManagerIntegrationTestComponent.builder() + .testDatabaseModule(new TestDatabaseModule(testFile)) + .build(); + component.inject(this); + injectEagerSingletons(component); + + lifecycleManager = component.getLifecycleManager(); + lifecycleManager.startServices("feedTest"); + lifecycleManager.waitForStartup(); + + feedManager = component.getFeedManager(); + blogManager = component.getBlogManager(); + } + + @Test + public void testFeedImportAndRemoval() throws Exception { + // initially, there's only the one personal blog + Collection blogs = blogManager.getBlogs(); + assertEquals(1, blogs.size()); + Blog personalBlog = blogs.iterator().next(); + + // add feed into a dedicated blog + String url = "https://www.schneier.com/blog/atom.xml"; + feedManager.addFeed(url); + + // then there's the feed's blog now + blogs = blogManager.getBlogs(); + assertEquals(2, blogs.size()); + Blog feedBlog = null; + for (Blog blog : blogs) { + if (!blog.equals(personalBlog)) feedBlog = blog; + } + assertNotNull(feedBlog); + + // check the feed got saved as expected + Collection feeds = feedManager.getFeeds(); + assertEquals(1, feeds.size()); + Feed feed = feeds.iterator().next(); + assertTrue(feed.getLastEntryTime() > 0); + assertTrue(feed.getAdded() > 0); + assertTrue(feed.getUpdated() > 0); + assertEquals(url, feed.getUrl()); + assertEquals(feedBlog, feed.getBlog()); + assertEquals("Schneier on Security", feed.getTitle()); + assertEquals("A blog covering security and security technology.", + feed.getDescription()); + assertEquals(feed.getTitle(), feed.getBlog().getName()); + assertEquals(feed.getTitle(), feed.getLocalAuthor().getName()); + + // now let's remove the feed's blog again + blogManager.removeBlog(feedBlog); + blogs = blogManager.getBlogs(); + assertEquals(1, blogs.size()); + assertEquals(personalBlog, blogs.iterator().next()); + assertEquals(0, feedManager.getFeeds().size()); + } + + @After + public void tearDown() throws Exception { + lifecycleManager.stopServices(); + lifecycleManager.waitForShutdown(); + TestUtils.deleteTestDirectory(testDir); + } + + protected void injectEagerSingletons( + FeedManagerIntegrationTestComponent component) { + component.inject(new FeedModule.EagerSingletons()); + component.inject(new BlogModule.EagerSingletons()); + component.inject(new ContactModule.EagerSingletons()); + component.inject(new CryptoModule.EagerSingletons()); + component.inject(new IdentityModule.EagerSingletons()); + component.inject(new LifecycleModule.EagerSingletons()); + component.inject(new SyncModule.EagerSingletons()); + component.inject(new SystemModule.EagerSingletons()); + component.inject(new TransportModule.EagerSingletons()); + } + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java new file mode 100644 index 000000000..ce5ec5a00 --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java @@ -0,0 +1,79 @@ +package org.briarproject.briar.feed; + +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.client.ClientModule; +import org.briarproject.bramble.contact.ContactModule; +import org.briarproject.bramble.crypto.CryptoModule; +import org.briarproject.bramble.data.DataModule; +import org.briarproject.bramble.db.DatabaseModule; +import org.briarproject.bramble.event.EventModule; +import org.briarproject.bramble.identity.IdentityModule; +import org.briarproject.bramble.lifecycle.LifecycleModule; +import org.briarproject.bramble.sync.SyncModule; +import org.briarproject.bramble.system.SystemModule; +import org.briarproject.bramble.test.TestDatabaseModule; +import org.briarproject.bramble.test.TestPluginConfigModule; +import org.briarproject.bramble.test.TestSeedProviderModule; +import org.briarproject.bramble.test.TestSocksModule; +import org.briarproject.bramble.transport.TransportModule; +import org.briarproject.briar.api.blog.BlogManager; +import org.briarproject.briar.api.feed.FeedManager; +import org.briarproject.briar.blog.BlogModule; +import org.briarproject.briar.client.BriarClientModule; +import org.briarproject.briar.test.TestDnsModule; + +import javax.inject.Singleton; + +import dagger.Component; + +@Singleton +@Component(modules = { + TestDatabaseModule.class, + TestPluginConfigModule.class, + TestSeedProviderModule.class, + TestSocksModule.class, + TestDnsModule.class, + LifecycleModule.class, + BriarClientModule.class, + ClientModule.class, + ContactModule.class, + CryptoModule.class, + BlogModule.class, + FeedModule.class, + DataModule.class, + DatabaseModule.class, + EventModule.class, + IdentityModule.class, + SyncModule.class, + SystemModule.class, + TransportModule.class +}) +interface FeedManagerIntegrationTestComponent { + + void inject(FeedManagerIntegrationTest testCase); + + void inject(FeedModule.EagerSingletons init); + + void inject(BlogModule.EagerSingletons init); + + void inject(ContactModule.EagerSingletons init); + + void inject(CryptoModule.EagerSingletons init); + + void inject(IdentityModule.EagerSingletons init); + + void inject(LifecycleModule.EagerSingletons init); + + void inject(SyncModule.EagerSingletons init); + + void inject(SystemModule.EagerSingletons init); + + void inject(TransportModule.EagerSingletons init); + + LifecycleManager getLifecycleManager(); + + FeedManager getFeedManager(); + + BlogManager getBlogManager(); + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java b/briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java new file mode 100644 index 000000000..13323b346 --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java @@ -0,0 +1,15 @@ +package org.briarproject.briar.test; + +import dagger.Module; +import dagger.Provides; +import okhttp3.Dns; + +@Module +public class TestDnsModule { + + @Provides + Dns provideDns() { + return Dns.SYSTEM; + } + +} From 0256ec0b8c6d3bc3dca1ab8965c6e6fde1ede268 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 11 Apr 2017 09:51:08 -0300 Subject: [PATCH 6/8] Show reblog icon only for reblogged posts --- .../java/org/briarproject/briar/android/view/AuthorView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java index 82442185e..fc145268d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java @@ -127,7 +127,7 @@ public class AuthorView extends RelativeLayout { public void setPersona(int persona) { switch (persona) { case NORMAL: - avatarIcon.setVisibility(VISIBLE); + avatarIcon.setVisibility(INVISIBLE); date.setVisibility(VISIBLE); setAvatarSize(R.dimen.blogs_avatar_normal_size); setTextSize(authorName, R.dimen.text_size_small); From 9bfb58a764e70ae592a278540f5a3b0bc3b47d9a Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 11 Apr 2017 11:31:14 -0300 Subject: [PATCH 7/8] Show blog posts from RSS feeds with a dedicated icon This adds a field to the post headers and some more tests. --- .../briar/android/blog/BlogPostItem.java | 4 ++ .../android/blog/BlogPostViewHolder.java | 7 +- .../briar/android/view/AuthorView.java | 26 +++++++ .../src/main/res/drawable/ic_rss_feed.xml | 25 +++++++ briar-android/src/main/res/values/attrs.xml | 2 + .../briar/api/blog/BlogCommentHeader.java | 5 +- .../briar/api/blog/BlogConstants.java | 1 + .../briar/api/blog/BlogPostHeader.java | 13 +++- .../briar/blog/BlogManagerImpl.java | 14 +++- .../briar/blog/BlogPostValidator.java | 2 + .../blog/BlogManagerIntegrationTest.java | 67 ++++++++++++++++++- .../feed/FeedManagerIntegrationTest.java | 8 +++ 12 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 briar-android/src/main/res/drawable/ic_rss_feed.xml diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java index d8561ad4d..546f9ac18 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java @@ -48,6 +48,10 @@ public class BlogPostItem implements Comparable { return body; } + public boolean isRssFeed() { + return header.isRssFeed(); + } + public boolean isRead() { return read; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java index ba1d11e68..b0f4187b4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java @@ -108,7 +108,8 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder { author.setAuthor(a); author.setAuthorStatus(post.getAuthorStatus()); author.setDate(post.getTimestamp()); - author.setPersona(AuthorView.NORMAL); + author.setPersona( + item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL); // TODO make author clickable more often #624 if (item.getHeader().getType() == POST) { author.setBlogLink(post.getGroupId()); @@ -168,7 +169,9 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder { reblogger.setVisibility(VISIBLE); reblogger.setPersona(AuthorView.REBLOGGER); - author.setPersona(AuthorView.COMMENTER); + author.setPersona(item.getHeader().getParent().isRssFeed() ? + AuthorView.RSS_FEED_REBLOGGED : + AuthorView.COMMENTER); // comments for (BlogCommentHeader c : item.getComments()) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java index fc145268d..67aa5a3b9 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java @@ -40,6 +40,8 @@ public class AuthorView extends RelativeLayout { public static final int REBLOGGER = 1; public static final int COMMENTER = 2; public static final int LIST = 3; + public static final int RSS_FEED = 4; + public static final int RSS_FEED_REBLOGGED = 5; private final CircleImageView avatar; private final ImageView avatarIcon; @@ -124,6 +126,12 @@ public class AuthorView extends RelativeLayout { setOnClickListener(null); } + /** + * Styles this view for a different persona. + * + * Attention: If used in a RecyclerView with RSS_FEED, + * call this after setAuthor() + */ public void setPersona(int persona) { switch (persona) { case NORMAL: @@ -158,6 +166,24 @@ public class AuthorView extends RelativeLayout { setCenterVertical(authorName, true); setCenterVertical(trustIndicator, true); break; + case RSS_FEED: + avatarIcon.setVisibility(INVISIBLE); + date.setVisibility(VISIBLE); + avatar.setImageResource(R.drawable.ic_rss_feed); + setAvatarSize(R.dimen.blogs_avatar_normal_size); + setTextSize(authorName, R.dimen.text_size_small); + setCenterVertical(authorName, false); + setCenterVertical(trustIndicator, false); + break; + case RSS_FEED_REBLOGGED: + avatarIcon.setVisibility(INVISIBLE); + date.setVisibility(VISIBLE); + avatar.setImageResource(R.drawable.ic_rss_feed); + setAvatarSize(R.dimen.blogs_avatar_comment_size); + setTextSize(authorName, R.dimen.text_size_tiny); + setCenterVertical(authorName, false); + setCenterVertical(trustIndicator, false); + break; } } diff --git a/briar-android/src/main/res/drawable/ic_rss_feed.xml b/briar-android/src/main/res/drawable/ic_rss_feed.xml new file mode 100644 index 000000000..6d441fa06 --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_rss_feed.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/values/attrs.xml b/briar-android/src/main/res/values/attrs.xml index 6d6207cd0..8655d2126 100644 --- a/briar-android/src/main/res/values/attrs.xml +++ b/briar-android/src/main/res/values/attrs.xml @@ -12,6 +12,8 @@ + + diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java index 70fded281..af28ca1a2 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java @@ -26,7 +26,7 @@ public class BlogCommentHeader extends BlogPostHeader { Status authorStatus, boolean read) { super(type, groupId, id, parent.getId(), timestamp, - timeReceived, author, authorStatus, read); + timeReceived, author, authorStatus, false, read); if (type != COMMENT && type != WRAPPED_COMMENT) throw new IllegalArgumentException("Incompatible Message Type"); @@ -41,6 +41,9 @@ public class BlogCommentHeader extends BlogPostHeader { } public BlogPostHeader getParent() { + if (parent instanceof BlogCommentHeader) + return ((BlogCommentHeader) parent).getParent(); return parent; } + } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java index 99c4514b2..4c8ff25d1 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java @@ -28,6 +28,7 @@ public interface BlogConstants { String KEY_AUTHOR_NAME = "name"; String KEY_PUBLIC_KEY = "publicKey"; String KEY_AUTHOR = "author"; + String KEY_RSS_FEED = "rssFeed"; String KEY_READ = "read"; String KEY_COMMENT = "comment"; String KEY_ORIGINAL_MSG_ID = "originalMessageId"; diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java index cf01dfc57..57a0ef58a 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java @@ -17,21 +17,23 @@ public class BlogPostHeader extends PostHeader { private final MessageType type; private final GroupId groupId; private final long timeReceived; + private final boolean rssFeed; public BlogPostHeader(MessageType type, GroupId groupId, MessageId id, @Nullable MessageId parentId, long timestamp, long timeReceived, - Author author, Status authorStatus, boolean read) { + Author author, Status authorStatus, boolean rssFeed, boolean read) { super(id, parentId, timestamp, author, authorStatus, read); this.type = type; this.groupId = groupId; this.timeReceived = timeReceived; + this.rssFeed = rssFeed; } public BlogPostHeader(MessageType type, GroupId groupId, MessageId id, long timestamp, long timeReceived, Author author, - Status authorStatus, boolean read) { + Status authorStatus, boolean rssFeed, boolean read) { this(type, groupId, id, null, timestamp, timeReceived, author, - authorStatus, read); + authorStatus, rssFeed, read); } public MessageType getType() { @@ -45,4 +47,9 @@ public class BlogPostHeader extends PostHeader { public long getTimeReceived() { return timeReceived; } + + public boolean isRssFeed() { + return rssFeed; + } + } diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java index 22b800916..91945f310 100644 --- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java @@ -61,6 +61,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_PARENT_ import static org.briarproject.briar.api.blog.BlogConstants.KEY_PARENT_MSG_ID; import static org.briarproject.briar.api.blog.BlogConstants.KEY_PUBLIC_KEY; import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ; +import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIMESTAMP; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE; @@ -253,15 +254,18 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, @Override public void addLocalPost(Transaction txn, BlogPost p) throws DbException { try { + GroupId groupId = p.getMessage().getGroupId(); + Blog b = getBlog(txn, groupId); + BdfDictionary meta = new BdfDictionary(); meta.put(KEY_TYPE, POST.getInt()); meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp()); meta.put(KEY_AUTHOR, authorToBdfDictionary(p.getAuthor())); meta.put(KEY_READ, true); + meta.put(KEY_RSS_FEED, b.isRssFeed()); clientHelper.addLocalMessage(txn, p.getMessage(), meta, true); // broadcast event about new post - GroupId groupId = p.getMessage().getGroupId(); MessageId postId = p.getMessage().getId(); BlogPostHeader h = getPostHeaderFromMetadata(txn, groupId, postId, meta); @@ -350,6 +354,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, wMessage = blogPostFactory .wrapPost(groupId, wDescriptor, wTimestamp, body); meta.put(KEY_TYPE, WRAPPED_POST.getInt()); + meta.put(KEY_RSS_FEED, pOriginalHeader.isRssFeed()); } else if (type == COMMENT) { Group wGroup = db.getGroup(txn, pOriginalHeader.getGroupId()); byte[] wDescriptor = wGroup.getDescriptor(); @@ -598,8 +603,11 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, String name = d.getString(KEY_AUTHOR_NAME); byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY); Author author = new Author(authorId, name, publicKey); + boolean isFeedPost = meta.getBoolean(KEY_RSS_FEED, false); Status authorStatus; - if (authorStatuses.containsKey(authorId)) { + if (isFeedPost) { + authorStatus = Status.UNKNOWN; + } else if (authorStatuses.containsKey(authorId)) { authorStatus = authorStatuses.get(authorId); } else { authorStatus = identityManager.getAuthorStatus(txn, authorId); @@ -616,7 +624,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, timestamp, timeReceived, author, authorStatus, read); } else { return new BlogPostHeader(type, groupId, id, timestamp, - timeReceived, author, authorStatus, read); + timeReceived, author, authorStatus, isFeedPost, read); } } diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java index 33fb0bddf..5e74400c5 100644 --- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java +++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java @@ -39,6 +39,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_PARENT_ import static org.briarproject.briar.api.blog.BlogConstants.KEY_PARENT_MSG_ID; import static org.briarproject.briar.api.blog.BlogConstants.KEY_PUBLIC_KEY; import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ; +import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIMESTAMP; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE; @@ -123,6 +124,7 @@ class BlogPostValidator extends BdfMessageValidator { BdfDictionary meta = new BdfDictionary(); meta.put(KEY_ORIGINAL_MSG_ID, m.getId()); meta.put(KEY_AUTHOR, authorToBdfDictionary(a)); + meta.put(KEY_RSS_FEED, b.isRssFeed()); return new BdfMessageContext(meta); } diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java index 643fe482c..c2c00ea57 100644 --- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java @@ -1,5 +1,6 @@ package org.briarproject.briar.blog; +import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.test.TestDatabaseModule; import org.briarproject.briar.api.blog.Blog; @@ -32,7 +33,7 @@ public class BlogManagerIntegrationTest extends BriarIntegrationTest { private BlogManager blogManager0, blogManager1; - private Blog blog0, blog1; + private Blog blog0, blog1, rssBlog; @Rule public ExpectedException thrown = ExpectedException.none(); @@ -50,6 +51,12 @@ public class BlogManagerIntegrationTest blog0 = blogFactory.createBlog(author0); blog1 = blogFactory.createBlog(author1); + + rssBlog = blogFactory.createFeedBlog(author0); + Transaction txn = db0.startTransaction(false); + blogManager0.addBlog(txn, rssBlog); + db0.commitTransaction(txn); + db0.endTransaction(txn); } @Override @@ -393,4 +400,62 @@ public class BlogManagerIntegrationTest assertEquals(2, headers0.size()); } + @Test + public void testFeedPost() throws Exception { + assertTrue(rssBlog.isRssFeed()); + + // add a feed post to rssBlog + final String body = getRandomString(42); + BlogPost p = blogPostFactory + .createBlogPost(rssBlog.getId(), clock.currentTimeMillis(), + null, author0, body); + blogManager0.addLocalPost(p); + + // make sure it got saved as an RSS feed post + Collection headers = + blogManager0.getPostHeaders(rssBlog.getId()); + assertEquals(1, headers.size()); + BlogPostHeader header = headers.iterator().next(); + assertEquals(POST, header.getType()); + assertTrue(header.isRssFeed()); + } + + @Test + public void testFeedReblog() throws Exception { + // add a feed post to rssBlog + final String body = getRandomString(42); + BlogPost p = blogPostFactory + .createBlogPost(rssBlog.getId(), clock.currentTimeMillis(), + null, author0, body); + blogManager0.addLocalPost(p); + + // reblog feed post to own blog + Collection headers = + blogManager0.getPostHeaders(rssBlog.getId()); + assertEquals(1, headers.size()); + BlogPostHeader header = headers.iterator().next(); + blogManager0.addLocalComment(author0, blog0.getId(), null, header); + + // make sure it got saved as an RSS feed post + headers = blogManager0.getPostHeaders(blog0.getId()); + assertEquals(1, headers.size()); + BlogCommentHeader commentHeader = + (BlogCommentHeader) headers.iterator().next(); + assertEquals(COMMENT, commentHeader.getType()); + assertTrue(commentHeader.getParent().isRssFeed()); + + // reblog reblogged post again to own blog + blogManager0 + .addLocalComment(author0, blog0.getId(), null, commentHeader); + + // make sure it got saved as an RSS feed post + headers = blogManager0.getPostHeaders(blog0.getId()); + assertEquals(2, headers.size()); + for (BlogPostHeader h: headers) { + assertTrue(h instanceof BlogCommentHeader); + assertEquals(COMMENT, h.getType()); + assertTrue(((BlogCommentHeader) h).getParent().isRssFeed()); + } + } + } diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java index 25d4992c4..2fc24aae5 100644 --- a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java @@ -12,6 +12,7 @@ import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.transport.TransportModule; import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.BlogManager; +import org.briarproject.briar.api.blog.BlogPostHeader; import org.briarproject.briar.api.feed.Feed; import org.briarproject.briar.api.feed.FeedManager; import org.briarproject.briar.blog.BlogModule; @@ -88,6 +89,13 @@ public class FeedManagerIntegrationTest extends BriarTestCase { assertEquals(feed.getTitle(), feed.getBlog().getName()); assertEquals(feed.getTitle(), feed.getLocalAuthor().getName()); + // check the feed entries have been added to the blog as expected + Collection headers = + blogManager.getPostHeaders(feedBlog.getId()); + for (BlogPostHeader header : headers) { + assertTrue(header.isRssFeed()); + } + // now let's remove the feed's blog again blogManager.removeBlog(feedBlog); blogs = blogManager.getBlogs(); From 4b955809f7c8e1d9258909cc687231695233efd0 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 12 Apr 2017 09:30:08 -0300 Subject: [PATCH 8/8] Address review comments --- .../bramble/api/identity/Author.java | 4 +++- .../android/blog/BlogPostViewHolder.java | 2 +- .../briar/android/blog/RssFeedAdapter.java | 7 +------ .../briar/android/view/AuthorView.java | 14 ++++++++++--- briar-android/src/main/res/values/strings.xml | 2 +- .../briar/api/blog/BlogCommentHeader.java | 6 +++++- .../org/briarproject/briar/api/feed/Feed.java | 18 +++++++--------- .../briar/api/feed/FeedConstants.java | 1 - .../briar/blog/BlogFactoryImpl.java | 21 ++++++++++++++----- .../briar/blog/BlogManagerImpl.java | 2 +- .../briar/feed/FeedFactoryImpl.java | 17 ++++++++------- .../briar/feed/FeedManagerImpl.java | 5 ++--- .../blog/BlogManagerIntegrationTest.java | 4 +++- 13 files changed, 60 insertions(+), 43 deletions(-) diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java index 7e89a7a7b..b7c208ff2 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java @@ -13,7 +13,9 @@ import javax.annotation.concurrent.Immutable; @NotNullByDefault public class Author { - public enum Status {ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES} + public enum Status { + NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES + } private final AuthorId id; private final String name; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java index b0f4187b4..76232fc57 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java @@ -169,7 +169,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder { reblogger.setVisibility(VISIBLE); reblogger.setPersona(AuthorView.REBLOGGER); - author.setPersona(item.getHeader().getParent().isRssFeed() ? + author.setPersona(item.getHeader().getRootPost().isRssFeed() ? AuthorView.RSS_FEED_REBLOGGED : AuthorView.COMMENTER); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java index e55dcef30..e4ed107b6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java @@ -39,12 +39,7 @@ class RssFeedAdapter extends BriarAdapter { if (item == null) return; // Feed Title - if (item.getTitle() != null) { - ui.title.setText(item.getTitle()); - ui.title.setVisibility(VISIBLE); - } else { - ui.title.setVisibility(GONE); - } + ui.title.setText(item.getTitle()); // Delete Button ui.delete.setOnClickListener(new OnClickListener() { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java index 67aa5a3b9..f03a88ecd 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java @@ -30,6 +30,7 @@ import static android.content.Context.LAYOUT_INFLATER_SERVICE; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.graphics.Typeface.BOLD; import static android.util.TypedValue.COMPLEX_UNIT_PX; +import static org.briarproject.bramble.api.identity.Author.Status.NONE; import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; @@ -85,7 +86,13 @@ public class AuthorView extends RelativeLayout { } public void setAuthorStatus(Status status) { - trustIndicator.setTrustLevel(status); + if (status != NONE) { + trustIndicator.setTrustLevel(status); + trustIndicator.setVisibility(VISIBLE); + } else { + trustIndicator.setVisibility(GONE); + } + if (status == OURSELVES) { authorName.setTypeface(authorNameTypeface, BOLD); } else { @@ -129,8 +136,9 @@ public class AuthorView extends RelativeLayout { /** * Styles this view for a different persona. * - * Attention: If used in a RecyclerView with RSS_FEED, - * call this after setAuthor() + * Attention: RSS_FEED and RSS_FEED_REBLOGGED change the avatar + * and override the one set by + * {@link AuthorView#setAuthor(Author)}. */ public void setPersona(int persona) { switch (persona) { diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 5103e596b..3c8a44883 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -305,7 +305,7 @@ Author: Last Updated: Remove Feed - Are you sure that you want to remove this feed and all its posts?\nNote that this will not remove the feed\'s blog from other people\'s devices. + Are you sure you want to remove this feed and all its posts?\nAny posts you have shared will not be removed from other people\'s devices. Remove Feed The feed could not be deleted! You haven\'t imported any RSS feeds.\n\nWhy don\'t you click the plus in the top right screen corner to add your first? diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java index af28ca1a2..f7914c9d5 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java @@ -41,8 +41,12 @@ public class BlogCommentHeader extends BlogPostHeader { } public BlogPostHeader getParent() { + return parent; + } + + public BlogPostHeader getRootPost() { if (parent instanceof BlogCommentHeader) - return ((BlogCommentHeader) parent).getParent(); + return ((BlogCommentHeader) parent).getRootPost(); return parent; } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java index 4932ecab7..f2fcaa387 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java @@ -16,18 +16,16 @@ public class Feed { private final Blog blog; private final LocalAuthor localAuthor; @Nullable - private final String title, description, author; + private final String description, author; private final long added, updated, lastEntryTime; public Feed(String url, Blog blog, LocalAuthor localAuthor, - @Nullable String title, @Nullable String description, - @Nullable String author, long added, long updated, - long lastEntryTime) { + @Nullable String description, @Nullable String author, long added, + long updated, long lastEntryTime) { this.url = url; this.blog = blog; this.localAuthor = localAuthor; - this.title = title; this.description = description; this.author = author; this.added = added; @@ -36,13 +34,12 @@ public class Feed { } public Feed(String url, Blog blog, LocalAuthor localAuthor, - @Nullable String title, @Nullable String description, - @Nullable String author, long added) { - this(url, blog, localAuthor, title, description, author, added, 0L, 0L); + @Nullable String description, @Nullable String author, long added) { + this(url, blog, localAuthor, description, author, added, 0L, 0L); } public Feed(String url, Blog blog, LocalAuthor localAuthor, long added) { - this(url, blog, localAuthor, null, null, null, added, 0L, 0L); + this(url, blog, localAuthor, null, null, added, 0L, 0L); } public String getUrl() { @@ -61,9 +58,8 @@ public class Feed { return localAuthor; } - @Nullable public String getTitle() { - return title; + return blog.getName(); } @Nullable diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java index 9dc944585..0fddb3aa7 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java @@ -21,7 +21,6 @@ public interface FeedConstants { String KEY_BLOG_TITLE = "blogTitle"; String KEY_PUBLIC_KEY = "publicKey"; String KEY_PRIVATE_KEY = "privateKey"; - String KEY_FEED_TITLE = "feedTitle"; String KEY_FEED_DESC = "feedDesc"; String KEY_FEED_AUTHOR = "feedAuthor"; String KEY_FEED_ADDED = "feedAdded"; diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java index deaae2cfe..c7d3a2001 100644 --- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java @@ -14,6 +14,9 @@ import org.briarproject.briar.api.blog.BlogFactory; import javax.annotation.concurrent.Immutable; import javax.inject.Inject; +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; + @Immutable @NotNullByDefault class BlogFactoryImpl implements BlogFactory { @@ -58,13 +61,21 @@ class BlogFactoryImpl implements BlogFactory { } @Override - public Blog parseBlog(Group g) throws FormatException { - byte[] descriptor = g.getDescriptor(); + public Blog parseBlog(Group group) throws FormatException { + byte[] descriptor = group.getDescriptor(); // Author Name, Public Key BdfList blog = clientHelper.toList(descriptor); - Author a = - authorFactory.createAuthor(blog.getString(0), blog.getRaw(1)); - return new Blog(g, a, blog.getBoolean(2)); + String name = blog.getString(0); + if (name.length() > MAX_AUTHOR_NAME_LENGTH) + throw new IllegalArgumentException(); + byte[] publicKey = blog.getRaw(1); + if (publicKey.length > MAX_PUBLIC_KEY_LENGTH) + throw new IllegalArgumentException(); + + Author author = + authorFactory.createAuthor(name, publicKey); + boolean rssFeed = blog.getBoolean(2); + return new Blog(group, author, rssFeed); } } diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java index 91945f310..39ccb5241 100644 --- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java @@ -606,7 +606,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, boolean isFeedPost = meta.getBoolean(KEY_RSS_FEED, false); Status authorStatus; if (isFeedPost) { - authorStatus = Status.UNKNOWN; + authorStatus = Status.NONE; } else if (authorStatuses.containsKey(authorId)) { authorStatus = authorStatuses.get(authorId); } else { diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java index 14aca1559..ec5d9c31b 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java @@ -10,18 +10,19 @@ import org.briarproject.bramble.api.data.BdfEntry; import org.briarproject.bramble.api.identity.AuthorFactory; import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.system.Clock; +import org.briarproject.bramble.util.StringUtils; import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.BlogFactory; import org.briarproject.briar.api.feed.Feed; import javax.inject.Inject; +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.briar.api.feed.FeedConstants.KEY_BLOG_TITLE; import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_ADDED; import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_AUTHOR; import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_DESC; import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_LAST_ENTRY; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_TITLE; import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_UPDATED; import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_URL; import static org.briarproject.briar.api.feed.FeedConstants.KEY_PRIVATE_KEY; @@ -45,11 +46,13 @@ class FeedFactoryImpl implements FeedFactory { @Override public Feed createFeed(String url, SyndFeed syndFeed) { - if (syndFeed.getTitle() == null) syndFeed.setTitle("RSS feed"); + String title = syndFeed.getTitle(); + if (title == null) title = "RSS"; + title = StringUtils.truncateUtf8(title, MAX_AUTHOR_NAME_LENGTH); KeyPair keyPair = cryptoComponent.generateSignatureKeyPair(); LocalAuthor localAuthor = authorFactory - .createLocalAuthor(syndFeed.getTitle(), + .createLocalAuthor(title, keyPair.getPublic().getEncoded(), keyPair.getPrivate().getEncoded()); Blog blog = blogFactory.createFeedBlog(localAuthor); @@ -62,8 +65,8 @@ class FeedFactoryImpl implements FeedFactory { public Feed createFeed(Feed feed, SyndFeed f, long lastEntryTime) { long updated = clock.currentTimeMillis(); return new Feed(feed.getUrl(), feed.getBlog(), feed.getLocalAuthor(), - f.getTitle(), f.getDescription(), f.getAuthor(), - feed.getAdded(), updated, lastEntryTime); + f.getDescription(), f.getAuthor(), feed.getAdded(), updated, + lastEntryTime); } @Override @@ -77,14 +80,13 @@ class FeedFactoryImpl implements FeedFactory { .createLocalAuthor(blogTitle, publicKey, privateKey); Blog blog = blogFactory.createFeedBlog(localAuthor); - String title = d.getOptionalString(KEY_FEED_TITLE); String desc = d.getOptionalString(KEY_FEED_DESC); String author = d.getOptionalString(KEY_FEED_AUTHOR); long added = d.getLong(KEY_FEED_ADDED, 0L); long updated = d.getLong(KEY_FEED_UPDATED, 0L); long lastEntryTime = d.getLong(KEY_FEED_LAST_ENTRY, 0L); - return new Feed(url, blog, localAuthor, title, desc, author, added, + return new Feed(url, blog, localAuthor, desc, author, added, updated, lastEntryTime); } @@ -101,7 +103,6 @@ class FeedFactoryImpl implements FeedFactory { new BdfEntry(KEY_FEED_UPDATED, feed.getUpdated()), new BdfEntry(KEY_FEED_LAST_ENTRY, feed.getLastEntryTime()) ); - if (feed.getTitle() != null) d.put(KEY_FEED_TITLE, feed.getTitle()); if (feed.getDescription() != null) d.put(KEY_FEED_DESC, feed.getDescription()); if (feed.getAuthor() != null) d.put(KEY_FEED_AUTHOR, feed.getAuthor()); diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java index 75d8977ea..bb97bb0d4 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java @@ -433,9 +433,8 @@ class FeedManagerImpl implements FeedManager, Client, EventListener, // build post body StringBuilder b = new StringBuilder(); - if (feed.getTitle() != null) { - b.append("

").append(feed.getTitle()).append("

"); - } + b.append("

").append(feed.getTitle()).append("

"); + if (!StringUtils.isNullOrEmpty(entry.getTitle())) { b.append("

").append(entry.getTitle()).append("

"); } diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java index c2c00ea57..165c1fab5 100644 --- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java @@ -1,6 +1,7 @@ package org.briarproject.briar.blog; import org.briarproject.bramble.api.db.Transaction; +import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.test.TestDatabaseModule; import org.briarproject.briar.api.blog.Blog; @@ -417,6 +418,7 @@ public class BlogManagerIntegrationTest assertEquals(1, headers.size()); BlogPostHeader header = headers.iterator().next(); assertEquals(POST, header.getType()); + assertEquals(Author.Status.NONE, header.getAuthorStatus()); assertTrue(header.isRssFeed()); } @@ -454,7 +456,7 @@ public class BlogManagerIntegrationTest for (BlogPostHeader h: headers) { assertTrue(h instanceof BlogCommentHeader); assertEquals(COMMENT, h.getType()); - assertTrue(((BlogCommentHeader) h).getParent().isRssFeed()); + assertTrue(((BlogCommentHeader) h).getRootPost().isRssFeed()); } }