diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 91837158a..08bb60faa 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -30,6 +30,7 @@ @import "legislation"; @import "legislation_process"; @import "legislation_process_form"; +@import "moderation_actions"; @import "notification_item"; @import "community"; @import "stats"; @@ -40,6 +41,7 @@ @import "debates/**/*"; @import "layout/**/*"; @import "machine_learning/**/*"; +@import "moderation/**/*"; @import "proposals/**/*"; @import "relationable/**/*"; @import "sdg/**/*"; diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index 45a7c45df..4c38b27d7 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -1877,6 +1877,10 @@ table { padding: $line-height / 4; position: relative; + &:empty { + display: none; + } + .divider { color: $text-light; display: inline-block; diff --git a/app/assets/stylesheets/moderation/users/index.scss b/app/assets/stylesheets/moderation/users/index.scss new file mode 100644 index 000000000..849153b24 --- /dev/null +++ b/app/assets/stylesheets/moderation/users/index.scss @@ -0,0 +1,18 @@ +.moderation-users-index { + th:last-child, + td:last-child { + text-align: $global-right; + } + + .table-actions { + justify-content: flex-end; + } + + .hide-link { + @include hollow-button($color-warning); + } + + .block-link { + @include hollow-button($alert-color); + } +} diff --git a/app/assets/stylesheets/moderation_actions.scss b/app/assets/stylesheets/moderation_actions.scss new file mode 100644 index 000000000..f3a00d662 --- /dev/null +++ b/app/assets/stylesheets/moderation_actions.scss @@ -0,0 +1,36 @@ +.moderation-actions { + &, + form { + display: inline; + } + + button { + @include link; + } + + @mixin separator { + content: ""; + display: inline-block; + margin: 0 0.3em; + vertical-align: middle; + } + + > * + *::before { + @include separator; + background: currentcolor; + height: 1em; + opacity: 0.5; + width: 1px; + } + + .comment & { + > *::before { + @include separator; + background: $text-light; + border-radius: 100%; + opacity: 1; + height: 0.25em; + width: 0.25em; + } + } +} diff --git a/app/components/admin/action_component.rb b/app/components/admin/action_component.rb index 2235bb682..33e00e01d 100644 --- a/app/components/admin/action_component.rb +++ b/app/components/admin/action_component.rb @@ -68,6 +68,8 @@ class Admin::ActionComponent < ApplicationComponent else t("admin.actions.confirm_action", action: text, name: record_name) end + elsif options[:confirm].respond_to?(:call) + options[:confirm].call(record_name) else options[:confirm] end diff --git a/app/components/moderation/users/index_component.html.erb b/app/components/moderation/users/index_component.html.erb new file mode 100644 index 000000000..ef5cb7db2 --- /dev/null +++ b/app/components/moderation/users/index_component.html.erb @@ -0,0 +1,47 @@ +
+

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

+ + <%= render Admin::SearchComponent.new(label: t("moderation.users.index.search_placeholder")) %> + + <% if users.present? %> +

<%= page_entries_info users %>

+ + + + + + + + <% users.each do |user| %> + + + + + <% end %> + +
<%= t("admin.hidden_users.index.user") %><%= t("admin.actions.actions") %>
+ <%= user.name %> + + <% if user.hidden? %> + <%= status(user) %> + <% else %> + <%= render Admin::TableActionsComponent.new(user, actions: []) do |actions| %> + <%= actions.action( + :hide, + text: t("moderation.users.index.hide"), + confirm: ->(name) { t("moderation.users.index.confirm_hide", name: name) }, + method: :put + ) %> + <%= actions.action( + :block, + text: t("moderation.users.index.block"), + confirm: ->(name) { t("moderation.users.index.confirm_block", name: name) }, + method: :put + ) %> + <% end %> + <% end %> +
+ + <%= paginate users %> + <% end %> +
diff --git a/app/components/moderation/users/index_component.rb b/app/components/moderation/users/index_component.rb new file mode 100644 index 000000000..ddfa20e85 --- /dev/null +++ b/app/components/moderation/users/index_component.rb @@ -0,0 +1,17 @@ +class Moderation::Users::IndexComponent < ApplicationComponent + attr_reader :users + + def initialize(users) + @users = users + end + + private + + def status(user) + t("admin.activity.show.actions.#{activity_action(user)}") + end + + def activity_action(user) + Activity.where(actionable: user, action: [:hide, :block]).last&.action || "block" + end +end diff --git a/app/components/shared/moderation_actions_component.html.erb b/app/components/shared/moderation_actions_component.html.erb new file mode 100644 index 000000000..a3aed9369 --- /dev/null +++ b/app/components/shared/moderation_actions_component.html.erb @@ -0,0 +1,23 @@ +
+ <% if can? :hide, record %> + <%= render Admin::ActionComponent.new( + :hide, + record, + path: hide_path, + method: :put, + remote: true, + confirm: true + ) %> + <% end %> + + <% if can? :hide, author %> + <%= render Admin::ActionComponent.new( + :block_author, + author, + path: block_moderation_user_path(author), + id: dom_id(author, "#{dom_id(record)}_block_author"), + method: :put, + confirm: ->(name) { t("moderation.users.index.confirm_block", name: name) } + ) %> + <% end %> +
diff --git a/app/components/shared/moderation_actions_component.rb b/app/components/shared/moderation_actions_component.rb new file mode 100644 index 000000000..f9f287e09 --- /dev/null +++ b/app/components/shared/moderation_actions_component.rb @@ -0,0 +1,22 @@ +class Shared::ModerationActionsComponent < ApplicationComponent + attr_reader :record + delegate :can?, to: :helpers + + def initialize(record) + @record = record + end + + def render? + can?(:hide, record) || can?(:hide, author) + end + + private + + def author + record.author + end + + def hide_path + polymorphic_path([:moderation, record], action: :hide) + end +end diff --git a/app/controllers/admin/hidden_users_controller.rb b/app/controllers/admin/hidden_users_controller.rb index 1b5635270..86801fcda 100644 --- a/app/controllers/admin/hidden_users_controller.rb +++ b/app/controllers/admin/hidden_users_controller.rb @@ -4,7 +4,7 @@ class Admin::HiddenUsersController < Admin::BaseController before_action :load_user, only: [:confirm_hide, :restore] def index - @users = User.only_hidden.send(@current_filter).page(params[:page]) + @users = User.only_hidden.send(@current_filter).order(hidden_at: :desc).page(params[:page]) end def show diff --git a/app/controllers/moderation/users_controller.rb b/app/controllers/moderation/users_controller.rb index 2c958eda8..b3a043d96 100644 --- a/app/controllers/moderation/users_controller.rb +++ b/app/controllers/moderation/users_controller.rb @@ -6,16 +6,16 @@ class Moderation::UsersController < Moderation::BaseController def index end - def hide_in_moderation_screen - block_user + def hide + hide_user redirect_with_query_params_to({ action: :index }, { notice: I18n.t("moderation.users.notice_hide") }) end - def hide + def block block_user - redirect_to debates_path + redirect_with_query_params_to index_path_options, { notice: I18n.t("moderation.users.notice_block") } end private @@ -24,8 +24,26 @@ class Moderation::UsersController < Moderation::BaseController @users = User.with_hidden.search(params[:search]).page(params[:page]).for_render end + def hide_user + @user.hide + Activity.log(current_user, :hide, @user) + end + def block_user @user.block Activity.log(current_user, :block, @user) end + + def index_path_options + if request.referer + referer_params = Rails.application.routes.recognize_path(request.referer) + + referer_params.except(:id).merge({ + controller: "/#{referer_params[:controller]}", + action: :index + }) + else + { action: :index } + end + end end diff --git a/app/models/ability.rb b/app/models/ability.rb index afe6f416e..45207eaae 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,10 +2,6 @@ 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 - if user # logged-in users merge Abilities::Valuator.new(user) if user.valuator? diff --git a/app/models/comment.rb b/app/models/comment.rb index 89e6c88f1..443d2322d 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -83,6 +83,10 @@ class Comment < ApplicationRecord self.user = author end + def human_name + body.truncate(32) + end + def total_votes cached_votes_total end diff --git a/app/models/user.rb b/app/models/user.rb index 0e4be645d..0c6e03bfe 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -241,6 +241,7 @@ class User < ApplicationRecord Debate.hide_all debate_ids Comment.hide_all comment_ids + Legislation::Proposal.hide_all legislation_proposal_ids Proposal.hide_all proposal_ids Budget::Investment.hide_all budget_investment_ids ProposalNotification.hide_all ProposalNotification.where(author_id: id).ids @@ -250,6 +251,7 @@ class User < ApplicationRecord ActiveRecord::Base.transaction do Debate.restore_all debates.where("hidden_at >= ?", hidden_at) Comment.restore_all comments.where("hidden_at >= ?", hidden_at) + Legislation::Proposal.restore_all legislation_proposals.only_hidden.where("hidden_at >= ?", hidden_at) Proposal.restore_all proposals.where("hidden_at >= ?", hidden_at) Budget::Investment.restore_all budget_investments.where("hidden_at >= ?", hidden_at) ProposalNotification.restore_all( diff --git a/app/views/budgets/investments/_actions.html.erb b/app/views/budgets/investments/_actions.html.erb index 9711a8268..8fd2110fa 100644 --- a/app/views/budgets/investments/_actions.html.erb +++ b/app/views/budgets/investments/_actions.html.erb @@ -1,10 +1 @@ -<% if can? :hide, investment %> - <%= link_to t("admin.actions.hide").capitalize, hide_moderation_budget_investment_path(investment), - method: :put, remote: true, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide"), name: investment.title) } %> -<% end %> - -<% if can? :hide, investment.author %> -  |  - <%= link_to t("admin.actions.hide_author").capitalize, hide_moderation_user_path(investment.author_id), - method: :put, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide_author"), name: investment.author.name) } %> -<% end %> +<%= render Shared::ModerationActionsComponent.new(investment) %> diff --git a/app/views/comments/_actions.html.erb b/app/views/comments/_actions.html.erb index 13a97c96a..7780d9167 100644 --- a/app/views/comments/_actions.html.erb +++ b/app/views/comments/_actions.html.erb @@ -2,23 +2,14 @@ <%= render "shared/flag_actions", flaggable: comment, divider: true %> - - <% if can? :hide, comment %> +<% if can?(:hide, comment) || can?(:hide, comment.user) %> + <% if comment.author == current_user %>  •  - <% if comment.author == current_user %> - <%= link_to t("comments.actions.delete"), - hide_comment_path(comment), - method: :put, remote: true, class: "delete-comment", - data: { confirm: t("comments.actions.confirm_delete") } %> - <% else %> - <%= link_to t("admin.actions.hide").capitalize, hide_moderation_comment_path(comment), - method: :put, remote: true, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide"), name: comment.body.truncate(32)) } %> - <% end %> + <%= link_to t("comments.actions.delete"), + hide_comment_path(comment), + method: :put, remote: true, class: "delete-comment", + data: { confirm: t("comments.actions.confirm_delete") } %> + <% else %> + <%= render Shared::ModerationActionsComponent.new(comment) %> <% end %> - - <% if can? :hide, comment.user %> -  •  - <%= link_to t("admin.actions.hide_author").capitalize, hide_moderation_user_path(comment.user_id), - method: :put, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide_author"), name: comment.author.name) } %> - <% end %> - +<% end %> diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index ae0cb1276..0ea042746 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -2,7 +2,7 @@ <% cache [locale_and_user_status(comment), comment, commentable_cache_key(comment.commentable), comment.author] do %>
- <% if comment.hidden? || comment.user.hidden? %> + <% if comment.hidden? %> <% if comment.children.size > 0 %>

<%= t("comments.comment.deleted") %>

diff --git a/app/views/debates/_actions.html.erb b/app/views/debates/_actions.html.erb index 1959ef29a..3605d3fcf 100644 --- a/app/views/debates/_actions.html.erb +++ b/app/views/debates/_actions.html.erb @@ -1,13 +1,4 @@ -<% if can? :hide, debate %> - <%= link_to t("admin.actions.hide").capitalize, hide_moderation_debate_path(debate), - method: :put, remote: true, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide"), name: debate.title) } %> -<% end %> - -<% if can? :hide, debate.author %> -  |  - <%= link_to t("admin.actions.hide_author").capitalize, hide_moderation_user_path(debate.author_id), - method: :put, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide_author"), name: debate.author.name) } %> -<% end %> +<%= render Shared::ModerationActionsComponent.new(debate) %> <% if can? :mark_featured, debate %>  |  diff --git a/app/views/legislation/proposals/_actions.html.erb b/app/views/legislation/proposals/_actions.html.erb index a887f422c..a0f59692d 100644 --- a/app/views/legislation/proposals/_actions.html.erb +++ b/app/views/legislation/proposals/_actions.html.erb @@ -1,10 +1 @@ -<% if can? :hide, proposal %> - <%= link_to t("admin.actions.hide").capitalize, hide_moderation_legislation_proposal_path(proposal), - method: :put, remote: true, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide"), name: proposal.title) } %> -<% end %> - -<% if can? :hide, proposal.author %> -  |  - <%= link_to t("admin.actions.hide_author").capitalize, hide_moderation_user_path(proposal.author_id), - method: :put, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide_author"), name: proposal.author.name) } %> -<% end %> +<%= render Shared::ModerationActionsComponent.new(proposal) %> diff --git a/app/views/moderation/users/index.html.erb b/app/views/moderation/users/index.html.erb index 9008f8fec..39b526378 100644 --- a/app/views/moderation/users/index.html.erb +++ b/app/views/moderation/users/index.html.erb @@ -1,29 +1 @@ -

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

