diff --git a/Gemfile b/Gemfile index e548957..01b401c 100644 --- a/Gemfile +++ b/Gemfile @@ -26,6 +26,8 @@ gem "nokogiri", "= 1.6.1" gem "htmlentities" gem "rdiscount" +gem "activerecord-typedstore" + # for twitter-posting bot gem "oauth" diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index 622bab4..2d77117 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -338,6 +338,16 @@ $(document).ready(function() { return Lobsters.moderateStory(this); }), + $(".toggle_dragons").click(function(a) { + var c = $(".dragon_threads").first(); + if (c) { + if (c.hasClass("hidden")) + c.removeClass("hidden"); + else + c.addClass("hidden"); + } + }), + $(document).on("click", "a.comment_replier", function() { if (!Lobsters.curUser) { Lobsters.bounceToLogin(); @@ -408,6 +418,34 @@ $(document).ready(function() { }); } }); + $(document).on("click", "a.comment_undeletor", function() { + if (confirm("Êtes-vous sûr de vouloir restaurer ce commentaire ?")) { + var li = $(this).closest(".comment"); + $.post("/comments/" + $(li).attr("data-shortid") + "/undelete", + function(d) { + $(li).replaceWith(d); + }); + } + }); + + $(document).on("click", "a.comment_dragon", function() { + if (confirm("Êtes-vous sûr de vouloir marquer ce fil de discussion ?")) { + var li = $(this).closest(".comment"); + $.post("/comments/" + $(li).attr("data-shortid") + "/dragon", + function(d) { + window.location.reload(); + }); + } + }); + $(document).on("click", "a.comment_undragon", function() { + if (confirm("Êtes-vous sûr de vouloir supprimer la marque de ce fil de discussion ?")) { + var li = $(this).closest(".comment"); + $.post("/comments/" + $(li).attr("data-shortid") + "/undragon", + function(d) { + window.location.reload(); + }); + } + }); $(document).on("click", "a.comment_moderator", function() { var reason = prompt("Raison de la modération :"); @@ -421,16 +459,6 @@ $(document).ready(function() { }); }); - $(document).on("click", "a.comment_undeletor", function() { - if (confirm("Êtes-vous sûr de vouloir dé-supprimer ce commentaire ?")) { - var li = $(this).closest(".comment"); - $.post("/comments/" + $(li).attr("data-shortid") + "/undelete", - function(d) { - $(li).replaceWith(d); - }); - } - }); - Lobsters.runSelect2(); $(document).on("click", "div.markdown_help_toggler .markdown_help_label", diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 52fef5f..6810ee4 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -737,6 +737,23 @@ div.comment_actions a { text-decoration: none; } +div.dragons { +} +.dragon_text { + background-color: #f8f8f8; + color: gray; + font-style: italic; + margin: 1em; + padding: 2em 0 2.5em 0; + text-align: center; +} +.dragon_text a { + color: gray; +} +.dragon_threads.hidden { + display: none; +} + a.pagelink { border: 1px solid #ddd; background-color: #fbfbfb; diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 144a3f2..67b8a7e 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -128,6 +128,26 @@ class CommentsController < ApplicationController :content_type => "text/html", :locals => { :comment => comment } end + def dragon + if !((comment = find_comment) && @user.is_moderator?) + return render :text => "can't find comment", :status => 400 + end + + comment.become_dragon_for_user(@user) + + render :text => "ok" + end + + def undragon + if !((comment = find_comment) && @user.is_moderator?) + return render :text => "can't find comment", :status => 400 + end + + comment.remove_dragon_for_user(@user) + + render :text => "ok" + end + def update if !((comment = find_comment) && comment.is_editable_by_user?(@user)) return render :text => "can't find comment", :status => 400 diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 317c607..533b255 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -81,7 +81,7 @@ private :email_replies, :email_messages, :email_mentions, :pushover_replies, :pushover_messages, :pushover_mentions, :mailing_list_mode, :show_avatars, :show_story_previews, - :show_submitted_story_threads + :show_submitted_story_threads, :hide_dragons ) end end diff --git a/app/models/comment.rb b/app/models/comment.rb index b18d956..f838d24 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -50,8 +50,8 @@ class Comment < ActiveRecord::Base end def self.arrange_for_user(user) - parents = self.order("(upvotes - downvotes) < 0 ASC, confidence DESC"). - group_by(&:parent_comment_id) + parents = self.order("is_dragon ASC, (upvotes - downvotes) < 0 ASC, " << + "confidence DESC").group_by(&:parent_comment_id) # top-down list of comments, regardless of indent level ordered = [] @@ -168,6 +168,36 @@ class Comment < ActiveRecord::Base end end + def become_dragon_for_user(user) + Comment.record_timestamps = false + + self.is_dragon = true + + m = Moderation.new + m.comment_id = self.id + m.moderator_user_id = user.id + m.action = "turned into a dragon" + m.save + + self.save(:validate => false) + Comment.record_timestamps = true + end + + def remove_dragon_for_user(user) + Comment.record_timestamps = false + + self.is_dragon = false + + m = Moderation.new + m.comment_id = self.id + m.moderator_user_id = user.id + m.action = "slayed dragon" + m.save + + self.save(:validate => false) + Comment.record_timestamps = true + end + # http://evanmiller.org/how-not-to-sort-by-average-rating.html # https://github.com/reddit/reddit/blob/master/r2/r2/lib/db/_sorts.pyx def calculated_confidence diff --git a/app/models/user.rb b/app/models/user.rb index 3bff609..115f365 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -33,6 +33,20 @@ class User < ActiveRecord::Base has_secure_password + typed_store :settings do |s| + s.boolean :email_notifications, :default => false + s.boolean :email_replies, :default => false + s.boolean :pushover_replies, :default => false + s.string :pushover_user_key + s.boolean :email_messages, :default => false + s.boolean :pushover_messages, :default => false + s.boolean :email_mentions, :default => false + s.boolean :show_avatars, :default => true + s.boolean :show_story_previews, :default => false + s.boolean :show_submitted_story_threads, :default => false + s.boolean :hide_dragons, :default => false + end + validates :email, :format => { :with => /\A[^@ ]+@[^@ ]+\.[^@ ]+\Z/ }, :uniqueness => { :case_sensitive => false } diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index 38b9055..c4be4ce 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -83,6 +83,15 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ? <% end %> <% end %> + <% if !comment.parent_comment_id && @user && @user.is_moderator? %> + | + <% if comment.is_dragon? %> + <%= t('.undragon') %> + <% else %> + <%= t('.dragon') %> + <% end %> + <% end %> + <% if @user && !comment.story.is_gone? && !comment.is_gone? %> | <%= t('.reply') %> diff --git a/app/views/settings/index.html.erb b/app/views/settings/index.html.erb index 73d800a..d4ab006 100644 --- a/app/views/settings/index.html.erb +++ b/app/views/settings/index.html.erb @@ -193,6 +193,10 @@ <%= f.check_box :show_avatars %> +
+ <%= f.label :hide_dragons, "Hide Dragons:", :class => "required" %> + <%= f.check_box :hide_dragons %> +

