From 5c5cf36b0d6609127793e22ed315edf5a5c689d2 Mon Sep 17 00:00:00 2001 From: fdb-hiroshima <35889323+fdb-hiroshima@users.noreply.github.com> Date: Sun, 23 Dec 2018 11:13:36 +0100 Subject: [PATCH] Allow for comment deletion (#363) * Allow for comment deletion Receive and emit deletion activity Add button to delete comment * Remove debug print and fix copy-past typo * Improve style of comment deletion button --- plume-models/src/comments.rs | 50 ++++++++++++++++++++++++++++-- src/inbox.rs | 8 +++++ src/main.rs | 1 + src/routes/comments.rs | 15 ++++++++- static/css/_article.scss | 7 +++-- static/css/feather.css | 4 +-- templates/partials/comment.rs.html | 11 +++++-- templates/posts/details.rs.html | 2 +- 8 files changed, 86 insertions(+), 12 deletions(-) diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 901a907b..50e323f7 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -1,4 +1,4 @@ -use activitypub::{activity::Create, link, object::Note}; +use activitypub::{activity::{Create, Delete}, link, object::{Note, Tombstone}}; use chrono::{self, NaiveDateTime}; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl}; use serde_json; @@ -7,7 +7,7 @@ use instance::Instance; use mentions::Mention; use notifications::*; use plume_common::activity_pub::{ - inbox::{FromActivity, Notify}, + inbox::{FromActivity, Notify, Deletable}, Id, IntoId, PUBLIC_VISIBILTY, }; use plume_common::utils; @@ -254,3 +254,49 @@ impl Notify for Comment { } } } + + +impl<'a> Deletable for Comment { + fn delete(&self, conn: &Connection) -> Delete { + let mut act = Delete::default(); + act.delete_props + .set_actor_link(self.get_author(conn).into_id()) + .expect("Comment::delete: actor error"); + + let mut tombstone = Tombstone::default(); + tombstone + .object_props + .set_id_string(self.ap_url.clone().expect("Comment::delete: no ap_url")) + .expect("Comment::delete: object.id error"); + act.delete_props + .set_object_object(tombstone) + .expect("Comment::delete: object error"); + + act.object_props + .set_id_string(format!("{}#delete", self.ap_url.clone().unwrap())) + .expect("Comment::delete: id error"); + act.object_props + .set_to_link_vec(vec![Id::new(PUBLIC_VISIBILTY)]) + .expect("Comment::delete: to error"); + + for m in Mention::list_for_comment(&conn, self.id) { + m.delete(conn); + } + diesel::update(comments::table).filter(comments::in_response_to_id.eq(self.id)) + .set(comments::in_response_to_id.eq(self.in_response_to_id)) + .execute(conn) + .expect("Comment::delete: DB error could not update other comments"); + diesel::delete(self) + .execute(conn) + .expect("Comment::delete: DB error"); + act + } + + fn delete_id(id: &str, actor_id: &str, conn: &Connection) { + let actor = User::find_by_ap_url(conn, actor_id); + let comment = Comment::find_by_ap_url(conn, id); + if let Some(comment) = comment.filter(|c| c.author_id == actor.unwrap().id) { + comment.delete(conn); + } + } +} diff --git a/src/inbox.rs b/src/inbox.rs index e5db005e..670aba12 100644 --- a/src/inbox.rs +++ b/src/inbox.rs @@ -65,6 +65,14 @@ pub trait Inbox { actor_id.as_ref(), &(conn, searcher), ); + Comment::delete_id( + &act.delete_props + .object_object::()? + .object_props + .id_string()?, + actor_id.as_ref(), + conn, + ); Ok(()) } "Follow" => { diff --git a/src/main.rs b/src/main.rs index dd7843df..d631d455 100644 --- a/src/main.rs +++ b/src/main.rs @@ -91,6 +91,7 @@ fn main() { routes::blogs::atom_feed, routes::comments::create, + routes::comments::delete, routes::comments::activity_pub, routes::instance::index, diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 6eeb5382..65f7d6ee 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -7,7 +7,8 @@ use rocket_i18n::I18n; use validator::Validate; use template_utils::Ructe; -use plume_common::{utils, activity_pub::{broadcast, ApRequest, ActivityStream}}; +use plume_common::{utils, activity_pub::{broadcast, ApRequest, + ActivityStream, inbox::Deletable}}; use plume_models::{ blogs::Blog, comments::*, @@ -86,6 +87,18 @@ pub fn create(blog_name: String, slug: String, form: LenientForm }) } +#[post("/~///comment//delete")] +pub fn delete(blog: String, slug: String, id: i32, user: User, conn: DbConn, worker: Worker) -> Redirect { + if let Some(comment) = Comment::get(&*conn, id) { + if comment.author_id == user.id { + let dest = User::one_by_instance(&*conn); + let delete_activity = comment.delete(&*conn); + worker.execute(move || broadcast(&user, delete_activity, dest)); + } + } + Redirect::to(uri!(super::posts::details: blog = blog, slug = slug, responding_to = _)) +} + #[get("/~/<_blog>/<_slug>/comment/")] pub fn activity_pub(_blog: String, _slug: String, id: i32, _ap: ApRequest, conn: DbConn) -> Option> { Comment::get(&*conn, id).map(|c| ActivityStream::new(c.to_activity(&*conn))) diff --git a/static/css/_article.scss b/static/css/_article.scss index ef194e33..f71640c5 100644 --- a/static/css/_article.scss +++ b/static/css/_article.scss @@ -202,17 +202,18 @@ main .article-meta { } // New comment form - form input[type="submit"] { + > form input[type="submit"] { font-size: 1em; } - // Response button - a.button { + // Response/delete buttons + a.button, form.inline, form.inline input { display: inline-block; padding: 0; background: none; color: $black; border: none; + margin-right: 2em; &::before { color: $purple; diff --git a/static/css/feather.css b/static/css/feather.css index e039f2b6..7f2d1292 100644 --- a/static/css/feather.css +++ b/static/css/feather.css @@ -17,7 +17,7 @@ fill: none; } -.icon { +.icon:before { font-family: "Feather"; speak: none; font-style: normal; @@ -257,4 +257,4 @@ .icon-command:before { content: "\e8fb"; } .icon-cloud:before { content: "\e8fc"; } .icon-hash:before { content: "\e8fd"; } -.icon-headphones:before { content: "\e8fe"; } \ No newline at end of file +.icon-headphones:before { content: "\e8fe"; } diff --git a/templates/partials/comment.rs.html b/templates/partials/comment.rs.html index a608eafd..284d125a 100644 --- a/templates/partials/comment.rs.html +++ b/templates/partials/comment.rs.html @@ -3,14 +3,14 @@ @use plume_models::users::User; @use routes::*; -@(ctx: BaseContext, comm: &Comment, author: User, in_reply_to: Option<&str>) +@(ctx: BaseContext, comm: &Comment, author: User, in_reply_to: Option<&str>, blog: &str, slug: &str)
@avatar(ctx.0, &author, Size::Small, true, ctx.1) @author.name(ctx.0) @author.get_fqn(ctx.0) - + @if let Some(ref ap_url) = comm.ap_url { } @@ -28,7 +28,12 @@ }
@i18n!(ctx.1, "Respond") + @if ctx.2.clone().map(|u| u.id == author.id).unwrap_or(false) { +
+ +
+ } @for res in comm.get_responses(ctx.0) { - @:comment(ctx, &res, res.get_author(ctx.0), comm.ap_url.as_ref().map(|u| &**u)) + @:comment(ctx, &res, res.get_author(ctx.0), comm.ap_url.as_ref().map(|u| &**u), blog, slug) } diff --git a/templates/posts/details.rs.html b/templates/posts/details.rs.html index 52ba3c38..571f60bf 100644 --- a/templates/posts/details.rs.html +++ b/templates/posts/details.rs.html @@ -146,7 +146,7 @@ @if !comments.is_empty() {
@for comm in comments { - @:comment(ctx, &comm, comm.get_author(ctx.0), Some(&article.ap_url)) + @:comment(ctx, &comm, comm.get_author(ctx.0), Some(&article.ap_url), &blog.get_fqn(ctx.0), &article.slug) }
} else {