From 30fe9f6e2ae712e07de2a813320efd6a41f1a2dd Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Mon, 20 Jun 2016 16:00:05 -0300
Subject: [PATCH] Change Blog Paradigm to Short-Form

Removes teaser and makes body mandatory.

It also adds support for deleting blogs and
introduces a getAuthorStatus() method to the IdentityManager
that takes a running transaction.
---
 .../briarproject/api/blogs/BlogConstants.java |   5 -
 .../briarproject/api/blogs/BlogManager.java   |  11 +-
 .../org/briarproject/api/blogs/BlogPost.java  |  24 +---
 .../api/blogs/BlogPostFactory.java            |   4 +-
 .../api/blogs/BlogPostHeader.java             |  17 +--
 .../api/event/BlogPostAddedEvent.java         |  32 +++++
 .../api/identity/IdentityManager.java         |   3 +
 .../briarproject/blogs/BlogManagerImpl.java   | 131 +++++++++++++-----
 .../blogs/BlogPostFactoryImpl.java            |  15 +-
 .../briarproject/blogs/BlogPostValidator.java |  37 ++---
 .../identity/IdentityManagerImpl.java         |  25 ++--
 .../sharing/BlogSharingManagerImpl.java       |   8 +-
 .../briarproject/sharing/SharingModule.java   |   3 +
 13 files changed, 202 insertions(+), 113 deletions(-)
 create mode 100644 briar-api/src/org/briarproject/api/event/BlogPostAddedEvent.java

