diff --git a/app/controllers/moderation/proposals_controller.rb b/app/controllers/moderation/proposals_controller.rb index ac051faee..52b4222e9 100644 --- a/app/controllers/moderation/proposals_controller.rb +++ b/app/controllers/moderation/proposals_controller.rb @@ -1,6 +1,44 @@ class Moderation::ProposalsController < Moderation::BaseController + + has_filters %w{pending_flag_review all with_ignored_flag}, only: :index + has_orders %w{created_at flags}, only: :index + + before_filter :load_proposals, only: [:index, :moderate] + load_and_authorize_resource + + def index + @proposals = @proposals.send(@current_filter) + .send("sort_by_#{@current_order}") + .page(params[:page]) + .per(50) + end + def hide @proposal.hide end + + def moderate + @proposals = @proposals.where(id: params[:proposal_ids]) + + if params[:hide_proposals].present? + @proposals.accessible_by(current_ability, :hide).each(&:hide) + + elsif params[:ignore_flags].present? + @proposals.accessible_by(current_ability, :ignore_flag).each(&:ignore_flag) + + elsif params[:block_authors].present? + author_ids = @proposals.pluck(:author_id).uniq + User.where(id: author_ids).accessible_by(current_ability, :block).each(&:block) + end + + redirect_to request.query_parameters.merge(action: :index) + end + + private + + def load_proposals + @proposals = Proposal.accessible_by(current_ability, :moderate) + end + end diff --git a/app/views/moderation/proposals/index.html.erb b/app/views/moderation/proposals/index.html.erb new file mode 100644 index 000000000..c7abaf18a --- /dev/null +++ b/app/views/moderation/proposals/index.html.erb @@ -0,0 +1,77 @@ +

<%= t("moderation.proposals.index.title") %>

+ +<%= render 'shared/filter_subnav', i18n_namespace: "moderation.proposals.index" %> + +
+

<%= page_entries_info @proposals %>

+
+
+ <%= t("moderation.proposals.index.order") %> + <%= render 'shared/order_selector', i18n_namespace: "moderation.proposals.index" %> +
+
+
+ +<%= form_tag moderate_moderation_proposals_path(request.query_parameters), method: :put do %> +

+ <%= t('shared.check') %>: + <%= link_to t('shared.check_all'), '#', data: {check_all: "proposal_ids[]"} %> + | + <%= link_to t('shared.check_none'), '#', data: {check_none: "proposal_ids[]"} %> +

