Backend for Automatic Personal Blogs

When a contact is added, her personal blog will also be added automatically.
Also, when a new identity is added, a personal blog for that identity is created.

Part of #436
This commit is contained in:
Torsten Grote
2016-06-21 17:00:13 -03:00
parent 30fe9f6e2a
commit a8f51fcb8a
8 changed files with 135 additions and 11 deletions

View File

@@ -11,13 +11,16 @@ public class Blog extends Forum {
private final String description;
@NotNull
private final Author author;
private final boolean permanent;
public Blog(@NotNull Group group, @NotNull String name,
@NotNull String description, @NotNull Author author) {
@NotNull String description, @NotNull Author author,
boolean permanent) {
super(group, name, null);
this.description = description;
this.author = author;
this.permanent = permanent;
}
@NotNull
@@ -29,4 +32,8 @@ public class Blog extends Forum {
public Author getAuthor() {
return author;
}
public boolean isPermanent() {
return permanent;
}
}

View File

@@ -19,6 +19,9 @@ public interface BlogConstants {
/** The maximum length of a blog post's body in bytes. */
int MAX_BLOG_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
/** The internal name of personal blogs that are created automatically */
String PERSONAL_BLOG_NAME = "briar.PERSONAL_BLOG_NAME";
/* Blog Sharing Constants */
String BLOG_TITLE = "blogTitle";
String BLOG_DESC = "blogDescription";

View File

@@ -1,6 +1,7 @@
package org.briarproject.api.blogs;
import org.briarproject.api.FormatException;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.identity.Author;
import org.briarproject.api.sync.Group;
import org.jetbrains.annotations.NotNull;
@@ -11,6 +12,9 @@ public interface BlogFactory {
Blog createBlog(@NotNull String name, @NotNull String description,
@NotNull Author author);
/** Creates a personal blog for a given author. */
Blog createPersonalBlog(@NotNull Author author);
/** Parses a blog with the given Group and description */
Blog parseBlog(@NotNull Group g, @NotNull String description)
throws FormatException;

View File

@@ -2,6 +2,7 @@ package org.briarproject.api.blogs;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.GroupId;
@@ -31,9 +32,12 @@ public interface BlogManager {
/** Returns the blog with the given ID. */
Blog getBlog(Transaction txn, GroupId g) throws DbException;
/** Returns all blogs to which the localAuthor created. */
/** Returns all blogs owned by the given localAuthor. */
Collection<Blog> getBlogs(LocalAuthor localAuthor) throws DbException;
/** Returns only the personal blog of the given author. */
Blog getPersonalBlog(Author author) throws DbException;
/** Returns all blogs to which the user subscribes. */
Collection<Blog> getBlogs() throws DbException;

View File

@@ -4,6 +4,7 @@ import org.briarproject.api.FormatException;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory;
@@ -13,6 +14,8 @@ import org.jetbrains.annotations.NotNull;
import javax.inject.Inject;
import static org.briarproject.api.blogs.BlogConstants.PERSONAL_BLOG_NAME;
class BlogFactoryImpl implements BlogFactory {
private final GroupFactory groupFactory;
@@ -31,6 +34,16 @@ class BlogFactoryImpl implements BlogFactory {
@Override
public Blog createBlog(@NotNull String name, @NotNull String description,
@NotNull Author author) {
return createBlog(name, description, author, false);
}
@Override
public Blog createPersonalBlog(@NotNull Author a) {
return createBlog(PERSONAL_BLOG_NAME, "", a, true);
}
private Blog createBlog(@NotNull String name, @NotNull String description,
@NotNull Author author, boolean permanent) {
try {
BdfList blog = BdfList.of(
name,
@@ -40,7 +53,7 @@ class BlogFactoryImpl implements BlogFactory {
byte[] descriptor = clientHelper.toByteArray(blog);
Group g = groupFactory
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
return new Blog(g, name, description, author);
return new Blog(g, name, description, author, permanent);
} catch (FormatException e) {
throw new RuntimeException(e);
}
@@ -55,7 +68,9 @@ class BlogFactoryImpl implements BlogFactory {
BdfList blog = clientHelper.toList(descriptor, 0, descriptor.length);
Author a =
authorFactory.createAuthor(blog.getString(1), blog.getRaw(2));
return new Blog(g, blog.getString(0), description, a);
// TODO change permanent depending on how this will be used
boolean permanent = false;
return new Blog(g, blog.getString(0), description, a, permanent);
}
}

View File

@@ -6,7 +6,10 @@ import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogPost;
import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
@@ -19,6 +22,8 @@ import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.Author.Status;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.IdentityManager.AddIdentityHook;
import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
@@ -51,8 +56,12 @@ 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_TIMESTAMP;
import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
import static org.briarproject.api.contact.ContactManager.AddContactHook;
import static org.briarproject.api.contact.ContactManager.RemoveContactHook;
class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager {
class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
AddContactHook, RemoveContactHook, Client,
AddIdentityHook, RemoveIdentityHook {
private static final Logger LOG =
Logger.getLogger(BlogManagerImpl.class.getName());
@@ -83,6 +92,68 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager {
return CLIENT_ID;
}
@Override
public void createLocalState(Transaction txn) throws DbException {
// Ensure every identity does have its own personal blog
// TODO this can probably be removed once #446 is resolved and all users migrated to a new version
for (LocalAuthor a : db.getLocalAuthors(txn)) {
Blog b = blogFactory.createPersonalBlog(a);
Group g = b.getGroup();
if (!db.containsGroup(txn, g.getId())) {
db.addGroup(txn, g);
for (ContactId c : db.getContacts(txn, a.getId())) {
db.setVisibleToContact(txn, c, g.getId(), true);
}
}
}
// Ensure that we have the personal blogs of all pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
}
@Override
public void addingContact(Transaction txn, Contact c) throws DbException {
// get personal blog of the contact
Blog b = blogFactory.createPersonalBlog(c.getAuthor());
Group g = b.getGroup();
if (!db.containsGroup(txn, g.getId())) {
// add the personal blog of the contact
db.addGroup(txn, g);
db.setVisibleToContact(txn, c.getId(), g.getId(), true);
// share our personal blog with the new contact
LocalAuthor a = db.getLocalAuthor(txn, c.getLocalAuthorId());
Blog b2 = blogFactory.createPersonalBlog(a);
db.setVisibleToContact(txn, c.getId(), b2.getId(), true);
}
}
@Override
public void removingContact(Transaction txn, Contact c) throws DbException {
if (c != null) {
Blog b = blogFactory.createPersonalBlog(c.getAuthor());
db.removeGroup(txn, b.getGroup());
}
}
@Override
public void addingIdentity(Transaction txn, LocalAuthor a)
throws DbException {
// add a personal blog for the new identity
LOG.info("New Personal Blog Added.");
Blog b = blogFactory.createPersonalBlog(a);
db.addGroup(txn, b.getGroup());
}
@Override
public void removingIdentity(Transaction txn, LocalAuthor a)
throws DbException {
// remove the personal blog of that identity
Blog b = blogFactory.createPersonalBlog(a);
db.removeGroup(txn, b.getGroup());
}
@Override
protected void incomingMessage(Transaction txn, Message m, BdfList list,
BdfDictionary meta) throws DbException, FormatException {
@@ -208,6 +279,11 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager {
return Collections.unmodifiableList(blogs);
}
@Override
public Blog getPersonalBlog(Author author) throws DbException {
return blogFactory.createPersonalBlog(author);
}
@Override
public Collection<Blog> getBlogs() throws DbException {
try {
@@ -291,7 +367,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager {
private String getBlogDescription(Transaction txn, GroupId g)
throws DbException, FormatException {
BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
return d.getString(KEY_DESCRIPTION);
return d.getString(KEY_DESCRIPTION, "");
}
private BlogPostHeader getPostHeaderFromMetadata(@Nullable Transaction txn,
@@ -321,5 +397,4 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager {
return new BlogPostHeader(title, id, parentId, timestamp, author,
authorStatus, contentType, read);
}
}

View File

@@ -4,11 +4,12 @@ import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogPostFactory;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.GroupFactory;
import org.briarproject.api.sync.ValidationManager;
import org.briarproject.api.system.Clock;
@@ -19,17 +20,31 @@ import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static org.briarproject.blogs.BlogManagerImpl.CLIENT_ID;
@Module
public class BlogsModule {
public static class EagerSingletons {
@Inject
BlogPostValidator blogPostValidator;
@Inject
BlogManager blogManager;
}
@Provides
@Singleton
BlogManager provideBlogManager(BlogManagerImpl blogManager) {
BlogManager provideBlogManager(BlogManagerImpl blogManager,
LifecycleManager lifecycleManager, ContactManager contactManager,
IdentityManager identityManager,
ValidationManager validationManager) {
lifecycleManager.registerClient(blogManager);
contactManager.registerAddContactHook(blogManager);
contactManager.registerRemoveContactHook(blogManager);
identityManager.registerAddIdentityHook(blogManager);
identityManager.registerRemoveIdentityHook(blogManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, blogManager);
return blogManager;
}
@@ -54,8 +69,7 @@ public class BlogsModule {
BlogPostValidator validator = new BlogPostValidator(crypto,
blogFactory, clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(
BlogManagerImpl.CLIENT_ID, validator);
validationManager.registerMessageValidator(CLIENT_ID, validator);
return validator;
}

View File

@@ -792,6 +792,8 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM
} else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US) {
removeFromList(txn, groupId, SHARED_WITH_US, f);
} else if (task == TASK_ADD_SHARED_SHAREABLE) {
// TODO we might want to call the add() method of the respective
// manager here, because blogs add a description for example
db.addGroup(txn, f.getGroup());
db.setVisibleToContact(txn, contactId, f.getId(), true);
} else if (task == TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US) {