diff --git a/briar-api/src/org/briarproject/api/blogs/BlogConstants.java b/briar-api/src/org/briarproject/api/blogs/BlogConstants.java
index 738e012a3e..5e483ffc6e 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogConstants.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogConstants.java
@@ -16,9 +16,6 @@ public interface BlogConstants {
 	/** The length of a blog post's title in UTF-8 bytes. */
 	int MAX_BLOG_POST_TITLE_LENGTH = 100;
 
-	/** The length of a blog post's teaser in UTF-8 bytes. */
-	int MAX_BLOG_POST_TEASER_LENGTH = 240;
-
 	/** The maximum length of a blog post's body in bytes. */
 	int MAX_BLOG_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
 
@@ -31,8 +28,6 @@ public interface BlogConstants {
 	// Metadata keys
 	String KEY_DESCRIPTION = "description";
 	String KEY_TITLE = "title";
-	String KEY_TEASER = "teaser";
-	String KEY_HAS_BODY = "hasBody";
 	String KEY_TIMESTAMP = "timestamp";
 	String KEY_PARENT = "parent";
 	String KEY_AUTHOR_ID = "id";
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogManager.java b/briar-api/src/org/briarproject/api/blogs/BlogManager.java
index e01fa5efb9..0effcfee4d 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogManager.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogManager.java
@@ -1,6 +1,5 @@
 package org.briarproject.api.blogs;
 
-import org.briarproject.api.FormatException;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
 import org.briarproject.api.identity.LocalAuthor;
@@ -20,6 +19,9 @@ public interface BlogManager {
 	Blog addBlog(LocalAuthor localAuthor, String name, String description)
 			throws DbException;
 
+	/** Removes and deletes a blog. */
+	void removeBlog(Blog b) throws DbException;
+
 	/** Stores a local blog post. */
 	void addLocalPost(BlogPost p) throws DbException;
 
@@ -45,4 +47,11 @@ public interface BlogManager {
 	/** Marks a blog post as read or unread. */
 	void setReadFlag(MessageId m, boolean read) throws DbException;
 
+	/** Registers a hook to be called whenever a blog is removed. */
+	void registerRemoveBlogHook(RemoveBlogHook hook);
+
+	interface RemoveBlogHook {
+		void removingBlog(Transaction txn, Blog b) throws DbException;
+	}
+
 }
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogPost.java b/briar-api/src/org/briarproject/api/blogs/BlogPost.java
index a6aa1167ae..3854c04727 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogPost.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogPost.java
@@ -1,43 +1,27 @@
 package org.briarproject.api.blogs;
 
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
 import org.briarproject.api.forum.ForumPost;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 public class BlogPost extends ForumPost {
 
 	@Nullable
 	private final String title;
-	@NotNull
-	private final String teaser;
-	private final boolean hasBody;
 
-	public BlogPost(@Nullable String title, @NotNull String teaser,
-			boolean hasBody, @NotNull Message message,
-			@Nullable MessageId parent, @NotNull Author author,
+	public BlogPost(@Nullable String title,	@NotNull Message message,
+			@Nullable MessageId parent,	@NotNull Author author,
 			@NotNull String contentType) {
 		super(message, parent, author, contentType);
 
 		this.title = title;
-		this.teaser = teaser;
-		this.hasBody = hasBody;
 	}
 
 	@Nullable
 	public String getTitle() {
 		return title;
 	}
-
-	@NotNull
-	public String getTeaser() {
-		return teaser;
-	}
-
-	public boolean hasBody() {
-		return hasBody;
-	}
 }
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java b/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
index 2565bfb0e7..6ced9ba20f 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
@@ -12,8 +12,8 @@ import java.security.GeneralSecurityException;
 public interface BlogPostFactory {
 
 	BlogPost createBlogPost(@NotNull GroupId groupId, @Nullable String title,
-			@NotNull String teaser, long timestamp, @Nullable MessageId parent,
+			long timestamp, @Nullable MessageId parent,
 			@NotNull LocalAuthor author, @NotNull String contentType,
-			@Nullable byte[] body)
+			@NotNull byte[] body)
 			throws FormatException, GeneralSecurityException;
 }
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java b/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java
index 24d421c139..b3a740bcb9 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java
@@ -11,33 +11,18 @@ public class BlogPostHeader extends PostHeader {
 
 	@Nullable
 	private final String title;
-	@NotNull
-	private final String teaser;
-	private final boolean hasBody;
 
-	public BlogPostHeader(@Nullable String title, @NotNull String teaser,
-			boolean hasBody, @NotNull MessageId id,
+	public BlogPostHeader(@Nullable String title, @NotNull MessageId id,
 			@Nullable MessageId parentId, long timestamp,
 			@NotNull Author author,	@NotNull Status authorStatus,
 			@NotNull String contentType, boolean read) {
 		super(id, parentId, timestamp, author, authorStatus, contentType, read);
 
 		this.title = title;
-		this.teaser = teaser;
-		this.hasBody = hasBody;
 	}
 
 	@Nullable
 	public String getTitle() {
 		return title;
 	}
-
-	@NotNull
-	public String getTeaser() {
-		return teaser;
-	}
-
-	public boolean hasBody() {
-		return hasBody;
-	}
 }
diff --git a/briar-api/src/org/briarproject/api/event/BlogPostAddedEvent.java b/briar-api/src/org/briarproject/api/event/BlogPostAddedEvent.java
new file mode 100644
index 0000000000..c00e4efbff
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/event/BlogPostAddedEvent.java
@@ -0,0 +1,32 @@
+package org.briarproject.api.event;
+
+import org.briarproject.api.blogs.BlogPostHeader;
+import org.briarproject.api.sync.GroupId;
+
+/** An event that is broadcast when a blog post was added to the database. */
+public class BlogPostAddedEvent extends Event {
+
+	private final GroupId groupId;
+	private final BlogPostHeader header;
+	private final boolean local;
+
+	public BlogPostAddedEvent(GroupId groupId, BlogPostHeader header,
+			boolean local) {
+
+		this.groupId = groupId;
+		this.header = header;
+		this.local = local;
+	}
+
+	public GroupId getGroupId() {
+		return groupId;
+	}
+
+	public BlogPostHeader getHeader() {
+		return header;
+	}
+
+	public boolean isLocal() {
+		return local;
+	}
+}
diff --git a/briar-api/src/org/briarproject/api/identity/IdentityManager.java b/briar-api/src/org/briarproject/api/identity/IdentityManager.java
index e5420a043f..e89f49d8ac 100644
--- a/briar-api/src/org/briarproject/api/identity/IdentityManager.java
+++ b/briar-api/src/org/briarproject/api/identity/IdentityManager.java
@@ -29,6 +29,9 @@ public interface IdentityManager {
 	/** Returns the trust-level status of the author */
 	Status getAuthorStatus(AuthorId a) throws DbException;
 
+	/** Returns the trust-level status of the author */
+	Status getAuthorStatus(Transaction txn, AuthorId a) throws DbException;
+
 	interface AddIdentityHook {
 		void addingIdentity(Transaction txn, LocalAuthor a) throws DbException;
 	}
diff --git a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
index eabfc251e3..70abde036f 100644
--- a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
+++ b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
@@ -10,9 +10,11 @@ import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfEntry;
 import org.briarproject.api.data.BdfList;
+import org.briarproject.api.data.MetadataParser;
 import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
+import org.briarproject.api.event.BlogPostAddedEvent;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.Author.Status;
 import org.briarproject.api.identity.AuthorId;
@@ -21,7 +23,9 @@ import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
+import org.briarproject.clients.BdfIncomingMessageHook;
 import org.briarproject.util.StringUtils;
 import org.jetbrains.annotations.Nullable;
 
@@ -31,24 +35,24 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
+import static java.util.logging.Level.WARNING;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
 import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
 import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
 import static org.briarproject.api.blogs.BlogConstants.KEY_DESCRIPTION;
-import static org.briarproject.api.blogs.BlogConstants.KEY_HAS_BODY;
 import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
 import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
-import static org.briarproject.api.blogs.BlogConstants.KEY_TEASER;
 import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
 import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
 
-class BlogManagerImpl implements BlogManager {
+class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager {
 
 	private static final Logger LOG =
 			Logger.getLogger(BlogManagerImpl.class.getName());
@@ -59,17 +63,19 @@ class BlogManagerImpl implements BlogManager {
 
 	private final DatabaseComponent db;
 	private final IdentityManager identityManager;
-	private final ClientHelper clientHelper;
 	private final BlogFactory blogFactory;
+	private final List<RemoveBlogHook> removeHooks;
 
 	@Inject
 	BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager,
-			ClientHelper clientHelper, BlogFactory blogFactory) {
+			ClientHelper clientHelper, MetadataParser metadataParser,
+			BlogFactory blogFactory) {
+		super(clientHelper, metadataParser);
 
 		this.db = db;
 		this.identityManager = identityManager;
-		this.clientHelper = clientHelper;
 		this.blogFactory = blogFactory;
+		removeHooks = new CopyOnWriteArrayList<RemoveBlogHook>();
 	}
 
 	@Override
@@ -77,6 +83,17 @@ class BlogManagerImpl implements BlogManager {
 		return CLIENT_ID;
 	}
 
+	@Override
+	protected void incomingMessage(Transaction txn, Message m, BdfList list,
+			BdfDictionary meta) throws DbException, FormatException {
+
+		GroupId groupId = m.getGroupId();
+		BlogPostHeader h = getPostHeaderFromMetadata(txn, m.getId(), meta);
+		BlogPostAddedEvent event =
+				new BlogPostAddedEvent(groupId, h, false);
+		txn.attach(event);
+	}
+
 	@Override
 	public Blog addBlog(LocalAuthor localAuthor, String name,
 			String description) throws DbException {
@@ -100,14 +117,26 @@ class BlogManagerImpl implements BlogManager {
 		return b;
 	}
 
+	@Override
+	public void removeBlog(Blog b) throws DbException {
+		Transaction txn = db.startTransaction(false);
+		try {
+			for (RemoveBlogHook hook : removeHooks)
+				hook.removingBlog(txn, b);
+			db.removeGroup(txn, b.getGroup());
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+	}
+
 	@Override
 	public void addLocalPost(BlogPost p) throws DbException {
+		BdfDictionary meta;
 		try {
-			BdfDictionary meta = new BdfDictionary();
+			meta = new BdfDictionary();
 			if (p.getTitle() != null) meta.put(KEY_TITLE, p.getTitle());
-			meta.put(KEY_TEASER, p.getTeaser());
 			meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
-			meta.put(KEY_HAS_BODY, p.hasBody());
 			if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
 
 			Author a = p.getAuthor();
@@ -123,6 +152,22 @@ class BlogManagerImpl implements BlogManager {
 		} catch (FormatException e) {
 			throw new RuntimeException(e);
 		}
+
+		// broadcast event about new post
+		Transaction txn = db.startTransaction(true);
+		try {
+			GroupId groupId = p.getMessage().getGroupId();
+			MessageId postId = p.getMessage().getId();
+			BlogPostHeader h = getPostHeaderFromMetadata(txn, postId, meta);
+			BlogPostAddedEvent event =
+					new BlogPostAddedEvent(groupId, h, true);
+			txn.attach(event);
+			txn.setComplete();
+		} catch (FormatException e) {
+			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+		} finally {
+			db.endTransaction(txn);
+		}
 	}
 
 	@Override
@@ -189,16 +234,20 @@ class BlogManagerImpl implements BlogManager {
 	@Nullable
 	public byte[] getPostBody(MessageId m) throws DbException {
 		try {
-			// content, signature
-			// content: parent, contentType, title, teaser, body, attachments
 			BdfList message = clientHelper.getMessageAsList(m);
-			BdfList content = message.getList(0);
-			return content.getRaw(4);
+			return getPostBody(message);
 		} catch (FormatException e) {
 			throw new DbException(e);
 		}
 	}
 
+	private byte[] getPostBody(BdfList message) throws FormatException {
+		// content, signature
+		// content: parent, contentType, title, body, attachments
+		BdfList content = message.getList(0);
+		return content.getRaw(3);
+	}
+
 	@Override
 	public Collection<BlogPostHeader> getPostHeaders(GroupId g)
 			throws DbException {
@@ -213,26 +262,9 @@ class BlogManagerImpl implements BlogManager {
 		for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
 			try {
 				BdfDictionary meta = entry.getValue();
-				String title = meta.getOptionalString(KEY_TITLE);
-				String teaser = meta.getString(KEY_TEASER);
-				boolean hasBody = meta.getBoolean(KEY_HAS_BODY);
-				long timestamp = meta.getLong(KEY_TIMESTAMP);
-				MessageId parentId = null;
-				if (meta.containsKey(KEY_PARENT))
-					parentId = new MessageId(meta.getRaw(KEY_PARENT));
-
-				BdfDictionary d = meta.getDictionary(KEY_AUTHOR);
-				AuthorId authorId = new AuthorId(d.getRaw(KEY_AUTHOR_ID));
-				String name = d.getString(KEY_AUTHOR_NAME);
-				byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
-				Author author = new Author(authorId, name, publicKey);
-				Status authorStatus = identityManager.getAuthorStatus(authorId);
-
-				String contentType = meta.getString(KEY_CONTENT_TYPE);
-				boolean read = meta.getBoolean(KEY_READ);
-				headers.add(new BlogPostHeader(title, teaser, hasBody,
-						entry.getKey(), parentId, timestamp, author,
-						authorStatus, contentType, read));
+				BlogPostHeader h =
+						getPostHeaderFromMetadata(null, entry.getKey(), meta);
+				headers.add(h);
 			} catch (FormatException e) {
 				throw new DbException(e);
 			}
@@ -251,10 +283,43 @@ class BlogManagerImpl implements BlogManager {
 		}
 	}
 
+	@Override
+	public void registerRemoveBlogHook(RemoveBlogHook hook) {
+		removeHooks.add(hook);
+	}
+
 	private String getBlogDescription(Transaction txn, GroupId g)
 			throws DbException, FormatException {
 		BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
 		return d.getString(KEY_DESCRIPTION);
 	}
 
+	private BlogPostHeader getPostHeaderFromMetadata(@Nullable Transaction txn,
+			MessageId id, BdfDictionary meta)
+			throws DbException, FormatException {
+
+		String title = meta.getOptionalString(KEY_TITLE);
+		long timestamp = meta.getLong(KEY_TIMESTAMP);
+		MessageId parentId = null;
+		if (meta.containsKey(KEY_PARENT))
+			parentId = new MessageId(meta.getRaw(KEY_PARENT));
+
+		BdfDictionary d = meta.getDictionary(KEY_AUTHOR);
+		AuthorId authorId = new AuthorId(d.getRaw(KEY_AUTHOR_ID));
+		String name = d.getString(KEY_AUTHOR_NAME);
+		byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
+		Author author = new Author(authorId, name, publicKey);
+		Status authorStatus;
+		if (txn == null)
+			authorStatus = identityManager.getAuthorStatus(authorId);
+		else {
+			authorStatus = identityManager.getAuthorStatus(txn, authorId);
+		}
+
+		String contentType = meta.getString(KEY_CONTENT_TYPE);
+		boolean read = meta.getBoolean(KEY_READ);
+		return new BlogPostHeader(title, id, parentId, timestamp, author,
+				authorStatus, contentType, read);
+	}
+
 }
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java b/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
index e9ced2d3fe..ce134d33c6 100644
--- a/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
+++ b/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
@@ -22,7 +22,6 @@ import java.security.GeneralSecurityException;
 import javax.inject.Inject;
 
 import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
-import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TEASER_LENGTH;
 import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_LENGTH;
 import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
 
@@ -39,25 +38,22 @@ class BlogPostFactoryImpl implements BlogPostFactory {
 
 	@Override
 	public BlogPost createBlogPost(@NotNull GroupId groupId,
-			@Nullable String title, @NotNull String teaser,	long timestamp,
+			@Nullable String title, long timestamp,
 			@Nullable MessageId parent,	@NotNull LocalAuthor author,
-			@NotNull String contentType, @Nullable byte[] body)
+			@NotNull String contentType, @NotNull byte[] body)
 			throws FormatException, GeneralSecurityException {
 
 		// Validate the arguments
 		if (title != null &&
 				StringUtils.toUtf8(title).length > MAX_BLOG_POST_TITLE_LENGTH)
 			throw new IllegalArgumentException();
-		if (StringUtils.toUtf8(teaser).length > MAX_BLOG_POST_TEASER_LENGTH)
-			throw new IllegalArgumentException();
 		if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH)
 			throw new IllegalArgumentException();
-		if (body != null && body.length > MAX_BLOG_POST_BODY_LENGTH)
+		if (body.length > MAX_BLOG_POST_BODY_LENGTH)
 			throw new IllegalArgumentException();
 
 		// Serialise the data to be signed
-		BdfList content =
-				BdfList.of(parent, contentType, title, teaser, body, null);
+		BdfList content = BdfList.of(parent, contentType, title, body, null);
 		BdfList signed = BdfList.of(groupId, timestamp, content);
 
 		// Generate the signature
@@ -72,7 +68,6 @@ class BlogPostFactoryImpl implements BlogPostFactory {
 		// Serialise the signed message
 		BdfList message = BdfList.of(content, sig);
 		Message m = clientHelper.createMessage(groupId, timestamp, message);
-		return new BlogPost(title, teaser, body != null, m, parent, author,
-				contentType);
+		return new BlogPost(title, m, parent, author, contentType);
 	}
 }
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
index bacf02dff6..1ff0e2d461 100644
--- a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
+++ b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
@@ -11,6 +11,7 @@ import org.briarproject.api.crypto.KeyParser;
 import org.briarproject.api.crypto.PublicKey;
 import org.briarproject.api.crypto.Signature;
 import org.briarproject.api.data.BdfDictionary;
+import org.briarproject.api.data.BdfEntry;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.data.MetadataEncoder;
 import org.briarproject.api.identity.Author;
@@ -25,15 +26,16 @@ import java.security.GeneralSecurityException;
 import java.util.Collection;
 import java.util.Collections;
 
+import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
+import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
+import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
 import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
-import static org.briarproject.api.blogs.BlogConstants.KEY_HAS_BODY;
 import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
+import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
-import static org.briarproject.api.blogs.BlogConstants.KEY_TEASER;
 import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
 import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
 import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
-import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TEASER_LENGTH;
 import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_LENGTH;
 import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
@@ -60,37 +62,36 @@ class BlogPostValidator extends BdfMessageValidator {
 		checkSize(body, 2);
 		BdfList content = body.getList(0);
 
-		// Content: Parent ID, content type, title (optional), teaser,
-		//          post body (optional), attachments (optional)
-		checkSize(body, 6);
+		// Content: Parent ID, content type, title (optional), post body,
+		//          attachments (optional)
+		checkSize(content, 5);
 		// Parent ID is optional
 		byte[] parent = content.getOptionalRaw(0);
 		checkLength(parent, UniqueId.LENGTH);
 		// Content type
 		String contentType = content.getString(1);
 		checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
+		if (!contentType.equals("text/plain"))
+			throw new InvalidMessageException("Invalid content type");
 		// Blog post title is optional
 		String title = content.getOptionalString(2);
 		checkLength(contentType, 0, MAX_BLOG_POST_TITLE_LENGTH);
-		// Blog teaser
-		String teaser = content.getString(3);
-		// TODO make sure that there is only text in the teaser
-		checkLength(contentType, 0, MAX_BLOG_POST_TEASER_LENGTH);
-		// Blog post body is optional
-		byte[] postBody = content.getOptionalRaw(4);
+		// Blog post body
+		byte[] postBody = content.getRaw(3);
 		checkLength(postBody, 0, MAX_BLOG_POST_BODY_LENGTH);
 		// Attachments
-		BdfDictionary attachments = content.getOptionalDictionary(5);
+		BdfDictionary attachments = content.getOptionalDictionary(4);
 		// TODO handle attachments somehow
 
 		// Signature
 		byte[] sig = body.getRaw(1);
 		checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
 		// Verify the signature
+		Author a;
 		try {
 			// Get the blog author
 			Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
-			Author a = b.getAuthor();
+			a = b.getAuthor();
 			// Parse the public key
 			KeyParser keyParser = crypto.getSignatureKeyParser();
 			PublicKey key = keyParser.parsePublicKey(a.getPublicKey());
@@ -111,8 +112,12 @@ class BlogPostValidator extends BdfMessageValidator {
 		BdfDictionary meta = new BdfDictionary();
 		Collection<MessageId> dependencies = null;
 		if (title != null) meta.put(KEY_TITLE, title);
-		meta.put(KEY_TEASER, teaser);
-		meta.put(KEY_HAS_BODY, postBody != null);
+		BdfDictionary author = BdfDictionary.of(
+				new BdfEntry(KEY_AUTHOR_ID, a.getId()),
+				new BdfEntry(KEY_AUTHOR_NAME, a.getName()),
+				new BdfEntry(KEY_PUBLIC_KEY, a.getPublicKey())
+		);
+		meta.put(KEY_AUTHOR, author);
 		meta.put(KEY_TIMESTAMP, m.getTimestamp());
 		if (parent != null) {
 			meta.put(KEY_PARENT, parent);
diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
index e04e206020..5b1434ad1e 100644
--- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
+++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
@@ -99,17 +99,24 @@ class IdentityManagerImpl implements IdentityManager {
 	public Status getAuthorStatus(AuthorId authorId) throws DbException {
 		Transaction txn = db.startTransaction(false);
 		try {
-			// Compare to the IDs of the user's identities
-			for (LocalAuthor a : db.getLocalAuthors(txn))
-				if (a.getId().equals(authorId)) return VERIFIED;
-			// Compare to the IDs of contacts' identities
-			for (Contact c : db.getContacts(txn))
-				if (c.getAuthor().getId().equals(authorId)) return VERIFIED;
-
-			// TODO also handle UNVERIFIED when #261 is implemented
-			return UNKNOWN;
+			return getAuthorStatus(txn, authorId);
 		} finally {
 			db.endTransaction(txn);
 		}
 	}
+
+	@Override
+	public Status getAuthorStatus(Transaction txn, AuthorId authorId)
+			throws DbException {
+		// Compare to the IDs of the user's identities
+		for (LocalAuthor a : db.getLocalAuthors(txn))
+			if (a.getId().equals(authorId)) return VERIFIED;
+		// Compare to the IDs of contacts' identities
+		for (Contact c : db.getContacts(txn))
+			if (c.getAuthor().getId().equals(authorId)) return VERIFIED;
+
+		// TODO also handle UNVERIFIED when #261 is implemented
+		return UNKNOWN;
+	}
+
 }
diff --git a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java
index ba67ff635e..a25520fac0 100644
--- a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java
+++ b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java
@@ -5,6 +5,7 @@ import org.briarproject.api.blogs.Blog;
 import org.briarproject.api.blogs.BlogFactory;
 import org.briarproject.api.blogs.BlogInvitationMessage;
 import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogManager.RemoveBlogHook;
 import org.briarproject.api.blogs.BlogSharingManager;
 import org.briarproject.api.blogs.BlogSharingMessage.BlogInvitation;
 import org.briarproject.api.clients.ClientHelper;
@@ -40,7 +41,7 @@ import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE;
 
 class BlogSharingManagerImpl extends
 		SharingManagerImpl<Blog, BlogInvitation, BlogInvitationMessage, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent>
-		implements BlogSharingManager {
+		implements BlogSharingManager, RemoveBlogHook {
 
 	static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
 			"bee438b5de0b3a685badc4e49d76e72d"
@@ -124,6 +125,11 @@ class BlogSharingManagerImpl extends
 		return irrFactory;
 	}
 
+	@Override
+	public void removingBlog(Transaction txn, Blog b) throws DbException {
+		removingShareable(txn, b);
+	}
+
 	static class SFactory implements
 			ShareableFactory<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState> {
 
diff --git a/briar-core/src/org/briarproject/sharing/SharingModule.java b/briar-core/src/org/briarproject/sharing/SharingModule.java
index d62c7dccf7..3af07e1292 100644
--- a/briar-core/src/org/briarproject/sharing/SharingModule.java
+++ b/briar-core/src/org/briarproject/sharing/SharingModule.java
@@ -1,5 +1,6 @@
 package org.briarproject.sharing;
 
+import org.briarproject.api.blogs.BlogManager;
 import org.briarproject.api.blogs.BlogSharingManager;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.clients.MessageQueueManager;
@@ -49,6 +50,7 @@ public class SharingModule {
 			LifecycleManager lifecycleManager,
 			ContactManager contactManager,
 			MessageQueueManager messageQueueManager,
+			BlogManager blogManager,
 			BlogSharingManagerImpl blogSharingManager) {
 
 		lifecycleManager.registerClient(blogSharingManager);
@@ -56,6 +58,7 @@ public class SharingModule {
 		contactManager.registerRemoveContactHook(blogSharingManager);
 		messageQueueManager.registerIncomingMessageHook(
 				BlogSharingManagerImpl.CLIENT_ID, blogSharingManager);
+		blogManager.registerRemoveBlogHook(blogSharingManager);
 
 		return blogSharingManager;
 	}
-- 
GitLab