+ + + + + + + <% @proposals.each do |proposal| %> + + + + + <% end %> +
+ <%= t("moderation.proposals.index.headers.proposal") %> + + <%= t("moderation.proposals.index.headers.moderate") %> +
+ <%= link_to proposal.title, proposal, target: "_blank" %> +
+ <%= l proposal.updated_at.to_date %> +  •  + <%= proposal.flags_count %> +  •  + <%= proposal.author.username %> +
+
+ <%= proposal.description %> +
+
+ <%= check_box_tag "proposal_ids[]", proposal.id, nil, id: "#{dom_id(proposal)}_check" %> +
+ + + <%= submit_tag t('moderation.proposals.index.block_authors'), + name: "block_authors", + class: "button radius alert", + data: {confirm: t('moderation.proposals.index.confirm')} + %> + +
+ <%= submit_tag t('moderation.proposals.index.hide_proposals'), + name: "hide_proposals", + class: "button radius alert", + data: {confirm: t('moderation.proposals.index.confirm')} + %> + <%= submit_tag t('moderation.proposals.index.ignore_flags'), + name: "ignore_flags", + class: "button radius success", + data: {confirm: t('moderation.proposals.index.confirm')} + %> +
+ + <%= paginate @proposals %> + +<% end %> + diff --git a/config/locales/en.yml b/config/locales/en.yml index d6d1b723c..41f65ae8d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -281,6 +281,9 @@ en: flag: Flag as inappropriate unflag: Undo flag collective: Collective + check: Select + check_all: All + check_none: None mailer: comment: subject: Someone has commented on your debate diff --git a/config/locales/es.yml b/config/locales/es.yml index c9c2e59b1..e9af90fc7 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -283,6 +283,9 @@ es: flag: Denunciar como inapropiado unflag: Deshacer denuncia collective: Colectivo + check: Seleccionar + check_all: Todos + check_none: Ninguno mailer: comment: subject: Alguien ha comentado en tu propuesta diff --git a/config/locales/moderation.en.yml b/config/locales/moderation.en.yml index 30e35ed88..3a7d2f6f1 100644 --- a/config/locales/moderation.en.yml +++ b/config/locales/moderation.en.yml @@ -42,6 +42,28 @@ en: all: All pending_flag_review: Pending with_ignored_flag: Ignored + proposals: + index: + hide_proposals: Hide proposals + block_authors: Block authors + ignore_flags: Ignore flags + title: Proposals + headers: + proposal: Proposal + moderate: Moderate + hide: Hide + ignore_flag: Ignore + ignored_flag: Ignored + filter: Filter + filters: + all: All + pending_flag_review: Pending + with_ignored_flag: Ignored + order: Order + orders: + created_at: Newest + flags: Most flagged + confirm: Are you sure? bulk: index: title: Bulk moderation diff --git a/config/locales/moderation.es.yml b/config/locales/moderation.es.yml index 4fb81845a..02f9fa689 100644 --- a/config/locales/moderation.es.yml +++ b/config/locales/moderation.es.yml @@ -42,6 +42,28 @@ es: all: Todos pending_flag_review: Pendientes with_ignored_flag: Ignorados + proposals: + index: + hide_proposals: Ocultar Propuestas + block_authors: Bloquear autores + ignore_flags: Marcar como revisadas + title: Propuestas + headers: + proposal: Propuesta + moderate: Moderar + hide: Hide + ignore_flag: Ignore + ignored_flag: Ignored + filter: Filtro + filters: + all: Todas + pending_flag_review: Pendientes de revisión + with_ignored_flag: Marcadas como revisadas + order: Ordenar por + orders: + created_at: Más recientes + flags: Más denunciadas + confirm: ¿Estás seguro? bulk: index: title: Moderar en bloque diff --git a/config/routes.rb b/config/routes.rb index 08ec22632..656117d0f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -134,8 +134,9 @@ Rails.application.routes.draw do resources :proposals, only: :index do member do put :hide - put :hide_in_moderation_screen - put :ignore_flag + end + collection do + put :moderate end end diff --git a/spec/features/moderation/proposals_spec.rb b/spec/features/moderation/proposals_spec.rb index 2a0eeff8c..50572c49e 100644 --- a/spec/features/moderation/proposals_spec.rb +++ b/spec/features/moderation/proposals_spec.rb @@ -35,4 +35,143 @@ feature 'Moderate proposals' do expect(page).to_not have_link('Block author') end end + + feature '/moderation/ screen' do + + background do + moderator = create(:moderator) + login_as(moderator.user) + end + + feature 'moderate in bulk' do + feature "When a proposal has been selected for moderation" do + background do + @proposal = create(:proposal) + visit moderation_proposals_path + + within("#proposal_#{@proposal.id}") do + check "proposal_#{@proposal.id}_check" + end + + expect(page).to_not have_css("proposal_#{@proposal.id}") + end + + scenario 'Hide the proposal' do + click_on "Hide proposals" + expect(page).to_not have_css("proposal_#{@proposal.id}") + expect(@proposal.reload).to be_hidden + expect(@proposal.author).to_not be_hidden + end + + scenario 'Block the author' do + click_on "Block authors" + expect(page).to_not have_css("proposal_#{@proposal.id}") + expect(@proposal.reload).to be_hidden + expect(@proposal.author).to be_hidden + end + + scenario 'Ignore the proposal' do + click_on "Ignore flags" + expect(page).to_not have_css("proposal_#{@proposal.id}") + expect(@proposal.reload).to be_ignored_flag + expect(@proposal.reload).to_not be_hidden + expect(@proposal.author).to_not be_hidden + end + end + + scenario "select all/none", :js do + create_list(:proposal, 20) + + visit moderation_proposals_path + + within('.js-check') { click_on 'All' } + + all('input[type=checkbox]').each do |checkbox| + expect(checkbox).to be_checked + end + + within('.js-check') { click_on 'None' } + + all('input[type=checkbox]').each do |checkbox| + expect(checkbox).to_not be_checked + end + end + + scenario "remembering page, filter and order" do + create_list(:proposal, 55) + + visit moderation_proposals_path(filter: 'all', page: '2', order: 'created_at') + + click_on "Ignore flags" + + expect(page).to have_selector('.js-order-selector[data-order="created_at"]') + + expect(current_url).to include('filter=all') + expect(current_url).to include('page=2') + expect(current_url).to include('order=created_at') + end + end + + scenario "Current filter is properly highlighted" do + visit moderation_proposals_path + expect(page).to_not have_link('Pending') + expect(page).to have_link('All') + expect(page).to have_link('Ignored') + + visit moderation_proposals_path(filter: 'all') + within('.sub-nav') do + expect(page).to_not have_link('All') + expect(page).to have_link('Pending') + expect(page).to have_link('Ignored') + end + + visit moderation_proposals_path(filter: 'pending_flag_review') + within('.sub-nav') do + expect(page).to have_link('All') + expect(page).to_not have_link('Pending') + expect(page).to have_link('Ignored') + end + + visit moderation_proposals_path(filter: 'with_ignored_flag') + within('.sub-nav') do + expect(page).to have_link('All') + expect(page).to have_link('Pending') + expect(page).to_not have_link('Ignored') + end + end + + scenario "Filtering proposals" do + create(:proposal, title: "Pending proposal") + create(:proposal, :hidden, title: "Hidden proposal") + create(:proposal, :with_ignored_flag, title: "Ignored proposal") + + visit moderation_proposals_path(filter: 'all') + expect(page).to have_content('Pending proposal') + expect(page).to_not have_content('Hidden proposal') + expect(page).to have_content('Ignored proposal') + + visit moderation_proposals_path(filter: 'pending_flag_review') + expect(page).to have_content('Pending proposal') + expect(page).to_not have_content('Hidden proposal') + expect(page).to_not have_content('Ignored proposal') + + visit moderation_proposals_path(filter: 'with_ignored_flag') + expect(page).to_not have_content('Pending proposal') + expect(page).to_not have_content('Hidden proposal') + expect(page).to have_content('Ignored proposal') + end + + scenario "sorting proposals" do + create(:proposal, title: "Flagged proposal", created_at: Time.now - 1.day, flags_count: 5) + create(:proposal, title: "Newer proposal", created_at: Time.now) + + visit moderation_proposals_path(order: 'created_at') + + expect("Newer proposal").to appear_before("Flagged proposal") + + visit moderation_proposals_path(order: 'flags') + + expect("Flagged proposal").to appear_before("Newer proposal") + end + end end