<%= f.submit t('.saveallsettings') %> diff --git a/app/views/stories/show.html.erb b/app/views/stories/show.html.erb index d88eb91..efe6449 100644 --- a/app/views/stories/show.html.erb +++ b/app/views/stories/show.html.erb @@ -45,9 +45,22 @@ <% comments_by_parent = @comments.group_by(&:parent_comment_id) %> <% subtree = comments_by_parent[nil] %> <% ancestors = [] %> + <% dragons = false %> <% while subtree %> <% if (comment = subtree.shift) %> + <% if comment.is_dragon? && !dragons %> +
+
+ + <%= raw(t('.toggledragons')) %> + +
+
"> + <% dragons = true %> + <% end %> +
  • <%= render "comments/comment", :comment => comment, :show_story => (comment.story_id != @story.id), @@ -64,5 +77,10 @@
  • <% end %> <% end %> + + <% if dragons %> +
    +
    + <% end %> <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index e3be902..09632f3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -101,6 +101,8 @@ en: delete: "delete" reply: "reply" about: "on:" + dragon: "dragon" + undragon: "undragon" email_message: notification.text: replyat: "Reply at " @@ -345,6 +347,8 @@ en: preview: "Preview" submit: "Submit a Story" submitbutton: "Submit" + show: + toggledragons: "— here be dragons —" users: list: administrator: "administrator" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 3d6c6e9..9e8262b 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -103,6 +103,8 @@ fr: delete: "supprimer" reply: "répondre" about: "sur :" + dragon: "dragon" + undragon: "non-dragon" global: markdownhelp: emphasizedtext: "italique" @@ -355,6 +357,8 @@ fr: preview: "Aperçu" submit: "Soumettre une info" submitbutton: "Soumettre" + show: + toggledragons: "— voici les dragons —" users: list: administrator: "administrateur" diff --git a/config/routes.rb b/config/routes.rb index e2320d3..65c07f0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -72,6 +72,9 @@ Lobsters::Application.routes.draw do post "delete" post "undelete" + + post "dragon" + post "undragon" end end get "/comments/page/:page" => "comments#index" diff --git a/db/migrate/20170119172852_move_user_settings.rb b/db/migrate/20170119172852_move_user_settings.rb new file mode 100644 index 0000000..a0a19eb --- /dev/null +++ b/db/migrate/20170119172852_move_user_settings.rb @@ -0,0 +1,58 @@ +class MoveUserSettings < ActiveRecord::Migration + def up + add_column :users, :settings, :text + + [ + :email_notifications, + :email_replies, + :pushover_replies, + :pushover_user_key, + :email_messages, + :pushover_messages, + :email_mentions, + :show_avatars, + :show_story_previews, + :show_submitted_story_threads, + ].each do |col| + rename_column :users, col, "old_#{col}" + end + + User.find_each do |u| + [ + :email_notifications, + :email_replies, + :pushover_replies, + :pushover_user_key, + :email_messages, + :pushover_messages, + :email_mentions, + :show_avatars, + :show_story_previews, + :show_submitted_story_threads, + ].each do |k| + u.settings[k] = u.send("old_#{k}") + end + + u.save(:validate => false) + end + end + + def down + remove_column :users, :settings + + [ + :email_notifications, + :email_replies, + :pushover_replies, + :pushover_user_key, + :email_messages, + :pushover_messages, + :email_mentions, + :show_avatars, + :show_story_previews, + :show_submitted_story_threads, + ].each do |col| + rename_column :users, "old#{col}", col + end + end +end diff --git a/db/migrate/20170119192703_add_dragons.rb b/db/migrate/20170119192703_add_dragons.rb new file mode 100644 index 0000000..ed03dd0 --- /dev/null +++ b/db/migrate/20170119192703_add_dragons.rb @@ -0,0 +1,5 @@ +class AddDragons < ActiveRecord::Migration + def change + add_column :comments, :is_dragon, :boolean, :default => false + end +end diff --git a/db/schema.rb b/db/schema.rb index fe2d5ef..823e889 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160704022756) do +ActiveRecord::Schema.define(version: 20170119192703) do create_table "comments", force: true do |t| t.datetime "created_at", null: false @@ -30,6 +30,7 @@ ActiveRecord::Schema.define(version: 20160704022756) do t.boolean "is_moderated", default: false t.boolean "is_from_email", default: false t.integer "hat_id" + t.boolean "is_dragon", default: false end add_index "comments", ["confidence"], name: "confidence_idx", using: :btree @@ -183,38 +184,39 @@ ActiveRecord::Schema.define(version: 20160704022756) do add_index "tags", ["tag"], name: "tag", unique: true, using: :btree create_table "users", force: true do |t| - t.string "username", limit: 50 - t.string "email", limit: 100 - t.string "password_digest", limit: 75 + t.string "username", limit: 50 + t.string "email", limit: 100 + t.string "password_digest", limit: 75 t.datetime "created_at" - t.boolean "email_notifications", default: false - t.boolean "is_admin", default: false - t.string "password_reset_token", limit: 75 - t.string "session_token", limit: 75, default: "", null: false - t.text "about", limit: 16777215 + t.boolean "old_email_notifications", default: false + t.boolean "is_admin", default: false + t.string "password_reset_token", limit: 75 + t.string "session_token", limit: 75, default: "", null: false + t.text "about", limit: 16777215 t.integer "invited_by_user_id" - t.boolean "email_replies", default: false - t.boolean "pushover_replies", default: false - t.string "pushover_user_key" - t.boolean "email_messages", default: true - t.boolean "pushover_messages", default: true - t.boolean "is_moderator", default: false - t.boolean "email_mentions", default: false - t.boolean "pushover_mentions", default: false - t.string "rss_token", limit: 75 - t.string "mailing_list_token", limit: 75 - t.integer "mailing_list_mode", default: 0 - t.integer "karma", default: 0, null: false + t.boolean "old_email_replies", default: false + t.boolean "old_pushover_replies", default: false + t.string "old_pushover_user_key" + t.boolean "old_email_messages", default: true + t.boolean "old_pushover_messages", default: true + t.boolean "is_moderator", default: false + t.boolean "old_email_mentions", default: false + t.boolean "pushover_mentions", default: false + t.string "rss_token", limit: 75 + t.string "mailing_list_token", limit: 75 + t.integer "mailing_list_mode", default: 0 + t.integer "karma", default: 0, null: false t.datetime "banned_at" t.integer "banned_by_user_id" - t.string "banned_reason", limit: 200 + t.string "banned_reason", limit: 200 t.datetime "deleted_at" - t.boolean "show_avatars", default: false - t.boolean "show_story_previews", default: false - t.boolean "show_submitted_story_threads", default: true + t.boolean "old_show_avatars", default: false + t.boolean "old_show_story_previews", default: false + t.boolean "old_show_submitted_story_threads", default: true t.datetime "disabled_invite_at" t.integer "disabled_invite_by_user_id" - t.string "disabled_invite_reason", limit: 200 + t.string "disabled_invite_reason", limit: 200 + t.text "settings" end add_index "users", ["mailing_list_mode"], name: "mailing_list_enabled", using: :btree