summaryrefslogtreecommitdiffstats
path: root/server/src/api/comment.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/api/comment.rs')
-rw-r--r--server/src/api/comment.rs343
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
+}