From 5d7e0c9dc1c4522b83d567d0a5556ba5f30a2bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Mon, 21 Sep 2015 12:39:17 +0200 Subject: [PATCH 01/13] adds activities --- db/migrate/20150921095553_create_activities.rb | 13 +++++++++++++ db/schema.rb | 14 +++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20150921095553_create_activities.rb diff --git a/db/migrate/20150921095553_create_activities.rb b/db/migrate/20150921095553_create_activities.rb new file mode 100644 index 000000000..3fff14205 --- /dev/null +++ b/db/migrate/20150921095553_create_activities.rb @@ -0,0 +1,13 @@ +class CreateActivities < ActiveRecord::Migration + def change + create_table :activities do |t| + t.integer :user_id + t.string :action + t.belongs_to :actionable, polymorphic: true + t.timestamps + end + + add_index :activities, [:actionable_id, :actionable_type] + add_index :activities, :user_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 741f82940..22b6b730c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,11 +11,23 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150917102718) do +ActiveRecord::Schema.define(version: 20150921095553) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "activities", force: :cascade do |t| + t.integer "user_id" + t.string "action" + t.integer "actionable_id" + t.string "actionable_type" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "activities", ["actionable_id", "actionable_type"], name: "index_activities_on_actionable_id_and_actionable_type", using: :btree + add_index "activities", ["user_id"], name: "index_activities_on_user_id", using: :btree + create_table "addresses", force: :cascade do |t| t.integer "user_id" t.string "street" From 9e922ad08037efd1bc1cb6e29fbd4dfc4c17e7c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Mon, 21 Sep 2015 15:39:33 +0200 Subject: [PATCH 02/13] adds activity model --- app/models/activity.rb | 22 ++++++++++++ spec/factories.rb | 6 ++++ spec/models/activity_spec.rb | 69 ++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 app/models/activity.rb create mode 100644 spec/models/activity_spec.rb diff --git a/app/models/activity.rb b/app/models/activity.rb new file mode 100644 index 000000000..87c68e556 --- /dev/null +++ b/app/models/activity.rb @@ -0,0 +1,22 @@ +class Activity < ActiveRecord::Base + + belongs_to :actionable, polymorphic: true + belongs_to :user + + VALID_ACTIONS = %w( hide block restore ) + + validates :action, inclusion: {in: VALID_ACTIONS} + + def self.log(user, action, actionable) + create(user: user, action: action.to_s, actionable: actionable) + end + + def self.on(actionable) + where(actionable_type: actionable.class.name, actionable_id: actionable.id) + end + + def self.by(user) + where(user: user) + end + +end diff --git a/spec/factories.rb b/spec/factories.rb index cda9c01a8..caec0d6d9 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -33,6 +33,12 @@ FactoryGirl.define do uid "MyString" end + factory :activity do + user + action "hide" + association :actionable, factory: :proposal + end + factory :verification_residence, class: Verification::Residence do user document_number '12345678Z' diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb new file mode 100644 index 000000000..f1133dbd8 --- /dev/null +++ b/spec/models/activity_spec.rb @@ -0,0 +1,69 @@ +require 'rails_helper' + +describe Activity do + + it "should be valid for different actionables" do + expect(build(:activity, actionable: create(:proposal))).to be_valid + expect(build(:activity, actionable: create(:debate))).to be_valid + expect(build(:activity, actionable: create(:comment))).to be_valid + expect(build(:activity, actionable: create(:user))).to be_valid + end + + it "should be a valid only with allowed actions" do + expect(build(:activity, action: "hide")).to be_valid + expect(build(:activity, action: "block")).to be_valid + expect(build(:activity, action: "restore")).to be_valid + expect(build(:activity, action: "dissapear")).to_not be_valid + end + + describe "log" do + it "should create an activity entry" do + user = create(:user) + proposal = create(:proposal) + + expect{ Activity.log(user, :hide, proposal) }.to change { Activity.count }.by(1) + + activity = Activity.last + expect(activity.user_id).to eq(user.id) + expect(activity.action).to eq("hide") + expect(activity.actionable).to eq(proposal) + end + end + + describe "on" do + it "should list all activity on an actionable object" do + proposal = create(:proposal) + activity1 = create(:activity, action: "hide", actionable: proposal) + activity2 = create(:activity, action: "restore", actionable: proposal) + activity3 = create(:activity, action: "hide", actionable: proposal) + create(:activity, action: "restore", actionable: create(:debate)) + create(:activity, action: "hide", actionable: create(:proposal)) + create(:activity, action: "hide", actionable: create(:comment)) + create(:activity, action: "block", actionable: create(:user)) + + expect(Activity.on(proposal).size).to eq 3 + [activity1, activity2, activity3].each do |a| + expect(Activity.on(proposal)).to include(a) + end + end + end + + describe "by" do + it "should list all activity of a user" do + user1 = create(:user) + activity1 = create(:activity, user: user1) + activity2 = create(:activity, user: user1, action: "restore", actionable: create(:debate)) + activity3 = create(:activity, user: user1, action: "hide", actionable: create(:proposal)) + activity4 = create(:activity, user: user1, action: "hide", actionable: create(:comment)) + activity5 = create(:activity, user: user1, action: "block", actionable: create(:user)) + create_list(:activity, 3) + + expect(Activity.by(user1).size).to eq 5 + + [activity1, activity2, activity3, activity4, activity5].each do |a| + expect(Activity.by(user1)).to include(a) + end + end + end + +end From 05237ca1e27009732f7d63c8206f31116b7b9bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 13:50:01 +0200 Subject: [PATCH 03/13] adds scopes for every actionable class to activity --- app/models/activity.rb | 6 ++++++ spec/models/activity_spec.rb | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/app/models/activity.rb b/app/models/activity.rb index 87c68e556..bb5338607 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -7,6 +7,12 @@ class Activity < ActiveRecord::Base validates :action, inclusion: {in: VALID_ACTIONS} + scope :on_proposals, -> { where(actionable_type: 'Proposal') } + scope :on_debates, -> { where(actionable_type: 'Debate') } + scope :on_users, -> { where(actionable_type: 'User') } + scope :on_comments, -> { where(actionable_type: 'Comment') } + scope :for_render, -> { includes(user: [:moderator, :administrator]).includes(:actionable) } + def self.log(user, action, actionable) create(user: user, action: action.to_s, actionable: actionable) end diff --git a/spec/models/activity_spec.rb b/spec/models/activity_spec.rb index f1133dbd8..0c7b99027 100644 --- a/spec/models/activity_spec.rb +++ b/spec/models/activity_spec.rb @@ -66,4 +66,23 @@ describe Activity do end end + describe "scopes by actionable" do + it "should filter by actionable type" do + on_proposal = create(:activity, actionable: create(:proposal)) + on_debate = create(:activity, actionable: create(:debate)) + on_comment = create(:activity, actionable: create(:comment)) + on_user = create(:activity, actionable: create(:user)) + + expect(Activity.on_proposals.size).to eq 1 + expect(Activity.on_debates.size).to eq 1 + expect(Activity.on_comments.size).to eq 1 + expect(Activity.on_users.size).to eq 1 + + expect(Activity.on_proposals.first).to eq on_proposal + expect(Activity.on_debates.first).to eq on_debate + expect(Activity.on_comments.first).to eq on_comment + expect(Activity.on_users.first).to eq on_user + end + end + end From 7741023e6a56317223b25d4d69f80a48554804dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 16:25:23 +0200 Subject: [PATCH 04/13] adds activity page to administration --- app/controllers/admin/activity_controller.rb | 8 ++++ .../moderation/proposals_controller.rb | 1 + app/views/admin/_menu.html.erb | 7 +++ app/views/admin/activity/show.html.erb | 43 +++++++++++++++++++ config/locales/activerecord.en.yml | 1 + config/locales/activerecord.es.yml | 3 ++ config/locales/admin.en.yml | 15 +++++++ config/locales/admin.es.yml | 15 +++++++ config/routes.rb | 2 + spec/features/admin/activity_spec.rb | 29 +++++++++++++ 10 files changed, 124 insertions(+) create mode 100644 app/controllers/admin/activity_controller.rb create mode 100644 app/views/admin/activity/show.html.erb create mode 100644 spec/features/admin/activity_spec.rb diff --git a/app/controllers/admin/activity_controller.rb b/app/controllers/admin/activity_controller.rb new file mode 100644 index 000000000..8f7993a09 --- /dev/null +++ b/app/controllers/admin/activity_controller.rb @@ -0,0 +1,8 @@ +class Admin::ActivityController < Admin::BaseController + has_filters %w{all on_users on_proposals on_debates on_comments} + + def show + @activity = Activity.for_render.send(@current_filter).order(created_at: :desc).page(params[:page]) + end + +end diff --git a/app/controllers/moderation/proposals_controller.rb b/app/controllers/moderation/proposals_controller.rb index e7cf86043..f8517eb4a 100644 --- a/app/controllers/moderation/proposals_controller.rb +++ b/app/controllers/moderation/proposals_controller.rb @@ -16,6 +16,7 @@ class Moderation::ProposalsController < Moderation::BaseController def hide @proposal.hide + Activity.log(current_user, :hide, @proposal) end def moderate diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index 42d979209..89e1b6641 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -60,6 +60,13 @@ <% end %> +
  • > + <%= link_to admin_activity_path do %> + + <%= t('admin.menu.activity') %> + <% end %> +
  • +
  • > <%= link_to admin_settings_path do %> diff --git a/app/views/admin/activity/show.html.erb b/app/views/admin/activity/show.html.erb new file mode 100644 index 000000000..0f422cfa8 --- /dev/null +++ b/app/views/admin/activity/show.html.erb @@ -0,0 +1,43 @@ +

    <%= t("admin.activity.show.title") %>

    + +<%= render 'shared/filter_subnav', i18n_namespace: "admin.activity.show" %> + +

    <%= page_entries_info @activity %>

    + + + + + + + + + <% @activity.each do |activity| %> + + + + + + + <% end %> +
    <%= t("admin.activity.show.type") %><%= t("admin.activity.show.action") %> <%= t("admin.activity.show.by") %>
    + <%= activity.actionable_type.constantize.model_name.human %>
    + <%= l activity.actionable.created_at.to_date %> +
    + <%= t("admin.activity.show.actions.#{activity.action}") %>
    + <%= l activity.created_at.to_date %> +
    + <% case activity.actionable_type %> + <% when "User" %> + <%= activity.actionable.username %> (<%= activity.actionable.email %>) + <% when "Comment" %> + <%= activity.actionable.body %> + <% else %> + <%= activity.actionable.title %> +
    +
    + <%= activity.actionable.description %> +
    + <% end %> +
    <%= activity.user.name %> (<%= activity.user.email %>)
    + +<%= paginate @activity %> diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index f17dc73de..303bd5ded 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -1,6 +1,7 @@ en: activerecord: models: + activity: Activity comment: Comment debate: Debate proposal: Proposal diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index 0d6ecba40..3ba5e7cca 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -1,6 +1,9 @@ es: activerecord: models: + activity: + one: actividad + other: actividades comment: one: Comentario other: Comentarios diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index 7317b2a31..a134cd673 100644 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -20,6 +20,7 @@ en: officials: Officials moderators: Moderators stats: Statistics + activity: Moderation Activity organizations: index: title: Organizations @@ -129,4 +130,18 @@ en: moderator: delete: Delete add: Add + activity: + show: + title: Activity of Moderators + filter: Show + filters: + all: All + on_proposals: On Proposals + on_debates: On Debates + on_comments: On Comments + on_users: On Users + actions: + hide: Hidden + restore: Restored + block: Blocked diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 01117ff42..8c6e43371 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -20,6 +20,7 @@ es: officials: Cargos públicos moderators: Moderadores stats: Estadísticas + activity: Actividad de moderadores organizations: index: title: Organizaciones @@ -129,3 +130,17 @@ es: moderator: delete: Borrar add: Añadir + activity: + show: + title: Actividad de los Moderadores + filter: Mostrar + filters: + all: Todo + on_proposals: Propuestas + on_debates: Debates + on_comments: Comentarios + on_users: Usuarios + actions: + hide: Ocultado + restore: Restaurado + block: Bloqueado diff --git a/config/routes.rb b/config/routes.rb index ae52181bb..e60b50ca8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -112,6 +112,8 @@ Rails.application.routes.draw do resources :moderators, only: [:index, :create, :destroy] do collection { get :search } end + + resource :activity, controller: :activity, only: :show end namespace :moderation do diff --git a/spec/features/admin/activity_spec.rb b/spec/features/admin/activity_spec.rb new file mode 100644 index 000000000..9382875f1 --- /dev/null +++ b/spec/features/admin/activity_spec.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +feature 'Admin activity' do + + background do + @admin = create(:administrator) + login_as(@admin.user) + end + + context "Proposals" do + scenario "Shows moderation activity on proposals", :js do + proposal = create(:proposal) + + visit proposal_path(proposal) + + within("#proposal_#{proposal.id}") do + click_link 'Hide' + end + + visit admin_activity_path + + within("#activity_#{Activity.last.id}") do + expect(page).to have_content(proposal.title) + expect(page).to have_content(@admin.user.username) + end + end + end + +end \ No newline at end of file From 1047df11a1b08e16004b8778fa07e3de50be8090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 16:25:49 +0200 Subject: [PATCH 05/13] includes hidden relations by default --- app/models/activity.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index bb5338607..b780bd9d2 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -1,7 +1,7 @@ class Activity < ActiveRecord::Base - belongs_to :actionable, polymorphic: true - belongs_to :user + belongs_to :actionable, -> { with_hidden }, polymorphic: true + belongs_to :user, -> { with_hidden } VALID_ACTIONS = %w( hide block restore ) From 23eba622a2389044f9891ec4f24ff2b7f54cdb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 16:26:20 +0200 Subject: [PATCH 06/13] fix bug when blocking users --- app/models/comment.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/comment.rb b/app/models/comment.rb index 6a3125285..187e35037 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -73,11 +73,11 @@ class Comment < ActiveRecord::Base end def after_hide - commentable_type.constantize.reset_counters(commentable_id, :comments) + commentable_type.constantize.with_hidden.reset_counters(commentable_id, :comments) end def after_restore - commentable_type.constantize.reset_counters(commentable_id, :comments) + commentable_type.constantize.with_hidden.reset_counters(commentable_id, :comments) end def reply? From 5aa5fc5b7ff1d5261ef05f0463f57eee7b2392be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 16:28:50 +0200 Subject: [PATCH 07/13] logs activity when bulk-hiding proposals --- .../moderation/proposals_controller.rb | 10 +++++--- spec/features/admin/activity_spec.rb | 25 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/app/controllers/moderation/proposals_controller.rb b/app/controllers/moderation/proposals_controller.rb index f8517eb4a..2b808b0b2 100644 --- a/app/controllers/moderation/proposals_controller.rb +++ b/app/controllers/moderation/proposals_controller.rb @@ -15,15 +15,14 @@ class Moderation::ProposalsController < Moderation::BaseController end def hide - @proposal.hide - Activity.log(current_user, :hide, @proposal) + hide_proposal @proposal end def moderate @proposals = @proposals.where(id: params[:proposal_ids]) if params[:hide_proposals].present? - @proposals.accessible_by(current_ability, :hide).each(&:hide) + @proposals.accessible_by(current_ability, :hide).each{|proposal| hide_proposal proposal} elsif params[:ignore_flags].present? @proposals.accessible_by(current_ability, :ignore_flag).each(&:ignore_flag) @@ -42,4 +41,9 @@ class Moderation::ProposalsController < Moderation::BaseController @proposals = Proposal.accessible_by(current_ability, :moderate) end + def hide_proposal(proposal) + proposal.hide + Activity.log(current_user, :hide, proposal) + end + end diff --git a/spec/features/admin/activity_spec.rb b/spec/features/admin/activity_spec.rb index 9382875f1..61b815850 100644 --- a/spec/features/admin/activity_spec.rb +++ b/spec/features/admin/activity_spec.rb @@ -24,6 +24,31 @@ feature 'Admin activity' do expect(page).to have_content(@admin.user.username) end end + + scenario "Shows moderation activity from moderation screen" do + proposal1 = create(:proposal) + proposal2 = create(:proposal) + proposal3 = create(:proposal) + + visit moderation_proposals_path(filter: 'all') + + within("#proposal_#{proposal1.id}") do + check "proposal_#{proposal1.id}_check" + end + + within("#proposal_#{proposal3.id}") do + check "proposal_#{proposal3.id}_check" + end + + click_on "Hide proposals" + + visit admin_activity_path + + + expect(page).to have_content(proposal1.title) + expect(page).to_not have_content(proposal2.title) + expect(page).to have_content(proposal3.title) + end end end \ No newline at end of file From 7ca3f574d8d1158d555f33585ccafc6e0ff99c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 16:35:38 +0200 Subject: [PATCH 08/13] logs activity: hiding debates --- .../moderation/debates_controller.rb | 9 +++- .../moderation/proposals_controller.rb | 2 +- spec/features/admin/activity_spec.rb | 44 +++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/app/controllers/moderation/debates_controller.rb b/app/controllers/moderation/debates_controller.rb index 1fe3eb4b4..1e93ed040 100644 --- a/app/controllers/moderation/debates_controller.rb +++ b/app/controllers/moderation/debates_controller.rb @@ -14,14 +14,14 @@ class Moderation::DebatesController < Moderation::BaseController end def hide - @debate.hide + hide_debate @debate end def moderate @debates = @debates.where(id: params[:debate_ids]) if params[:hide_debates].present? - @debates.accessible_by(current_ability, :hide).each(&:hide) + @debates.accessible_by(current_ability, :hide).each {|debate| hide_debate debate} elsif params[:ignore_flags].present? @debates.accessible_by(current_ability, :ignore_flag).each(&:ignore_flag) @@ -40,4 +40,9 @@ class Moderation::DebatesController < Moderation::BaseController @debates = Debate.accessible_by(current_ability, :moderate) end + def hide_debate(debate) + debate.hide + Activity.log(current_user, :hide, debate) + end + end diff --git a/app/controllers/moderation/proposals_controller.rb b/app/controllers/moderation/proposals_controller.rb index 2b808b0b2..b60d27e50 100644 --- a/app/controllers/moderation/proposals_controller.rb +++ b/app/controllers/moderation/proposals_controller.rb @@ -22,7 +22,7 @@ class Moderation::ProposalsController < Moderation::BaseController @proposals = @proposals.where(id: params[:proposal_ids]) if params[:hide_proposals].present? - @proposals.accessible_by(current_ability, :hide).each{|proposal| hide_proposal proposal} + @proposals.accessible_by(current_ability, :hide).each {|proposal| hide_proposal proposal} elsif params[:ignore_flags].present? @proposals.accessible_by(current_ability, :ignore_flag).each(&:ignore_flag) diff --git a/spec/features/admin/activity_spec.rb b/spec/features/admin/activity_spec.rb index 61b815850..2979117d9 100644 --- a/spec/features/admin/activity_spec.rb +++ b/spec/features/admin/activity_spec.rb @@ -51,4 +51,48 @@ feature 'Admin activity' do end end + context "Debates" do + scenario "Shows moderation activity on debates", :js do + debate = create(:debate) + + visit debate_path(debate) + + within("#debate_#{debate.id}") do + click_link 'Hide' + end + + visit admin_activity_path + + within("#activity_#{Activity.last.id}") do + expect(page).to have_content(debate.title) + expect(page).to have_content(@admin.user.username) + end + end + + scenario "Shows moderation activity from moderation screen" do + debate1 = create(:debate) + debate2 = create(:debate) + debate3 = create(:debate) + + visit moderation_debates_path(filter: 'all') + + within("#debate_#{debate1.id}") do + check "debate_#{debate1.id}_check" + end + + within("#debate_#{debate3.id}") do + check "debate_#{debate3.id}_check" + end + + click_on "Hide debates" + + visit admin_activity_path + + + expect(page).to have_content(debate1.title) + expect(page).to_not have_content(debate2.title) + expect(page).to have_content(debate3.title) + end + end + end \ No newline at end of file From 064ff631b23186bd91689f48bb7cd246e7ad2c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 16:42:24 +0200 Subject: [PATCH 09/13] logs activity: hiding comments --- .../moderation/comments_controller.rb | 9 +++- spec/features/admin/activity_spec.rb | 45 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/app/controllers/moderation/comments_controller.rb b/app/controllers/moderation/comments_controller.rb index 07c0201ee..d5ce4d431 100644 --- a/app/controllers/moderation/comments_controller.rb +++ b/app/controllers/moderation/comments_controller.rb @@ -14,14 +14,14 @@ class Moderation::CommentsController < Moderation::BaseController end def hide - @comment.hide + hide_comment @comment end def moderate @comments = @comments.where(id: params[:comment_ids]) if params[:hide_comments].present? - @comments.accessible_by(current_ability, :hide).each(&:hide) + @comments.accessible_by(current_ability, :hide).each {|comment| hide_comment comment} elsif params[:ignore_flags].present? @comments.accessible_by(current_ability, :ignore_flag).each(&:ignore_flag) @@ -40,4 +40,9 @@ class Moderation::CommentsController < Moderation::BaseController @comments = Comment.accessible_by(current_ability, :moderate) end + def hide_comment(comment) + comment.hide + Activity.log(current_user, :hide, comment) + end + end diff --git a/spec/features/admin/activity_spec.rb b/spec/features/admin/activity_spec.rb index 2979117d9..d406421e8 100644 --- a/spec/features/admin/activity_spec.rb +++ b/spec/features/admin/activity_spec.rb @@ -95,4 +95,49 @@ feature 'Admin activity' do end end + context "Comments" do + scenario "Shows moderation activity on comments", :js do + debate = create(:debate) + comment = create(:comment, commentable: debate) + + visit debate_path(debate) + + within("#comment_#{comment.id}") do + click_link 'Hide' + end + + visit admin_activity_path + + within("#activity_#{Activity.last.id}") do + expect(page).to have_content(comment.body) + expect(page).to have_content(@admin.user.username) + end + end + + scenario "Shows moderation activity from moderation screen" do + comment1 = create(:comment, body: "SPAM") + comment2 = create(:comment) + comment3 = create(:comment, body: "Offensive!") + + visit moderation_comments_path(filter: 'all') + + within("#comment_#{comment1.id}") do + check "comment_#{comment1.id}_check" + end + + within("#comment_#{comment3.id}") do + check "comment_#{comment3.id}_check" + end + + click_on "Hide comments" + + visit admin_activity_path + + + expect(page).to have_content(comment1.body) + expect(page).to_not have_content(comment2.body) + expect(page).to have_content(comment3.body) + end + end + end \ No newline at end of file From 88ddf161d65df18782482beeeccbab30c6364e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 16:48:52 +0200 Subject: [PATCH 10/13] fixes missing/unused i18n --- config/i18n-tasks.yml | 1 + config/locales/admin.en.yml | 4 +++- config/locales/admin.es.yml | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 6173538ef..b1fa6d362 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -108,6 +108,7 @@ ignore_unused: - 'admin.proposals.index.filter*' - 'admin.organizations.index.filter*' - 'admin.users.index.filter*' + - 'admin.activity.show.filter*' - 'admin.comments.index.hidden_*' - 'moderation.comments.index.filter*' - 'moderation.comments.index.order*' diff --git a/config/locales/admin.en.yml b/config/locales/admin.en.yml index a134cd673..c5d5616a4 100644 --- a/config/locales/admin.en.yml +++ b/config/locales/admin.en.yml @@ -133,6 +133,9 @@ en: activity: show: title: Activity of Moderators + action: Action + by: Moderated by + type: Type filter: Show filters: all: All @@ -144,4 +147,3 @@ en: hide: Hidden restore: Restored block: Blocked - diff --git a/config/locales/admin.es.yml b/config/locales/admin.es.yml index 8c6e43371..0e7903934 100644 --- a/config/locales/admin.es.yml +++ b/config/locales/admin.es.yml @@ -133,6 +133,9 @@ es: activity: show: title: Actividad de los Moderadores + action: 'Acción' + by: Moderado por + type: Tipo filter: Mostrar filters: all: Todo From 6082f77ec2276a1a12db5d9d6212701d7e8267d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 17:08:02 +0200 Subject: [PATCH 11/13] logs activity: user blocking from users moderation screen but also from proposals/debates/comments bulk moderation screens --- .../moderation/comments_controller.rb | 7 +- .../moderation/debates_controller.rb | 7 +- .../moderation/proposals_controller.rb | 7 +- .../moderation/users_controller.rb | 11 +- spec/features/admin/activity_spec.rb | 117 ++++++++++++++++++ 5 files changed, 144 insertions(+), 5 deletions(-) diff --git a/app/controllers/moderation/comments_controller.rb b/app/controllers/moderation/comments_controller.rb index d5ce4d431..7177911e8 100644 --- a/app/controllers/moderation/comments_controller.rb +++ b/app/controllers/moderation/comments_controller.rb @@ -28,7 +28,7 @@ class Moderation::CommentsController < Moderation::BaseController elsif params[:block_authors].present? author_ids = @comments.pluck(:user_id).uniq - User.where(id: author_ids).accessible_by(current_ability, :block).each(&:block) + User.where(id: author_ids).accessible_by(current_ability, :block).each {|user| block_user user} end redirect_to request.query_parameters.merge(action: :index) @@ -45,4 +45,9 @@ class Moderation::CommentsController < Moderation::BaseController Activity.log(current_user, :hide, comment) end + def block_user(user) + user.block + Activity.log(current_user, :block, user) + end + end diff --git a/app/controllers/moderation/debates_controller.rb b/app/controllers/moderation/debates_controller.rb index 1e93ed040..57c6a0e05 100644 --- a/app/controllers/moderation/debates_controller.rb +++ b/app/controllers/moderation/debates_controller.rb @@ -28,7 +28,7 @@ class Moderation::DebatesController < Moderation::BaseController elsif params[:block_authors].present? author_ids = @debates.pluck(:author_id).uniq - User.where(id: author_ids).accessible_by(current_ability, :block).each(&:block) + User.where(id: author_ids).accessible_by(current_ability, :block).each {|user| block_user user} end redirect_to request.query_parameters.merge(action: :index) @@ -45,4 +45,9 @@ class Moderation::DebatesController < Moderation::BaseController Activity.log(current_user, :hide, debate) end + def block_user(user) + user.block + Activity.log(current_user, :block, user) + end + end diff --git a/app/controllers/moderation/proposals_controller.rb b/app/controllers/moderation/proposals_controller.rb index b60d27e50..e59f3c79f 100644 --- a/app/controllers/moderation/proposals_controller.rb +++ b/app/controllers/moderation/proposals_controller.rb @@ -29,7 +29,7 @@ class Moderation::ProposalsController < Moderation::BaseController elsif params[:block_authors].present? author_ids = @proposals.pluck(:author_id).uniq - User.where(id: author_ids).accessible_by(current_ability, :block).each(&:block) + User.where(id: author_ids).accessible_by(current_ability, :block).each {|user| block_user user} end redirect_to request.query_parameters.merge(action: :index) @@ -46,4 +46,9 @@ class Moderation::ProposalsController < Moderation::BaseController Activity.log(current_user, :hide, proposal) end + def block_user(user) + user.block + Activity.log(current_user, :block, user) + end + end diff --git a/app/controllers/moderation/users_controller.rb b/app/controllers/moderation/users_controller.rb index 39f4c200a..fc8b4488e 100644 --- a/app/controllers/moderation/users_controller.rb +++ b/app/controllers/moderation/users_controller.rb @@ -8,12 +8,14 @@ class Moderation::UsersController < Moderation::BaseController end def hide_in_moderation_screen - @user.block + block_user + redirect_to request.query_parameters.merge(action: :index), notice: I18n.t('moderation.users.notice_hide') end def hide - @user.block + block_user + redirect_to debates_path end @@ -23,4 +25,9 @@ class Moderation::UsersController < Moderation::BaseController @users = User.with_hidden.search(params[:name_or_email]).page(params[:page]).for_render end + def block_user + @user.block + Activity.log(current_user, :block, @user) + end + end diff --git a/spec/features/admin/activity_spec.rb b/spec/features/admin/activity_spec.rb index d406421e8..a029caedf 100644 --- a/spec/features/admin/activity_spec.rb +++ b/spec/features/admin/activity_spec.rb @@ -140,4 +140,121 @@ feature 'Admin activity' do end end + context "User" do + scenario "Shows moderation activity on users" do + proposal = create(:proposal) + + visit proposal_path(proposal) + + within("#proposal_#{proposal.id}") do + click_link 'Ban author' + end + + visit admin_activity_path + + within("#activity_#{Activity.last.id}") do + expect(page).to have_content(proposal.author.username) + expect(page).to have_content(proposal.author.email) + expect(page).to have_content(@admin.user.username) + expect(page).not_to have_content(proposal.title) + end + end + + scenario "Shows moderation activity from moderation screen" do + user = create(:user) + + visit moderation_users_path(name_or_email: user.username) + + within(".admin-list") do + click_link 'Ban' + end + + visit admin_activity_path + + within("#activity_#{Activity.last.id}") do + expect(page).to have_content(user.username) + expect(page).to have_content(user.email) + expect(page).to have_content(@admin.user.username) + end + end + + scenario "Shows moderation activity from proposals moderation screen" do + proposal1 = create(:proposal) + proposal2 = create(:proposal) + proposal3 = create(:proposal) + + visit moderation_proposals_path(filter: 'all') + + within("#proposal_#{proposal1.id}") do + check "proposal_#{proposal1.id}_check" + end + + within("#proposal_#{proposal3.id}") do + check "proposal_#{proposal3.id}_check" + end + + click_on "Block authors" + + visit admin_activity_path + + expect(page).to have_content(proposal1.author.username) + expect(page).to have_content(proposal1.author.email) + expect(page).to have_content(proposal3.author.username) + expect(page).to have_content(proposal3.author.email) + expect(page).to_not have_content(proposal2.author.username) + end + + scenario "Shows moderation activity from debates moderation screen" do + debate1 = create(:debate) + debate2 = create(:debate) + debate3 = create(:debate) + + visit moderation_debates_path(filter: 'all') + + within("#debate_#{debate1.id}") do + check "debate_#{debate1.id}_check" + end + + within("#debate_#{debate3.id}") do + check "debate_#{debate3.id}_check" + end + + click_on "Block authors" + + visit admin_activity_path + + expect(page).to have_content(debate1.author.username) + expect(page).to have_content(debate1.author.email) + expect(page).to have_content(debate3.author.username) + expect(page).to have_content(debate3.author.email) + expect(page).to_not have_content(debate2.author.username) + end + + scenario "Shows moderation activity from comments moderation screen" do + comment1 = create(:comment, body: "SPAM") + comment2 = create(:comment) + comment3 = create(:comment, body: "Offensive!") + + visit moderation_comments_path(filter: 'all') + + within("#comment_#{comment1.id}") do + check "comment_#{comment1.id}_check" + end + + within("#comment_#{comment3.id}") do + check "comment_#{comment3.id}_check" + end + + click_on "Block authors" + + visit admin_activity_path + + expect(page).to have_content(comment1.author.username) + expect(page).to have_content(comment1.author.email) + expect(page).to have_content(comment3.author.username) + expect(page).to have_content(comment3.author.email) + expect(page).to_not have_content(comment2.author.username) + end + end + end \ No newline at end of file From 02b1e3020575493ac6d4b05bb1d0f265a25e692c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Tue, 22 Sep 2015 17:24:13 +0200 Subject: [PATCH 12/13] logs activity: restore admin actions on User/Proposal/Debate/Comment --- app/controllers/admin/comments_controller.rb | 1 + app/controllers/admin/debates_controller.rb | 1 + app/controllers/admin/proposals_controller.rb | 1 + app/controllers/admin/users_controller.rb | 1 + app/views/admin/users/index.html.erb | 2 +- spec/features/admin/activity_spec.rb | 80 ++++++++++++++++++- 6 files changed, 82 insertions(+), 4 deletions(-) diff --git a/app/controllers/admin/comments_controller.rb b/app/controllers/admin/comments_controller.rb index ebe2f1066..3a9c360f5 100644 --- a/app/controllers/admin/comments_controller.rb +++ b/app/controllers/admin/comments_controller.rb @@ -14,6 +14,7 @@ class Admin::CommentsController < Admin::BaseController def restore @comment.restore + Activity.log(current_user, :restore, @comment) redirect_to request.query_parameters.merge(action: :index) end diff --git a/app/controllers/admin/debates_controller.rb b/app/controllers/admin/debates_controller.rb index 544c3f23d..41e5af848 100644 --- a/app/controllers/admin/debates_controller.rb +++ b/app/controllers/admin/debates_controller.rb @@ -14,6 +14,7 @@ class Admin::DebatesController < Admin::BaseController def restore @debate.restore + Activity.log(current_user, :restore, @debate) redirect_to request.query_parameters.merge(action: :index) end diff --git a/app/controllers/admin/proposals_controller.rb b/app/controllers/admin/proposals_controller.rb index 4f399bff7..57e8809e2 100644 --- a/app/controllers/admin/proposals_controller.rb +++ b/app/controllers/admin/proposals_controller.rb @@ -14,6 +14,7 @@ class Admin::ProposalsController < Admin::BaseController def restore @proposal.restore + Activity.log(current_user, :restore, @proposal) redirect_to request.query_parameters.merge(action: :index) end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 013fe4e90..eed1c4fea 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -20,6 +20,7 @@ class Admin::UsersController < Admin::BaseController def restore @user.restore + Activity.log(current_user, :restore, @user) redirect_to request.query_parameters.merge(action: :index) end diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb index b3c0ad40b..bb71fcd8f 100644 --- a/app/views/admin/users/index.html.erb +++ b/app/views/admin/users/index.html.erb @@ -6,7 +6,7 @@
      <% @users.each do |user| %> -
    • +
    • <%= link_to user.name, admin_user_path(user) %> <%= link_to t("admin.actions.restore"), diff --git a/spec/features/admin/activity_spec.rb b/spec/features/admin/activity_spec.rb index a029caedf..d2836b2d1 100644 --- a/spec/features/admin/activity_spec.rb +++ b/spec/features/admin/activity_spec.rb @@ -21,6 +21,7 @@ feature 'Admin activity' do within("#activity_#{Activity.last.id}") do expect(page).to have_content(proposal.title) + expect(page).to have_content("Hidden") expect(page).to have_content(@admin.user.username) end end @@ -44,11 +45,28 @@ feature 'Admin activity' do visit admin_activity_path - expect(page).to have_content(proposal1.title) expect(page).to_not have_content(proposal2.title) expect(page).to have_content(proposal3.title) end + + scenario "Shows admin restores" do + proposal = create(:proposal, :hidden) + + visit admin_proposals_path + + within("#proposal_#{proposal.id}") do + click_on "Restore" + end + + visit admin_activity_path + + within("#activity_#{Activity.last.id}") do + expect(page).to have_content(proposal.title) + expect(page).to have_content("Restored") + expect(page).to have_content(@admin.user.username) + end + end end context "Debates" do @@ -65,6 +83,7 @@ feature 'Admin activity' do within("#activity_#{Activity.last.id}") do expect(page).to have_content(debate.title) + expect(page).to have_content("Hidden") expect(page).to have_content(@admin.user.username) end end @@ -88,11 +107,28 @@ feature 'Admin activity' do visit admin_activity_path - expect(page).to have_content(debate1.title) expect(page).to_not have_content(debate2.title) expect(page).to have_content(debate3.title) end + + scenario "Shows admin restores" do + debate = create(:debate, :hidden) + + visit admin_debates_path + + within("#debate_#{debate.id}") do + click_on "Restore" + end + + visit admin_activity_path + + within("#activity_#{Activity.last.id}") do + expect(page).to have_content(debate.title) + expect(page).to have_content("Restored") + expect(page).to have_content(@admin.user.username) + end + end end context "Comments" do @@ -110,6 +146,7 @@ feature 'Admin activity' do within("#activity_#{Activity.last.id}") do expect(page).to have_content(comment.body) + expect(page).to have_content("Hidden") expect(page).to have_content(@admin.user.username) end end @@ -133,11 +170,28 @@ feature 'Admin activity' do visit admin_activity_path - expect(page).to have_content(comment1.body) expect(page).to_not have_content(comment2.body) expect(page).to have_content(comment3.body) end + + scenario "Shows admin restores" do + comment = create(:comment, :hidden) + + visit admin_comments_path + + within("#comment_#{comment.id}") do + click_on "Restore" + end + + visit admin_activity_path + + within("#activity_#{Activity.last.id}") do + expect(page).to have_content(comment.body) + expect(page).to have_content("Restored") + expect(page).to have_content(@admin.user.username) + end + end end context "User" do @@ -153,6 +207,7 @@ feature 'Admin activity' do visit admin_activity_path within("#activity_#{Activity.last.id}") do + expect(page).to have_content("Blocked") expect(page).to have_content(proposal.author.username) expect(page).to have_content(proposal.author.email) expect(page).to have_content(@admin.user.username) @@ -255,6 +310,25 @@ feature 'Admin activity' do expect(page).to have_content(comment3.author.email) expect(page).to_not have_content(comment2.author.username) end + + scenario "Shows admin restores" do + user = create(:user, :hidden) + + visit admin_users_path + + within("#user_#{user.id}") do + click_on "Restore" + end + + visit admin_activity_path + + within("#activity_#{Activity.last.id}") do + expect(page).to have_content(user.username) + expect(page).to have_content(user.email) + expect(page).to have_content("Restored") + expect(page).to have_content(@admin.user.username) + end + end end end \ No newline at end of file From 9969884a504f7e6e209a6e2bcb3791409a4f1102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baz=C3=A1n?= Date: Wed, 23 Sep 2015 12:32:36 +0200 Subject: [PATCH 13/13] minirefactors --- app/models/activity.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/activity.rb b/app/models/activity.rb index b780bd9d2..977204669 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -18,7 +18,7 @@ class Activity < ActiveRecord::Base end def self.on(actionable) - where(actionable_type: actionable.class.name, actionable_id: actionable.id) + where(actionable: actionable) end def self.by(user)