forked from Plume/Plume
Resolve activitystream TODOs
This commit is contained in:
parent
0e24ccbf29
commit
4a86af6fc1
14 changed files with 172 additions and 42 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -869,6 +869,7 @@ name = "plume"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"activitystreams 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"activitystreams-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"activitystreams-traits 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"activitystreams-types 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -4,6 +4,7 @@ name = "plume"
|
|||
version = "0.1.0"
|
||||
[dependencies]
|
||||
activitystreams = "0.1"
|
||||
activitystreams-derive = "0.1"
|
||||
activitystreams-traits = "0.1"
|
||||
activitystreams-types = "0.1"
|
||||
array_tool = "1.0"
|
||||
|
|
|
@ -8,7 +8,7 @@ use diesel::PgConnection;
|
|||
use failure::Error;
|
||||
use serde_json;
|
||||
|
||||
use activity_pub::{broadcast, IntoId};
|
||||
use activity_pub::{broadcast, Id, IntoId};
|
||||
use activity_pub::actor::Actor as APActor;
|
||||
use activity_pub::sign::*;
|
||||
use models::blogs::Blog;
|
||||
|
@ -118,7 +118,7 @@ pub trait Inbox {
|
|||
}
|
||||
}
|
||||
|
||||
fn accept_follow<A: Signer + IntoId, B: Clone + WithInbox + Actor>(
|
||||
fn accept_follow<A: Signer + IntoId + Clone, B: Clone + WithInbox + Actor>(
|
||||
&self,
|
||||
conn: &PgConnection,
|
||||
from: &A,
|
||||
|
@ -132,8 +132,8 @@ pub trait Inbox {
|
|||
following_id: target_id
|
||||
});
|
||||
|
||||
let mut accept = Accept::default();//new(target, follow, conn);
|
||||
accept.set_actor_link(from.into()).unwrap();
|
||||
let mut accept = Accept::default();
|
||||
accept.set_actor_link::<Id>(from.clone().into_id()).unwrap();
|
||||
accept.set_object_object(follow).unwrap();
|
||||
broadcast(conn, &*from, accept, vec![target.clone()]);
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ pub fn broadcast<A: Activity + Clone, S: sign::Signer, T: inbox::WithInbox + Act
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Id {
|
||||
#[serde(flatten)]
|
||||
id: String
|
||||
|
@ -117,7 +117,7 @@ impl Id {
|
|||
}
|
||||
|
||||
pub trait IntoId {
|
||||
fn into(&self) -> Id;
|
||||
fn into_id(self) -> Id;
|
||||
}
|
||||
|
||||
impl Link for Id {}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate activitystreams;
|
||||
#[macro_use]
|
||||
extern crate activitystreams_derive;
|
||||
extern crate activitystreams_traits;
|
||||
extern crate activitystreams_types;
|
||||
extern crate array_tool;
|
||||
|
|
|
@ -12,7 +12,7 @@ use openssl::pkey::{PKey, Private};
|
|||
use openssl::rsa::Rsa;
|
||||
use openssl::sign::Signer;
|
||||
|
||||
use activity_pub::{ActivityStream, Id};
|
||||
use activity_pub::{ActivityStream, Id, IntoId};
|
||||
use activity_pub::actor::{Actor as APActor, ActorType};
|
||||
use activity_pub::inbox::WithInbox;
|
||||
use activity_pub::sign;
|
||||
|
@ -175,8 +175,8 @@ impl Blog {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<Id> for Blog {
|
||||
fn into(self) -> Id {
|
||||
impl IntoId for Blog {
|
||||
fn into_id(self) -> Id {
|
||||
Id::new(self.ap_url)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
use activitystreams_types::{
|
||||
activity::Create,
|
||||
object::{Note, properties::ObjectProperties}
|
||||
};
|
||||
use chrono;
|
||||
use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods};
|
||||
use serde_json;
|
||||
|
||||
use activity_pub::{ap_url, PUBLIC_VISIBILTY};
|
||||
use activity_pub::{ap_url, IntoId, PUBLIC_VISIBILTY};
|
||||
use activity_pub::actor::Actor;
|
||||
use activity_pub::object::Object;
|
||||
use models::posts::Post;
|
||||
|
@ -71,6 +75,37 @@ impl Comment {
|
|||
pub fn get_post(&self, conn: &PgConnection) -> Post {
|
||||
Post::get(conn, self.post_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn into_activity(&self, conn: &PgConnection) -> Note {
|
||||
let mut to = self.get_author(conn).get_followers(conn).into_iter().map(|f| f.ap_url).collect::<Vec<String>>();
|
||||
to.append(&mut self.get_post(conn).get_receivers_urls(conn));
|
||||
to.push(PUBLIC_VISIBILTY.to_string());
|
||||
|
||||
let mut comment = Note::default();
|
||||
comment.object_props = ObjectProperties {
|
||||
id: Some(serde_json::to_value(self.ap_url.clone()).unwrap()),
|
||||
summary: Some(serde_json::to_value(self.spoiler_text.clone()).unwrap()),
|
||||
content: Some(serde_json::to_value(self.content.clone()).unwrap()),
|
||||
in_reply_to: Some(serde_json::to_value(self.in_response_to_id.map_or_else(|| self.get_post(conn).ap_url, |id| {
|
||||
let comm = Comment::get(conn, id).unwrap();
|
||||
comm.ap_url.clone().unwrap_or(comm.compute_id(conn))
|
||||
})).unwrap()),
|
||||
published: Some(serde_json::to_value(self.creation_date).unwrap()),
|
||||
attributed_to: Some(serde_json::to_value(self.get_author(conn).compute_id(conn)).unwrap()),
|
||||
to: Some(serde_json::to_value(to).unwrap()),
|
||||
cc: Some(serde_json::to_value(Vec::<serde_json::Value>::new()).unwrap()),
|
||||
..ObjectProperties::default()
|
||||
};
|
||||
comment
|
||||
}
|
||||
|
||||
pub fn create_activity(&self, conn: &PgConnection) -> Create {
|
||||
let mut act = Create::default();
|
||||
act.set_actor_link(self.get_author(conn).into_id()).unwrap();
|
||||
act.set_object_object(self.into_activity(conn)).unwrap();
|
||||
act.object_props.set_id_string(format!("{}/activity", self.ap_url.clone().unwrap())).unwrap();
|
||||
act
|
||||
}
|
||||
}
|
||||
|
||||
impl Object for Comment {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use activitystreams_types::activity;
|
||||
use chrono;
|
||||
use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods};
|
||||
use serde_json;
|
||||
|
||||
use activity_pub::IntoId;
|
||||
use activity_pub::actor::Actor;
|
||||
use activity_pub::object::Object;
|
||||
use models::posts::Post;
|
||||
|
@ -66,8 +68,25 @@ impl Like {
|
|||
.into_iter().nth(0)
|
||||
}
|
||||
|
||||
pub fn delete(&self, conn: &PgConnection) {
|
||||
pub fn delete(&self, conn: &PgConnection) -> activity::Undo {
|
||||
diesel::delete(self).execute(conn).unwrap();
|
||||
|
||||
let mut act = activity::Undo::default();
|
||||
act.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap();
|
||||
act.set_object_object(self.into_activity(conn)).unwrap();
|
||||
act
|
||||
}
|
||||
|
||||
pub fn into_activity(&self, conn: &PgConnection) -> activity::Like {
|
||||
let mut act = activity::Like::default();
|
||||
act.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap();
|
||||
act.set_object_link(Post::get(conn, self.post_id).unwrap().into_id()).unwrap();
|
||||
act.object_props.set_id_string(format!("{}/like/{}",
|
||||
User::get(conn, self.user_id).unwrap().ap_url,
|
||||
Post::get(conn, self.post_id).unwrap().ap_url
|
||||
)).unwrap();
|
||||
|
||||
act
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
use activitystreams_types::{
|
||||
activity::Create,
|
||||
object::{Article, properties::ObjectProperties}
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl};
|
||||
use diesel::dsl::any;
|
||||
use serde_json;
|
||||
|
||||
use BASE_URL;
|
||||
use activity_pub::{PUBLIC_VISIBILTY, ap_url};
|
||||
use activity_pub::{PUBLIC_VISIBILTY, ap_url, Id, IntoId};
|
||||
use activity_pub::actor::Actor;
|
||||
use activity_pub::object::Object;
|
||||
use models::blogs::Blog;
|
||||
|
@ -137,6 +141,40 @@ impl Post {
|
|||
});
|
||||
to
|
||||
}
|
||||
|
||||
pub fn into_activity(&self, conn: &PgConnection) -> Article {
|
||||
let mut to = self.get_receivers_urls(conn);
|
||||
to.push(PUBLIC_VISIBILTY.to_string());
|
||||
|
||||
let mut article = Article::default();
|
||||
article.object_props = ObjectProperties {
|
||||
name: Some(serde_json::to_value(self.title.clone()).unwrap()),
|
||||
id: Some(serde_json::to_value(self.ap_url.clone()).unwrap()),
|
||||
attributed_to: Some(serde_json::to_value(self.get_authors(conn).into_iter().map(|x| x.ap_url).collect::<Vec<String>>()).unwrap()),
|
||||
content: Some(serde_json::to_value(self.content.clone()).unwrap()),
|
||||
published: Some(serde_json::to_value(self.creation_date).unwrap()),
|
||||
tag: Some(serde_json::to_value(Vec::<serde_json::Value>::new()).unwrap()),
|
||||
url: Some(serde_json::to_value(self.compute_id(conn)).unwrap()),
|
||||
to: Some(serde_json::to_value(to).unwrap()),
|
||||
cc: Some(serde_json::to_value(Vec::<serde_json::Value>::new()).unwrap()),
|
||||
..ObjectProperties::default()
|
||||
};
|
||||
article
|
||||
}
|
||||
|
||||
pub fn create_activity(&self, conn: &PgConnection) -> Create {
|
||||
let mut act = Create::default();
|
||||
act.object_props.set_id_string(format!("{}/activity", self.ap_url)).unwrap();
|
||||
act.set_actor_link(Id::new(self.get_authors(conn)[0].clone().ap_url)).unwrap();
|
||||
act.set_object_object(self.into_activity(conn)).unwrap();
|
||||
act
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoId for Post {
|
||||
fn into_id(self) -> Id {
|
||||
Id::new(self.ap_url.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Object for Post {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use activitystreams_traits::{Actor, Object};
|
||||
use activitystreams_traits::{Actor, Object, Link};
|
||||
use activitystreams_types::{
|
||||
activity::Create,
|
||||
collection::OrderedCollection
|
||||
actor::Person,
|
||||
collection::OrderedCollection,
|
||||
object::properties::ObjectProperties,
|
||||
CustomObject
|
||||
};
|
||||
use bcrypt;
|
||||
use chrono::NaiveDateTime;
|
||||
|
@ -227,8 +229,11 @@ impl User {
|
|||
}
|
||||
|
||||
pub fn outbox(&self, conn: &PgConnection) -> ActivityStream<OrderedCollection> {
|
||||
let mut coll = OrderedCollection::default(); // TODO
|
||||
coll.collection_props.items = serde_json::to_value(self.get_activities(conn)).unwrap();
|
||||
let acts = self.get_activities(conn);
|
||||
let n_acts = acts.len();
|
||||
let mut coll = OrderedCollection::default();
|
||||
coll.collection_props.items = serde_json::to_value(acts).unwrap();
|
||||
coll.collection_props.set_total_items_u64(n_acts as u64).unwrap();
|
||||
ActivityStream::new(coll)
|
||||
}
|
||||
|
||||
|
@ -237,10 +242,8 @@ impl User {
|
|||
use schema::post_authors;
|
||||
let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id);
|
||||
let posts = posts::table.filter(posts::id.eq(any(posts_by_self))).load::<Post>(conn).unwrap();
|
||||
posts.into_iter().map(|_| {
|
||||
// TODO Create::new(self, &p, conn)
|
||||
// TODO: add a method to convert Post -> Create
|
||||
serde_json::to_value(Create::default()).unwrap()
|
||||
posts.into_iter().map(|p| {
|
||||
serde_json::to_value(p.create_activity(conn)).unwrap()
|
||||
}).collect::<Vec<serde_json::Value>>()
|
||||
}
|
||||
|
||||
|
@ -278,6 +281,42 @@ impl User {
|
|||
pub fn get_keypair(&self) -> PKey<Private> {
|
||||
PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap()
|
||||
}
|
||||
|
||||
pub fn into_activity(&self, conn: &PgConnection) -> CustomObject<ApProps, Person> {
|
||||
let mut actor = Person::default();
|
||||
actor.object_props = ObjectProperties {
|
||||
id: Some(serde_json::to_value(self.compute_id(conn)).unwrap()),
|
||||
name: Some(serde_json::to_value(self.get_display_name()).unwrap()),
|
||||
summary: Some(serde_json::to_value(self.get_summary()).unwrap()),
|
||||
url: Some(serde_json::to_value(self.compute_id(conn)).unwrap()),
|
||||
..ObjectProperties::default()
|
||||
};
|
||||
|
||||
CustomObject::new(actor, ApProps {
|
||||
inbox: Some(serde_json::to_value(self.compute_inbox(conn)).unwrap()),
|
||||
outbox: Some(serde_json::to_value(self.compute_outbox(conn)).unwrap()),
|
||||
preferred_username: Some(serde_json::to_value(self.get_actor_id()).unwrap()),
|
||||
endpoints: Some(json!({
|
||||
"sharedInbox": ap_url(format!("{}/inbox", BASE_URL.as_str()))
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Properties)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ApProps {
|
||||
#[activitystreams(ab(Object, Link))]
|
||||
inbox: Option<serde_json::Value>,
|
||||
|
||||
#[activitystreams(ab(Object, Link))]
|
||||
outbox: Option<serde_json::Value>,
|
||||
|
||||
#[activitystreams(ab(Object, Link))]
|
||||
preferred_username: Option<serde_json::Value>,
|
||||
|
||||
#[activitystreams(ab(Object))]
|
||||
endpoints: Option<serde_json::Value>
|
||||
}
|
||||
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for User {
|
||||
|
@ -359,7 +398,7 @@ impl APActor for User {
|
|||
}
|
||||
|
||||
impl IntoId for User {
|
||||
fn into(&self) -> Id {
|
||||
fn into_id(self) -> Id {
|
||||
Id::new(self.ap_url.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use activitystreams_types::activity::Create;
|
||||
use rocket::request::Form;
|
||||
use rocket::response::Redirect;
|
||||
use rocket_contrib::Template;
|
||||
|
@ -41,8 +40,8 @@ fn create(blog: String, slug: String, query: CommentQuery, data: Form<NewComment
|
|||
sensitive: false,
|
||||
spoiler_text: "".to_string()
|
||||
});
|
||||
// TODO: let act = Create::new(&user, &comment, &*conn);
|
||||
// broadcast(&*conn, &user, act, user.get_followers(&*conn));
|
||||
|
||||
broadcast(&*conn, &user, comment.create_activity(&*conn), user.get_followers(&*conn));
|
||||
|
||||
Redirect::to(format!("/~/{}/{}/#comment-{}", blog, slug, comment.id).as_ref())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use activitystreams_types::activity::{Like, Undo};
|
||||
use rocket::response::Redirect;
|
||||
|
||||
use activity_pub::broadcast;
|
||||
|
@ -18,12 +17,12 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect {
|
|||
ap_url: "".to_string()
|
||||
});
|
||||
like.update_ap_url(&*conn);
|
||||
// TODO: let act = Like::new(&user, &post, &*conn);
|
||||
// TODO: broadcast(&*conn, &user, act, user.get_followers(&*conn));
|
||||
|
||||
broadcast(&*conn, &user, like.into_activity(&*conn), user.get_followers(&*conn));
|
||||
} else {
|
||||
let like = likes::Like::find_by_user_on_post(&*conn, &user, &post).unwrap();
|
||||
// TODO: like.delete(&*conn);
|
||||
// TODO: broadcast(&*conn, &user, Undo::new(&user, &like, &*conn), user.get_followers(&*conn));
|
||||
let delete_act = like.delete(&*conn);
|
||||
broadcast(&*conn, &user, delete_act, user.get_followers(&*conn));
|
||||
}
|
||||
|
||||
Redirect::to(format!("/~/{}/{}/", blog, slug).as_ref())
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use activitystreams_types::activity::Create;
|
||||
use heck::KebabCase;
|
||||
use rocket::request::Form;
|
||||
use rocket::response::Redirect;
|
||||
use rocket_contrib::Template;
|
||||
use serde_json;
|
||||
|
||||
use activity_pub::{broadcast, context, activity_pub, ActivityPub, Id};
|
||||
use activity_pub::{broadcast, context, activity_pub, ActivityPub};
|
||||
use activity_pub::object::Object;
|
||||
use db_conn::DbConn;
|
||||
use models::blogs::*;
|
||||
|
@ -38,7 +37,7 @@ fn details(blog: String, slug: String, conn: DbConn, user: Option<User>) -> Temp
|
|||
|
||||
#[get("/~/<_blog>/<slug>", rank = 3, format = "application/activity+json")]
|
||||
fn activity_details(_blog: String, slug: String, conn: DbConn) -> ActivityPub {
|
||||
// TODO: posts in different blogs may have the same slug
|
||||
// FIXME: posts in different blogs may have the same slug
|
||||
let post = Post::find_by_slug(&*conn, slug).unwrap();
|
||||
|
||||
let mut act = post.serialize(&*conn);
|
||||
|
@ -85,12 +84,8 @@ fn create(blog_name: String, data: Form<NewPostForm>, user: User, conn: DbConn)
|
|||
author_id: user.id
|
||||
});
|
||||
|
||||
// TODO: use Post -> Create conversion
|
||||
// let act = Create::default();
|
||||
// act.object_props.set_id_string(format!("{}/activity", post.compute_id(&*conn)));
|
||||
// act.set_actor_link(Id::new(user.ap_url));
|
||||
// act.set_object_object();
|
||||
// broadcast(&*conn, &user, act, user.get_followers(&*conn));
|
||||
let act = post.create_activity(&*conn);
|
||||
broadcast(&*conn, &user, act, user.get_followers(&*conn));
|
||||
|
||||
Redirect::to(format!("/~/{}/{}", blog_name, slug).as_str())
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use rocket::response::Redirect;
|
|||
use rocket_contrib::Template;
|
||||
use serde_json;
|
||||
|
||||
use activity_pub::{activity_pub, ActivityPub, ActivityStream, context, broadcast};
|
||||
use activity_pub::{activity_pub, ActivityPub, ActivityStream, context, broadcast, Id, IntoId};
|
||||
use activity_pub::actor::Actor;
|
||||
use activity_pub::inbox::Inbox;
|
||||
use db_conn::DbConn;
|
||||
|
@ -56,8 +56,10 @@ fn follow(name: String, conn: DbConn, user: User) -> Redirect {
|
|||
follower_id: user.id,
|
||||
following_id: target.id
|
||||
});
|
||||
let act = Follow::default();
|
||||
// TODO
|
||||
let mut act = Follow::default();
|
||||
act.set_actor_link::<Id>(user.clone().into_id()).unwrap();
|
||||
act.set_object_object(user.into_activity(&*conn)).unwrap();
|
||||
act.object_props.set_id_string(format!("{}/follow/{}", user.ap_url, target.ap_url)).unwrap();
|
||||
broadcast(&*conn, &user, act, vec![target]);
|
||||
Redirect::to(format!("/@/{}", name).as_ref())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue