From 1458037ee8c7ca1768e97b8a3eefbf40b4a9d3ee Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 20 Aug 2015 15:35:47 +0200 Subject: [PATCH 01/23] adds db migrations for flagging comments & debates as inappropiate --- ...0150820103351_create_inappropiate_flags.rb | 15 ++++++++ ...dd_inappropiate_flag_fields_to_comments.rb | 6 +++ ...add_inappropiate_flag_fields_to_debates.rb | 6 +++ db/schema.rb | 37 ++++++++++++++----- 4 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 db/migrate/20150820103351_create_inappropiate_flags.rb create mode 100644 db/migrate/20150820103800_add_inappropiate_flag_fields_to_comments.rb create mode 100644 db/migrate/20150820104552_add_inappropiate_flag_fields_to_debates.rb diff --git a/db/migrate/20150820103351_create_inappropiate_flags.rb b/db/migrate/20150820103351_create_inappropiate_flags.rb new file mode 100644 index 000000000..982d9f705 --- /dev/null +++ b/db/migrate/20150820103351_create_inappropiate_flags.rb @@ -0,0 +1,15 @@ +class CreateInappropiateFlags < ActiveRecord::Migration + def change + create_table :inappropiate_flags do |t| + t.belongs_to :user, index: true, foreign_key: true + + t.string :flaggable_type + t.integer :flaggable_id + + t.timestamps + end + + add_index :inappropiate_flags, [:flaggable_type, :flaggable_id] + add_index :inappropiate_flags, [:user_id, :flaggable_type, :flaggable_id], :name => "access_inappropiate_flags" + end +end diff --git a/db/migrate/20150820103800_add_inappropiate_flag_fields_to_comments.rb b/db/migrate/20150820103800_add_inappropiate_flag_fields_to_comments.rb new file mode 100644 index 000000000..92a53581b --- /dev/null +++ b/db/migrate/20150820103800_add_inappropiate_flag_fields_to_comments.rb @@ -0,0 +1,6 @@ +class AddInappropiateFlagFieldsToComments < ActiveRecord::Migration + def change + add_column :comments, :flagged_as_inappropiate_at, :datetime + add_column :comments, :inappropiate_flags_count, :integer, default: 0 + end +end diff --git a/db/migrate/20150820104552_add_inappropiate_flag_fields_to_debates.rb b/db/migrate/20150820104552_add_inappropiate_flag_fields_to_debates.rb new file mode 100644 index 000000000..c5376ed62 --- /dev/null +++ b/db/migrate/20150820104552_add_inappropiate_flag_fields_to_debates.rb @@ -0,0 +1,6 @@ +class AddInappropiateFlagFieldsToDebates < ActiveRecord::Migration + def change + add_column :debates, :flagged_as_inappropiate_at, :datetime + add_column :debates, :inappropiate_flags_count, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index f2bbe60fc..7ac004cee 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: 20150817150457) do +ActiveRecord::Schema.define(version: 20150820104552) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -41,14 +41,16 @@ ActiveRecord::Schema.define(version: 20150817150457) do t.string "title" t.text "body" t.string "subject" - t.integer "user_id", null: false + t.integer "user_id", null: false t.integer "parent_id" t.integer "lft" t.integer "rgt" t.datetime "created_at" t.datetime "updated_at" - t.integer "children_count", default: 0 + t.integer "children_count", default: 0 t.datetime "hidden_at" + t.datetime "flagged_as_inappropiate_at" + t.integer "inappropiate_flags_count", default: 0 end add_index "comments", ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type", using: :btree @@ -56,17 +58,31 @@ ActiveRecord::Schema.define(version: 20150817150457) do add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree create_table "debates", force: :cascade do |t| - t.string "title", limit: 80 + t.string "title", limit: 80 t.text "description" t.integer "author_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.datetime "hidden_at" t.string "visit_id" + t.datetime "flagged_as_inappropiate_at" + t.integer "inappropiate_flags_count", default: 0 end add_index "debates", ["hidden_at"], name: "index_debates_on_hidden_at", using: :btree + create_table "inappropiate_flags", force: :cascade do |t| + t.integer "user_id" + t.string "flaggable_type" + t.integer "flaggable_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "inappropiate_flags", ["flaggable_type", "flaggable_id"], name: "index_inappropiate_flags_on_flaggable_type_and_flaggable_id", using: :btree + add_index "inappropiate_flags", ["user_id", "flaggable_type", "flaggable_id"], name: "access_inappropiate_flags", using: :btree + add_index "inappropiate_flags", ["user_id"], name: "index_inappropiate_flags_on_user_id", using: :btree + create_table "moderators", force: :cascade do |t| t.integer "user_id" end @@ -138,11 +154,11 @@ ActiveRecord::Schema.define(version: 20150817150457) do t.string "unconfirmed_email" t.string "nickname" t.string "phone_number", limit: 30 - t.boolean "use_nickname", default: false, null: false - t.boolean "email_on_debate_comment", default: false - t.boolean "email_on_comment_reply", default: false + t.boolean "use_nickname", default: false, null: false + t.boolean "email_on_debate_comment", default: false + t.boolean "email_on_comment_reply", default: false t.string "official_position" - t.integer "official_level", default: 0 + t.integer "official_level", default: 0 end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree @@ -195,6 +211,7 @@ ActiveRecord::Schema.define(version: 20150817150457) do add_index "votes", ["voter_id", "voter_type", "vote_scope"], name: "index_votes_on_voter_id_and_voter_type_and_vote_scope", using: :btree add_foreign_key "administrators", "users" + add_foreign_key "inappropiate_flags", "users" add_foreign_key "moderators", "users" add_foreign_key "organizations", "users" end From 8944327405738b10e63ebf44755874f3b1957581 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 20 Aug 2015 16:03:00 +0200 Subject: [PATCH 02/23] adds InappropiateFlag model with relations and specs --- app/models/comment.rb | 2 + app/models/debate.rb | 1 + app/models/inappropiate_flag.rb | 41 +++++++++++++++++ app/models/user.rb | 1 + spec/models/inappropiate_flag_spec.rb | 64 +++++++++++++++++++++++++++ 5 files changed, 109 insertions(+) create mode 100644 app/models/inappropiate_flag.rb create mode 100644 spec/models/inappropiate_flag_spec.rb diff --git a/app/models/comment.rb b/app/models/comment.rb index 0141c1ffa..5cfe5a2bd 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -11,6 +11,8 @@ class Comment < ActiveRecord::Base belongs_to :commentable, polymorphic: true belongs_to :user + has_many :inappropiate_flags, :as => :flaggable + default_scope { includes(:user) } scope :recent, -> { order(id: :desc) } diff --git a/app/models/debate.rb b/app/models/debate.rb index bd7936e73..4040ba6db 100644 --- a/app/models/debate.rb +++ b/app/models/debate.rb @@ -12,6 +12,7 @@ class Debate < ActiveRecord::Base acts_as_paranoid column: :hidden_at belongs_to :author, class_name: 'User', foreign_key: 'author_id' + has_many :inappropiate_flags, :as => :flaggable validates :title, presence: true validates :description, presence: true diff --git a/app/models/inappropiate_flag.rb b/app/models/inappropiate_flag.rb new file mode 100644 index 000000000..10c7efc17 --- /dev/null +++ b/app/models/inappropiate_flag.rb @@ -0,0 +1,41 @@ +class InappropiateFlag < ActiveRecord::Base + + belongs_to :user + belongs_to :flaggable, polymorphic: true, counter_cache: true, touch: :flagged_as_inappropiate_at + + scope(:by_user_and_flaggable, lambda do |user, flaggable| + where(user_id: user.id, + flaggable_type: flaggable.class.to_s, + flaggable_id: flaggable.id) + end) + + + class AlreadyFlaggedError < StandardError + def initialize + super "The flaggable was already flagged as inappropiate by this user" + end + end + + class NotFlaggedError < StandardError + def initialize + super "The flaggable was not flagged as inappropiate by this user" + end + end + + + def self.flag!(user, flaggable) + raise AlreadyFlaggedError if flagged?(user, flaggable) + create(user: user, flaggable: flaggable) + end + + def self.unflag!(user, flaggable) + flags = by_user_and_flaggable(user, flaggable) + raise NotFlaggedError if flags.empty? + flags.destroy_all + end + + def self.flagged?(user, flaggable) + by_user_and_flaggable(user, flaggable).exists? + end + +end diff --git a/app/models/user.rb b/app/models/user.rb index b6374f518..134f315fc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,6 +8,7 @@ class User < ActiveRecord::Base has_one :administrator has_one :moderator has_one :organization + has_many :inappropiate_flags validates :first_name, presence: true, if: :use_first_name? validates :last_name, presence: true, if: :use_last_name? diff --git a/spec/models/inappropiate_flag_spec.rb b/spec/models/inappropiate_flag_spec.rb new file mode 100644 index 000000000..0ae831ee0 --- /dev/null +++ b/spec/models/inappropiate_flag_spec.rb @@ -0,0 +1,64 @@ +require 'rails_helper' + +describe InappropiateFlag do + + let(:user) { create(:user) } + let(:comment) { create(:comment) } + + describe '.flag!' do + + it 'creates a flag when there is none' do + expect { described_class.flag!(user, comment) }.to change{ InappropiateFlag.count }.by(1) + expect(InappropiateFlag.last.user).to eq(user) + expect(InappropiateFlag.last.flaggable).to eq(comment) + end + + it 'raises an error if the flag has already been created' do + described_class.flag!(user, comment) + expect { described_class.flag!(user, comment) }.to raise_error(InappropiateFlag::AlreadyFlaggedError) + end + + it 'increases the flag count' do + expect { described_class.flag!(user, comment) }.to change{ comment.reload.inappropiate_flags_count }.by(1) + end + + it 'updates the flagged_as date' do + expect { described_class.flag!(user, comment) }.to change{ comment.reload.flagged_as_inappropiate_at } + end + end + + describe '.unflag!' do + it 'raises an error if the flag does not exist' do + expect { described_class.unflag!(user, comment) }.to raise_error(InappropiateFlag::NotFlaggedError) + end + + describe 'when the flag already exists' do + before(:each) { described_class.flag!(user, comment) } + + it 'removes an existing flag' do + expect { described_class.unflag!(user, comment) }.to change{ InappropiateFlag.count }.by(-1) + end + + it 'decreases the flag count' do + expect { described_class.unflag!(user, comment) }.to change{ comment.reload.inappropiate_flags_count }.by(-1) + end + + it 'does not update the flagged_as date' do + expect { described_class.unflag!(user, comment) }.to_not change{ comment.flagged_as_inappropiate_at } + end + end + + end + + describe '.flagged?' do + it 'returns false when the user has not flagged the comment' do + expect(described_class.flagged?(user, comment)).to_not be + end + + it 'returns true when the user has flagged the comment' do + described_class.flag!(user, comment) + expect(described_class.flagged?(user, comment)).to be + end + end + +end From 246145e4cbc5f8301a01ffbd2b3116ed968b7f71 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 20 Aug 2015 18:45:32 +0200 Subject: [PATCH 03/23] adds abilities for regular users --- app/models/ability.rb | 16 ++++++++++++++++ app/models/comment.rb | 4 ++++ spec/models/ability_spec.rb | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/app/models/ability.rb b/app/models/ability.rb index 9cce129a2..7c63c032f 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -16,6 +16,22 @@ class Ability can :create, Comment can :create, Debate + can :flag_as_inappropiate, Comment do |comment| + comment.author != user && !InappropiateFlag.flagged?(user, comment) + end + + can :undo_flag_as_inappropiate, Comment do |comment| + comment.author != user && InappropiateFlag.flagged?(user, comment) + end + + can :flag_as_inappropiate, Debate do |debate| + debate.author != user && !InappropiateFlag.flagged?(user, debate) + end + + can :undo_flag_as_inappropiate, Debate do |debate| + debate.author != user && InappropiateFlag.flagged?(user, debate) + end + unless user.organization? can :vote, Debate can :vote, Comment diff --git a/app/models/comment.rb b/app/models/comment.rb index 5cfe5a2bd..fd838d192 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -34,6 +34,10 @@ class Comment < ActiveRecord::Base user end + def author=(author) + self.user= author + end + def total_votes votes_for.size end diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 20a3456d4..a044c244e 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -22,12 +22,48 @@ describe Ability do it { should be_able_to(:show, debate) } it { should be_able_to(:vote, debate) } + it { should be_able_to(:show, user) } it { should be_able_to(:edit, user) } it { should be_able_to(:create, Comment) } it { should be_able_to(:vote, Comment) } + describe 'flagging content as inappropiate' do + it { should be_able_to(:flag_as_inappropiate, debate) } + it { should_not be_able_to(:undo_flag_as_inappropiate, debate) } + it { should be_able_to(:flag_as_inappropiate, comment) } + it { should_not be_able_to(:undo_flag_as_inappropiate, comment) } + + describe "own comments" do + let(:own_comment) { create(:comment, author: user) } + + it { should_not be_able_to(:flag_as_inappropiate, own_comment) } + it { should_not be_able_to(:undo_flag_as_inappropiate, own_comment) } + end + + describe "own debates" do + let(:own_debate) { create(:debate, author: user) } + + it { should_not be_able_to(:flag_as_inappropiate, own_debate) } + it { should_not be_able_to(:undo_flag_as_inappropiate, own_debate) } + end + + describe "already-flagged comments" do + before(:each) { InappropiateFlag.flag!(user, comment) } + + it { should_not be_able_to(:flag_as_inappropiate, comment) } + it { should be_able_to(:undo_flag_as_inappropiate, comment) } + end + + describe "already-flagged debates" do + before(:each) { InappropiateFlag.flag!(user, debate) } + + it { should_not be_able_to(:flag_as_inappropiate, debate) } + it { should be_able_to(:undo_flag_as_inappropiate, debate) } + end + end + describe "other users" do let(:other_user) { create(:user) } it { should_not be_able_to(:show, other_user) } From e5f893c8d18903723c48393dbf0e861bd6c8a499 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 20 Aug 2015 18:45:56 +0200 Subject: [PATCH 04/23] Adds i18n texts for flagging content --- config/locales/en.yml | 2 ++ config/locales/es.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/config/locales/en.yml b/config/locales/en.yml index 79181cd13..c462ad5b6 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -138,6 +138,8 @@ en: tags_cloud: tags: Topics organization: Organization + flag_as_inappropiate: Flag as inappropiate + undo_flag_as_inappropiate: Undo flag as inappropiate mailer: comment: subject: Someone has commented on your debate diff --git a/config/locales/es.yml b/config/locales/es.yml index 856560e46..aeed2e080 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -138,6 +138,8 @@ es: tags_cloud: tags: Temas organization: Organización + flag_as_inappropiate: Denunciar como inapropiado + undo_flag_as_inappropiate: Deshacer denunciar como inapropiado mailer: comment: subject: Alguien ha comentado en tu debate From e7effbf77ce0cce4865fe9217f1526e3c2803fb7 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 20 Aug 2015 18:47:16 +0200 Subject: [PATCH 05/23] Allows flagging comments as inappropriate --- app/controllers/comments_controller.rb | 10 ++++++ app/views/comments/_comment.html.erb | 5 +++ .../_flag_as_inappropiate_actions.html.erb | 6 ++++ .../comments/flag_as_inappropiate.js.erb | 1 + .../comments/undo_flag_as_inappropiate.js.erb | 1 + config/routes.rb | 12 +++++-- spec/features/comments_spec.rb | 35 +++++++++++++++++++ 7 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 app/views/comments/_flag_as_inappropiate_actions.html.erb create mode 100644 app/views/comments/flag_as_inappropiate.js.erb create mode 100644 app/views/comments/undo_flag_as_inappropiate.js.erb diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index a10d80da9..e1d94f9b0 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -22,6 +22,16 @@ class CommentsController < ApplicationController respond_with @comment end + def flag_as_inappropiate + InappropiateFlag.flag!(current_user, @comment) + respond_with @comment + end + + def undo_flag_as_inappropiate + InappropiateFlag.unflag!(current_user, @comment) + respond_with @comment + end + private def comment_params diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index d0317d52f..3d5b470d5 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -37,6 +37,11 @@ <% end %>  • <%= time_ago_in_words(comment.created_at) %> + + + <%= render 'comments/flag_as_inappropiate_actions', comment: comment %> + + <% if comment.user.official? %> diff --git a/app/views/comments/_flag_as_inappropiate_actions.html.erb b/app/views/comments/_flag_as_inappropiate_actions.html.erb new file mode 100644 index 000000000..fad83d4d6 --- /dev/null +++ b/app/views/comments/_flag_as_inappropiate_actions.html.erb @@ -0,0 +1,6 @@ +<% if can? :flag_as_inappropiate, comment %> + <%= link_to t('shared.flag_as_inappropiate'), flag_as_inappropiate_comment_path(comment), method: :put, remote: true %> +<% end %> +<% if can? :undo_flag_as_inappropiate, comment %> + <%= link_to t('shared.undo_flag_as_inappropiate'), undo_flag_as_inappropiate_comment_path(comment), method: :put, remote: true %> +<% end %> diff --git a/app/views/comments/flag_as_inappropiate.js.erb b/app/views/comments/flag_as_inappropiate.js.erb new file mode 100644 index 000000000..58f356f2b --- /dev/null +++ b/app/views/comments/flag_as_inappropiate.js.erb @@ -0,0 +1 @@ +$("#<%= dom_id(@comment) %> .js-flag-as-inappropiate-actions").html('<%= j render("comments/flag_as_inappropiate_actions", comment: @comment) %>'); diff --git a/app/views/comments/undo_flag_as_inappropiate.js.erb b/app/views/comments/undo_flag_as_inappropiate.js.erb new file mode 100644 index 000000000..58f356f2b --- /dev/null +++ b/app/views/comments/undo_flag_as_inappropiate.js.erb @@ -0,0 +1 @@ +$("#<%= dom_id(@comment) %> .js-flag-as-inappropiate-actions").html('<%= j render("comments/flag_as_inappropiate_actions", comment: @comment) %>'); diff --git a/config/routes.rb b/config/routes.rb index 66b74546b..ebeb85df1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,10 +13,18 @@ Rails.application.routes.draw do root 'welcome#index' resources :debates do - member { post :vote } + member do + post :vote + put :flag_as_inappropiate + put :undo_flag_as_inappropiate + end resources :comments, only: :create, shallow: true do - member { post :vote } + member do + post :vote + put :flag_as_inappropiate + put :undo_flag_as_inappropiate + end end end diff --git a/spec/features/comments_spec.rb b/spec/features/comments_spec.rb index b820fffcd..50083e236 100644 --- a/spec/features/comments_spec.rb +++ b/spec/features/comments_spec.rb @@ -133,4 +133,39 @@ feature 'Comments' do expect(page).to have_css(".comment.comment.comment.comment.comment.comment.comment.comment") end + scenario "Flagging as inappropiate", :js do + user = create(:user) + debate = create(:debate) + comment = create(:comment, commentable: debate) + + login_as(user) + visit debate_path(debate) + + within "#comment_#{comment.id}" do + expect(page).to_not have_link "Undo flag as inappropiate" + click_on 'Flag as inappropiate' + expect(page).to have_link "Undo flag as inappropiate" + end + + expect(InappropiateFlag.flagged?(user, comment)).to be + end + + scenario "Undoing flagging as inappropiate", :js do + user = create(:user) + debate = create(:debate) + comment = create(:comment, commentable: debate) + InappropiateFlag.flag!(user, comment) + + login_as(user) + visit debate_path(debate) + + within "#comment_#{comment.id}" do + expect(page).to_not have_link("Flag as inappropiate", exact: true) + click_on 'Undo flag as inappropiate' + expect(page).to have_link("Flag as inappropiate", exact: true) + end + + expect(InappropiateFlag.flagged?(user, comment)).to_not be + end + end From 12e8cf2056f89ae976fba06a57852cfb327b6d89 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 20 Aug 2015 19:26:36 +0200 Subject: [PATCH 06/23] Allows flagging debates as inappropriate --- app/controllers/debates_controller.rb | 10 ++++++ .../_flag_as_inappropiate_actions.html.erb | 6 ++++ ...efresh_flag_as_inappropiate_actions.js.erb | 1 + app/views/debates/show.html.erb | 4 +++ spec/features/debates_spec.rb | 33 +++++++++++++++++++ 5 files changed, 54 insertions(+) create mode 100644 app/views/debates/_flag_as_inappropiate_actions.html.erb create mode 100644 app/views/debates/_refresh_flag_as_inappropiate_actions.js.erb diff --git a/app/controllers/debates_controller.rb b/app/controllers/debates_controller.rb index c30f02233..7de7cad0e 100644 --- a/app/controllers/debates_controller.rb +++ b/app/controllers/debates_controller.rb @@ -51,6 +51,16 @@ class DebatesController < ApplicationController set_debate_votes(@debate) end + def flag_as_inappropiate + InappropiateFlag.flag!(current_user, @debate) + respond_with @debate, template: 'debates/_refresh_flag_as_inappropiate_actions' + end + + def undo_flag_as_inappropiate + InappropiateFlag.unflag!(current_user, @debate) + respond_with @debate, template: 'debates/_refresh_flag_as_inappropiate_actions' + end + private def debate_params diff --git a/app/views/debates/_flag_as_inappropiate_actions.html.erb b/app/views/debates/_flag_as_inappropiate_actions.html.erb new file mode 100644 index 000000000..8003600e0 --- /dev/null +++ b/app/views/debates/_flag_as_inappropiate_actions.html.erb @@ -0,0 +1,6 @@ +<% if can? :flag_as_inappropiate, debate %> + <%= link_to t('shared.flag_as_inappropiate'), flag_as_inappropiate_debate_path(debate), method: :put, remote: true %> +<% end %> +<% if can? :undo_flag_as_inappropiate, debate %> + <%= link_to t('shared.undo_flag_as_inappropiate'), undo_flag_as_inappropiate_debate_path(debate), method: :put, remote: true %> +<% end %> diff --git a/app/views/debates/_refresh_flag_as_inappropiate_actions.js.erb b/app/views/debates/_refresh_flag_as_inappropiate_actions.js.erb new file mode 100644 index 000000000..7be008e35 --- /dev/null +++ b/app/views/debates/_refresh_flag_as_inappropiate_actions.js.erb @@ -0,0 +1 @@ +$("#<%= dom_id(@debate) %> .js-flag-as-inappropiate-actions").html('<%= j render("debates/flag_as_inappropiate_actions", debate: @debate) %>'); diff --git a/app/views/debates/show.html.erb b/app/views/debates/show.html.erb index 03c7e2c8c..71a8aa59d 100644 --- a/app/views/debates/show.html.erb +++ b/app/views/debates/show.html.erb @@ -41,6 +41,10 @@  •    <%= link_to t("debates.show.comments", count: @debate.comment_threads.count), "#comments" %> + + + <%= render 'debates/flag_as_inappropiate_actions', debate: @debate %> + <%= @debate.description %> diff --git a/spec/features/debates_spec.rb b/spec/features/debates_spec.rb index eade338f5..c685725d8 100644 --- a/spec/features/debates_spec.rb +++ b/spec/features/debates_spec.rb @@ -315,4 +315,37 @@ feature 'Debates' do end end + scenario "Flagging as inappropiate", :js do + user = create(:user) + debate = create(:debate) + + login_as(user) + visit debate_path(debate) + + within "#debate_#{debate.id}" do + expect(page).to_not have_link "Undo flag as inappropiate" + click_on 'Flag as inappropiate' + expect(page).to have_link "Undo flag as inappropiate" + end + + expect(InappropiateFlag.flagged?(user, debate)).to be + end + + scenario "Undoing flagging as inappropiate", :js do + user = create(:user) + debate = create(:debate) + InappropiateFlag.flag!(user, debate) + + login_as(user) + visit debate_path(debate) + + within "#debate_#{debate.id}" do + expect(page).to_not have_link("Flag as inappropiate", exact: true) + click_on 'Undo flag as inappropiate' + expect(page).to have_link("Flag as inappropiate", exact: true) + end + + expect(InappropiateFlag.flagged?(user, debate)).to_not be + end + end From 01eb89f5f4c10a81188ab8688fff41100c5f8cb8 Mon Sep 17 00:00:00 2001 From: kikito Date: Thu, 20 Aug 2015 22:54:05 +0200 Subject: [PATCH 07/23] DRYes comment views related with flagging as inappropiate --- app/controllers/comments_controller.rb | 4 ++-- ...te.js.erb => _refresh_flag_as_inappropiate_actions.js.erb} | 0 app/views/comments/undo_flag_as_inappropiate.js.erb | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) rename app/views/comments/{flag_as_inappropiate.js.erb => _refresh_flag_as_inappropiate_actions.js.erb} (100%) delete mode 100644 app/views/comments/undo_flag_as_inappropiate.js.erb diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index e1d94f9b0..20df3bf2d 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -24,12 +24,12 @@ class CommentsController < ApplicationController def flag_as_inappropiate InappropiateFlag.flag!(current_user, @comment) - respond_with @comment + respond_with @comment, template: 'comments/_refresh_flag_as_inappropiate_actions' end def undo_flag_as_inappropiate InappropiateFlag.unflag!(current_user, @comment) - respond_with @comment + respond_with @comment, template: 'comments/_refresh_flag_as_inappropiate_actions' end private diff --git a/app/views/comments/flag_as_inappropiate.js.erb b/app/views/comments/_refresh_flag_as_inappropiate_actions.js.erb similarity index 100% rename from app/views/comments/flag_as_inappropiate.js.erb rename to app/views/comments/_refresh_flag_as_inappropiate_actions.js.erb diff --git a/app/views/comments/undo_flag_as_inappropiate.js.erb b/app/views/comments/undo_flag_as_inappropiate.js.erb deleted file mode 100644 index 58f356f2b..000000000 --- a/app/views/comments/undo_flag_as_inappropiate.js.erb +++ /dev/null @@ -1 +0,0 @@ -$("#<%= dom_id(@comment) %> .js-flag-as-inappropiate-actions").html('<%= j render("comments/flag_as_inappropiate_actions", comment: @comment) %>'); From 5328355c3e5a4b0c09972d2b3fd04d4511ec0ae0 Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 21 Aug 2015 19:09:26 +0200 Subject: [PATCH 08/23] Add tests for visible admin links and fix bug --- app/views/shared/_admin_login_items.html.erb | 4 ++-- spec/features/moderation_spec.rb | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/views/shared/_admin_login_items.html.erb b/app/views/shared/_admin_login_items.html.erb index e268def4b..1d22679c5 100644 --- a/app/views/shared/_admin_login_items.html.erb +++ b/app/views/shared/_admin_login_items.html.erb @@ -6,10 +6,10 @@ <% end %> - <% if current_user.moderator? %> + <% if current_user.moderator? || current_user.administrator? %>
  • <%= link_to t("layouts.header.moderation"), moderation_root_path %>
  • <% end %> <% end %> - \ No newline at end of file + diff --git a/spec/features/moderation_spec.rb b/spec/features/moderation_spec.rb index cd3052f70..78df5e56f 100644 --- a/spec/features/moderation_spec.rb +++ b/spec/features/moderation_spec.rb @@ -5,6 +5,9 @@ feature 'Admin' do scenario 'Access as regular user is not authorized' do login_as(user) + visit root_path + + expect(page).to_not have_link("Moderation") visit moderation_root_path expect(current_path).to eq(root_path) @@ -15,7 +18,10 @@ feature 'Admin' do create(:moderator, user: user) login_as(user) - visit moderation_root_path + visit root_path + + expect(page).to have_link("Moderation") + click_on "Moderation" expect(current_path).to eq(moderation_root_path) expect(page).to_not have_content "not authorized" @@ -25,7 +31,10 @@ feature 'Admin' do create(:administrator, user: user) login_as(user) - visit moderation_root_path + visit root_path + + expect(page).to have_link("Moderation") + click_on "Moderation" expect(current_path).to eq(moderation_root_path) expect(page).to_not have_content "not authorized" From 007ca4afe08dc6182386de600354d1d21e373776 Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 21 Aug 2015 20:34:56 +0200 Subject: [PATCH 09/23] Adds reviewed_at to comments and debates tables --- db/migrate/20150821180131_add_reviewed_at_to_comments.rb | 5 +++++ db/migrate/20150821180155_add_reviewed_at_to_debates.rb | 5 +++++ db/schema.rb | 6 ++++-- 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20150821180131_add_reviewed_at_to_comments.rb create mode 100644 db/migrate/20150821180155_add_reviewed_at_to_debates.rb diff --git a/db/migrate/20150821180131_add_reviewed_at_to_comments.rb b/db/migrate/20150821180131_add_reviewed_at_to_comments.rb new file mode 100644 index 000000000..b854002ac --- /dev/null +++ b/db/migrate/20150821180131_add_reviewed_at_to_comments.rb @@ -0,0 +1,5 @@ +class AddReviewedAtToComments < ActiveRecord::Migration + def change + add_column :comments, :reviewed_at, :datetime + end +end diff --git a/db/migrate/20150821180155_add_reviewed_at_to_debates.rb b/db/migrate/20150821180155_add_reviewed_at_to_debates.rb new file mode 100644 index 000000000..c02ed3e3b --- /dev/null +++ b/db/migrate/20150821180155_add_reviewed_at_to_debates.rb @@ -0,0 +1,5 @@ +class AddReviewedAtToDebates < ActiveRecord::Migration + def change + add_column :debates, :reviewed_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index 1619b85cd..fa65f765c 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: 20150820104552) do +ActiveRecord::Schema.define(version: 20150821180155) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -51,6 +51,7 @@ ActiveRecord::Schema.define(version: 20150820104552) do t.datetime "hidden_at" t.datetime "flagged_as_inappropiate_at" t.integer "inappropiate_flags_count", default: 0 + t.datetime "reviewed_at" end add_index "comments", ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type", using: :btree @@ -67,6 +68,7 @@ ActiveRecord::Schema.define(version: 20150820104552) do t.string "visit_id" t.datetime "flagged_as_inappropiate_at" t.integer "inappropiate_flags_count", default: 0 + t.datetime "reviewed_at" end add_index "debates", ["hidden_at"], name: "index_debates_on_hidden_at", using: :btree @@ -153,12 +155,12 @@ ActiveRecord::Schema.define(version: 20150820104552) do t.datetime "confirmation_sent_at" t.string "unconfirmed_email" t.string "nickname" + t.string "phone_number", limit: 30 t.boolean "use_nickname", default: false, null: false t.boolean "email_on_debate_comment", default: false t.boolean "email_on_comment_reply", default: false t.string "official_position" t.integer "official_level", default: 0 - t.string "phone_number", limit: 30 t.datetime "hidden_at" end From 0fe7740a41866649b5d0f28fbb3a0cd377e1e046 Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 21 Aug 2015 20:36:04 +0200 Subject: [PATCH 10/23] Refactors ability making flagging more efficient --- app/models/ability.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 1c849ae42..6093a373b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -17,19 +17,19 @@ class Ability can :create, Debate can :flag_as_inappropiate, Comment do |comment| - comment.author != user && !InappropiateFlag.flagged?(user, comment) + comment.author_id != user.id && !InappropiateFlag.flagged?(user, comment) end can :undo_flag_as_inappropiate, Comment do |comment| - comment.author != user && InappropiateFlag.flagged?(user, comment) + comment.author_id != user.id && InappropiateFlag.flagged?(user, comment) end can :flag_as_inappropiate, Debate do |debate| - debate.author != user && !InappropiateFlag.flagged?(user, debate) + debate.author_id != user.id && !InappropiateFlag.flagged?(user, debate) end can :undo_flag_as_inappropiate, Debate do |debate| - debate.author != user && InappropiateFlag.flagged?(user, debate) + debate.author_id != user.id && InappropiateFlag.flagged?(user, debate) end unless user.organization? From 5e32fd2fa404a55d9a33fea665d6738674d590bb Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 21 Aug 2015 20:39:40 +0200 Subject: [PATCH 11/23] Adds new abilities for moderators extends hiding (i.e. you can not hide your own content or yourself) and adds marking as reviewed --- app/models/ability.rb | 23 +++++++++++++++++++-- spec/models/ability_spec.rb | 41 +++++++++++++++++++++++++++++++------ 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 6093a373b..9032038a1 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,9 +2,15 @@ class Ability include CanCan::Ability def initialize(user) + + # If someone can hide something, he can also hide it + # from the moderation screen + alias_action :hide_in_moderation_screen, to: :hide + # Not logged in users can :read, Debate + if user # logged-in users can [:read, :update], User, id: user.id @@ -42,9 +48,22 @@ class Ability can(:verify, Organization){ |o| !o.verified? } can(:reject, Organization){ |o| !o.rejected? } - can :hide, Comment - can :hide, Debate + can :read, Comment + + can :hide, Comment, hidden_at: nil + cannot :hide, Comment, user_id: user.id + + can :mark_as_reviewed, Comment, reviewed_at: nil, hidden_at: nil + cannot :mark_as_reviewed, Comment, user_id: user.id + + can :hide, Debate, hidden_at: nil + cannot :hide, Debate, author_id: user.id + + can :mark_as_reviewed, Debate, reviewed_at: nil, hidden_at: nil + cannot :mark_as_reviewed, Debate, author_id: user.id + can :hide, User + cannot :hide, User, id: user.id end if user.administrator? diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 337b52507..cc458857c 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -102,6 +102,7 @@ describe Ability do before { create(:moderator, user: user) } let(:other_user) { create(:user) } + it { should be_able_to(:index, Debate) } it { should be_able_to(:show, debate) } it { should be_able_to(:vote, debate) } @@ -123,13 +124,41 @@ describe Ability do it { should be_able_to( :verify, rejected_organization) } end - it { should be_able_to(:hide, comment) } - it { should be_able_to(:hide, debate) } - it { should be_able_to(:hide, other_user) } + describe "hiding, reviewing and restoring" do + let(:own_comment) { create(:comment, author: user) } + let(:own_debate) { create(:debate, author: user) } + let(:hidden_comment) { create(:comment, hidden_at: Time.now) } + let(:hidden_debate) { create(:debate, hidden_at: Time.now) } + let(:reviewed_comment) { create(:comment, reviewed_at: Time.now) } + let(:reviewed_debate) { create(:debate, reviewed_at: Time.now) } - it { should_not be_able_to(:restore, comment) } - it { should_not be_able_to(:restore, debate) } - it { should_not be_able_to(:restore, other_user) } + it { should be_able_to(:hide, comment) } + it { should be_able_to(:hide_in_moderation_screen, comment) } + it { should_not be_able_to(:hide, hidden_comment) } + it { should_not be_able_to(:hide, own_comment) } + + it { should be_able_to(:hide, debate) } + it { should be_able_to(:hide_in_moderation_screen, debate) } + it { should_not be_able_to(:hide, hidden_debate) } + it { should_not be_able_to(:hide, own_debate) } + + it { should be_able_to(:mark_as_reviewed, comment) } + it { should_not be_able_to(:mark_as_reviewed, hidden_comment) } + it { should_not be_able_to(:mark_as_reviewed, reviewed_comment) } + it { should_not be_able_to(:mark_as_reviewed, own_comment) } + + it { should be_able_to(:mark_as_reviewed, debate) } + it { should_not be_able_to(:mark_as_reviewed, hidden_debate) } + it { should_not be_able_to(:mark_as_reviewed, reviewed_debate) } + it { should_not be_able_to(:mark_as_reviewed, own_debate) } + + it { should_not be_able_to(:hide, user) } + it { should be_able_to(:hide, other_user) } + + it { should_not be_able_to(:restore, comment) } + it { should_not be_able_to(:restore, debate) } + it { should_not be_able_to(:restore, other_user) } + end end describe "Administrator" do From b4bd70436a64449968ef33c302bce7cb6903aa0f Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 21 Aug 2015 21:57:00 +0200 Subject: [PATCH 12/23] Adds routes for new methods in comment & debate moderation --- config/routes.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index a6cd35bf7..f224609b9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -71,12 +71,20 @@ Rails.application.routes.draw do member { put :hide } end - resources :debates, only: [] do - member { put :hide } + resources :debates, only: :index do + member do + put :hide + put :hide_in_moderation_screen + put :mark_as_reviewed + end end - resources :comments, only: [] do - member { put :hide } + resources :comments, only: :index do + member do + put :hide + put :hide_in_moderation_screen + put :mark_as_reviewed + end end end From 2c49d309527f2bbeef7a29ba2900fdc23a5fd282 Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 21 Aug 2015 21:58:48 +0200 Subject: [PATCH 13/23] Adds review-related methods to comment & debate --- app/models/comment.rb | 8 ++++++++ app/models/debate.rb | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/app/models/comment.rb b/app/models/comment.rb index dfa2cc0d1..7f326168d 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -46,6 +46,14 @@ class Comment < ActiveRecord::Base hidden? || user.hidden? end + def reviewed? + reviewed_at.present? + end + + def mark_as_reviewed + update(reviewed_at: Time.now) + end + # TODO: faking counter cache since there is a bug with acts_as_nested_set :counter_cache # Remove when https://github.com/collectiveidea/awesome_nested_set/issues/294 is fixed # and reset counters using diff --git a/app/models/debate.rb b/app/models/debate.rb index d708e1a35..042c334c7 100644 --- a/app/models/debate.rb +++ b/app/models/debate.rb @@ -69,6 +69,14 @@ class Debate < ActiveRecord::Base count < 0 ? 0 : count end + def reviewed? + reviewed_at.present? + end + + def mark_as_reviewed + update(reviewed_at: Time.now) + end + protected def sanitize_description From b7718a2e5dce380941ba0a9f42543a6b845bd30f Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 21 Aug 2015 21:59:11 +0200 Subject: [PATCH 14/23] Adds i18n for comment moderation view --- config/locales/moderation.en.yml | 10 ++++++++++ config/locales/moderation.es.yml | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/config/locales/moderation.en.yml b/config/locales/moderation.en.yml index cf53051ed..e9eb8bbfa 100644 --- a/config/locales/moderation.en.yml +++ b/config/locales/moderation.en.yml @@ -3,4 +3,14 @@ en: dashboard: index: title: Moderation + comments: + index: + title: Comments flagged as inappropiate + debate: Debate + comment: Comment + updated_at: Date + flags: Flags + hide: Hide + mark_as_reviewed: Mark as reviewed + reviewed: Reviewed diff --git a/config/locales/moderation.es.yml b/config/locales/moderation.es.yml index a921f0827..6e3d6eb68 100644 --- a/config/locales/moderation.es.yml +++ b/config/locales/moderation.es.yml @@ -3,5 +3,14 @@ es: dashboard: index: title: Moderación - + comments: + index: + title: Comentarios Denunciados como Inapropiados + debate: Debate + comment: Comentario + updated_at: Fecha + flags: Denuncias + hide: Ocultar + mark_as_reviewed: Marcar como revisado + reviewed: Revisado From 00b51bbc238b9631a624e5330b91228183a5bd90 Mon Sep 17 00:00:00 2001 From: kikito Date: Fri, 21 Aug 2015 22:01:47 +0200 Subject: [PATCH 15/23] Adds first version of comment moderation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Missing: * Ensure removing a flag updates the flagged_at attribute correctly * Showing the debate when the comment is not a “root” * Filter (pending / reviewed / all) * Order * Same for debates --- .../moderation/comments_controller.rb | 27 +++- app/views/moderation/comments/index.html.erb | 36 +++++ spec/features/moderation/comments_spec.rb | 131 ++++++++++++------ 3 files changed, 153 insertions(+), 41 deletions(-) create mode 100644 app/views/moderation/comments/index.html.erb diff --git a/app/controllers/moderation/comments_controller.rb b/app/controllers/moderation/comments_controller.rb index c250fc460..781f6b1e7 100644 --- a/app/controllers/moderation/comments_controller.rb +++ b/app/controllers/moderation/comments_controller.rb @@ -1,8 +1,31 @@ class Moderation::CommentsController < Moderation::BaseController + before_filter :load_comments, only: :index + + load_and_authorize_resource + + def index + @comments = @comments.page(params[:page]) + end + def hide - @comment = Comment.find(params[:id]) @comment.hide end -end \ No newline at end of file + def hide_in_moderation_screen + @comment.hide + redirect_to action: :index + end + + def mark_as_reviewed + @comment.mark_as_reviewed + redirect_to action: :index + end + + private + + def load_comments + @comments = Comment.accessible_by(current_ability, :hide).where('inappropiate_flags_count > 0').includes(:commentable) + end + +end diff --git a/app/views/moderation/comments/index.html.erb b/app/views/moderation/comments/index.html.erb new file mode 100644 index 000000000..f89e58cc1 --- /dev/null +++ b/app/views/moderation/comments/index.html.erb @@ -0,0 +1,36 @@ +

    <%= t('moderation.comments.index.title') %>

    + +

    <%= page_entries_info @comments %>

    + + + + + + + + + <% @comments.each do |comment| %> + + + + + + + <% if can? :mark_as_reviewed, comment %> + + <% end %> + <% if comment.reviewed? %> + + <% end %> + + <% end %> +
    <%= t('moderation.comments.index.flags') %><%= t('moderation.comments.index.debate') %><%= t('moderation.comments.index.comment') %><%= t('moderation.comments.index.updated_at') %>
    <%= comment.inappropiate_flags_count %><%= link_to(comment.debate.title, comment.debate) %><%= comment.body %><%= l comment.updated_at.to_date %> + <%= link_to t('moderation.comments.index.hide'), hide_in_moderation_screen_moderation_comment_path(comment), method: :put %> + + <%= link_to t('moderation.comments.index.mark_as_reviewed'), mark_as_reviewed_moderation_comment_path(comment), method: :put %> + + <%= t('moderation.comments.index.reviewed') %> +
    + +<%= paginate @comments %> + diff --git a/spec/features/moderation/comments_spec.rb b/spec/features/moderation/comments_spec.rb index 099946d82..ef3970b20 100644 --- a/spec/features/moderation/comments_spec.rb +++ b/spec/features/moderation/comments_spec.rb @@ -2,61 +2,64 @@ require 'rails_helper' feature 'Moderate Comments' do - scenario 'Hide', :js do - citizen = create(:user) - moderator = create(:moderator) + feature 'Hiding Comments' do - debate = create(:debate) - comment = create(:comment, commentable: debate, body: 'SPAM') + scenario 'Hide', :js do + citizen = create(:user) + moderator = create(:moderator) - login_as(moderator.user) - visit debate_path(debate) + debate = create(:debate) + comment = create(:comment, commentable: debate, body: 'SPAM') - within("#comment_#{comment.id}") do - click_link 'Hide' - expect(page).to have_css('.comment .faded') + login_as(moderator.user) + visit debate_path(debate) + + within("#comment_#{comment.id}") do + click_link 'Hide' + expect(page).to have_css('.comment .faded') + end + + login_as(citizen) + visit debate_path(debate) + + expect(page).to have_css('.comment', count: 1) + expect(page).to have_content('This comment has been deleted') + expect(page).to_not have_content('SPAM') end - login_as(citizen) - visit debate_path(debate) + scenario 'Children visible', :js do + citizen = create(:user) + moderator = create(:moderator) - expect(page).to have_css('.comment', count: 1) - expect(page).to have_content('This comment has been deleted') - expect(page).to_not have_content('SPAM') - end + debate = create(:debate) + comment = create(:comment, commentable: debate, body: 'SPAM') + create(:comment, commentable: debate, body: 'Acceptable reply', parent_id: comment.id) - scenario 'Children visible', :js do - citizen = create(:user) - moderator = create(:moderator) + login_as(moderator.user) + visit debate_path(debate) - debate = create(:debate) - comment = create(:comment, commentable: debate, body: 'SPAM') - reply = create(:comment, commentable: debate, body: 'Acceptable reply', parent_id: comment.id) + within("#comment_#{comment.id}") do + first(:link, "Hide").click + expect(page).to have_css('.comment .faded') + end - login_as(moderator.user) - visit debate_path(debate) + login_as(citizen) + visit debate_path(debate) - within("#comment_#{comment.id}") do - first(:link, "Hide").click - expect(page).to have_css('.comment .faded') + expect(page).to have_css('.comment', count: 2) + expect(page).to have_content('This comment has been deleted') + expect(page).to_not have_content('SPAM') + + expect(page).to have_content('Acceptable reply') end - - login_as(citizen) - visit debate_path(debate) - - expect(page).to have_css('.comment', count: 2) - expect(page).to have_content('This comment has been deleted') - expect(page).to_not have_content('SPAM') - - expect(page).to have_content('Acceptable reply') end - scenario 'Moderator actions' do + scenario 'Moderator actions in the comment' do citizen = create(:user) moderator = create(:moderator) debate = create(:debate) - comment = create(:comment, commentable: debate) + create(:comment, commentable: debate) login_as(moderator.user) visit debate_path(debate) @@ -69,4 +72,54 @@ feature 'Moderate Comments' do expect(page).to_not have_css("#moderator-comment-actions") end -end \ No newline at end of file + feature '/moderation/ menu' do + + background do + moderator = create(:moderator) + login_as(moderator.user) + + user = create(:user) + debate = create(:debate, title: 'Democracy') + @comment = create(:comment, commentable: debate, body: 'spammy spam') + InappropiateFlag.flag!(user, @comment) + + visit moderation_comments_path + end + + scenario 'Flagged comment shows the right params' do + within("#comment_#{@comment.id}") do + expect(page).to have_link('Democracy') + expect(page).to have_content('spammy spam') + expect(page).to have_content('1') + expect(page).to have_link('Hide') + expect(page).to have_content('Mark as reviewed') + end + end + + scenario 'Hiding a comment' do + within("#comment_#{@comment.id}") do + click_link('Hide') + end + + expect(current_path).to eq(moderation_comments_path) + expect(page).to_not have_selector("#comment_#{@comment.id}") + + expect(@comment.reload).to be_hidden + end + + scenario 'Marking a comment as reviewed' do + within("#comment_#{@comment.id}") do + click_link('Mark as reviewed') + end + + expect(current_path).to eq(moderation_comments_path) + + within("#comment_#{@comment.id}") do + expect(page).to have_content('Reviewed') + end + + expect(@comment.reload).to be_reviewed + end + end + +end From 4f0c49f921375a1a2d4c9a2596db7e8b4df65ebc Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 22 Aug 2015 09:15:20 +0200 Subject: [PATCH 16/23] Changes moderation/comments to allow for different commentable_types --- app/models/comment.rb | 4 ++++ app/views/moderation/comments/index.html.erb | 10 ++++++---- config/locales/moderation.en.yml | 3 ++- config/locales/moderation.es.yml | 3 ++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/models/comment.rb b/app/models/comment.rb index 7f326168d..5056414b2 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -30,6 +30,10 @@ class Comment < ActiveRecord::Base commentable if commentable.class == Debate end + def author_id + user_id + end + def author user end diff --git a/app/views/moderation/comments/index.html.erb b/app/views/moderation/comments/index.html.erb index f89e58cc1..1d3a0e2f3 100644 --- a/app/views/moderation/comments/index.html.erb +++ b/app/views/moderation/comments/index.html.erb @@ -5,16 +5,18 @@ - - + + + <% @comments.each do |comment| %> - - + + + diff --git a/config/locales/moderation.en.yml b/config/locales/moderation.en.yml index e9eb8bbfa..192da6fe3 100644 --- a/config/locales/moderation.en.yml +++ b/config/locales/moderation.en.yml @@ -6,7 +6,8 @@ en: comments: index: title: Comments flagged as inappropiate - debate: Debate + commentable_type: Type + commentable: Root comment: Comment updated_at: Date flags: Flags diff --git a/config/locales/moderation.es.yml b/config/locales/moderation.es.yml index 6e3d6eb68..421b50a78 100644 --- a/config/locales/moderation.es.yml +++ b/config/locales/moderation.es.yml @@ -6,7 +6,8 @@ es: comments: index: title: Comentarios Denunciados como Inapropiados - debate: Debate + commentable_type: Tipo + commentable: Raíz comment: Comentario updated_at: Fecha flags: Denuncias From 97857490576d55013c9ff47ef5641929e5caa4db Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 22 Aug 2015 19:46:28 +0200 Subject: [PATCH 17/23] Refactors organization factories & admin controller --- app/controllers/admin/organizations_controller.rb | 4 ++-- spec/factories.rb | 13 +++++++------ spec/features/admin/organizations_spec.rb | 11 ++++++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/app/controllers/admin/organizations_controller.rb b/app/controllers/admin/organizations_controller.rb index 19003ad3a..de99bbc44 100644 --- a/app/controllers/admin/organizations_controller.rb +++ b/app/controllers/admin/organizations_controller.rb @@ -1,6 +1,6 @@ class Admin::OrganizationsController < Admin::BaseController - before_filter :set_valid_filters - before_filter :parse_filter + before_filter :set_valid_filters, only: :index + before_filter :parse_filter, only: :index load_and_authorize_resource diff --git a/spec/factories.rb b/spec/factories.rb index a6090efbb..de12209e0 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -46,14 +46,14 @@ FactoryGirl.define do factory :organization do user sequence(:name) { |n| "org#{n}" } - end - factory :verified_organization, parent: :organization do - verified_at { Time.now} - end + trait :verified do + verified_at Time.now + end - factory :rejected_organization, parent: :organization do - rejected_at { Time.now} + trait :rejected do + rejected_at Time.now + end end factory :tag, class: 'ActsAsTaggableOn::Tag' do @@ -83,4 +83,5 @@ FactoryGirl.define do id { SecureRandom.uuid } started_at DateTime.now end + end diff --git a/spec/features/admin/organizations_spec.rb b/spec/features/admin/organizations_spec.rb index 954e2ee8f..d13758117 100644 --- a/spec/features/admin/organizations_spec.rb +++ b/spec/features/admin/organizations_spec.rb @@ -25,7 +25,7 @@ feature 'Admin::Organizations' do end scenario "verified organizations have link to reject" do - organization = create(:verified_organization) + organization = create(:organization, :verified) visit admin_organizations_path expect(page).to have_content ('Verified') @@ -40,7 +40,7 @@ feature 'Admin::Organizations' do end scenario "rejected organizations have link to verify" do - organization = create(:rejected_organization) + organization = create(:organization, :rejected) visit admin_organizations_path expect(page).to have_link('Verify') @@ -87,8 +87,8 @@ feature 'Admin::Organizations' do scenario "Filtering organizations" do create(:organization, name: "Pending Organization") - create(:rejected_organization, name: "Rejected Organization") - create(:verified_organization, name: "Verified Organization") + create(:organization, :rejected, name: "Rejected Organization") + create(:organization, :verified, name: "Verified Organization") visit admin_organizations_path(filter: 'all') expect(page).to have_content('Pending Organization') @@ -112,7 +112,8 @@ feature 'Admin::Organizations' do end scenario "Verifying organization links remember the pagination setting and the filter" do - 30.times { create(:organization) } + per_page = Kaminari.config.default_per_page + (per_page + 2).times { create(:organization) } visit admin_organizations_path(filter: 'pending', page: 2) From 8bc92811b6977b214b60ffc14b820ca1c32e2096 Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 22 Aug 2015 20:20:47 +0200 Subject: [PATCH 18/23] Adds traits to debate & comment factories --- spec/factories.rb | 20 ++++++++++++++++++++ spec/models/ability_spec.rb | 16 ++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/spec/factories.rb b/spec/factories.rb index de12209e0..7cec55bfa 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -17,6 +17,16 @@ FactoryGirl.define do trait :hidden do hidden_at Time.now end + + trait :reviewed do + reviewed_at Time.now + end + + trait :flagged_as_inappropiate do + after :create do |debate| + InappropiateFlag.flag!(FactoryGirl.create(:user), debate) + end + end end factory :vote do @@ -33,6 +43,16 @@ FactoryGirl.define do trait :hidden do hidden_at Time.now end + + trait :reviewed do + reviewed_at Time.now + end + + trait :flagged_as_inappropiate do + after :create do |debate| + InappropiateFlag.flag!(FactoryGirl.create(:user), debate) + end + end end factory :administrator do diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index cc458857c..1ff4933b0 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -111,8 +111,8 @@ describe Ability do describe "organizations" do let(:pending_organization) { create(:organization) } - let(:rejected_organization) { create(:rejected_organization) } - let(:verified_organization) { create(:verified_organization) } + let(:rejected_organization) { create(:organization, :rejected) } + let(:verified_organization) { create(:organization, :verified) } it { should be_able_to( :verify, pending_organization) } it { should be_able_to( :reject, pending_organization) } @@ -125,12 +125,12 @@ describe Ability do end describe "hiding, reviewing and restoring" do - let(:own_comment) { create(:comment, author: user) } - let(:own_debate) { create(:debate, author: user) } - let(:hidden_comment) { create(:comment, hidden_at: Time.now) } - let(:hidden_debate) { create(:debate, hidden_at: Time.now) } - let(:reviewed_comment) { create(:comment, reviewed_at: Time.now) } - let(:reviewed_debate) { create(:debate, reviewed_at: Time.now) } + let(:own_comment) { create(:comment, author: user) } + let(:own_debate) { create(:debate, author: user) } + let(:hidden_comment) { create(:comment, :hidden) } + let(:hidden_debate) { create(:debate, :hidden) } + let(:reviewed_comment) { create(:comment, :reviewed) } + let(:reviewed_debate) { create(:debate, :reviewed) } it { should be_able_to(:hide, comment) } it { should be_able_to(:hide_in_moderation_screen, comment) } From 169809496e55b01c18d989a56a8af6551c6a1398 Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 22 Aug 2015 20:28:48 +0200 Subject: [PATCH 19/23] Adds filters & refactors to comment moderation --- .../moderation/comments_controller.rb | 19 ++++-- app/models/comment.rb | 5 ++ app/views/moderation/comments/index.html.erb | 16 ++++- config/locales/moderation.en.yml | 5 ++ config/locales/moderation.es.yml | 6 ++ spec/features/moderation/comments_spec.rb | 58 +++++++++++++++++++ 6 files changed, 103 insertions(+), 6 deletions(-) diff --git a/app/controllers/moderation/comments_controller.rb b/app/controllers/moderation/comments_controller.rb index 781f6b1e7..e44021e72 100644 --- a/app/controllers/moderation/comments_controller.rb +++ b/app/controllers/moderation/comments_controller.rb @@ -1,10 +1,12 @@ class Moderation::CommentsController < Moderation::BaseController - + before_filter :set_valid_filters, only: :index + before_filter :parse_filter, only: :index before_filter :load_comments, only: :index load_and_authorize_resource def index + @comments = @comments.send(@filter) @comments = @comments.page(params[:page]) end @@ -14,18 +16,27 @@ class Moderation::CommentsController < Moderation::BaseController def hide_in_moderation_screen @comment.hide - redirect_to action: :index + redirect_to request.query_parameters.merge(action: :index) end def mark_as_reviewed @comment.mark_as_reviewed - redirect_to action: :index + redirect_to request.query_parameters.merge(action: :index) end private def load_comments - @comments = Comment.accessible_by(current_ability, :hide).where('inappropiate_flags_count > 0').includes(:commentable) + @comments = Comment.accessible_by(current_ability, :hide).flagged_as_inappropiate.sorted_for_moderation.includes(:commentable) + end + + def set_valid_filters + @valid_filters = %w{all pending_review reviewed} + end + + def parse_filter + @filter = params[:filter] + @filter = 'all' unless @valid_filters.include?(@filter) end end diff --git a/app/models/comment.rb b/app/models/comment.rb index 5056414b2..7c18efc6b 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -16,6 +16,11 @@ class Comment < ActiveRecord::Base default_scope { includes(:user) } scope :recent, -> { order(id: :desc) } + scope :sorted_for_moderation, -> { order(inappropiate_flags_count: :desc, updated_at: :desc) } + scope :pending_review, -> { where(reviewed_at: nil, hidden_at: nil) } + scope :reviewed, -> { where("reviewed_at IS NOT NULL AND hidden_at IS NULL") } + scope :flagged_as_inappropiate, -> { where("inappropiate_flags_count > 0") } + def self.build(commentable, user, body) new commentable: commentable, user_id: user.id, diff --git a/app/views/moderation/comments/index.html.erb b/app/views/moderation/comments/index.html.erb index 1d3a0e2f3..4393f4cb3 100644 --- a/app/views/moderation/comments/index.html.erb +++ b/app/views/moderation/comments/index.html.erb @@ -1,5 +1,17 @@

    <%= t('moderation.comments.index.title') %>

    +

    + <%= t('moderation.comments.index.filter') %>: + <% @valid_filters.each do |filter| %> + <% if @filter == filter %> + <%= t("moderation.comments.index.filters.#{filter}") %> + <% else %> + <%= link_to t("moderation.comments.index.filters.#{filter}"), + moderation_comments_path(filter: filter) %> + <% end %> + <% end %> +

    +

    <%= page_entries_info @comments %>

    <%= t('moderation.comments.index.flags') %><%= t('moderation.comments.index.debate') %><%= t('moderation.comments.index.comment') %> <%= t('moderation.comments.index.updated_at') %><%= t('moderation.comments.index.commentable_type') %><%= t('moderation.comments.index.commentable') %><%= t('moderation.comments.index.comment') %>
    <%= comment.inappropiate_flags_count %><%= link_to(comment.debate.title, comment.debate) %><%= comment.body %> <%= l comment.updated_at.to_date %><%= comment.commentable_type.constantize.model_name.human %><%= link_to comment.commentable.title, comment.commentable %><%= comment.body %> <%= link_to t('moderation.comments.index.hide'), hide_in_moderation_screen_moderation_comment_path(comment), method: :put %>
    @@ -18,11 +30,11 @@ <% if can? :mark_as_reviewed, comment %> <% end %> <% if comment.reviewed? %> diff --git a/config/locales/moderation.en.yml b/config/locales/moderation.en.yml index 192da6fe3..107d95518 100644 --- a/config/locales/moderation.en.yml +++ b/config/locales/moderation.en.yml @@ -14,4 +14,9 @@ en: hide: Hide mark_as_reviewed: Mark as reviewed reviewed: Reviewed + filter: Filter + filters: + all: All + pending_review: Pending + reviewed: Reviewed diff --git a/config/locales/moderation.es.yml b/config/locales/moderation.es.yml index 421b50a78..7f53cdb3d 100644 --- a/config/locales/moderation.es.yml +++ b/config/locales/moderation.es.yml @@ -14,4 +14,10 @@ es: hide: Ocultar mark_as_reviewed: Marcar como revisado reviewed: Revisado + filter: Filtrar + filters: + all: Todos + pending_review: Pendientes + reviewed: Revisados + diff --git a/spec/features/moderation/comments_spec.rb b/spec/features/moderation/comments_spec.rb index ef3970b20..b6cf463c1 100644 --- a/spec/features/moderation/comments_spec.rb +++ b/spec/features/moderation/comments_spec.rb @@ -120,6 +120,64 @@ feature 'Moderate Comments' do expect(@comment.reload).to be_reviewed end + + scenario "Current filter is properly highlighted" do + visit moderation_comments_path + expect(page).to_not have_link('All') + expect(page).to have_link('Pending') + expect(page).to have_link('Reviewed') + + visit moderation_comments_path(filter: 'all') + expect(page).to_not have_link('All') + expect(page).to have_link('Pending') + expect(page).to have_link('Reviewed') + + visit moderation_comments_path(filter: 'pending_review') + expect(page).to have_link('All') + expect(page).to_not have_link('Pending') + expect(page).to have_link('Reviewed') + + visit moderation_comments_path(filter: 'reviewed') + expect(page).to have_link('All') + expect(page).to have_link('Pending') + expect(page).to_not have_link('Reviewed') + end + + scenario "Filtering comments" do + create(:comment, :flagged_as_inappropiate, body: "Pending comment") + create(:comment, :flagged_as_inappropiate, :hidden, body: "Hidden comment") + create(:comment, :flagged_as_inappropiate, :reviewed, body: "Reviewed comment") + + visit moderation_comments_path(filter: 'all') + expect(page).to have_content('Pending comment') + expect(page).to_not have_content('Hidden comment') + expect(page).to have_content('Reviewed comment') + + visit moderation_comments_path(filter: 'pending_review') + expect(page).to have_content('Pending comment') + expect(page).to_not have_content('Hidden comment') + expect(page).to_not have_content('Reviewed comment') + + visit moderation_comments_path(filter: 'reviewed') + expect(page).to_not have_content('Pending comment') + expect(page).to_not have_content('Hidden comment') + expect(page).to have_content('Reviewed comment') + end + + scenario "Reviewing links remember the pagination setting and the filter" do + per_page = Kaminari.config.default_per_page + (per_page + 2).times { create(:comment, :flagged_as_inappropiate) } + + visit moderation_comments_path(filter: 'pending_review', page: 2) + + click_link('Mark as reviewed', match: :first) + + uri = URI.parse(current_url) + query_params = Rack::Utils.parse_nested_query(uri.query).symbolize_keys + + expect(query_params[:filter]).to eq('pending_review') + expect(query_params[:page]).to eq('2') + end end end From 47842cb1ccdaac4639be0a72d922c5df42dcc44b Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 22 Aug 2015 20:56:05 +0200 Subject: [PATCH 20/23] Refactors spec/features/moderation/comments_spec --- spec/features/moderation/comments_spec.rb | 87 ++++++++++++----------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/spec/features/moderation/comments_spec.rb b/spec/features/moderation/comments_spec.rb index b6cf463c1..e52a87c17 100644 --- a/spec/features/moderation/comments_spec.rb +++ b/spec/features/moderation/comments_spec.rb @@ -77,48 +77,6 @@ feature 'Moderate Comments' do background do moderator = create(:moderator) login_as(moderator.user) - - user = create(:user) - debate = create(:debate, title: 'Democracy') - @comment = create(:comment, commentable: debate, body: 'spammy spam') - InappropiateFlag.flag!(user, @comment) - - visit moderation_comments_path - end - - scenario 'Flagged comment shows the right params' do - within("#comment_#{@comment.id}") do - expect(page).to have_link('Democracy') - expect(page).to have_content('spammy spam') - expect(page).to have_content('1') - expect(page).to have_link('Hide') - expect(page).to have_content('Mark as reviewed') - end - end - - scenario 'Hiding a comment' do - within("#comment_#{@comment.id}") do - click_link('Hide') - end - - expect(current_path).to eq(moderation_comments_path) - expect(page).to_not have_selector("#comment_#{@comment.id}") - - expect(@comment.reload).to be_hidden - end - - scenario 'Marking a comment as reviewed' do - within("#comment_#{@comment.id}") do - click_link('Mark as reviewed') - end - - expect(current_path).to eq(moderation_comments_path) - - within("#comment_#{@comment.id}") do - expect(page).to have_content('Reviewed') - end - - expect(@comment.reload).to be_reviewed end scenario "Current filter is properly highlighted" do @@ -178,6 +136,49 @@ feature 'Moderate Comments' do expect(query_params[:filter]).to eq('pending_review') expect(query_params[:page]).to eq('2') end - end + feature 'A flagged comment exists' do + + background do + debate = create(:debate, title: 'Democracy') + @comment = create(:comment, :flagged_as_inappropiate, commentable: debate, body: 'spammy spam') + visit moderation_comments_path + end + + scenario 'It is displayed with the correct attributes' do + within("#comment_#{@comment.id}") do + expect(page).to have_link('Democracy') + expect(page).to have_content('spammy spam') + expect(page).to have_content('1') + expect(page).to have_link('Hide') + expect(page).to have_link('Mark as reviewed') + end + end + + scenario 'Hiding the comment' do + within("#comment_#{@comment.id}") do + click_link('Hide') + end + + expect(current_path).to eq(moderation_comments_path) + expect(page).to_not have_selector("#comment_#{@comment.id}") + + expect(@comment.reload).to be_hidden + end + + scenario 'Marking the comment as reviewed' do + within("#comment_#{@comment.id}") do + click_link('Mark as reviewed') + end + + expect(current_path).to eq(moderation_comments_path) + + within("#comment_#{@comment.id}") do + expect(page).to have_content('Reviewed') + end + + expect(@comment.reload).to be_reviewed + end + end + end end From 5eeaa7d1893af58ac8d3a3ee988e69b1e33f2722 Mon Sep 17 00:00:00 2001 From: kikito Date: Sat, 22 Aug 2015 23:20:55 +0200 Subject: [PATCH 21/23] implements /moderation/debates --- .../moderation/comments_controller.rb | 20 ++-- .../moderation/debates_controller.rb | 38 +++++- app/models/debate.rb | 5 + app/views/moderation/comments/index.html.erb | 10 +- app/views/moderation/debates/index.html.erb | 48 ++++++++ config/locales/moderation.en.yml | 27 ++++- config/locales/moderation.es.yml | 27 ++++- spec/features/moderation/debates_spec.rb | 111 +++++++++++++++++- 8 files changed, 258 insertions(+), 28 deletions(-) create mode 100644 app/views/moderation/debates/index.html.erb diff --git a/app/controllers/moderation/comments_controller.rb b/app/controllers/moderation/comments_controller.rb index e44021e72..03667e286 100644 --- a/app/controllers/moderation/comments_controller.rb +++ b/app/controllers/moderation/comments_controller.rb @@ -26,17 +26,17 @@ class Moderation::CommentsController < Moderation::BaseController private - def load_comments - @comments = Comment.accessible_by(current_ability, :hide).flagged_as_inappropiate.sorted_for_moderation.includes(:commentable) - end + def load_comments + @comments = Comment.accessible_by(current_ability, :hide).flagged_as_inappropiate.sorted_for_moderation.includes(:commentable) + end - def set_valid_filters - @valid_filters = %w{all pending_review reviewed} - end + def set_valid_filters + @valid_filters = %w{all pending_review reviewed} + end - def parse_filter - @filter = params[:filter] - @filter = 'all' unless @valid_filters.include?(@filter) - end + def parse_filter + @filter = params[:filter] + @filter = 'all' unless @valid_filters.include?(@filter) + end end diff --git a/app/controllers/moderation/debates_controller.rb b/app/controllers/moderation/debates_controller.rb index 622116765..22e4eac6b 100644 --- a/app/controllers/moderation/debates_controller.rb +++ b/app/controllers/moderation/debates_controller.rb @@ -1,8 +1,42 @@ class Moderation::DebatesController < Moderation::BaseController + before_filter :set_valid_filters, only: :index + before_filter :parse_filter, only: :index + before_filter :load_debates, only: :index + + load_and_authorize_resource + + def index + @debates = @debates.send(@filter) + @debates = @debates.page(params[:page]) + end def hide - @debate = Debate.find(params[:id]) @debate.hide end -end \ No newline at end of file + def hide_in_moderation_screen + @debate.hide + redirect_to request.query_parameters.merge(action: :index) + end + + def mark_as_reviewed + @debate.mark_as_reviewed + redirect_to request.query_parameters.merge(action: :index) + end + + private + + def load_debates + @debates = Debate.accessible_by(current_ability, :hide).flagged_as_inappropiate.sorted_for_moderation + end + + def set_valid_filters + @valid_filters = %w{all pending_review reviewed} + end + + def parse_filter + @filter = params[:filter] + @filter = 'all' unless @valid_filters.include?(@filter) + end + +end diff --git a/app/models/debate.rb b/app/models/debate.rb index 042c334c7..c16048f70 100644 --- a/app/models/debate.rb +++ b/app/models/debate.rb @@ -23,6 +23,11 @@ class Debate < ActiveRecord::Base before_validation :sanitize_description before_validation :sanitize_tag_list + scope :sorted_for_moderation, -> { order(inappropiate_flags_count: :desc, updated_at: :desc) } + scope :pending_review, -> { where(reviewed_at: nil, hidden_at: nil) } + scope :reviewed, -> { where("reviewed_at IS NOT NULL AND hidden_at IS NULL") } + scope :flagged_as_inappropiate, -> { where("inappropiate_flags_count > 0") } + # Ahoy setup visitable # Ahoy will automatically assign visit_id on create diff --git a/app/views/moderation/comments/index.html.erb b/app/views/moderation/comments/index.html.erb index 4393f4cb3..0d89114f1 100644 --- a/app/views/moderation/comments/index.html.erb +++ b/app/views/moderation/comments/index.html.erb @@ -16,11 +16,11 @@
    <%= link_to comment.commentable.title, comment.commentable %> <%= comment.body %> - <%= link_to t('moderation.comments.index.hide'), hide_in_moderation_screen_moderation_comment_path(comment), method: :put %> + <%= link_to t('moderation.comments.index.hide'), hide_in_moderation_screen_moderation_comment_path(comment, request.query_parameters), method: :put %> - <%= link_to t('moderation.comments.index.mark_as_reviewed'), mark_as_reviewed_moderation_comment_path(comment), method: :put %> + <%= link_to t('moderation.comments.index.mark_as_reviewed'), mark_as_reviewed_moderation_comment_path(comment, request.query_parameters), method: :put %>
    - - - - - + + + + + <% @comments.each do |comment| %> diff --git a/app/views/moderation/debates/index.html.erb b/app/views/moderation/debates/index.html.erb new file mode 100644 index 000000000..611f68e16 --- /dev/null +++ b/app/views/moderation/debates/index.html.erb @@ -0,0 +1,48 @@ +

    <%= t('moderation.debates.index.title') %>

    + +

    + <%= t('moderation.debates.index.filter') %>: + <% @valid_filters.each do |filter| %> + <% if @filter == filter %> + <%= t("moderation.debates.index.filters.#{filter}") %> + <% else %> + <%= link_to t("moderation.debates.index.filters.#{filter}"), + moderation_debates_path(filter: filter) %> + <% end %> + <% end %> +

    + +

    <%= page_entries_info @debates %>

    + +
    <%= t('moderation.comments.index.flags') %><%= t('moderation.comments.index.updated_at') %><%= t('moderation.comments.index.commentable_type') %><%= t('moderation.comments.index.commentable') %><%= t('moderation.comments.index.comment') %><%= t('moderation.comments.index.headers.flags') %><%= t('moderation.comments.index.headers.updated_at') %><%= t('moderation.comments.index.headers.commentable_type') %><%= t('moderation.comments.index.headers.commentable') %><%= t('moderation.comments.index.headers.comment') %>
    + + + + + + + <% @debates.each do |debate| %> + + + + + + + <% if can? :mark_as_reviewed, debate %> + + <% end %> + <% if debate.reviewed? %> + + <% end %> + + <% end %> +
    <%= t('moderation.debates.index.headers.flags') %><%= t('moderation.debates.index.headers.updated_at') %><%= t('moderation.debates.index.headers.title') %><%= t('moderation.debates.index.headers.description') %>
    <%= debate.inappropiate_flags_count %><%= l debate.updated_at.to_date %><%= link_to debate.title, debate %><%= debate.description %> + <%= link_to t('moderation.debates.index.hide'), hide_in_moderation_screen_moderation_debate_path(debate, request.query_parameters), method: :put %> + + <%= link_to t('moderation.debates.index.mark_as_reviewed'), mark_as_reviewed_moderation_debate_path(debate, request.query_parameters), method: :put %> + + <%= t('moderation.debates.index.reviewed') %> +
    + +<%= paginate @debates %> + diff --git a/config/locales/moderation.en.yml b/config/locales/moderation.en.yml index 107d95518..0fc762d8d 100644 --- a/config/locales/moderation.en.yml +++ b/config/locales/moderation.en.yml @@ -6,11 +6,28 @@ en: comments: index: title: Comments flagged as inappropiate - commentable_type: Type - commentable: Root - comment: Comment - updated_at: Date - flags: Flags + headers: + flags: Flags + updated_at: Date + commentable_type: Type + commentable: Root + comment: Comment + hide: Hide + mark_as_reviewed: Mark as reviewed + reviewed: Reviewed + filter: Filter + filters: + all: All + pending_review: Pending + reviewed: Reviewed + debates: + index: + title: Debates flagged as inappropiate + headers: + flags: Flags + updated_at: Date + title: Title + description: Description hide: Hide mark_as_reviewed: Mark as reviewed reviewed: Reviewed diff --git a/config/locales/moderation.es.yml b/config/locales/moderation.es.yml index 7f53cdb3d..bae811511 100644 --- a/config/locales/moderation.es.yml +++ b/config/locales/moderation.es.yml @@ -6,11 +6,28 @@ es: comments: index: title: Comentarios Denunciados como Inapropiados - commentable_type: Tipo - commentable: Raíz - comment: Comentario - updated_at: Fecha - flags: Denuncias + headers: + flags: Denuncias + updated_at: Fecha + commentable_type: Tipo + commentable: Raíz + comment: Comentario + hide: Ocultar + mark_as_reviewed: Marcar como revisado + reviewed: Revisado + filter: Filtrar + filters: + all: Todos + pending_review: Pendientes + reviewed: Revisados + debates: + index: + title: Debates Denunciados como Inapropiados + headers: + flags: Denuncias + updated_at: Fecha + title: Title + description: Descripción hide: Ocultar mark_as_reviewed: Marcar como revisado reviewed: Revisado diff --git a/spec/features/moderation/debates_spec.rb b/spec/features/moderation/debates_spec.rb index 5692a8b5b..bb2b58c24 100644 --- a/spec/features/moderation/debates_spec.rb +++ b/spec/features/moderation/debates_spec.rb @@ -23,4 +23,113 @@ feature 'Moderate debates' do expect(page).to have_css('.debate', count: 0) end -end \ No newline at end of file + feature '/moderation/ menu' do + + background do + moderator = create(:moderator) + login_as(moderator.user) + end + + scenario "Current filter is properly highlighted" do + visit moderation_debates_path + expect(page).to_not have_link('All') + expect(page).to have_link('Pending') + expect(page).to have_link('Reviewed') + + visit moderation_debates_path(filter: 'all') + expect(page).to_not have_link('All') + expect(page).to have_link('Pending') + expect(page).to have_link('Reviewed') + + visit moderation_debates_path(filter: 'pending_review') + expect(page).to have_link('All') + expect(page).to_not have_link('Pending') + expect(page).to have_link('Reviewed') + + visit moderation_debates_path(filter: 'reviewed') + expect(page).to have_link('All') + expect(page).to have_link('Pending') + expect(page).to_not have_link('Reviewed') + end + + scenario "Filtering debates" do + create(:debate, :flagged_as_inappropiate, title: "Pending debate") + create(:debate, :flagged_as_inappropiate, :hidden, title: "Hidden debate") + create(:debate, :flagged_as_inappropiate, :reviewed, title: "Reviewed debate") + + visit moderation_debates_path(filter: 'all') + expect(page).to have_content('Pending debate') + expect(page).to_not have_content('Hidden debate') + expect(page).to have_content('Reviewed debate') + + visit moderation_debates_path(filter: 'pending_review') + expect(page).to have_content('Pending debate') + expect(page).to_not have_content('Hidden debate') + expect(page).to_not have_content('Reviewed debate') + + visit moderation_debates_path(filter: 'reviewed') + expect(page).to_not have_content('Pending debate') + expect(page).to_not have_content('Hidden debate') + expect(page).to have_content('Reviewed debate') + end + + scenario "Reviewing links remember the pagination setting and the filter" do + per_page = Kaminari.config.default_per_page + (per_page + 2).times { create(:debate, :flagged_as_inappropiate) } + + visit moderation_debates_path(filter: 'pending_review', page: 2) + + click_link('Mark as reviewed', match: :first) + + uri = URI.parse(current_url) + query_params = Rack::Utils.parse_nested_query(uri.query).symbolize_keys + + expect(query_params[:filter]).to eq('pending_review') + expect(query_params[:page]).to eq('2') + end + + feature 'A flagged debate exists' do + + background do + @debate = create(:debate, :flagged_as_inappropiate, title: 'spammy spam', description: 'buy buy buy') + visit moderation_debates_path + end + + scenario 'It is displayed with the correct attributes' do + within("#debate_#{@debate.id}") do + expect(page).to have_link('spammy spam') + expect(page).to have_content('buy buy buy') + expect(page).to have_content('1') + expect(page).to have_link('Hide') + expect(page).to have_link('Mark as reviewed') + end + end + + scenario 'Hiding the debate' do + within("#debate_#{@debate.id}") do + click_link('Hide') + end + + expect(current_path).to eq(moderation_debates_path) + expect(page).to_not have_selector("#debate_#{@debate.id}") + + expect(@debate.reload).to be_hidden + end + + scenario 'Marking the debate as reviewed' do + within("#debate_#{@debate.id}") do + click_link('Mark as reviewed') + end + + expect(current_path).to eq(moderation_debates_path) + + within("#debate_#{@debate.id}") do + expect(page).to have_content('Reviewed') + end + + expect(@debate.reload).to be_reviewed + end + end + end + +end From 5db63228ade1a8403017d4f5008e8c578613f75e Mon Sep 17 00:00:00 2001 From: kikito Date: Sun, 23 Aug 2015 00:02:40 +0200 Subject: [PATCH 22/23] Adds moderation sidebar --- app/views/moderation/_menu.html.erb | 24 +++++++++++++++++++++--- config/locales/moderation.en.yml | 3 +++ config/locales/moderation.es.yml | 5 ++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/views/moderation/_menu.html.erb b/app/views/moderation/_menu.html.erb index 21997abca..c5eef1be7 100644 --- a/app/views/moderation/_menu.html.erb +++ b/app/views/moderation/_menu.html.erb @@ -1,3 +1,21 @@ -
    - Moderation links -
    \ No newline at end of file + diff --git a/config/locales/moderation.en.yml b/config/locales/moderation.en.yml index 0fc762d8d..2728f10d9 100644 --- a/config/locales/moderation.en.yml +++ b/config/locales/moderation.en.yml @@ -1,5 +1,8 @@ en: moderation: + menu: + flagged_debates: Debates + flagged_comments: Comments dashboard: index: title: Moderation diff --git a/config/locales/moderation.es.yml b/config/locales/moderation.es.yml index bae811511..2afaec7a9 100644 --- a/config/locales/moderation.es.yml +++ b/config/locales/moderation.es.yml @@ -1,5 +1,8 @@ es: moderation: + menu: + flagged_debates: Debates + flagged_comments: Comentarios dashboard: index: title: Moderación @@ -26,7 +29,7 @@ es: headers: flags: Denuncias updated_at: Fecha - title: Title + title: Título description: Descripción hide: Ocultar mark_as_reviewed: Marcar como revisado From 720630e0abac997dfdfda8526afc4a0cb88a4a53 Mon Sep 17 00:00:00 2001 From: kikito Date: Sun, 23 Aug 2015 18:32:00 +0200 Subject: [PATCH 23/23] Fixes failing spec which expected a menu id --- app/views/moderation/_menu.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/moderation/_menu.html.erb b/app/views/moderation/_menu.html.erb index c5eef1be7..41e84287a 100644 --- a/app/views/moderation/_menu.html.erb +++ b/app/views/moderation/_menu.html.erb @@ -1,5 +1,5 @@