mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 20:59:54 +01:00
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.
This commit is contained in:
@@ -16,9 +16,6 @@ public interface BlogConstants {
|
|||||||
/** The length of a blog post's title in UTF-8 bytes. */
|
/** The length of a blog post's title in UTF-8 bytes. */
|
||||||
int MAX_BLOG_POST_TITLE_LENGTH = 100;
|
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. */
|
/** The maximum length of a blog post's body in bytes. */
|
||||||
int MAX_BLOG_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
|
int MAX_BLOG_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
|
||||||
|
|
||||||
@@ -31,8 +28,6 @@ public interface BlogConstants {
|
|||||||
// Metadata keys
|
// Metadata keys
|
||||||
String KEY_DESCRIPTION = "description";
|
String KEY_DESCRIPTION = "description";
|
||||||
String KEY_TITLE = "title";
|
String KEY_TITLE = "title";
|
||||||
String KEY_TEASER = "teaser";
|
|
||||||
String KEY_HAS_BODY = "hasBody";
|
|
||||||
String KEY_TIMESTAMP = "timestamp";
|
String KEY_TIMESTAMP = "timestamp";
|
||||||
String KEY_PARENT = "parent";
|
String KEY_PARENT = "parent";
|
||||||
String KEY_AUTHOR_ID = "id";
|
String KEY_AUTHOR_ID = "id";
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.api.blogs;
|
package org.briarproject.api.blogs;
|
||||||
|
|
||||||
import org.briarproject.api.FormatException;
|
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.Transaction;
|
import org.briarproject.api.db.Transaction;
|
||||||
import org.briarproject.api.identity.LocalAuthor;
|
import org.briarproject.api.identity.LocalAuthor;
|
||||||
@@ -20,6 +19,9 @@ public interface BlogManager {
|
|||||||
Blog addBlog(LocalAuthor localAuthor, String name, String description)
|
Blog addBlog(LocalAuthor localAuthor, String name, String description)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/** Removes and deletes a blog. */
|
||||||
|
void removeBlog(Blog b) throws DbException;
|
||||||
|
|
||||||
/** Stores a local blog post. */
|
/** Stores a local blog post. */
|
||||||
void addLocalPost(BlogPost p) throws DbException;
|
void addLocalPost(BlogPost p) throws DbException;
|
||||||
|
|
||||||
@@ -45,4 +47,11 @@ public interface BlogManager {
|
|||||||
/** Marks a blog post as read or unread. */
|
/** Marks a blog post as read or unread. */
|
||||||
void setReadFlag(MessageId m, boolean read) throws DbException;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,27 @@
|
|||||||
package org.briarproject.api.blogs;
|
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.forum.ForumPost;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.sync.Message;
|
import org.briarproject.api.sync.Message;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class BlogPost extends ForumPost {
|
public class BlogPost extends ForumPost {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String title;
|
private final String title;
|
||||||
@NotNull
|
|
||||||
private final String teaser;
|
|
||||||
private final boolean hasBody;
|
|
||||||
|
|
||||||
public BlogPost(@Nullable String title, @NotNull String teaser,
|
public BlogPost(@Nullable String title, @NotNull Message message,
|
||||||
boolean hasBody, @NotNull Message message,
|
@Nullable MessageId parent, @NotNull Author author,
|
||||||
@Nullable MessageId parent, @NotNull Author author,
|
|
||||||
@NotNull String contentType) {
|
@NotNull String contentType) {
|
||||||
super(message, parent, author, contentType);
|
super(message, parent, author, contentType);
|
||||||
|
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.teaser = teaser;
|
|
||||||
this.hasBody = hasBody;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getTeaser() {
|
|
||||||
return teaser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasBody() {
|
|
||||||
return hasBody;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import java.security.GeneralSecurityException;
|
|||||||
public interface BlogPostFactory {
|
public interface BlogPostFactory {
|
||||||
|
|
||||||
BlogPost createBlogPost(@NotNull GroupId groupId, @Nullable String title,
|
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,
|
@NotNull LocalAuthor author, @NotNull String contentType,
|
||||||
@Nullable byte[] body)
|
@NotNull byte[] body)
|
||||||
throws FormatException, GeneralSecurityException;
|
throws FormatException, GeneralSecurityException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,33 +11,18 @@ public class BlogPostHeader extends PostHeader {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String title;
|
private final String title;
|
||||||
@NotNull
|
|
||||||
private final String teaser;
|
|
||||||
private final boolean hasBody;
|
|
||||||
|
|
||||||
public BlogPostHeader(@Nullable String title, @NotNull String teaser,
|
public BlogPostHeader(@Nullable String title, @NotNull MessageId id,
|
||||||
boolean hasBody, @NotNull MessageId id,
|
|
||||||
@Nullable MessageId parentId, long timestamp,
|
@Nullable MessageId parentId, long timestamp,
|
||||||
@NotNull Author author, @NotNull Status authorStatus,
|
@NotNull Author author, @NotNull Status authorStatus,
|
||||||
@NotNull String contentType, boolean read) {
|
@NotNull String contentType, boolean read) {
|
||||||
super(id, parentId, timestamp, author, authorStatus, contentType, read);
|
super(id, parentId, timestamp, author, authorStatus, contentType, read);
|
||||||
|
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.teaser = teaser;
|
|
||||||
this.hasBody = hasBody;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getTeaser() {
|
|
||||||
return teaser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasBody() {
|
|
||||||
return hasBody;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,9 @@ public interface IdentityManager {
|
|||||||
/** Returns the trust-level status of the author */
|
/** Returns the trust-level status of the author */
|
||||||
Status getAuthorStatus(AuthorId a) throws DbException;
|
Status getAuthorStatus(AuthorId a) throws DbException;
|
||||||
|
|
||||||
|
/** Returns the trust-level status of the author */
|
||||||
|
Status getAuthorStatus(Transaction txn, AuthorId a) throws DbException;
|
||||||
|
|
||||||
interface AddIdentityHook {
|
interface AddIdentityHook {
|
||||||
void addingIdentity(Transaction txn, LocalAuthor a) throws DbException;
|
void addingIdentity(Transaction txn, LocalAuthor a) throws DbException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ import org.briarproject.api.clients.ClientHelper;
|
|||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
import org.briarproject.api.data.BdfEntry;
|
import org.briarproject.api.data.BdfEntry;
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
|
import org.briarproject.api.data.MetadataParser;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.Transaction;
|
import org.briarproject.api.db.Transaction;
|
||||||
|
import org.briarproject.api.event.BlogPostAddedEvent;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.identity.Author.Status;
|
import org.briarproject.api.identity.Author.Status;
|
||||||
import org.briarproject.api.identity.AuthorId;
|
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.ClientId;
|
||||||
import org.briarproject.api.sync.Group;
|
import org.briarproject.api.sync.Group;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
import org.briarproject.api.sync.Message;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
import org.briarproject.clients.BdfIncomingMessageHook;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -31,24 +35,24 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
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;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
|
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_AUTHOR_NAME;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
|
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_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_PARENT;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
|
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_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_TIMESTAMP;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
|
import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
|
||||||
|
|
||||||
class BlogManagerImpl implements BlogManager {
|
class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(BlogManagerImpl.class.getName());
|
Logger.getLogger(BlogManagerImpl.class.getName());
|
||||||
@@ -59,17 +63,19 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final IdentityManager identityManager;
|
private final IdentityManager identityManager;
|
||||||
private final ClientHelper clientHelper;
|
|
||||||
private final BlogFactory blogFactory;
|
private final BlogFactory blogFactory;
|
||||||
|
private final List<RemoveBlogHook> removeHooks;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager,
|
BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager,
|
||||||
ClientHelper clientHelper, BlogFactory blogFactory) {
|
ClientHelper clientHelper, MetadataParser metadataParser,
|
||||||
|
BlogFactory blogFactory) {
|
||||||
|
super(clientHelper, metadataParser);
|
||||||
|
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.identityManager = identityManager;
|
this.identityManager = identityManager;
|
||||||
this.clientHelper = clientHelper;
|
|
||||||
this.blogFactory = blogFactory;
|
this.blogFactory = blogFactory;
|
||||||
|
removeHooks = new CopyOnWriteArrayList<RemoveBlogHook>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -77,6 +83,17 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
return CLIENT_ID;
|
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
|
@Override
|
||||||
public Blog addBlog(LocalAuthor localAuthor, String name,
|
public Blog addBlog(LocalAuthor localAuthor, String name,
|
||||||
String description) throws DbException {
|
String description) throws DbException {
|
||||||
@@ -101,13 +118,25 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLocalPost(BlogPost p) throws DbException {
|
public void removeBlog(Blog b) throws DbException {
|
||||||
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
BdfDictionary meta = new BdfDictionary();
|
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 {
|
||||||
|
meta = new BdfDictionary();
|
||||||
if (p.getTitle() != null) meta.put(KEY_TITLE, p.getTitle());
|
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_TIMESTAMP, p.getMessage().getTimestamp());
|
||||||
meta.put(KEY_HAS_BODY, p.hasBody());
|
|
||||||
if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
|
if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
|
||||||
|
|
||||||
Author a = p.getAuthor();
|
Author a = p.getAuthor();
|
||||||
@@ -123,6 +152,22 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new RuntimeException(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
|
@Override
|
||||||
@@ -189,16 +234,20 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public byte[] getPostBody(MessageId m) throws DbException {
|
public byte[] getPostBody(MessageId m) throws DbException {
|
||||||
try {
|
try {
|
||||||
// content, signature
|
|
||||||
// content: parent, contentType, title, teaser, body, attachments
|
|
||||||
BdfList message = clientHelper.getMessageAsList(m);
|
BdfList message = clientHelper.getMessageAsList(m);
|
||||||
BdfList content = message.getList(0);
|
return getPostBody(message);
|
||||||
return content.getRaw(4);
|
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(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
|
@Override
|
||||||
public Collection<BlogPostHeader> getPostHeaders(GroupId g)
|
public Collection<BlogPostHeader> getPostHeaders(GroupId g)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -213,26 +262,9 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
||||||
try {
|
try {
|
||||||
BdfDictionary meta = entry.getValue();
|
BdfDictionary meta = entry.getValue();
|
||||||
String title = meta.getOptionalString(KEY_TITLE);
|
BlogPostHeader h =
|
||||||
String teaser = meta.getString(KEY_TEASER);
|
getPostHeaderFromMetadata(null, entry.getKey(), meta);
|
||||||
boolean hasBody = meta.getBoolean(KEY_HAS_BODY);
|
headers.add(h);
|
||||||
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));
|
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(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)
|
private String getBlogDescription(Transaction txn, GroupId g)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
|
BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
|
||||||
return d.getString(KEY_DESCRIPTION);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import java.security.GeneralSecurityException;
|
|||||||
import javax.inject.Inject;
|
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_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_BLOG_POST_TITLE_LENGTH;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
|
import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||||
|
|
||||||
@@ -39,25 +38,22 @@ class BlogPostFactoryImpl implements BlogPostFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlogPost createBlogPost(@NotNull GroupId groupId,
|
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,
|
@Nullable MessageId parent, @NotNull LocalAuthor author,
|
||||||
@NotNull String contentType, @Nullable byte[] body)
|
@NotNull String contentType, @NotNull byte[] body)
|
||||||
throws FormatException, GeneralSecurityException {
|
throws FormatException, GeneralSecurityException {
|
||||||
|
|
||||||
// Validate the arguments
|
// Validate the arguments
|
||||||
if (title != null &&
|
if (title != null &&
|
||||||
StringUtils.toUtf8(title).length > MAX_BLOG_POST_TITLE_LENGTH)
|
StringUtils.toUtf8(title).length > MAX_BLOG_POST_TITLE_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
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)
|
if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (body != null && body.length > MAX_BLOG_POST_BODY_LENGTH)
|
if (body.length > MAX_BLOG_POST_BODY_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
// Serialise the data to be signed
|
// Serialise the data to be signed
|
||||||
BdfList content =
|
BdfList content = BdfList.of(parent, contentType, title, body, null);
|
||||||
BdfList.of(parent, contentType, title, teaser, body, null);
|
|
||||||
BdfList signed = BdfList.of(groupId, timestamp, content);
|
BdfList signed = BdfList.of(groupId, timestamp, content);
|
||||||
|
|
||||||
// Generate the signature
|
// Generate the signature
|
||||||
@@ -72,7 +68,6 @@ class BlogPostFactoryImpl implements BlogPostFactory {
|
|||||||
// Serialise the signed message
|
// Serialise the signed message
|
||||||
BdfList message = BdfList.of(content, sig);
|
BdfList message = BdfList.of(content, sig);
|
||||||
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
||||||
return new BlogPost(title, teaser, body != null, m, parent, author,
|
return new BlogPost(title, m, parent, author, contentType);
|
||||||
contentType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.briarproject.api.crypto.KeyParser;
|
|||||||
import org.briarproject.api.crypto.PublicKey;
|
import org.briarproject.api.crypto.PublicKey;
|
||||||
import org.briarproject.api.crypto.Signature;
|
import org.briarproject.api.crypto.Signature;
|
||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.api.data.BdfEntry;
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
import org.briarproject.api.data.MetadataEncoder;
|
import org.briarproject.api.data.MetadataEncoder;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
@@ -25,15 +26,16 @@ import java.security.GeneralSecurityException;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
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_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_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_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_TIMESTAMP;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
|
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_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_BLOG_POST_TITLE_LENGTH;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
|
import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||||
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||||
@@ -60,37 +62,36 @@ class BlogPostValidator extends BdfMessageValidator {
|
|||||||
checkSize(body, 2);
|
checkSize(body, 2);
|
||||||
BdfList content = body.getList(0);
|
BdfList content = body.getList(0);
|
||||||
|
|
||||||
// Content: Parent ID, content type, title (optional), teaser,
|
// Content: Parent ID, content type, title (optional), post body,
|
||||||
// post body (optional), attachments (optional)
|
// attachments (optional)
|
||||||
checkSize(body, 6);
|
checkSize(content, 5);
|
||||||
// Parent ID is optional
|
// Parent ID is optional
|
||||||
byte[] parent = content.getOptionalRaw(0);
|
byte[] parent = content.getOptionalRaw(0);
|
||||||
checkLength(parent, UniqueId.LENGTH);
|
checkLength(parent, UniqueId.LENGTH);
|
||||||
// Content type
|
// Content type
|
||||||
String contentType = content.getString(1);
|
String contentType = content.getString(1);
|
||||||
checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
|
checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
|
||||||
|
if (!contentType.equals("text/plain"))
|
||||||
|
throw new InvalidMessageException("Invalid content type");
|
||||||
// Blog post title is optional
|
// Blog post title is optional
|
||||||
String title = content.getOptionalString(2);
|
String title = content.getOptionalString(2);
|
||||||
checkLength(contentType, 0, MAX_BLOG_POST_TITLE_LENGTH);
|
checkLength(contentType, 0, MAX_BLOG_POST_TITLE_LENGTH);
|
||||||
// Blog teaser
|
// Blog post body
|
||||||
String teaser = content.getString(3);
|
byte[] postBody = content.getRaw(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);
|
|
||||||
checkLength(postBody, 0, MAX_BLOG_POST_BODY_LENGTH);
|
checkLength(postBody, 0, MAX_BLOG_POST_BODY_LENGTH);
|
||||||
// Attachments
|
// Attachments
|
||||||
BdfDictionary attachments = content.getOptionalDictionary(5);
|
BdfDictionary attachments = content.getOptionalDictionary(4);
|
||||||
// TODO handle attachments somehow
|
// TODO handle attachments somehow
|
||||||
|
|
||||||
// Signature
|
// Signature
|
||||||
byte[] sig = body.getRaw(1);
|
byte[] sig = body.getRaw(1);
|
||||||
checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
|
checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
|
Author a;
|
||||||
try {
|
try {
|
||||||
// Get the blog author
|
// Get the blog author
|
||||||
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
|
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
|
||||||
Author a = b.getAuthor();
|
a = b.getAuthor();
|
||||||
// Parse the public key
|
// Parse the public key
|
||||||
KeyParser keyParser = crypto.getSignatureKeyParser();
|
KeyParser keyParser = crypto.getSignatureKeyParser();
|
||||||
PublicKey key = keyParser.parsePublicKey(a.getPublicKey());
|
PublicKey key = keyParser.parsePublicKey(a.getPublicKey());
|
||||||
@@ -111,8 +112,12 @@ class BlogPostValidator extends BdfMessageValidator {
|
|||||||
BdfDictionary meta = new BdfDictionary();
|
BdfDictionary meta = new BdfDictionary();
|
||||||
Collection<MessageId> dependencies = null;
|
Collection<MessageId> dependencies = null;
|
||||||
if (title != null) meta.put(KEY_TITLE, title);
|
if (title != null) meta.put(KEY_TITLE, title);
|
||||||
meta.put(KEY_TEASER, teaser);
|
BdfDictionary author = BdfDictionary.of(
|
||||||
meta.put(KEY_HAS_BODY, postBody != null);
|
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());
|
meta.put(KEY_TIMESTAMP, m.getTimestamp());
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
meta.put(KEY_PARENT, parent);
|
meta.put(KEY_PARENT, parent);
|
||||||
|
|||||||
@@ -99,17 +99,24 @@ class IdentityManagerImpl implements IdentityManager {
|
|||||||
public Status getAuthorStatus(AuthorId authorId) throws DbException {
|
public Status getAuthorStatus(AuthorId authorId) throws DbException {
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
// Compare to the IDs of the user's identities
|
return getAuthorStatus(txn, authorId);
|
||||||
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;
|
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.briarproject.api.blogs.Blog;
|
|||||||
import org.briarproject.api.blogs.BlogFactory;
|
import org.briarproject.api.blogs.BlogFactory;
|
||||||
import org.briarproject.api.blogs.BlogInvitationMessage;
|
import org.briarproject.api.blogs.BlogInvitationMessage;
|
||||||
import org.briarproject.api.blogs.BlogManager;
|
import org.briarproject.api.blogs.BlogManager;
|
||||||
|
import org.briarproject.api.blogs.BlogManager.RemoveBlogHook;
|
||||||
import org.briarproject.api.blogs.BlogSharingManager;
|
import org.briarproject.api.blogs.BlogSharingManager;
|
||||||
import org.briarproject.api.blogs.BlogSharingMessage.BlogInvitation;
|
import org.briarproject.api.blogs.BlogSharingMessage.BlogInvitation;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
@@ -40,7 +41,7 @@ import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE;
|
|||||||
|
|
||||||
class BlogSharingManagerImpl extends
|
class BlogSharingManagerImpl extends
|
||||||
SharingManagerImpl<Blog, BlogInvitation, BlogInvitationMessage, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent>
|
SharingManagerImpl<Blog, BlogInvitation, BlogInvitationMessage, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent>
|
||||||
implements BlogSharingManager {
|
implements BlogSharingManager, RemoveBlogHook {
|
||||||
|
|
||||||
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
||||||
"bee438b5de0b3a685badc4e49d76e72d"
|
"bee438b5de0b3a685badc4e49d76e72d"
|
||||||
@@ -124,6 +125,11 @@ class BlogSharingManagerImpl extends
|
|||||||
return irrFactory;
|
return irrFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removingBlog(Transaction txn, Blog b) throws DbException {
|
||||||
|
removingShareable(txn, b);
|
||||||
|
}
|
||||||
|
|
||||||
static class SFactory implements
|
static class SFactory implements
|
||||||
ShareableFactory<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState> {
|
ShareableFactory<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState> {
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.sharing;
|
package org.briarproject.sharing;
|
||||||
|
|
||||||
|
import org.briarproject.api.blogs.BlogManager;
|
||||||
import org.briarproject.api.blogs.BlogSharingManager;
|
import org.briarproject.api.blogs.BlogSharingManager;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.clients.MessageQueueManager;
|
import org.briarproject.api.clients.MessageQueueManager;
|
||||||
@@ -49,6 +50,7 @@ public class SharingModule {
|
|||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager,
|
||||||
ContactManager contactManager,
|
ContactManager contactManager,
|
||||||
MessageQueueManager messageQueueManager,
|
MessageQueueManager messageQueueManager,
|
||||||
|
BlogManager blogManager,
|
||||||
BlogSharingManagerImpl blogSharingManager) {
|
BlogSharingManagerImpl blogSharingManager) {
|
||||||
|
|
||||||
lifecycleManager.registerClient(blogSharingManager);
|
lifecycleManager.registerClient(blogSharingManager);
|
||||||
@@ -56,6 +58,7 @@ public class SharingModule {
|
|||||||
contactManager.registerRemoveContactHook(blogSharingManager);
|
contactManager.registerRemoveContactHook(blogSharingManager);
|
||||||
messageQueueManager.registerIncomingMessageHook(
|
messageQueueManager.registerIncomingMessageHook(
|
||||||
BlogSharingManagerImpl.CLIENT_ID, blogSharingManager);
|
BlogSharingManagerImpl.CLIENT_ID, blogSharingManager);
|
||||||
|
blogManager.registerRemoveBlogHook(blogSharingManager);
|
||||||
|
|
||||||
return blogSharingManager;
|
return blogSharingManager;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user