Save mentions

This commit is contained in:
Bat 2018-06-20 21:58:11 +01:00
parent c4cc4a4e13
commit d7b71848fc
5 changed files with 57 additions and 35 deletions

View File

@ -1,4 +1,4 @@
#![feature(plugin, custom_derive, decl_macro, iterator_find_map)] #![feature(plugin, custom_derive, decl_macro, iterator_find_map, iterator_flatten)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
extern crate activitypub; extern crate activitypub;

View File

@ -33,6 +33,7 @@ impl Mention {
get!(mentions); get!(mentions);
find_by!(mentions, find_by_ap_url, ap_url as String); find_by!(mentions, find_by_ap_url, ap_url as String);
list_by!(mentions, list_for_user, mentioned_id as i32); list_by!(mentions, list_for_user, mentioned_id as i32);
list_by!(mentions, list_for_post, post_id as i32);
pub fn get_mentioned(&self, conn: &PgConnection) -> Option<User> { pub fn get_mentioned(&self, conn: &PgConnection) -> Option<User> {
User::get(conn, self.mentioned_id) User::get(conn, self.mentioned_id)
@ -46,6 +47,14 @@ impl Mention {
self.post_id.and_then(|id| Comment::get(conn, id)) self.post_id.and_then(|id| Comment::get(conn, id))
} }
pub fn build_activity(conn: &PgConnection, ment: String) -> link::Mention {
let user = User::find_by_fqn(conn, ment.clone());
let mut mention = link::Mention::default();
mention.link_props.set_href_string(user.clone().map(|u| u.ap_url).unwrap_or(String::new())).expect("Error setting mention's href");
mention.link_props.set_name_string(format!("@{}", ment)).expect("Error setting mention's name");
mention
}
pub fn to_activity(&self, conn: &PgConnection) -> link::Mention { pub fn to_activity(&self, conn: &PgConnection) -> link::Mention {
let user = self.get_mentioned(conn); let user = self.get_mentioned(conn);
let mut mention = link::Mention::default(); let mut mention = link::Mention::default();

View File

@ -145,6 +145,8 @@ impl Post {
let mut to = self.get_receivers_urls(conn); let mut to = self.get_receivers_urls(conn);
to.push(PUBLIC_VISIBILTY.to_string()); to.push(PUBLIC_VISIBILTY.to_string());
let mentions = Mention::list_for_post(conn, self.id).into_iter().map(|m| m.to_activity(conn)).collect::<Vec<link::Mention>>();
let mut article = Article::default(); let mut article = Article::default();
article.object_props = ObjectProperties { article.object_props = ObjectProperties {
name: Some(serde_json::to_value(self.title.clone()).unwrap()), name: Some(serde_json::to_value(self.title.clone()).unwrap()),
@ -152,11 +154,11 @@ impl Post {
attributed_to: Some(serde_json::to_value(self.get_authors(conn).into_iter().map(|x| x.ap_url).collect::<Vec<String>>()).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()), content: Some(serde_json::to_value(self.content.clone()).unwrap()),
published: Some(serde_json::to_value(self.creation_date).unwrap()), published: Some(serde_json::to_value(self.creation_date).unwrap()),
tag: Some(serde_json::to_value(Vec::<serde_json::Value>::new()).unwrap()), tag: Some(serde_json::to_value(mentions).unwrap()),
url: Some(serde_json::to_value(self.compute_id(conn)).unwrap()), url: Some(serde_json::to_value(self.compute_id(conn)).unwrap()),
to: Some(serde_json::to_value(to).unwrap()), to: Some(serde_json::to_value(to).unwrap()),
cc: Some(serde_json::to_value(Vec::<serde_json::Value>::new()).unwrap()), cc: Some(serde_json::to_value(Vec::<serde_json::Value>::new()).unwrap()),
..ObjectProperties::default() ..ObjectProperties::default()
}; };
article article
} }

View File

@ -4,11 +4,12 @@ use rocket::response::{Redirect, Flash};
use rocket_contrib::Template; use rocket_contrib::Template;
use serde_json; use serde_json;
use activity_pub::{broadcast, context, activity_pub, ActivityPub}; use activity_pub::{broadcast, context, activity_pub, ActivityPub, Id};
use db_conn::DbConn; use db_conn::DbConn;
use models::{ use models::{
blogs::*, blogs::*,
comments::Comment, comments::Comment,
mentions::Mention,
post_authors::*, post_authors::*,
posts::*, posts::*,
users::User users::User
@ -87,7 +88,7 @@ fn create(blog_name: String, data: Form<NewPostForm>, user: User, conn: DbConn)
if slug == "new" || Post::find_by_slug(&*conn, slug.clone(), blog.id).is_some() { if slug == "new" || Post::find_by_slug(&*conn, slug.clone(), blog.id).is_some() {
Redirect::to(uri!(new: blog = blog_name)) Redirect::to(uri!(new: blog = blog_name))
} else { } else {
let content = utils::md_to_html(form.content.to_string().as_ref()); let (content, mentions) = utils::md_to_html(form.content.to_string().as_ref());
let post = Post::insert(&*conn, NewPost { let post = Post::insert(&*conn, NewPost {
blog_id: blog.id, blog_id: blog.id,
@ -104,6 +105,10 @@ fn create(blog_name: String, data: Form<NewPostForm>, user: User, conn: DbConn)
author_id: user.id author_id: user.id
}); });
for m in mentions.into_iter() {
Mention::from_activity(&*conn, Mention::build_activity(&*conn, m), Id::new(post.compute_id(&*conn)));
}
let act = post.create_activity(&*conn); let act = post.create_activity(&*conn);
broadcast(&*conn, &user, act, user.get_followers(&*conn)); broadcast(&*conn, &user, act, user.get_followers(&*conn));

View File

@ -20,45 +20,51 @@ pub fn requires_login(message: &str, url: Uri) -> Flash<Redirect> {
Flash::new(Redirect::to(Uri::new(format!("/login?m={}", gettext(message.to_string())))), "callback", url.as_str()) Flash::new(Redirect::to(Uri::new(format!("/login?m={}", gettext(message.to_string())))), "callback", url.as_str())
} }
/// Returns (HTML, mentions)
pub fn md_to_html(md: &str) -> String { pub fn md_to_html(md: &str) -> (String, Vec<String>) {
let parser = Parser::new_ext(md, Options::all()); let parser = Parser::new_ext(md, Options::all());
let parser = parser.flat_map(|evt| match evt { let (parser, mentions): (Vec<Vec<Event>>, Vec<Vec<String>>) = parser.map(|evt| match evt {
Event::Text(txt) => txt.chars().fold((vec![], false, String::new(), 0), |(mut events, in_mention, text_acc, n), c| { Event::Text(txt) => {
if in_mention { let (evts, _, _, _, new_mentions) = txt.chars().fold((vec![], false, String::new(), 0, vec![]), |(mut events, in_mention, text_acc, n, mut mentions), c| {
if (c.is_alphanumeric() || c == '@' || c == '.' || c == '-' || c == '_') && (n < (txt.chars().count() - 1)) { if in_mention {
(events, in_mention, text_acc + c.to_string().as_ref(), n + 1) if (c.is_alphanumeric() || c == '@' || c == '.' || c == '-' || c == '_') && (n < (txt.chars().count() - 1)) {
} else { (events, in_mention, text_acc + c.to_string().as_ref(), n + 1, mentions)
let mention = text_acc + c.to_string().as_ref(); } else {
let short_mention = mention.clone(); let mention = text_acc + c.to_string().as_ref();
let short_mention = short_mention.splitn(1, '@').nth(0).unwrap_or(""); let short_mention = mention.clone();
let link = Tag::Link(format!("/@/{}/", mention).into(), short_mention.to_string().into()); let short_mention = short_mention.splitn(1, '@').nth(0).unwrap_or("");
let link = Tag::Link(format!("/@/{}/", mention).into(), short_mention.to_string().into());
events.push(Event::Start(link.clone())); mentions.push(mention);
events.push(Event::Text(format!("@{}", short_mention).into())); events.push(Event::Start(link.clone()));
events.push(Event::End(link)); events.push(Event::Text(format!("@{}", short_mention).into()));
events.push(Event::End(link));
(events, false, c.to_string(), n + 1) (events, false, c.to_string(), n + 1, mentions)
} }
} else { } else {
if c == '@' { if c == '@' {
events.push(Event::Text(text_acc.into())); events.push(Event::Text(text_acc.into()));
(events, true, String::new(), n + 1) (events, true, String::new(), n + 1, mentions)
} else { } else {
if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention. if n >= (txt.chars().count() - 1) { // Add the text after at the end, even if it is not followed by a mention.
events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into())) events.push(Event::Text((text_acc.clone() + c.to_string().as_ref()).into()))
}
(events, in_mention, text_acc + c.to_string().as_ref(), n + 1, mentions)
} }
(events, in_mention, text_acc + c.to_string().as_ref(), n + 1)
} }
} });
}).0, (evts, new_mentions)
_ => vec![evt] },
}); _ => (vec![evt], vec![])
}).unzip();
let parser = parser.into_iter().flatten();
let mentions = mentions.into_iter().flatten();
// TODO: fetch mentionned profiles in background, if needed // TODO: fetch mentionned profiles in background, if needed
let mut buf = String::new(); let mut buf = String::new();
html::push_html(&mut buf, parser); html::push_html(&mut buf, parser);
buf (buf, mentions.collect())
} }