diff options
Diffstat (limited to 'server/src/api/comment.rs')
-rw-r--r-- | server/src/api/comment.rs | 343 |
1 files changed, 193 insertions, 150 deletions
diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index 058c7267..56217458 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -1,4 +1,42 @@ -use super::*; +use crate::{ + api::{APIError, Oper, Perform}, + apub::{ApubLikeableType, ApubObjectType}, + db::{ + comment::*, + comment_view::*, + community_view::*, + moderator::*, + post::*, + site_view::*, + user::*, + user_mention::*, + user_view::*, + Crud, + Likeable, + ListingType, + Saveable, + SortType, + }, + naive_now, + remove_slurs, + scrape_text_for_mentions, + send_email, + settings::Settings, + websocket::{ + server::{JoinCommunityRoom, SendComment}, + UserOperation, + WebsocketInfo, + }, + MentionData, +}; +use diesel::{ + r2d2::{ConnectionManager, Pool}, + PgConnection, +}; +use failure::Error; +use log::error; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; #[derive(Serialize, Deserialize)] pub struct CreateComment { @@ -76,8 +114,6 @@ impl Perform for Oper<CreateComment> { let user_id = claims.id; - let hostname = &format!("https://{}", Settings::get().hostname); - let conn = pool.get()?; // Check for a community ban @@ -87,7 +123,8 @@ impl Perform for Oper<CreateComment> { } // Check for a site ban - if UserView::read(&conn, user_id)?.banned { + let user = User_::read(&conn, user_id)?; + if user.banned { return Err(APIError::err("site_ban").into()); } @@ -101,7 +138,10 @@ impl Perform for Oper<CreateComment> { removed: None, deleted: None, read: None, + published: None, updated: None, + ap_id: "http://fake.com".into(), + local: true, }; let inserted_comment = match Comment::create(&conn, &comment_form) { @@ -109,107 +149,16 @@ impl Perform for Oper<CreateComment> { Err(_e) => return Err(APIError::err("couldnt_create_comment").into()), }; - let mut recipient_ids = Vec::new(); - - // Scan the comment for user mentions, add those rows - let extracted_usernames = extract_usernames(&comment_form.content); - - for username_mention in &extracted_usernames { - if let Ok(mention_user) = User_::read_from_name(&conn, (*username_mention).to_string()) { - // You can't mention yourself - // At some point, make it so you can't tag the parent creator either - // This can cause two notifications, one for reply and the other for mention - if mention_user.id != user_id { - recipient_ids.push(mention_user.id); - - let user_mention_form = UserMentionForm { - recipient_id: mention_user.id, - comment_id: inserted_comment.id, - read: None, - }; - - // Allow this to fail softly, since comment edits might re-update or replace it - // Let the uniqueness handle this fail - match UserMention::create(&conn, &user_mention_form) { - Ok(_mention) => (), - Err(_e) => error!("{}", &_e), - }; - - // Send an email to those users that have notifications on - if mention_user.send_notifications_to_email { - if let Some(mention_email) = mention_user.email { - let subject = &format!( - "{} - Mentioned by {}", - Settings::get().hostname, - claims.username - ); - let html = &format!( - "<h1>User Mention</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>", - claims.username, comment_form.content, hostname - ); - match send_email(subject, &mention_email, &mention_user.name, html) { - Ok(_o) => _o, - Err(e) => error!("{}", e), - }; - } - } - } - } - } + let updated_comment = match Comment::update_ap_id(&conn, inserted_comment.id) { + Ok(comment) => comment, + Err(_e) => return Err(APIError::err("couldnt_create_comment").into()), + }; - // Send notifs to the parent commenter / poster - match data.parent_id { - Some(parent_id) => { - let parent_comment = Comment::read(&conn, parent_id)?; - if parent_comment.creator_id != user_id { - let parent_user = User_::read(&conn, parent_comment.creator_id)?; - recipient_ids.push(parent_user.id); + updated_comment.send_create(&user, &conn)?; - if parent_user.send_notifications_to_email { - if let Some(comment_reply_email) = parent_user.email { - let subject = &format!( - "{} - Reply from {}", - Settings::get().hostname, - claims.username - ); - let html = &format!( - "<h1>Comment Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>", - claims.username, comment_form.content, hostname - ); - match send_email(subject, &comment_reply_email, &parent_user.name, html) { - Ok(_o) => _o, - Err(e) => error!("{}", e), - }; - } - } - } - } - // Its a post - None => { - if post.creator_id != user_id { - let parent_user = User_::read(&conn, post.creator_id)?; - recipient_ids.push(parent_user.id); - - if parent_user.send_notifications_to_email { - if let Some(post_reply_email) = parent_user.email { - let subject = &format!( - "{} - Reply from {}", - Settings::get().hostname, - claims.username - ); - let html = &format!( - "<h1>Post Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>", - claims.username, comment_form.content, hostname - ); - match send_email(subject, &post_reply_email, &parent_user.name, html) { - Ok(_o) => _o, - Err(e) => error!("{}", e), - }; - } - } - } - } - }; + // Scan the comment for user mentions, add those rows + let mentions = scrape_text_for_mentions(&comment_form.content); + let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post); // You like your own comment by default let like_form = CommentLikeForm { @@ -224,6 +173,8 @@ impl Perform for Oper<CreateComment> { Err(_e) => return Err(APIError::err("couldnt_like_comment").into()), }; + updated_comment.send_like(&user, &conn)?; + let comment_view = CommentView::read(&conn, inserted_comment.id, Some(user_id))?; let mut res = CommentResponse { @@ -266,6 +217,8 @@ impl Perform for Oper<EditComment> { let conn = pool.get()?; + let user = User_::read(&conn, user_id)?; + let orig_comment = CommentView::read(&conn, data.edit_id, None)?; // You are allowed to mark the comment as read even if you're banned. @@ -290,13 +243,15 @@ impl Perform for Oper<EditComment> { } // Check for a site ban - if UserView::read(&conn, user_id)?.banned { + if user.banned { return Err(APIError::err("site_ban").into()); } } let content_slurs_removed = remove_slurs(&data.content.to_owned()); + let read_comment = Comment::read(&conn, data.edit_id)?; + let comment_form = CommentForm { content: content_slurs_removed, parent_id: data.parent_id, @@ -305,66 +260,42 @@ impl Perform for Oper<EditComment> { removed: data.removed.to_owned(), deleted: data.deleted.to_owned(), read: data.read.to_owned(), + published: None, updated: if data.read.is_some() { orig_comment.updated } else { Some(naive_now()) }, + ap_id: read_comment.ap_id, + local: read_comment.local, }; - let _updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) { + let updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) { Ok(comment) => comment, Err(_e) => return Err(APIError::err("couldnt_update_comment").into()), }; - let mut recipient_ids = Vec::new(); - - // Scan the comment for user mentions, add those rows - let extracted_usernames = extract_usernames(&comment_form.content); - - for username_mention in &extracted_usernames { - let mention_user = User_::read_from_name(&conn, (*username_mention).to_string()); - - if mention_user.is_ok() { - let mention_user_id = mention_user?.id; - - // You can't mention yourself - // At some point, make it so you can't tag the parent creator either - // This can cause two notifications, one for reply and the other for mention - if mention_user_id != user_id { - recipient_ids.push(mention_user_id); - - let user_mention_form = UserMentionForm { - recipient_id: mention_user_id, - comment_id: data.edit_id, - read: None, - }; - - // Allow this to fail softly, since comment edits might re-update or replace it - // Let the uniqueness handle this fail - match UserMention::create(&conn, &user_mention_form) { - Ok(_mention) => (), - Err(_e) => error!("{}", &_e), - } - } - } - } - - // Add to recipient ids - match data.parent_id { - Some(parent_id) => { - let parent_comment = Comment::read(&conn, parent_id)?; - if parent_comment.creator_id != user_id { - let parent_user = User_::read(&conn, parent_comment.creator_id)?; - recipient_ids.push(parent_user.id); - } + if let Some(deleted) = data.deleted.to_owned() { + if deleted { + updated_comment.send_delete(&user, &conn)?; + } else { + updated_comment.send_undo_delete(&user, &conn)?; } - None => { - let post = Post::read(&conn, data.post_id)?; - recipient_ids.push(post.creator_id); + } else if let Some(removed) = data.removed.to_owned() { + if removed { + updated_comment.send_remove(&user, &conn)?; + } else { + updated_comment.send_undo_remove(&user, &conn)?; } + } else { + updated_comment.send_update(&user, &conn)?; } + let post = Post::read(&conn, data.post_id)?; + + let mentions = scrape_text_for_mentions(&comment_form.content); + let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post); + // Mod tables if let Some(removed) = data.removed.to_owned() { let form = ModRemoveCommentForm { @@ -480,7 +411,8 @@ impl Perform for Oper<CreateCommentLike> { } // Check for a site ban - if UserView::read(&conn, user_id)?.banned { + let user = User_::read(&conn, user_id)?; + if user.banned { return Err(APIError::err("site_ban").into()); } @@ -517,6 +449,14 @@ impl Perform for Oper<CreateCommentLike> { Ok(like) => like, Err(_e) => return Err(APIError::err("couldnt_like_comment").into()), }; + + if like_form.score == 1 { + comment.send_like(&user, &conn)?; + } else if like_form.score == -1 { + comment.send_dislike(&user, &conn)?; + } + } else { + comment.send_undo_like(&user, &conn)?; } // Have to refetch the comment to get the current state @@ -601,3 +541,106 @@ impl Perform for Oper<GetComments> { Ok(GetCommentsResponse { comments }) } } + +pub fn send_local_notifs( + conn: &PgConnection, + mentions: &[MentionData], + comment: &Comment, + user: &User_, + post: &Post, +) -> Vec<i32> { + let mut recipient_ids = Vec::new(); + let hostname = &format!("https://{}", Settings::get().hostname); + + // Send the local mentions + for mention in mentions + .iter() + .filter(|m| m.is_local() && m.name.ne(&user.name)) + .collect::<Vec<&MentionData>>() + { + if let Ok(mention_user) = User_::read_from_name(&conn, &mention.name) { + // TODO + // At some point, make it so you can't tag the parent creator either + // This can cause two notifications, one for reply and the other for mention + recipient_ids.push(mention_user.id); + + let user_mention_form = UserMentionForm { + recipient_id: mention_user.id, + comment_id: comment.id, + read: None, + }; + + // Allow this to fail softly, since comment edits might re-update or replace it + // Let the uniqueness handle this fail + match UserMention::create(&conn, &user_mention_form) { + Ok(_mention) => (), + Err(_e) => error!("{}", &_e), + }; + + // Send an email to those users that have notifications on + if mention_user.send_notifications_to_email { + if let Some(mention_email) = mention_user.email { + let subject = &format!("{} - Mentioned by {}", Settings::get().hostname, user.name,); + let html = &format!( + "<h1>User Mention</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>", + user.name, comment.content, hostname + ); + match send_email(subject, &mention_email, &mention_user.name, html) { + Ok(_o) => _o, + Err(e) => error!("{}", e), + }; + } + } + } + } + + // Send notifs to the parent commenter / poster + match comment.parent_id { + Some(parent_id) => { + if let Ok(parent_comment) = Comment::read(&conn, parent_id) { + if parent_comment.creator_id != user.id { + if let Ok(parent_user) = User_::read(&conn, parent_comment.creator_id) { + recipient_ids.push(parent_user.id); + + if parent_user.send_notifications_to_email { + if let Some(comment_reply_email) = parent_user.email { + let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,); + let html = &format!( + "<h1>Comment Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>", + user.name, comment.content, hostname + ); + match send_email(subject, &comment_reply_email, &parent_user.name, html) { + Ok(_o) => _o, + Err(e) => error!("{}", e), + }; + } + } + } + } + } + } + // Its a post + None => { + if post.creator_id != user.id { + if let Ok(parent_user) = User_::read(&conn, post.creator_id) { + recipient_ids.push(parent_user.id); + + if parent_user.send_notifications_to_email { + if let Some(post_reply_email) = parent_user.email { + let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,); + let html = &format!( + "<h1>Post Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>", + user.name, comment.content, hostname + ); + match send_email(subject, &post_reply_email, &parent_user.name, html) { + Ok(_o) => _o, + Err(e) => error!("{}", e), + }; + } + } + } + } + } + }; + recipient_ids +} |