- -<%= render Admin::SearchComponent.new(label: t("moderation.users.index.search_placeholder")) %> - -<% if @users.present? %> -

<%= page_entries_info @users %>

-<% end %> - - - - <% @users.each do |user| %> - - - - <% end %> - - <% end %> - -
- <%= user.name %> - - <% if user.hidden? %> - <%= t("moderation.users.index.hidden") %> - <% else %> - <%= link_to t("moderation.users.index.hide"), hide_in_moderation_screen_moderation_user_path(user, request.query_parameters), - method: :put, class: "button hollow alert" %> -
- -<%= paginate @users %> +<%= render Moderation::Users::IndexComponent.new(@users) %> diff --git a/app/views/proposal_notifications/_actions.html.erb b/app/views/proposal_notifications/_actions.html.erb index 5c3f96ff1..e3c4b356c 100644 --- a/app/views/proposal_notifications/_actions.html.erb +++ b/app/views/proposal_notifications/_actions.html.erb @@ -1,16 +1 @@ -<% if can? :hide, (notification || notification.author) %> -
- - <% if can? :hide, notification %> - <%= link_to t("admin.actions.hide").capitalize, hide_moderation_proposal_notification_path(notification), - method: :put, remote: true, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide"), name: notification.title) } %> - <% end %> - - <% if can? :hide, notification.author %> -  •  - <%= link_to t("admin.actions.hide_author").capitalize, hide_moderation_user_path(notification.author_id), - method: :put, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide_author"), name: notification.author.name) } %> - <% end %> - -
-<% end %> +
<%= render Shared::ModerationActionsComponent.new(notification) %>
diff --git a/app/views/proposals/_actions.html.erb b/app/views/proposals/_actions.html.erb index a568c04df..a0f59692d 100644 --- a/app/views/proposals/_actions.html.erb +++ b/app/views/proposals/_actions.html.erb @@ -1,10 +1 @@ -<% if can? :hide, proposal %> - <%= link_to t("admin.actions.hide").capitalize, hide_moderation_proposal_path(proposal), - method: :put, remote: true, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide"), name: proposal.title) } %> -<% end %> - -<% if can? :hide, proposal.author %> -  |  - <%= link_to t("admin.actions.hide_author").capitalize, hide_moderation_user_path(proposal.author_id), - method: :put, data: { confirm: t("admin.actions.confirm_action", action: t("admin.actions.hide_author"), name: proposal.author.name) } %> -<% end %> +<%= render Shared::ModerationActionsComponent.new(proposal) %> diff --git a/app/views/proposals/_notifications.html.erb b/app/views/proposals/_notifications.html.erb index c91058d27..6452bdc66 100644 --- a/app/views/proposals/_notifications.html.erb +++ b/app/views/proposals/_notifications.html.erb @@ -13,9 +13,7 @@

