Refactor ban-related functionality into use cases
This commit is contained in:
parent
be0cf69368
commit
9a35584284
3
Gemfile
3
Gemfile
|
@ -36,6 +36,9 @@ gem "hcaptcha", "~> 6.0", git: "https://github.com/Retrospring/hcaptcha.git", re
|
|||
|
||||
gem "rolify", "~> 5.2"
|
||||
|
||||
gem "dry-initializer", "~> 3.0"
|
||||
gem "dry-types", "~> 1.4"
|
||||
|
||||
gem 'ruby-progressbar'
|
||||
|
||||
gem 'rails_admin'
|
||||
|
|
21
Gemfile.lock
21
Gemfile.lock
|
@ -154,6 +154,25 @@ GEM
|
|||
docile (1.4.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dry-configurable (0.12.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
dry-core (~> 0.5, >= 0.5.0)
|
||||
dry-container (0.8.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
dry-configurable (~> 0.1, >= 0.1.3)
|
||||
dry-core (0.7.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
dry-inflector (0.2.1)
|
||||
dry-initializer (3.0.4)
|
||||
dry-logic (1.2.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
dry-core (~> 0.5, >= 0.5)
|
||||
dry-types (1.5.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
dry-container (~> 0.3)
|
||||
dry-core (~> 0.5, >= 0.5)
|
||||
dry-inflector (~> 0.1, >= 0.1.2)
|
||||
dry-logic (~> 1.0, >= 1.0.2)
|
||||
equalizer (0.0.11)
|
||||
erubi (1.10.0)
|
||||
excon (0.89.0)
|
||||
|
@ -606,6 +625,8 @@ DEPENDENCIES
|
|||
devise (~> 4.0)
|
||||
devise-async
|
||||
devise-i18n
|
||||
dry-initializer (~> 3.0)
|
||||
dry-types (~> 1.4)
|
||||
factory_bot_rails
|
||||
fake_email_validator
|
||||
faker
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
require 'use_case/user/ban'
|
||||
require 'use_case/user/unban'
|
||||
require 'errors'
|
||||
|
||||
class Ajax::ModerationController < AjaxController
|
||||
def vote
|
||||
params.require :id
|
||||
|
@ -108,38 +112,49 @@ class Ajax::ModerationController < AjaxController
|
|||
|
||||
params.require :user
|
||||
params.require :ban
|
||||
params.require :permaban
|
||||
params.require :duration
|
||||
params.require :duration_unit
|
||||
|
||||
duration = params[:duration].to_s
|
||||
duration = params[:duration].to_i
|
||||
duration_unit = params[:duration_unit].to_s
|
||||
reason = params[:reason].to_s
|
||||
target = User.find_by_screen_name!(params[:user])
|
||||
target_user = User.find_by_screen_name!(params[:user])
|
||||
unban = params[:ban] == '0'
|
||||
perma = params[:permaban] == '1'
|
||||
perma = params[:duration].nil?
|
||||
|
||||
if !unban && target.has_role?(:administrator)
|
||||
if !unban && target_user.has_role?(:administrator)
|
||||
@response[:status] = :nopriv
|
||||
@response[:message] = I18n.t('messages.moderation.ban.nopriv')
|
||||
return
|
||||
end
|
||||
|
||||
if unban
|
||||
target.unban
|
||||
UseCase::User::Unban.call(target_user.id)
|
||||
@response[:message] = I18n.t('messages.moderation.ban.unban')
|
||||
@response[:success] = true
|
||||
@response[:status] = :okay
|
||||
return
|
||||
elsif perma
|
||||
target.ban nil, nil, reason, current_user
|
||||
@response[:message] = I18n.t('messages.moderation.ban.perma')
|
||||
expiry = nil
|
||||
else
|
||||
target.ban duration, duration_unit, reason, current_user
|
||||
@response[:message] = "User banned for #{duration} #{duration_unit}"
|
||||
params.require :duration
|
||||
params.require :duration_unit
|
||||
|
||||
raise Errors::InvalidBanDuration unless %w[hours days weeks months].include? duration_unit
|
||||
|
||||
expiry = DateTime.now + duration.public_send(duration_unit)
|
||||
@response[:message] = I18n.t('messages.moderation.ban.temp', date: expiry.to_s)
|
||||
end
|
||||
target.save!
|
||||
|
||||
UseCase::User::Ban.call(
|
||||
target_user_id: target_user.id,
|
||||
expiry: expiry,
|
||||
reason: reason,
|
||||
source_user_id: current_user.id)
|
||||
|
||||
target_user.save!
|
||||
|
||||
@response[:status] = :okay
|
||||
@response[:success] = target.banned? == !unban
|
||||
@response[:success] = true
|
||||
end
|
||||
|
||||
def privilege
|
||||
|
|
|
@ -30,12 +30,15 @@ load = ->
|
|||
|
||||
data = {
|
||||
ban: checktostr banCheckbox
|
||||
permaban: checktostr permabanCheckbox
|
||||
until: modalForm.elements["until"].value.trim()
|
||||
reason: modalForm.elements["reason"].value.trim()
|
||||
user: modalForm.elements["user"].value
|
||||
}
|
||||
|
||||
if banCheckbox.checked
|
||||
data.reason = modalForm.elements["reason"].value.trim()
|
||||
unless permabanCheckbox.checked
|
||||
data.duration = modalForm.elements["duration"].value.trim()
|
||||
data.duration_unit = modalForm.elements["duration_unit"].value.trim()
|
||||
|
||||
$.ajax
|
||||
url: '/ajax/mod/ban'
|
||||
type: 'POST'
|
||||
|
@ -43,6 +46,7 @@ load = ->
|
|||
success: (data, status, jqxhr) ->
|
||||
showNotification data.message, data.success
|
||||
error: (jqxhr, status, error) ->
|
||||
console.error 'request failed', data
|
||||
console.log jqxhr, status, error
|
||||
showNotification translate('frontend.error.message'), false
|
||||
complete: (jqxhr, status) ->
|
||||
|
|
|
@ -228,7 +228,7 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def unban
|
||||
self.user_bans.current.update(expires_at: DateTime.now)
|
||||
self.user_bans.current.update!(expires_at: DateTime.now)
|
||||
end
|
||||
|
||||
# Bans a user.
|
||||
|
@ -237,9 +237,6 @@ class User < ApplicationRecord
|
|||
# @param reason [String] Reason for the ban. This is displayed to the user.
|
||||
# @param banned_by [User] User who instated the ban
|
||||
def ban(duration, duration_unit = 'hours', reason = nil, banned_by = nil)
|
||||
raise Errors::InvalidBanDuration unless %w[hours days weeks months].include? duration_unit
|
||||
|
||||
expiry = duration && DateTime.now + duration.public_send(duration_unit)
|
||||
self.user_bans.create(expires_at: expiry, reason: reason, banned_by: banned_by)
|
||||
end
|
||||
|
||||
|
|
|
@ -2,5 +2,5 @@ class UserBan < ApplicationRecord
|
|||
belongs_to :user
|
||||
belongs_to :banned_by, class_name: 'User'
|
||||
|
||||
scope :current, -> { where('expires_at IS NULL or expires_at < NOW()') }
|
||||
scope :current, -> { where('expires_at IS NULL or expires_at > NOW()') }
|
||||
end
|
||||
|
|
|
@ -14,7 +14,12 @@
|
|||
#ban-controls{ style: user.banned? ? '' : 'display: none' }
|
||||
= f.check_box :permaban, label: t('views.modal.bancontrol.permanent'), checked: user.permanently_banned?
|
||||
#ban-controls-time{ style: user.permanently_banned? ? 'display: none' : '' }
|
||||
= f.text_field :until, label: '', required: true, value: (user.banned_until || DateTime.current).strftime('%m/%d/%Y %I:%M %p')
|
||||
= f.text_field :duration, label: '', required: true
|
||||
.form-check.form-check-inline
|
||||
= f.radio_button :duration_unit, 'hours', label: 'Hours', checked: true
|
||||
= f.radio_button :duration_unit, 'days', label: 'Days'
|
||||
= f.radio_button :duration_unit, 'weeks', label: 'Weeks'
|
||||
= f.radio_button :duration_unit, 'months', label: 'Months'
|
||||
= f.text_field :reason, placeholder: t('views.modal.bancontrol.reason'), value: user.ban_reason
|
||||
.modal-footer
|
||||
%button.btn.btn-default{ name: 'stop-time', type: :button, data: { dismiss: :modal } }= t 'views.actions.close'
|
||||
|
|
|
@ -1,7 +1,26 @@
|
|||
module Errors
|
||||
class Base < StandardError
|
||||
def status
|
||||
500
|
||||
end
|
||||
|
||||
def code
|
||||
@code ||= self.class.name.sub('Errors::', '').underscore
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidBanDuration < Base
|
||||
class BadRequest < Base
|
||||
def status
|
||||
400
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidBanDuration < BadRequest
|
||||
end
|
||||
|
||||
class Forbidden < Base
|
||||
def status
|
||||
403
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'dry-types'
|
||||
|
||||
module Types
|
||||
include Dry.Types()
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'dry-initializer'
|
||||
require 'types'
|
||||
require 'errors'
|
||||
|
||||
module UseCase
|
||||
class Base
|
||||
extend Dry::Initializer
|
||||
|
||||
def self.call(*args, **kwargs)
|
||||
new(*args, **kwargs).call
|
||||
end
|
||||
|
||||
def call
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,50 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'use_case/base'
|
||||
|
||||
module UseCase
|
||||
module User
|
||||
class Ban < UseCase::Base
|
||||
REASON_SPAM = 'Spam'
|
||||
REASON_HARASSMENT = 'Harassment'
|
||||
REASON_BAN_EVASION = 'Ban evasion'
|
||||
|
||||
option :target_user_id, type: Types::Coercible::Integer
|
||||
option :expiry, types: Types::Nominal::DateTime.optional
|
||||
option :source_user_id, type: Types::Coercible::Integer.optional
|
||||
option :reason, type: Types::Coercible::String.optional
|
||||
|
||||
def call
|
||||
ban = ::UserBan.create!(
|
||||
user: target_user,
|
||||
expires_at: expiry,
|
||||
banned_by: source_user,
|
||||
reason: reason
|
||||
)
|
||||
|
||||
if reason == REASON_SPAM
|
||||
target_user.update!(
|
||||
display_name: nil,
|
||||
bio: '',
|
||||
location: '',
|
||||
website: '',
|
||||
profile_picture: nil,
|
||||
profile_header: nil
|
||||
)
|
||||
end
|
||||
|
||||
{
|
||||
ban: ban
|
||||
}
|
||||
end
|
||||
|
||||
def target_user
|
||||
@target_user ||= ::User.find(target_user_id)
|
||||
end
|
||||
|
||||
def source_user
|
||||
@source_user ||= ::User.find(source_user_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'use_case/base'
|
||||
|
||||
module UseCase
|
||||
module User
|
||||
class Unban < UseCase::Base
|
||||
param :target_user_id, type: Types::Coercible::Integer
|
||||
|
||||
def call
|
||||
UserBan.current.where(user_id: target_user_id).update_all(
|
||||
expires_at: DateTime.now
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue