From 3640e1e7a3b542239194cf1d6de2dd408455c309 Mon Sep 17 00:00:00 2001 From: joshua stein Date: Thu, 12 Jul 2012 13:30:20 -0500 Subject: [PATCH] comment deleting and undeleting --- app/assets/javascripts/application.js.erb | 20 +++++ app/assets/stylesheets/application.css | 5 ++ app/controllers/comments_controller.rb | 26 ++++++ app/controllers/stories_controller.rb | 4 +- app/models/comment.rb | 86 +++++++++++++++++-- app/models/story.rb | 6 +- app/views/comments/_comment.html.erb | 46 +++++++--- config/routes.rb | 2 + .../20120712174445_add_comment_deleted.rb | 9 ++ db/schema.rb | 34 ++++---- 10 files changed, 197 insertions(+), 41 deletions(-) create mode 100644 db/migrate/20120712174445_add_comment_deleted.rb diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index f4be092..1bb1695 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -233,6 +233,26 @@ $(document).ready(function() { li.load("/comments/" + $(li).attr("data-shortid") + "/edit", { "edit": 1 }); }); + + $("a.comment_deletor").live("click", function() { + if (confirm("Are you sure you want to delete this comment?")) { + var li = $(this).parents("li.comment").first(); + $.post("/comments/" + $(li).attr("data-shortid") + "/delete", + function(d) { + $(li).replaceWith(d); + }); + } + }); + + $("a.comment_undeletor").live("click", function() { + if (confirm("Are you sure you want to undelete this comment?")) { + var li = $(this).parents("li.comment").first(); + $.post("/comments/" + $(li).attr("data-shortid") + "/undelete", + function(d) { + $(li).replaceWith(d); + }); + } + }); $("#story_tags_a").select2({ formatSelection: function(what) { diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 576cbbf..a480095 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -68,6 +68,11 @@ span.fakea { color: gray; } +span.na { + color: gray; + font-style: italic; +} + div.shorten_first_p p:first-child { margin-top: 0.5em; } diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index b01e752..824ab91 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -66,6 +66,32 @@ class CommentsController < ApplicationController :comment => comment } end + def delete + if !((comment = Comment.find_by_short_id(params[:comment_id])) && + comment.is_editable_by_user?(@user)) + return render :text => "can't find comment", :status => 400 + end + + comment.delete_for_user(@user) + + render :partial => "comment", :layout => false, + :content_type => "text/html", :locals => { :story => comment.story, + :comment => comment } + end + + def undelete + if !((comment = Comment.find_by_short_id(params[:comment_id])) && + comment.is_undeletable_by_user?(@user)) + return render :text => "can't find comment", :status => 400 + end + + comment.undelete_for_user(@user) + + render :partial => "comment", :layout => false, + :content_type => "text/html", :locals => { :story => comment.story, + :comment => comment } + end + def update if !((comment = Comment.find_by_short_id(params[:comment_id])) && comment.is_editable_by_user?(@user)) diff --git a/app/controllers/stories_controller.rb b/app/controllers/stories_controller.rb index fd53bac..582b0f4 100644 --- a/app/controllers/stories_controller.rb +++ b/app/controllers/stories_controller.rb @@ -116,7 +116,7 @@ class StoriesController < ApplicationController @short_url = @story.short_id_url @comments = Comment.ordered_for_story_or_thread_for_user(@story.id, nil, - @user ? @user.id : nil) + @user ? @user : nil) @comment = Comment.new if @user @@ -147,7 +147,7 @@ class StoriesController < ApplicationController end @comments = Comment.ordered_for_story_or_thread_for_user(@story.id, - @showing_comment.thread_id, @user ? @user.id : nil) + @showing_comment.thread_id, @user ? @user : nil) @comments.each do |c,x| if c.id == @showing_comment.id diff --git a/app/models/comment.rb b/app/models/comment.rb index ef019a3..64ad2f5 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -23,8 +23,11 @@ class Comment < ActiveRecord::Base has "(upvotes - downvotes)", :as => :score, :type => :integer, :sortable => true - + + has is_moderated, is_deleted has created_at + + where "is_moderated = 0 AND is_deleted = 0" end validate do @@ -42,11 +45,15 @@ class Comment < ActiveRecord::Base end def self.regenerate_markdown + Comment.record_timestamps = false + Comment.all.each do |c| c.markeddown_comment = c.generated_markeddown_comment - Comment.record_timestamps = false c.save(:validate => false) end + + Comment.record_timestamps = true + nil end @@ -72,6 +79,10 @@ class Comment < ActiveRecord::Base self.story.update_comment_count! end + + def is_gone? + is_deleted? || is_moderated? + end def mark_submitter Keystore.increment_value_for("user:#{self.user_id}:comments_posted") @@ -97,7 +108,30 @@ class Comment < ActiveRecord::Base rescue end end - + + def delete_for_user(user) + Comment.record_timestamps = false + + if user.is_admin? && user.id != self.user_id + self.is_moderated = true + else + self.is_deleted = true + end + + self.save(:validate => false) + Comment.record_timestamps = true + end + + def undelete_for_user(user) + Comment.record_timestamps = false + + self.is_moderated = false + self.is_deleted = false + + self.save(:validate => false) + Comment.record_timestamps = true + end + def give_upvote_or_downvote_and_recalculate_confidence!(upvote, downvote) self.upvotes += upvote.to_i self.downvotes += downvote.to_i @@ -157,7 +191,7 @@ class Comment < ActiveRecord::Base self.updated_at && (self.updated_at - self.created_at > 1.minute) end - def self.ordered_for_story_or_thread_for_user(story_id, thread_id, user_id) + def self.ordered_for_story_or_thread_for_user(story_id, thread_id, user) parents = {} if thread_id @@ -187,17 +221,51 @@ class Comment < ActiveRecord::Base } recursor.call(nil, 0) - # TODO: handle deleted comments, show for user_id + # for deleted comments, if they have no children, they can be removed from + # the tree. otherwise they have to stay and a "[deleted]" stub will be + # shown + new_ordered = [] + ordered.each_with_index do |c,x| + if c.is_gone? + if ordered[x + 1] && (ordered[x + 1].indent_level > c.indent_level) + # we have child comments, so we must stay + elsif user && (user.is_admin? || c.user_id == user.id) + # admins and authors should be able to see their deleted comments + else + # drop this one + next + end + end - ordered + new_ordered.push c + end + + new_ordered end def is_editable_by_user?(user) - if !user || user.id != self.user_id + if user && user.is_admin? + return true + elsif user && user.id == self.user_id + if self.is_moderated? + return false + else + return (Time.now.to_i - (self.updated_at ? self.updated_at.to_i : + self.created_at.to_i) < (60 * MAX_EDIT_MINS)) + end + else + return false + end + end + + def is_undeletable_by_user?(user) + if user && user.is_admin? + return true + elsif user && user.id == self.user_id && !self.is_moderated? + return true + else return false end - - (Time.now.to_i - self.created_at.to_i < (60 * MAX_EDIT_MINS)) end def url diff --git a/app/models/story.rb b/app/models/story.rb index af37ffd..e4b6e46 100644 --- a/app/models/story.rb +++ b/app/models/story.rb @@ -39,6 +39,8 @@ class Story < ActiveRecord::Base :title => 10, :tags => 5, } + + where "is_moderated = 0 AND is_expired = 0" end validate do @@ -278,7 +280,9 @@ class Story < ActiveRecord::Base end def is_undeletable_by_user?(user) - if user && (user.is_admin? || user.id == self.user_id) + if user && user.is_admin? + return true + elsif user && user.id == self.user_id && !self.is_moderated? return true else return false diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index ba645eb..a9ddb93 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -6,13 +6,15 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ? <%= comment.score <= -3 ? "negative_3" : "" %> <%= comment.score <= -5 ? "negative_5" : "" %> <%= comment.score <= -7 ? "negative_7" : "" %>"> -
- -
- <%= comment.score %> -
- -
+ <% if !comment.is_gone? %> +
+ +
+ <%= comment.score %> +
+ +
+ <% end %>
- <%= raw comment.markeddown_comment %> + <% if comment.is_gone? %> +

+ + [Comment removed by <%= comment.is_deleted? ? "author" : + "moderator" %>] + +

+ <% else %> + <%= raw comment.markeddown_comment %> + <% end %>
diff --git a/config/routes.rb b/config/routes.rb index 8e4a4df..5e7336c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -45,6 +45,8 @@ Lobsters::Application.routes.draw do post "edit" post "preview" post "update" + post "delete" + post "undelete" end post "/comments/post_to/:story_id" => "comments#create" post "/comments/preview_to/:story_id" => "comments#preview_new" diff --git a/db/migrate/20120712174445_add_comment_deleted.rb b/db/migrate/20120712174445_add_comment_deleted.rb new file mode 100644 index 0000000..8599649 --- /dev/null +++ b/db/migrate/20120712174445_add_comment_deleted.rb @@ -0,0 +1,9 @@ +class AddCommentDeleted < ActiveRecord::Migration + def up + add_column "comments", "is_deleted", :boolean, :default => false + add_column "comments", "is_moderated", :boolean, :default => false + end + + def down + end +end diff --git a/db/schema.rb b/db/schema.rb index 45c2333..b757815 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,21 +11,23 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20120706221602) do +ActiveRecord::Schema.define(:version => 20120712174445) do create_table "comments", :force => true do |t| - t.datetime "created_at", :null => false + t.datetime "created_at", :null => false t.datetime "updated_at" - t.string "short_id", :limit => 10, :default => "", :null => false - t.integer "story_id", :null => false - t.integer "user_id", :null => false + t.string "short_id", :limit => 10, :default => "", :null => false + t.integer "story_id", :null => false + t.integer "user_id", :null => false t.integer "parent_comment_id" t.integer "thread_id" - t.text "comment", :null => false - t.integer "upvotes", :default => 0, :null => false - t.integer "downvotes", :default => 0, :null => false - t.decimal "confidence", :precision => 20, :scale => 17 + t.text "comment", :null => false + t.integer "upvotes", :default => 0, :null => false + t.integer "downvotes", :default => 0, :null => false + t.decimal "confidence", :precision => 20, :scale => 19, :default => 0.0, :null => false t.text "markeddown_comment" + t.boolean "is_deleted", :default => false + t.boolean "is_moderated", :default => false end add_index "comments", ["confidence"], :name => "confidence_idx" @@ -67,14 +69,14 @@ ActiveRecord::Schema.define(:version => 20120706221602) do t.datetime "created_at" t.integer "user_id" t.string "url", :limit => 250, :default => "" - t.string "title", :limit => 150, :default => "", :null => false + t.string "title", :limit => 150, :default => "", :null => false t.text "description" - t.string "short_id", :limit => 6, :default => "", :null => false - t.integer "is_expired", :limit => 1, :default => 0, :null => false - t.integer "upvotes", :default => 0, :null => false - t.integer "downvotes", :default => 0, :null => false - t.integer "is_moderated", :limit => 1, :default => 0, :null => false - t.decimal "hotness", :precision => 20, :scale => 10 + t.string "short_id", :limit => 6, :default => "", :null => false + t.integer "is_expired", :limit => 1, :default => 0, :null => false + t.integer "upvotes", :default => 0, :null => false + t.integer "downvotes", :default => 0, :null => false + t.integer "is_moderated", :limit => 1, :default => 0, :null => false + t.decimal "hotness", :precision => 20, :scale => 10, :default => 0.0, :null => false t.text "markeddown_description" end