<%= notification.created_at.to_date %>

<%= simple_format sanitize_and_auto_link(notification.body), {}, sanitize: false %> - - <%= render "proposal_notifications/actions", notification: notification %> - + <%= render "proposal_notifications/actions", notification: notification %>
<% end %>
diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index ea4897c6a..a740a897a 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -4,12 +4,12 @@ en: title: Administration actions: actions: Actions + block_author: "Block author" confirm_action: "Are you sure? %{action} \"%{name}\"" confirm_delete: "Are you sure? This action will delete \"%{name}\" and can't be undone." confirm_hide: Confirm moderation delete: "Delete" hide: Hide - hide_author: Hide author label: "%{action} %{name}" restore: Restore mark_featured: Featured diff --git a/config/locales/en/moderation.yml b/config/locales/en/moderation.yml index 7f3b8d83c..cdf884c4c 100644 --- a/config/locales/en/moderation.yml +++ b/config/locales/en/moderation.yml @@ -101,8 +101,11 @@ en: title: Proposal notifications users: index: - hidden: Blocked - hide: Block + block: Block + confirm_block: "Are you sure? This will hide the user \"%{name}\" and all their contents." + confirm_hide: "Are you sure? This will hide the user \"%{name}\" without hiding their contents." + hide: Hide search_placeholder: email or name of user title: Block users - notice_hide: User blocked. All of this user's debates and comments have been hidden. + notice_block: The user has been blocked. All contents authored by this user have been hidden. + notice_hide: The user has been hidden. Contents authored by this user are still available. diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 82a55c334..9ff38ec50 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -4,12 +4,12 @@ es: title: Administración actions: actions: Acciones + block_author: "Bloquear al autor" confirm_action: "¿Estás seguro? %{action} \"%{name}\"" confirm_delete: "¿Estás seguro? Esta acción borrará \"%{name}\" y no se puede deshacer." confirm_hide: Confirmar moderación delete: Borrar hide: Ocultar - hide_author: Bloquear al autor label: "%{action} %{name}" restore: Volver a mostrar mark_featured: Destacar diff --git a/config/locales/es/moderation.yml b/config/locales/es/moderation.yml index 1cc2f47d0..706b61735 100644 --- a/config/locales/es/moderation.yml +++ b/config/locales/es/moderation.yml @@ -101,8 +101,11 @@ es: title: Notificaciones de propuestas users: index: - hidden: Bloqueado - hide: Bloquear + block: Bloquear + confirm_block: "¿Seguro? Esto ocultará a \"%{name}\" así como todos sus contenidos." + confirm_hide: "¿Seguro? Esto ocultará a \"%{name}\" sin ocultar sus contenidos." + hide: Ocultar search_placeholder: email o nombre de usuario title: Bloquear usuarios - notice_hide: Usuario bloqueado. Se han ocultado todos sus debates y comentarios. + notice_block: Usuario bloqueado. Se han ocultado todos sus contenidos y comentarios. + notice_hide: Usuario ocultado. Todos sus contenidos y comentarios siguen disponibles. diff --git a/config/routes/moderation.rb b/config/routes/moderation.rb index 83984badd..2bf1f41e7 100644 --- a/config/routes/moderation.rb +++ b/config/routes/moderation.rb @@ -4,7 +4,7 @@ namespace :moderation do resources :users, only: :index do member do put :hide - put :hide_in_moderation_screen + put :block end end diff --git a/spec/components/moderation/users/index_component_spec.rb b/spec/components/moderation/users/index_component_spec.rb new file mode 100644 index 000000000..da1f8505f --- /dev/null +++ b/spec/components/moderation/users/index_component_spec.rb @@ -0,0 +1,103 @@ +require "rails_helper" + +describe Moderation::Users::IndexComponent, controller: Moderation::UsersController do + describe "actions or status" do + let(:component) { Moderation::Users::IndexComponent.new(User.with_hidden.page(1)) } + + it "shows actions to block or hide users" do + create(:user) + + render_inline component + + page.find("table") do |table| + expect(table).to have_button "Hide" + expect(table).to have_button "Block" + expect(table).not_to have_content "Blocked" + expect(table).not_to have_content "Hidden" + end + end + + context "for hidden users" do + it "shows 'blocked' when the user has been blocked" do + user = create(:user, :hidden) + Activity.log(nil, :block, user) + + render_inline component + + page.find("table") do |table| + expect(table).to have_content "Blocked" + expect(table).not_to have_content "Hidden" + expect(table).not_to have_button + end + end + + it "shows 'hidden' when the user has been hidden" do + user = create(:user, :hidden) + Activity.log(nil, :hide, user) + + render_inline component + + page.find("table") do |table| + expect(table).to have_content "Hidden" + expect(table).not_to have_content "Blocked" + expect(table).not_to have_button + end + end + + it "shows 'blocked' when there are no activities to hide the user" do + create(:user, :hidden) + + render_inline component + + page.find("table") do |table| + expect(table).to have_content "Blocked" + end + end + + it "is not affected by activities for other users" do + blocked = create(:user, :hidden, username: "Very bad user") + hidden = create(:user, :hidden, username: "Slightly bad user") + + Activity.log(nil, :block, blocked) + Activity.log(nil, :hide, hidden) + + render_inline component + + page.find("tr", text: "Very bad user") do |row| + expect(row).to have_content "Blocked" + end + + page.find("tr", text: "Slightly bad user") do |row| + expect(row).to have_content "Hidden" + end + end + + it "doesn't consider activities other than block or hide" do + user = create(:user, :hidden) + Activity.log(nil, :block, user) + Activity.log(nil, :restore, user) + + render_inline component + + page.find("table") do |table| + expect(table).to have_content "Blocked" + end + end + + it "shows actions after the user has been restored" do + user = create(:user, :hidden) + Activity.log(nil, :block, user) + user.restore + + render_inline component + + page.find("table") do |table| + expect(table).to have_button "Hide" + expect(table).to have_button "Block" + expect(table).not_to have_content "Blocked" + expect(table).not_to have_content "Hidden" + end + end + end + end +end diff --git a/spec/components/shared/moderation_actions_component_spec.rb b/spec/components/shared/moderation_actions_component_spec.rb new file mode 100644 index 000000000..f305a14d6 --- /dev/null +++ b/spec/components/shared/moderation_actions_component_spec.rb @@ -0,0 +1,68 @@ +require "rails_helper" + +describe Shared::ModerationActionsComponent do + include Rails.application.routes.url_helpers + before { sign_in(create(:administrator).user) } + + describe "Hide button" do + it "is shown for debates" do + debate = create(:debate) + + render_inline Shared::ModerationActionsComponent.new(debate) + + page.find("form[action='#{hide_moderation_debate_path(debate)}']") do + expect(page).to have_button "Hide" + end + end + + it "is shown for proposals" do + proposal = create(:proposal) + + render_inline Shared::ModerationActionsComponent.new(proposal) + + page.find("form[action='#{hide_moderation_proposal_path(proposal)}']") do + expect(page).to have_button "Hide" + end + end + + it "is shown for proposal notifications" do + notification = create(:proposal_notification) + + render_inline Shared::ModerationActionsComponent.new(notification) + + page.find("form[action='#{hide_moderation_proposal_notification_path(notification)}']") do + expect(page).to have_button "Hide" + end + end + + it "is shown for comments" do + comment = create(:comment) + + render_inline Shared::ModerationActionsComponent.new(comment) + + page.find("form[action='#{hide_moderation_comment_path(comment)}']") do + expect(page).to have_button "Hide" + end + end + + it "is shown for budget investments" do + investment = create(:budget_investment) + + render_inline Shared::ModerationActionsComponent.new(investment) + + page.find("form[action='#{hide_moderation_budget_investment_path(investment)}']") do + expect(page).to have_button "Hide" + end + end + + it "is shown for legislation proposals" do + proposal = create(:legislation_proposal) + + render_inline Shared::ModerationActionsComponent.new(proposal) + + page.find("form[action='#{hide_moderation_legislation_proposal_path(proposal)}']") do + expect(page).to have_button "Hide" + end + end + end +end diff --git a/spec/controllers/moderation/users_controller_spec.rb b/spec/controllers/moderation/users_controller_spec.rb index 1699c9c4e..a1e9fcea2 100644 --- a/spec/controllers/moderation/users_controller_spec.rb +++ b/spec/controllers/moderation/users_controller_spec.rb @@ -2,14 +2,44 @@ require "rails_helper" describe Moderation::UsersController do before { sign_in create(:moderator).user } + let(:user) { create(:user, email: "user@consul.dev") } - describe "PUT hide_in_moderation_screen" do + describe "PUT hide" do it "keeps query parameters while using protected redirects" do user = create(:user, email: "user@consul.dev") - get :hide_in_moderation_screen, params: { id: user, search: "user@consul.dev", host: "evil.dev" } + get :hide, params: { id: user, name_or_email: "user@consul.dev", host: "evil.dev" } + + expect(response).to redirect_to "/moderation/users?name_or_email=user%40consul.dev" + end + end + + describe "PUT block" do + it "keeps query parameters while using protected redirects" do + user = create(:user, email: "user@consul.dev") + + get :block, params: { id: user, search: "user@consul.dev", host: "evil.dev" } expect(response).to redirect_to "/moderation/users?search=user%40consul.dev" end + + it "redirects to the index of the section where it was called with a notice" do + proposal = create(:proposal, author: user) + request.env["HTTP_REFERER"] = proposal_path(proposal) + + put :block, params: { id: user } + + expect(response).to redirect_to proposals_path + expect(flash[:notice]).to eq "The user has been blocked. All contents authored by this user have been hidden." + end + + it "redirects to the index with a nested resource" do + investment = create(:budget_investment, author: user) + request.env["HTTP_REFERER"] = budget_investment_path(investment.budget, investment) + + put :block, params: { id: user } + + expect(response).to redirect_to budget_investments_path(investment.budget) + end end end diff --git a/spec/factories/legislations.rb b/spec/factories/legislations.rb index 94693417b..069899884 100644 --- a/spec/factories/legislations.rb +++ b/spec/factories/legislations.rb @@ -164,5 +164,9 @@ FactoryBot.define do terms_of_service { "1" } process factory: :legislation_process author factory: :user + + trait :hidden do + hidden_at { Time.current } + end end end diff --git a/spec/models/abilities/moderator_spec.rb b/spec/models/abilities/moderator_spec.rb index 77bf1ae36..9f3cb14dc 100644 --- a/spec/models/abilities/moderator_spec.rb +++ b/spec/models/abilities/moderator_spec.rb @@ -52,7 +52,6 @@ describe Abilities::Moderator do let(:ignored_proposal) { create(:proposal, :with_ignored_flag) } 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 be_able_to(:hide, own_comment) } @@ -60,12 +59,10 @@ describe Abilities::Moderator do it { should_not be_able_to(:moderate, 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(:hide, proposal) } - it { should be_able_to(:hide_in_moderation_screen, proposal) } it { should be_able_to(:hide, own_proposal) } it { should_not be_able_to(:hide, hidden_proposal) } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index d24392549..9c326f846 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -758,6 +758,21 @@ describe User do end end + describe "#block" do + it "hides legislation proposals created by the user" do + user = create(:user) + other_user = create(:user) + + proposal = create(:legislation_proposal, author: user) + other_proposal = create(:legislation_proposal, author: other_user) + + user.block + + expect(Legislation::Proposal.all).to eq [other_proposal] + expect(Legislation::Proposal.with_hidden).to match_array [proposal, other_proposal] + end + end + describe "#full_restore" do it "restore all previous hidden user content" do user = create(:user, :hidden) @@ -768,18 +783,21 @@ describe User do investment = create(:budget_investment, :hidden, author: user) proposal = create(:proposal, :hidden, author: user) proposal_notification = create(:proposal_notification, :hidden, proposal: proposal) + legislation_proposal = create(:legislation_proposal, :hidden, author: user) old_hidden_comment = create(:comment, hidden_at: 3.days.ago, author: user) old_hidden_debate = create(:debate, hidden_at: 3.days.ago, author: user) old_hidden_investment = create(:budget_investment, hidden_at: 3.days.ago, author: user) old_hidden_proposal = create(:proposal, hidden_at: 3.days.ago, author: user) old_hidden_proposal_notification = create(:proposal_notification, hidden_at: 3.days.ago, proposal: proposal) + old_hidden_legislation_proposal = create(:legislation_proposal, hidden_at: 3.days.ago, author: user) other_user_comment = create(:comment, :hidden, author: other_user) other_user_debate = create(:debate, :hidden, author: other_user) other_user_proposal = create(:proposal, :hidden, author: other_user) other_user_investment = create(:budget_investment, :hidden, author: other_user) other_user_proposal_notification = create(:proposal_notification, :hidden, proposal: other_user_proposal) + other_user_legislation_proposal = create(:legislation_proposal, :hidden, author: other_user) user.full_restore @@ -788,18 +806,21 @@ describe User do expect(investment.reload).not_to be_hidden expect(proposal.reload).not_to be_hidden expect(proposal_notification.reload).not_to be_hidden + expect(legislation_proposal.reload).not_to be_hidden expect(old_hidden_comment.reload).to be_hidden expect(old_hidden_debate.reload).to be_hidden expect(old_hidden_investment.reload).to be_hidden expect(old_hidden_proposal.reload).to be_hidden expect(old_hidden_proposal_notification.reload).to be_hidden + expect(old_hidden_legislation_proposal.reload).to be_hidden expect(other_user_comment.reload).to be_hidden expect(other_user_debate.reload).to be_hidden expect(other_user_investment.reload).to be_hidden expect(other_user_proposal.reload).to be_hidden expect(other_user_proposal_notification.reload).to be_hidden + expect(other_user_legislation_proposal.reload).to be_hidden end end end diff --git a/spec/system/admin/activity_spec.rb b/spec/system/admin/activity_spec.rb index f973b041b..ee6023993 100644 --- a/spec/system/admin/activity_spec.rb +++ b/spec/system/admin/activity_spec.rb @@ -14,7 +14,7 @@ describe "Admin activity" do visit proposal_path(proposal) within("#proposal_#{proposal.id}") do - accept_confirm("Are you sure? Hide \"#{proposal.title}\"") { click_link "Hide" } + accept_confirm("Are you sure? Hide \"#{proposal.title}\"") { click_button "Hide" } end expect(page).to have_css("#proposal_#{proposal.id}.faded") @@ -82,7 +82,7 @@ describe "Admin activity" do visit debate_path(debate) within("#debate_#{debate.id}") do - accept_confirm("Are you sure? Hide \"#{debate.title}\"") { click_link "Hide" } + accept_confirm("Are you sure? Hide \"#{debate.title}\"") { click_button "Hide" } end expect(page).to have_css("#debate_#{debate.id}.faded") @@ -150,7 +150,7 @@ describe "Admin activity" do visit debate_path(debate) within("#comment_#{comment.id}") do - accept_confirm("Are you sure? Hide \"#{comment.body}\"") { click_link "Hide" } + accept_confirm("Are you sure? Hide \"#{comment.body}\"") { click_button "Hide" } expect(page).to have_css(".faded") end @@ -217,9 +217,11 @@ describe "Admin activity" do visit proposal_path(proposal) within("#proposal_#{proposal.id}") do - accept_confirm("Are you sure? Hide author \"#{proposal.author.name}\"") { click_link "Hide author" } + accept_confirm("Are you sure? This will hide the user \"#{proposal.author.name}\" and all their contents.") do + click_button "Block author" + end - expect(page).to have_current_path(debates_path) + expect(page).to have_current_path(proposals_path) end visit admin_activity_path @@ -239,9 +241,11 @@ describe "Admin activity" do visit moderation_users_path(search: user.username) within("#moderation_users") do - click_link "Block" + accept_confirm { click_button "Block" } end + expect(page).to have_content "The user has been blocked" + visit admin_activity_path within first("tbody tr") do diff --git a/spec/system/admin/hidden_comments_spec.rb b/spec/system/admin/hidden_comments_spec.rb index 2ac07cfa9..36902e4d8 100644 --- a/spec/system/admin/hidden_comments_spec.rb +++ b/spec/system/admin/hidden_comments_spec.rb @@ -13,10 +13,12 @@ describe "Admin hidden comments", :admin do visit proposal_path(proposal) within("#proposal_#{proposal.id}") do - accept_confirm("Are you sure? Hide author \"#{proposal.author.name}\"") { click_link "Hide author" } + accept_confirm("Are you sure? This will hide the user \"#{proposal.author.name}\" and all their contents.") do + click_button "Block author" + end end - expect(page).to have_current_path debates_path + expect(page).to have_current_path proposals_path visit admin_hidden_comments_path diff --git a/spec/system/admin/hidden_users_spec.rb b/spec/system/admin/hidden_users_spec.rb index 59b19602e..0cf4744d4 100644 --- a/spec/system/admin/hidden_users_spec.rb +++ b/spec/system/admin/hidden_users_spec.rb @@ -83,7 +83,7 @@ describe "Admin hidden users", :admin do visit admin_hidden_users_path(filter: "with_confirmed_hide", page: 2) - accept_confirm("Are you sure? Restore \"#{users[2].name}\"") do + accept_confirm("Are you sure? Restore \"#{users[-3].name}\"") do click_button "Restore", match: :first, exact: true end diff --git a/spec/system/comments/budget_investments_valuation_spec.rb b/spec/system/comments/budget_investments_valuation_spec.rb index eccb8ce26..47d9823cc 100644 --- a/spec/system/comments/budget_investments_valuation_spec.rb +++ b/spec/system/comments/budget_investments_valuation_spec.rb @@ -275,7 +275,7 @@ describe "Internal valuation comments on Budget::Investments" do expect(page).to have_no_css(".comment-votes") expect(page).to have_no_css(".js-flag-actions") - expect(page).to have_no_css(".js-moderation-actions") + expect(page).to have_no_css(".moderation-actions") end end diff --git a/spec/system/comments/debates_spec.rb b/spec/system/comments/debates_spec.rb index 706cd8142..57cca11d7 100644 --- a/spec/system/comments/debates_spec.rb +++ b/spec/system/comments/debates_spec.rb @@ -371,6 +371,16 @@ describe "Commenting debates" do end end + scenario "Show comment when the author is hidden" do + create(:comment, body: "This is pointless", commentable: debate, author: create(:user, :hidden)) + + visit debate_path(debate) + + within ".comment", text: "This is pointless" do + expect(page).to have_content "User deleted" + end + end + scenario "Errors on reply" do comment = create(:comment, commentable: debate, user: user) diff --git a/spec/system/moderation/budget_investments_spec.rb b/spec/system/moderation/budget_investments_spec.rb index aa0014182..76d3f7c2f 100644 --- a/spec/system/moderation/budget_investments_spec.rb +++ b/spec/system/moderation/budget_investments_spec.rb @@ -10,7 +10,7 @@ describe "Moderate budget investments" do login_as(mod.user) visit budget_investment_path(budget, investment) - accept_confirm("Are you sure? Hide \"#{investment.title}\"") { click_link "Hide" } + accept_confirm("Are you sure? Hide \"#{investment.title}\"") { click_button "Hide" } expect(page).to have_css(".faded", count: 2) @@ -23,12 +23,11 @@ describe "Moderate budget investments" do login_as(mod.user) visit budget_investment_path(budget, investment) - accept_confirm("Are you sure? Hide author \"#{investment.author.name}\"") { click_link "Hide author" } - - expect(page).to have_current_path(debates_path) - - visit budget_investments_path(budget.id, heading_id: heading.id) + accept_confirm("Are you sure? This will hide the user \"#{investment.author.name}\" and all their contents.") do + click_button "Block author" + end + expect(page).to have_current_path(budget_investments_path(budget)) expect(page).not_to have_content(investment.title) end @@ -39,8 +38,8 @@ describe "Moderate budget investments" do visit budget_investment_path(budget, investment) within "#budget_investment_#{investment.id}" do - expect(page).not_to have_link("Hide") - expect(page).not_to have_link("Hide author") + expect(page).not_to have_button "Hide" + expect(page).not_to have_button "Block author" end end @@ -75,7 +74,7 @@ describe "Moderate budget investments" do click_button "Search" within "tr", text: investment.author.name do - expect(page).to have_link "Block" + expect(page).to have_button "Block" end end diff --git a/spec/system/moderation/comments_spec.rb b/spec/system/moderation/comments_spec.rb index 27ca96c10..aeaf5c280 100644 --- a/spec/system/moderation/comments_spec.rb +++ b/spec/system/moderation/comments_spec.rb @@ -11,7 +11,7 @@ describe "Moderate comments" do visit debate_path(comment.commentable) within("#comment_#{comment.id}") do - accept_confirm("Are you sure? Hide") { click_link "Hide" } + accept_confirm("Are you sure? Hide") { click_button "Hide" } expect(page).to have_css(".comment .faded") end @@ -31,8 +31,8 @@ describe "Moderate comments" do visit debate_path(comment.commentable) within("#comment_#{comment.id}") do - expect(page).not_to have_link("Hide") - expect(page).not_to have_link("Block author") + expect(page).not_to have_button "Hide" + expect(page).not_to have_button "Block author" end end @@ -95,7 +95,7 @@ describe "Moderate comments" do click_button "Search" within "tr", text: comment.user.name do - expect(page).to have_link "Block" + expect(page).to have_button "Block" end end diff --git a/spec/system/moderation/debates_spec.rb b/spec/system/moderation/debates_spec.rb index 9a743807a..8cd734858 100644 --- a/spec/system/moderation/debates_spec.rb +++ b/spec/system/moderation/debates_spec.rb @@ -11,7 +11,7 @@ describe "Moderate debates" do visit debate_path(debate) within("#debate_#{debate.id}") do - accept_confirm("Are you sure? Hide") { click_link "Hide" } + accept_confirm("Are you sure? Hide") { click_button "Hide" } end expect(find("div#debate_#{debate.id}.faded")).to have_text debate.title @@ -30,8 +30,8 @@ describe "Moderate debates" do visit debate_path(debate) within("#debate_#{debate.id}") do - expect(page).not_to have_link("Hide") - expect(page).not_to have_link("Block author") + expect(page).not_to have_button "Hide" + expect(page).not_to have_button "Block author" end end @@ -66,7 +66,7 @@ describe "Moderate debates" do click_button "Search" within "tr", text: debate.author.name do - expect(page).to have_link "Block" + expect(page).to have_button "Block" end end diff --git a/spec/system/moderation/legislation_proposals_spec.rb b/spec/system/moderation/legislation_proposals_spec.rb index 98f7ea48a..3da95c244 100644 --- a/spec/system/moderation/legislation_proposals_spec.rb +++ b/spec/system/moderation/legislation_proposals_spec.rb @@ -11,7 +11,7 @@ describe "Moderate legislation proposals" do visit legislation_process_proposal_path(legislation_process, legislation_proposal) within("#legislation_proposal_#{legislation_proposal.id}") do - accept_confirm("Are you sure? Hide \"#{legislation_proposal.title}\"") { click_link "Hide" } + accept_confirm("Are you sure? Hide \"#{legislation_proposal.title}\"") { click_button "Hide" } end expect(page).to have_css("#legislation_proposal_#{legislation_proposal.id}.faded") @@ -21,6 +21,6 @@ describe "Moderate legislation proposals" do visit legislation_process_proposals_path(legislation_process) expect(page).to have_css(".proposal-content", count: 0) - expect(page).not_to have_link("Hide") + expect(page).not_to have_button "Hide" end end diff --git a/spec/system/moderation/proposal_notifications_spec.rb b/spec/system/moderation/proposal_notifications_spec.rb index b81dba51c..d2ad9b7d2 100644 --- a/spec/system/moderation/proposal_notifications_spec.rb +++ b/spec/system/moderation/proposal_notifications_spec.rb @@ -12,7 +12,7 @@ describe "Moderate proposal notifications" do click_link "Notifications (1)" within("#proposal_notification_#{proposal_notification.id}") do - accept_confirm("Are you sure? Hide") { click_link "Hide" } + accept_confirm("Are you sure? Hide") { click_button "Hide" } end expect(page).to have_css("#proposal_notification_#{proposal_notification.id}.faded") @@ -34,8 +34,8 @@ describe "Moderate proposal notifications" do click_link "Notifications (1)" within("#proposal_notification_#{proposal_notification.id}") do - expect(page).not_to have_link("Hide") - expect(page).not_to have_link("Block author") + expect(page).not_to have_button "Hide" + expect(page).not_to have_button "Block author" end end @@ -70,7 +70,7 @@ describe "Moderate proposal notifications" do click_button "Search" within "tr", text: proposal_notification.author.name do - expect(page).to have_link "Block" + expect(page).to have_button "Block" end end diff --git a/spec/system/moderation/proposals_spec.rb b/spec/system/moderation/proposals_spec.rb index d2043d923..ceef0bf86 100644 --- a/spec/system/moderation/proposals_spec.rb +++ b/spec/system/moderation/proposals_spec.rb @@ -10,7 +10,7 @@ describe "Moderate proposals" do visit proposal_path(proposal) within("#proposal_#{proposal.id}") do - accept_confirm("Are you sure? Hide") { click_link "Hide" } + accept_confirm("Are you sure? Hide") { click_button "Hide" } end expect(page).to have_css("#proposal_#{proposal.id}.faded") @@ -29,8 +29,8 @@ describe "Moderate proposals" do visit proposal_path(proposal) within("#proposal_#{proposal.id}") do - expect(page).to have_link("Hide") - expect(page).not_to have_link("Block author") + expect(page).to have_button "Hide" + expect(page).not_to have_button "Block author" end end @@ -65,7 +65,7 @@ describe "Moderate proposals" do click_button "Search" within "tr", text: proposal.author.name do - expect(page).to have_link "Block" + expect(page).to have_button "Block" end end diff --git a/spec/system/moderation/users_spec.rb b/spec/system/moderation/users_spec.rb index 9bf390005..42247e333 100644 --- a/spec/system/moderation/users_spec.rb +++ b/spec/system/moderation/users_spec.rb @@ -24,7 +24,9 @@ describe "Moderate users" do visit debate_path(debate1) within("#debate_#{debate1.id}") do - accept_confirm("Are you sure? Hide author \"#{debate1.author.name}\"") { click_link "Hide author" } + accept_confirm("Are you sure? This will hide the user \"#{debate1.author.name}\" and all their contents.") do + click_button "Block author" + end end expect(page).to have_current_path(debates_path) @@ -64,7 +66,8 @@ describe "Moderate users" do within("#moderation_users") do expect(page).to have_content citizen.name expect(page).not_to have_content "Blocked" - click_link "Block" + + accept_confirm { click_button "Block" } end within("#moderation_users") do @@ -72,4 +75,23 @@ describe "Moderate users" do expect(page).to have_content "Blocked" end end + + scenario "Hide users in the moderation section" do + create(:user, username: "Rick") + + login_as(create(:moderator).user) + visit moderation_users_path(search: "Rick") + + within("#moderation_users") do + accept_confirm('This will hide the user "Rick" without hiding their contents') do + click_button "Hide" + end + end + + expect(page).to have_content "The user has been hidden" + + within("#moderation_users") do + expect(page).to have_content "Hidden" + end + end end