diff --git a/plume-common/src/utils.rs b/plume-common/src/utils.rs index 73559a77..98514f9f 100644 --- a/plume-common/src/utils.rs +++ b/plume-common/src/utils.rs @@ -41,7 +41,7 @@ enum State { } /// Returns (HTML, mentions, hashtags) -pub fn md_to_html(md: &str) -> (String, HashSet, HashSet) { +pub fn md_to_html(md: &str, base_url: &str) -> (String, HashSet, HashSet) { let parser = Parser::new_ext(md, Options::all()); let (parser, mentions, hashtags): (Vec, Vec, Vec) = parser @@ -80,7 +80,7 @@ pub fn md_to_html(md: &str) -> (String, HashSet, HashSet) { } let mention = text_acc; let short_mention = mention.splitn(1, '@').nth(0).unwrap_or(""); - let link = Tag::Link(format!("/@/{}/", &mention).into(), short_mention.to_owned().into()); + let link = Tag::Link(format!("//{}/@/{}/", base_url, &mention).into(), short_mention.to_owned().into()); mentions.push(mention.clone()); events.push(Event::Start(link.clone())); @@ -100,7 +100,7 @@ pub fn md_to_html(md: &str) -> (String, HashSet, HashSet) { text_acc.push(c); } let hashtag = text_acc; - let link = Tag::Link(format!("/tag/{}", &hashtag.to_camel_case()).into(), hashtag.to_owned().into()); + let link = Tag::Link(format!("//{}/tag/{}", base_url, &hashtag.to_camel_case()).into(), hashtag.to_owned().into()); hashtags.push(hashtag.clone()); events.push(Event::Start(link.clone())); @@ -188,7 +188,7 @@ mod tests { ]; for (md, mentions) in tests { - assert_eq!(md_to_html(md).1, mentions.into_iter().map(|s| s.to_string()).collect::>()); + assert_eq!(md_to_html(md, "").1, mentions.into_iter().map(|s| s.to_string()).collect::>()); } } @@ -207,7 +207,7 @@ mod tests { ]; for (md, mentions) in tests { - assert_eq!(md_to_html(md).2, mentions.into_iter().map(|s| s.to_string()).collect::>()); + assert_eq!(md_to_html(md, "").2, mentions.into_iter().map(|s| s.to_string()).collect::>()); } } } diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index b51dd393..901a907b 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -91,7 +91,10 @@ impl Comment { } pub fn to_activity(&self, conn: &Connection) -> Note { - let (html, mentions, _hashtags) = utils::md_to_html(self.content.get().as_ref()); + let (html, mentions, _hashtags) = utils::md_to_html(self.content.get().as_ref(), + &Instance::get_local(conn) + .expect("Comment::to_activity: instance error") + .public_domain); let author = User::get(conn, self.author_id).expect("Comment::to_activity: author error"); let mut note = Note::default(); diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index b7faad95..c7b14a5d 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -58,13 +58,13 @@ impl Follow { .set_actor_link::(user.clone().into_id()) .expect("Follow::to_activity: actor error"); act.follow_props - .set_object_object(user.to_activity(&*conn)) + .set_object_link::(target.clone().into_id()) .expect("Follow::to_activity: object error"); act.object_props .set_id_string(self.ap_url.clone()) .expect("Follow::to_activity: id error"); act.object_props - .set_to_link(target.clone().into_id()) + .set_to_link(target.into_id()) .expect("Follow::to_activity: target error"); act.object_props .set_cc_link_vec::(vec![]) @@ -82,14 +82,12 @@ impl Follow { from_id: i32, target_id: i32, ) -> Follow { - let from_url: String = from.clone().into_id().into(); - let target_url: String = target.clone().into_id().into(); let res = Follow::insert( conn, NewFollow { follower_id: from_id, following_id: target_id, - ap_url: format!("{}/follow/{}", from_url, target_url), + ap_url: follow.object_props.id_string().expect("Follow::accept_follow: get id error"), }, ); @@ -98,7 +96,7 @@ impl Follow { accept .object_props .set_id_string(accept_id) - .expect("Follow::accept_follow: id error"); + .expect("Follow::accept_follow: set id error"); accept .object_props .set_to_link(from.clone().into_id()) @@ -199,7 +197,7 @@ impl Deletable for Follow { .set_id_string(format!("{}/undo", self.ap_url)) .expect("Follow::delete: id error"); undo.undo_props - .set_object_object(self.to_activity(conn)) + .set_object_link::(self.clone().into_id()) .expect("Follow::delete: object error"); undo } @@ -214,3 +212,9 @@ impl Deletable for Follow { } } } + +impl IntoId for Follow { + fn into_id(self) -> Id { + Id::new(self.ap_url) + } +} diff --git a/plume-models/src/instance.rs b/plume-models/src/instance.rs index 86d01817..6d01ab19 100644 --- a/plume-models/src/instance.rs +++ b/plume-models/src/instance.rs @@ -139,8 +139,8 @@ impl Instance { short_description: SafeString, long_description: SafeString, ) { - let (sd, _, _) = md_to_html(short_description.as_ref()); - let (ld, _, _) = md_to_html(long_description.as_ref()); + let (sd, _, _) = md_to_html(short_description.as_ref(), &self.public_domain); + let (ld, _, _) = md_to_html(long_description.as_ref(), &self.public_domain); diesel::update(self) .set(( instances::name.eq(name), diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 333ff728..94f968ee 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -431,8 +431,8 @@ impl Post { } pub fn to_activity(&self, conn: &Connection) -> LicensedArticle { - let mut to = self.get_receivers_urls(conn); - to.push(PUBLIC_VISIBILTY.to_string()); + let cc = self.get_receivers_urls(conn); + let to = vec![PUBLIC_VISIBILTY.to_string()]; let mut mentions_json = Mention::list_for_post(conn, self.id) .into_iter() @@ -526,7 +526,7 @@ impl Post { .expect("Post::to_activity: to error"); article .object_props - .set_cc_link_vec::(vec![]) + .set_cc_link_vec::(cc.into_iter().map(Id::new).collect()) .expect("Post::to_activity: cc error"); let mut license = Licensed::default(); license.set_license_string(self.license.clone()).expect("Post::to_activity: license error"); @@ -627,7 +627,7 @@ impl Post { post.license = license; } - let mut txt_hashtags = md_to_html(&post.source) + let mut txt_hashtags = md_to_html(&post.source, "") .2 .into_iter() .map(|s| s.to_camel_case()) @@ -889,7 +889,7 @@ impl<'a> FromActivity for Post } // save mentions and tags - let mut hashtags = md_to_html(&post.source) + let mut hashtags = md_to_html(&post.source, "") .2 .into_iter() .map(|s| s.to_camel_case()) diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 49e80098..242af35d 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -555,7 +555,7 @@ impl User { serde_json::from_str(text).expect("User::fetch_outbox: parsing error"); json["items"] .as_array() - .expect("Outbox.items is not an array") + .unwrap_or(&vec![]) .into_iter() .filter_map(|j| serde_json::from_value(j.clone()).ok()) .collect::>() @@ -587,7 +587,7 @@ impl User { serde_json::from_str(text).expect("User::fetch_followers_ids: parsing error"); json["items"] .as_array() - .expect("User::fetch_followers_ids: not an array error") + .unwrap_or(&vec![]) .into_iter() .filter_map(|j| serde_json::from_value(j.clone()).ok()) .collect::>() diff --git a/src/inbox.rs b/src/inbox.rs index 9a2425fa..e5db005e 100644 --- a/src/inbox.rs +++ b/src/inbox.rs @@ -24,7 +24,7 @@ use serde_json; use std::io::Read; use plume_common::activity_pub::{ - inbox::{Deletable, FromActivity, InboxError}, + inbox::{Deletable, FromActivity, InboxError, Notify}, Id,request::Digest, }; use plume_models::{ @@ -68,7 +68,7 @@ pub trait Inbox { Ok(()) } "Follow" => { - Follow::from_activity(conn, serde_json::from_value(act.clone())?, actor_id); + Follow::from_activity(conn, serde_json::from_value(act.clone())?, actor_id).notify(conn); Ok(()) } "Like" => { @@ -81,44 +81,57 @@ pub trait Inbox { } "Undo" => { let act: Undo = serde_json::from_value(act.clone())?; - match act.undo_props.object["type"] - .as_str() - .expect("Inbox::received: undo without original type error") - { - "Like" => { - likes::Like::delete_id( - &act.undo_props - .object_object::()? - .object_props - .id_string()?, - actor_id.as_ref(), - conn, - ); - Ok(()) + if let Some(t) = act.undo_props.object["type"].as_str() { + match t { + "Like" => { + likes::Like::delete_id( + &act.undo_props + .object_object::()? + .object_props + .id_string()?, + actor_id.as_ref(), + conn, + ); + Ok(()) + } + "Announce" => { + Reshare::delete_id( + &act.undo_props + .object_object::()? + .object_props + .id_string()?, + actor_id.as_ref(), + conn, + ); + Ok(()) + } + "Follow" => { + Follow::delete_id( + &act.undo_props + .object_object::()? + .object_props + .id_string()?, + actor_id.as_ref(), + conn, + ); + Ok(()) + } + _ => Err(InboxError::CantUndo)?, } - "Announce" => { - Reshare::delete_id( - &act.undo_props - .object_object::()? - .object_props - .id_string()?, - actor_id.as_ref(), - conn, - ); + } else { + let link = act.undo_props.object.as_str().expect("Inbox::received: undo don't contain type and isn't Link"); + if let Some(like) = likes::Like::find_by_ap_url(conn, link) { + likes::Like::delete_id(&like.ap_url, actor_id.as_ref(), conn); Ok(()) - } - "Follow" => { - Follow::delete_id( - &act.undo_props - .object_object::()? - .object_props - .id_string()?, - actor_id.as_ref(), - conn, - ); + } else if let Some(reshare) = Reshare::find_by_ap_url(conn, link) { + Reshare::delete_id(&reshare.ap_url, actor_id.as_ref(), conn); Ok(()) + } else if let Some(follow) = Follow::find_by_ap_url(conn, link) { + Follow::delete_id(&follow.ap_url, actor_id.as_ref(), conn); + Ok(()) + } else { + Err(InboxError::NoType)? } - _ => Err(InboxError::CantUndo)?, } } "Update" => { diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 144e6512..6eeb5382 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -12,6 +12,7 @@ use plume_models::{ blogs::Blog, comments::*, db_conn::DbConn, + instance::Instance, mentions::Mention, posts::Post, safe_string::SafeString, @@ -35,7 +36,7 @@ pub fn create(blog_name: String, slug: String, form: LenientForm let post = Post::find_by_slug(&*conn, &slug, blog.id).ok_or(None)?; form.validate() .map(|_| { - let (html, mentions, _hashtags) = utils::md_to_html(form.content.as_ref()); + let (html, mentions, _hashtags) = utils::md_to_html(form.content.as_ref(), &Instance::get_local(&conn).expect("comments::create: Error getting local instance").public_domain); let comm = Comment::insert(&*conn, NewComment { content: SafeString::new(html.as_ref()), in_response_to_id: form.responding_to, @@ -64,7 +65,7 @@ pub fn create(blog_name: String, slug: String, form: LenientForm let comments = Comment::list_by_post(&*conn, post.id); let previous = form.responding_to.map(|r| Comment::get(&*conn, r) - .expect("posts::details_reponse: Error retrieving previous comment")); + .expect("comments::create: Error retrieving previous comment")); Some(render!(posts::details( &(&*conn, &intl.catalog, Some(user.clone())), diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 70181872..00451d59 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -201,7 +201,7 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien // actually it's not "Ok"… Ok(Redirect::to(uri!(super::blogs::details: name = blog, page = _))) } else { - let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref()); + let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref(), &Instance::get_local(&conn).expect("posts::update: Error getting local instance").public_domain); // update publication date if when this article is no longer a draft let newly_published = if !post.published && !form.draft { @@ -309,7 +309,7 @@ pub fn create(blog_name: String, form: LenientForm, user: User, con // actually it's not "Ok"… Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _))) } else { - let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref()); + let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref(), &Instance::get_local(&conn).expect("posts::create: Error getting l ocal instance").public_domain); let post = Post::insert(&*conn, NewPost { blog_id: blog.id, diff --git a/src/routes/user.rs b/src/routes/user.rs index ae96513e..89d99e56 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -164,7 +164,7 @@ pub fn follow_auth(name: String, i18n: I18n) -> Flash { ) } -#[get("/@//followers?")] +#[get("/@//followers?", rank = 2)] pub fn followers(name: String, conn: DbConn, account: Option, page: Option, intl: I18n) -> Result { let page = page.unwrap_or_default(); let user = User::find_by_fqn(&*conn, &name).ok_or_else(|| render!(errors::not_found(&(&*conn, &intl.catalog, account.clone()))))?; @@ -387,7 +387,7 @@ pub fn inbox( }) } -#[get("/@//followers")] +#[get("/@//followers", rank = 1)] pub fn ap_followers( name: String, conn: DbConn,