|
|
|
@ -6,14 +6,17 @@ use activitypub::{
|
|
|
|
|
use canapi::{Error, Provider};
|
|
|
|
|
use chrono::{NaiveDateTime, TimeZone, Utc};
|
|
|
|
|
use diesel::{self, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl};
|
|
|
|
|
use heck::KebabCase;
|
|
|
|
|
use heck::{CamelCase, KebabCase};
|
|
|
|
|
use serde_json;
|
|
|
|
|
|
|
|
|
|
use plume_api::posts::PostEndpoint;
|
|
|
|
|
use plume_common::activity_pub::{
|
|
|
|
|
Hashtag, Source,
|
|
|
|
|
PUBLIC_VISIBILTY, Id, IntoId,
|
|
|
|
|
inbox::{Deletable, FromActivity}
|
|
|
|
|
use plume_common::{
|
|
|
|
|
activity_pub::{
|
|
|
|
|
Hashtag, Source,
|
|
|
|
|
PUBLIC_VISIBILTY, Id, IntoId,
|
|
|
|
|
inbox::{Deletable, FromActivity}
|
|
|
|
|
},
|
|
|
|
|
utils::md_to_html
|
|
|
|
|
};
|
|
|
|
|
use {BASE_URL, ap_url, Connection};
|
|
|
|
|
use blogs::Blog;
|
|
|
|
@ -26,6 +29,7 @@ use tags::Tag;
|
|
|
|
|
use users::User;
|
|
|
|
|
use schema::posts;
|
|
|
|
|
use safe_string::SafeString;
|
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
|
|
|
|
|
#[derive(Queryable, Identifiable, Serialize, Clone, AsChangeset)]
|
|
|
|
|
pub struct Post {
|
|
|
|
@ -385,9 +389,93 @@ impl Post {
|
|
|
|
|
post.source = source.content;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut txt_hashtags = md_to_html(&post.source).2.into_iter().map(|s| s.to_camel_case()).collect::<HashSet<_>>();
|
|
|
|
|
if let Some(serde_json::Value::Array(mention_tags)) = updated.object_props.tag.clone() {
|
|
|
|
|
let mut mentions = vec![];
|
|
|
|
|
let mut tags = vec![];
|
|
|
|
|
let mut hashtags = vec![];
|
|
|
|
|
for tag in mention_tags.into_iter() {
|
|
|
|
|
serde_json::from_value::<link::Mention>(tag.clone())
|
|
|
|
|
.map(|m| mentions.push(m))
|
|
|
|
|
.ok();
|
|
|
|
|
|
|
|
|
|
serde_json::from_value::<Hashtag>(tag.clone())
|
|
|
|
|
.map(|t| {
|
|
|
|
|
let tag_name = t.name_string().expect("Post::from_activity: tag name error");
|
|
|
|
|
if txt_hashtags.remove(&tag_name) {
|
|
|
|
|
hashtags.push(t);
|
|
|
|
|
} else {
|
|
|
|
|
tags.push(t);
|
|
|
|
|
}
|
|
|
|
|
}).ok();
|
|
|
|
|
}
|
|
|
|
|
post.update_mentions(conn, mentions);
|
|
|
|
|
post.update_tags(conn, tags);
|
|
|
|
|
post.update_hashtags(conn, hashtags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
post.update(conn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn update_mentions(&self, conn: &Connection, mentions: Vec<link::Mention>) {
|
|
|
|
|
let mentions = mentions.into_iter().map(|m| (m.link_props.href_string().ok()
|
|
|
|
|
.and_then(|ap_url| User::find_by_ap_url(conn, ap_url))
|
|
|
|
|
.map(|u| u.id),m))
|
|
|
|
|
.filter_map(|(id, m)| if let Some(id)=id {Some((m,id))} else {None}).collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
let old_mentions = Mention::list_for_post(&conn, self.id);
|
|
|
|
|
let old_user_mentioned = old_mentions.iter()
|
|
|
|
|
.map(|m| m.mentioned_id).collect::<HashSet<_>>();
|
|
|
|
|
for (m,id) in mentions.iter() {
|
|
|
|
|
if !old_user_mentioned.contains(&id) {
|
|
|
|
|
Mention::from_activity(&*conn, m.clone(), self.id, true, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let new_mentions = mentions.into_iter().map(|(_m,id)| id).collect::<HashSet<_>>();
|
|
|
|
|
for m in old_mentions.iter().filter(|m| !new_mentions.contains(&m.mentioned_id)) {
|
|
|
|
|
m.delete(&conn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn update_tags(&self, conn: &Connection, tags: Vec<Hashtag>) {
|
|
|
|
|
let tags_name = tags.iter().filter_map(|t| t.name_string().ok()).collect::<HashSet<_>>();
|
|
|
|
|
|
|
|
|
|
let old_tags = Tag::for_post(&*conn, self.id).into_iter().collect::<Vec<_>>();
|
|
|
|
|
let old_tags_name = old_tags.iter().filter_map(|tag| if !tag.is_hashtag {Some(tag.tag.clone())} else {None}).collect::<HashSet<_>>();
|
|
|
|
|
|
|
|
|
|
for t in tags.into_iter() {
|
|
|
|
|
if !t.name_string().map(|n| old_tags_name.contains(&n)).unwrap_or(true) {
|
|
|
|
|
Tag::from_activity(conn, t, self.id, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ot in old_tags {
|
|
|
|
|
if !tags_name.contains(&ot.tag) {
|
|
|
|
|
ot.delete(conn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn update_hashtags(&self, conn: &Connection, tags: Vec<Hashtag>) {
|
|
|
|
|
let tags_name = tags.iter().filter_map(|t| t.name_string().ok()).collect::<HashSet<_>>();
|
|
|
|
|
|
|
|
|
|
let old_tags = Tag::for_post(&*conn, self.id).into_iter().collect::<Vec<_>>();
|
|
|
|
|
let old_tags_name = old_tags.iter().filter_map(|tag| if tag.is_hashtag {Some(tag.tag.clone())} else {None}).collect::<HashSet<_>>();
|
|
|
|
|
|
|
|
|
|
for t in tags.into_iter() {
|
|
|
|
|
if !t.name_string().map(|n| old_tags_name.contains(&n)).unwrap_or(true) {
|
|
|
|
|
Tag::from_activity(conn, t, self.id, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ot in old_tags {
|
|
|
|
|
if !tags_name.contains(&ot.tag) {
|
|
|
|
|
ot.delete(conn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn to_json(&self, conn: &Connection) -> serde_json::Value {
|
|
|
|
|
let blog = self.get_blog(conn);
|
|
|
|
|
json!({
|
|
|
|
@ -447,6 +535,7 @@ impl FromActivity<Article, Connection> for Post {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// save mentions and tags
|
|
|
|
|
let mut hashtags = md_to_html(&post.source).2.into_iter().map(|s| s.to_camel_case()).collect::<HashSet<_>>();
|
|
|
|
|
if let Some(serde_json::Value::Array(tags)) = article.object_props.tag.clone() {
|
|
|
|
|
for tag in tags.into_iter() {
|
|
|
|
|
serde_json::from_value::<link::Mention>(tag.clone())
|
|
|
|
@ -454,7 +543,10 @@ impl FromActivity<Article, Connection> for Post {
|
|
|
|
|
.ok();
|
|
|
|
|
|
|
|
|
|
serde_json::from_value::<Hashtag>(tag.clone())
|
|
|
|
|
.map(|t| Tag::from_activity(conn, t, post.id))
|
|
|
|
|
.map(|t| {
|
|
|
|
|
let tag_name = t.name_string().expect("Post::from_activity: tag name error");
|
|
|
|
|
Tag::from_activity(conn, t, post.id, hashtags.remove(&tag_name));
|
|
|
|
|
})
|
|
|
|
|
.ok();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -475,6 +567,9 @@ impl Deletable<Connection, Delete> for Post {
|
|
|
|
|
act.object_props.set_id_string(format!("{}#delete", self.ap_url)).expect("Post::delete: id error");
|
|
|
|
|
act.object_props.set_to_link_vec(vec![Id::new(PUBLIC_VISIBILTY)]).expect("Post::delete: to error");
|
|
|
|
|
|
|
|
|
|
for m in Mention::list_for_post(&conn, self.id) {
|
|
|
|
|
m.delete(conn);
|
|
|
|
|
}
|
|
|
|
|
diesel::delete(self).execute(conn).expect("Post::delete: DB error");
|
|
|
|
|
act
|
|
|
|
|
}
|
|
|
|
|