Add ability to filter audit log in admin UI (#13381)
This commit is contained in:
		@@ -2,8 +2,18 @@
 | 
			
		||||
 | 
			
		||||
module Admin
 | 
			
		||||
  class ActionLogsController < BaseController
 | 
			
		||||
    def index
 | 
			
		||||
      @action_logs = Admin::ActionLog.page(params[:page])
 | 
			
		||||
    before_action :set_action_logs
 | 
			
		||||
 | 
			
		||||
    def index; end
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def set_action_logs
 | 
			
		||||
      @action_logs = Admin::ActionLogFilter.new(filter_params).results.page(params[:page])
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def filter_params
 | 
			
		||||
      params.slice(:page, *Admin::ActionLogFilter::KEYS).permit(:page, *Admin::ActionLogFilter::KEYS)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -9,79 +9,8 @@ module Admin::ActionLogsHelper
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def relevant_log_changes(log)
 | 
			
		||||
    if log.target_type == 'CustomEmoji' && [:enable, :disable, :destroy].include?(log.action)
 | 
			
		||||
      log.recorded_changes.slice('domain')
 | 
			
		||||
    elsif log.target_type == 'CustomEmoji' && log.action == :update
 | 
			
		||||
      log.recorded_changes.slice('domain', 'visible_in_picker')
 | 
			
		||||
    elsif log.target_type == 'User' && [:promote, :demote].include?(log.action)
 | 
			
		||||
      log.recorded_changes.slice('moderator', 'admin')
 | 
			
		||||
    elsif log.target_type == 'User' && [:change_email].include?(log.action)
 | 
			
		||||
      log.recorded_changes.slice('email', 'unconfirmed_email')
 | 
			
		||||
    elsif log.target_type == 'DomainBlock'
 | 
			
		||||
      log.recorded_changes.slice('severity', 'reject_media')
 | 
			
		||||
    elsif log.target_type == 'Status' && log.action == :update
 | 
			
		||||
      log.recorded_changes.slice('sensitive')
 | 
			
		||||
    elsif log.target_type == 'Announcement' && log.action == :update
 | 
			
		||||
      log.recorded_changes.slice('text', 'starts_at', 'ends_at', 'all_day')
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def log_extra_attributes(hash)
 | 
			
		||||
    safe_join(hash.to_a.map { |key, value| safe_join([content_tag(:span, key, class: 'diff-key'), '=', log_change(value)]) }, ' ')
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def log_change(val)
 | 
			
		||||
    return content_tag(:span, val, class: 'diff-neutral') unless val.is_a?(Array)
 | 
			
		||||
    safe_join([content_tag(:span, val.first, class: 'diff-old'), content_tag(:span, val.last, class: 'diff-new')], '→')
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def icon_for_log(log)
 | 
			
		||||
    case log.target_type
 | 
			
		||||
    when 'Account', 'User'
 | 
			
		||||
      'user'
 | 
			
		||||
    when 'CustomEmoji'
 | 
			
		||||
      'file'
 | 
			
		||||
    when 'Report'
 | 
			
		||||
      'flag'
 | 
			
		||||
    when 'DomainBlock'
 | 
			
		||||
      'lock'
 | 
			
		||||
    when 'DomainAllow'
 | 
			
		||||
      'plus-circle'
 | 
			
		||||
    when 'EmailDomainBlock'
 | 
			
		||||
      'envelope'
 | 
			
		||||
    when 'Status'
 | 
			
		||||
      'pencil'
 | 
			
		||||
    when 'AccountWarning'
 | 
			
		||||
      'warning'
 | 
			
		||||
    when 'Announcement'
 | 
			
		||||
      'bullhorn'
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def class_for_log_icon(log)
 | 
			
		||||
    case log.action
 | 
			
		||||
    when :enable, :unsuspend, :unsilence, :confirm, :promote, :resolve
 | 
			
		||||
      'positive'
 | 
			
		||||
    when :create
 | 
			
		||||
      opposite_verbs?(log) ? 'negative' : 'positive'
 | 
			
		||||
    when :update, :reset_password, :disable_2fa, :memorialize, :change_email
 | 
			
		||||
      'neutral'
 | 
			
		||||
    when :demote, :silence, :disable, :suspend, :remove_avatar, :remove_header, :reopen
 | 
			
		||||
      'negative'
 | 
			
		||||
    when :destroy
 | 
			
		||||
      opposite_verbs?(log) ? 'positive' : 'negative'
 | 
			
		||||
    else
 | 
			
		||||
      ''
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def opposite_verbs?(log)
 | 
			
		||||
    %w(DomainBlock EmailDomainBlock AccountWarning).include?(log.target_type)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def linkable_log_target(record)
 | 
			
		||||
    case record.class.name
 | 
			
		||||
    when 'Account'
 | 
			
		||||
@@ -99,7 +28,7 @@ module Admin::ActionLogsHelper
 | 
			
		||||
    when 'AccountWarning'
 | 
			
		||||
      link_to record.target_account.acct, admin_account_path(record.target_account_id)
 | 
			
		||||
    when 'Announcement'
 | 
			
		||||
      link_to "##{record.id}", edit_admin_announcement_path(record.id)
 | 
			
		||||
      link_to truncate(record.text), edit_admin_announcement_path(record.id)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@@ -118,7 +47,7 @@ module Admin::ActionLogsHelper
 | 
			
		||||
        I18n.t('admin.action_logs.deleted_status')
 | 
			
		||||
      end
 | 
			
		||||
    when 'Announcement'
 | 
			
		||||
      "##{attributes['id']}"
 | 
			
		||||
      truncate(attributes['text'])
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ module Admin::FilterHelper
 | 
			
		||||
    InviteFilter::KEYS,
 | 
			
		||||
    RelationshipFilter::KEYS,
 | 
			
		||||
    AnnouncementFilter::KEYS,
 | 
			
		||||
    Admin::ActionLogFilter::KEYS,
 | 
			
		||||
  ].flatten.freeze
 | 
			
		||||
 | 
			
		||||
  def filter_link_to(text, link_to_params, link_class_params = link_to_params)
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,10 @@ delegate(document, '.media-spoiler-hide-button', 'click', () => {
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
delegate(document, '.filter-subset--with-select select', 'change', ({ target }) => {
 | 
			
		||||
  target.form.submit();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const onDomainBlockSeverityChange = (target) => {
 | 
			
		||||
  const rejectMediaDiv   = document.querySelector('.input.with_label.domain_block_reject_media');
 | 
			
		||||
  const rejectReportsDiv = document.querySelector('.input.with_label.domain_block_reject_reports');
 | 
			
		||||
 
 | 
			
		||||
@@ -418,6 +418,11 @@ body,
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &--with-select strong {
 | 
			
		||||
      display: block;
 | 
			
		||||
      margin-bottom: 10px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    a {
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
      color: $darker-text-color;
 | 
			
		||||
@@ -551,19 +556,22 @@ body,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.log-entry {
 | 
			
		||||
  margin-bottom: 20px;
 | 
			
		||||
  line-height: 20px;
 | 
			
		||||
  padding: 15px 0;
 | 
			
		||||
  background: $ui-base-color;
 | 
			
		||||
  border-bottom: 1px solid lighten($ui-base-color, 4%);
 | 
			
		||||
 | 
			
		||||
  &:last-child {
 | 
			
		||||
    border-bottom: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__header {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: flex-start;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    background: $ui-base-color;
 | 
			
		||||
    color: $darker-text-color;
 | 
			
		||||
    border-radius: 4px 4px 0 0;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    padding: 0 10px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__avatar {
 | 
			
		||||
@@ -590,44 +598,6 @@ body,
 | 
			
		||||
    color: $dark-text-color;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__extras {
 | 
			
		||||
    background: lighten($ui-base-color, 6%);
 | 
			
		||||
    border-radius: 0 0 4px 4px;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    color: $darker-text-color;
 | 
			
		||||
    font-family: $font-monospace, monospace;
 | 
			
		||||
    font-size: 12px;
 | 
			
		||||
    word-wrap: break-word;
 | 
			
		||||
    min-height: 20px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__icon {
 | 
			
		||||
    font-size: 28px;
 | 
			
		||||
    margin-right: 10px;
 | 
			
		||||
    color: $dark-text-color;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &__icon__overlay {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 10px;
 | 
			
		||||
    right: 10px;
 | 
			
		||||
    width: 10px;
 | 
			
		||||
    height: 10px;
 | 
			
		||||
    border-radius: 50%;
 | 
			
		||||
 | 
			
		||||
    &.positive {
 | 
			
		||||
      background: $success-green;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.negative {
 | 
			
		||||
      background: lighten($error-red, 12%);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &.neutral {
 | 
			
		||||
      background: $ui-highlight-color;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  a,
 | 
			
		||||
  .username,
 | 
			
		||||
  .target {
 | 
			
		||||
@@ -635,18 +605,6 @@ body,
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    font-weight: 500;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .diff-old {
 | 
			
		||||
    color: lighten($error-red, 12%);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .diff-neutral {
 | 
			
		||||
    color: $secondary-text-color;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .diff-new {
 | 
			
		||||
    color: $success-green;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a.name-tag,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										81
									
								
								app/models/admin/action_log_filter.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								app/models/admin/action_log_filter.rb
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
# frozen_string_literal: true
 | 
			
		||||
 | 
			
		||||
class Admin::ActionLogFilter
 | 
			
		||||
  KEYS = %i(
 | 
			
		||||
    action_type
 | 
			
		||||
    account_id
 | 
			
		||||
    target_account_id
 | 
			
		||||
  ).freeze
 | 
			
		||||
 | 
			
		||||
  ACTION_TYPE_MAP = {
 | 
			
		||||
    assigned_to_self_report: { target_type: 'Report', action: 'assigned_to_self' }.freeze,
 | 
			
		||||
    change_email_user: { target_type: 'User', action: 'change_email' }.freeze,
 | 
			
		||||
    confirm_user: { target_type: 'User', action: 'confirm' }.freeze,
 | 
			
		||||
    create_account_warning: { target_type: 'AccountWarning', action: 'create' }.freeze,
 | 
			
		||||
    create_announcement: { target_type: 'Announcement', action: 'create' }.freeze,
 | 
			
		||||
    create_custom_emoji: { target_type: 'CustomEmoji', action: 'create' }.freeze,
 | 
			
		||||
    create_domain_allow: { target_type: 'DomainAllow', action: 'create' }.freeze,
 | 
			
		||||
    create_domain_block: { target_type: 'DomainBlock', action: 'create' }.freeze,
 | 
			
		||||
    create_email_domain_block: { target_type: 'EmailDomainBlock', action: 'create' }.freeze,
 | 
			
		||||
    demote_user: { target_type: 'User', action: 'demote' }.freeze,
 | 
			
		||||
    destroy_announcement: { target_type: 'Announcement', action: 'destroy' }.freeze,
 | 
			
		||||
    destroy_custom_emoji: { target_type: 'CustomEmoji', action: 'destroy' }.freeze,
 | 
			
		||||
    destroy_domain_allow: { target_type: 'DomainAllow', action: 'destroy' }.freeze,
 | 
			
		||||
    destroy_domain_block: { target_type: 'DomainBlock', action: 'destroy' }.freeze,
 | 
			
		||||
    destroy_email_domain_block: { target_type: 'EmailDomainBlock', action: 'destroy' }.freeze,
 | 
			
		||||
    destroy_status: { target_type: 'Status', action: 'destroy' }.freeze,
 | 
			
		||||
    disable_2fa_user: { target_type: 'User', action: 'disable' }.freeze,
 | 
			
		||||
    disable_custom_emoji: { target_type: 'CustomEmoji', action: 'disable' }.freeze,
 | 
			
		||||
    disable_user: { target_type: 'User', action: 'disable' }.freeze,
 | 
			
		||||
    enable_custom_emoji: { target_type: 'CustomEmoji', action: 'enable' }.freeze,
 | 
			
		||||
    enable_user: { target_type: 'User', action: 'enable' }.freeze,
 | 
			
		||||
    memorialize_account: { target_type: 'Account', action: 'memorialize' }.freeze,
 | 
			
		||||
    promote_user: { target_type: 'User', action: 'promote' }.freeze,
 | 
			
		||||
    remove_avatar_user: { target_type: 'User', action: 'remove_avatar' }.freeze,
 | 
			
		||||
    reopen_report: { target_type: 'Report', action: 'reopen' }.freeze,
 | 
			
		||||
    reset_password_user: { target_type: 'User', action: 'reset_password' }.freeze,
 | 
			
		||||
    resolve_report: { target_type: 'Report', action: 'resolve' }.freeze,
 | 
			
		||||
    silence_account: { target_type: 'Account', action: 'silence' }.freeze,
 | 
			
		||||
    suspend_account: { target_type: 'Account', action: 'suspend' }.freeze,
 | 
			
		||||
    unassigned_report: { target_type: 'Report', action: 'unassigned' }.freeze,
 | 
			
		||||
    unsilence_account: { target_type: 'Account', action: 'unsilence' }.freeze,
 | 
			
		||||
    unsuspend_account: { target_type: 'Account', action: 'unsuspend' }.freeze,
 | 
			
		||||
    update_announcement: { target_type: 'Announcement', action: 'update' }.freeze,
 | 
			
		||||
    update_custom_emoji: { target_type: 'CustomEmoji', action: 'update' }.freeze,
 | 
			
		||||
    update_status: { target_type: 'Status', action: 'update' }.freeze,
 | 
			
		||||
  }.freeze
 | 
			
		||||
 | 
			
		||||
  attr_reader :params
 | 
			
		||||
 | 
			
		||||
  def initialize(params)
 | 
			
		||||
    @params = params
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def results
 | 
			
		||||
    scope = Admin::ActionLog.includes(:target)
 | 
			
		||||
 | 
			
		||||
    params.each do |key, value|
 | 
			
		||||
      next if key.to_s == 'page'
 | 
			
		||||
 | 
			
		||||
      scope.merge!(scope_for(key.to_s, value.to_s.strip)) if value.present?
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    scope
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  def scope_for(key, value)
 | 
			
		||||
    case key
 | 
			
		||||
    when 'action_type'
 | 
			
		||||
      Admin::ActionLog.where(ACTION_TYPE_MAP[value.to_sym])
 | 
			
		||||
    when 'account_id'
 | 
			
		||||
      Admin::ActionLog.where(account_id: value)
 | 
			
		||||
    when 'target_account_id'
 | 
			
		||||
      account = Account.find(value)
 | 
			
		||||
      Admin::ActionLog.where(target: [account, account.user].compact)
 | 
			
		||||
    else
 | 
			
		||||
      raise "Unknown filter: #{key}"
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@@ -53,7 +53,7 @@
 | 
			
		||||
      .dashboard__counters__num= number_with_delimiter @account.targeted_reports.count
 | 
			
		||||
      .dashboard__counters__label= t '.targeted_reports'
 | 
			
		||||
  %div
 | 
			
		||||
    %div
 | 
			
		||||
    = link_to admin_action_logs_path(target_account_id: @account.id) do
 | 
			
		||||
      .dashboard__counters__text
 | 
			
		||||
        - if @account.local? && @account.user.nil?
 | 
			
		||||
          %span.neutral= t('admin.accounts.deleted')
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,3 @@
 | 
			
		||||
        = t("admin.action_logs.actions.#{action_log.action}_#{action_log.target_type.underscore}", name: content_tag(:span, action_log.account.username, class: 'username'), target: content_tag(:span, log_target(action_log), class: 'target')).html_safe
 | 
			
		||||
      .log-entry__timestamp
 | 
			
		||||
        %time.formatted{ datetime: action_log.created_at.iso8601 }
 | 
			
		||||
    .spacer
 | 
			
		||||
    .log-entry__icon
 | 
			
		||||
      = fa_icon icon_for_log(action_log)
 | 
			
		||||
      .log-entry__icon__overlay{ class: class_for_log_icon(action_log) }
 | 
			
		||||
  .log-entry__extras
 | 
			
		||||
    = log_extra_attributes relevant_log_changes(action_log)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,28 @@
 | 
			
		||||
- content_for :page_title do
 | 
			
		||||
  = t('admin.action_logs.title')
 | 
			
		||||
 | 
			
		||||
= render @action_logs
 | 
			
		||||
- content_for :header_tags do
 | 
			
		||||
  = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
 | 
			
		||||
 | 
			
		||||
= form_tag admin_action_logs_url, method: 'GET', class: 'simple_form' do
 | 
			
		||||
  = hidden_field_tag :target_account_id, params[:target_account_id] if params[:target_account_id].present?
 | 
			
		||||
 | 
			
		||||
  .filters
 | 
			
		||||
    .filter-subset.filter-subset--with-select
 | 
			
		||||
      %strong= t('admin.action_logs.filter_by_user')
 | 
			
		||||
      .input.select.optional
 | 
			
		||||
        = select_tag :account_id, options_from_collection_for_select(Account.joins(:user).merge(User.staff), :id, :username, params[:account_id]), prompt: I18n.t('admin.accounts.moderation.all')
 | 
			
		||||
 | 
			
		||||
    .filter-subset.filter-subset--with-select
 | 
			
		||||
      %strong= t('admin.action_logs.filter_by_action')
 | 
			
		||||
      .input.select.optional
 | 
			
		||||
        = select_tag :action_type, options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key]}, params[:action_type]), prompt: I18n.t('admin.accounts.moderation.all')
 | 
			
		||||
 | 
			
		||||
- if @action_logs.empty?
 | 
			
		||||
  %div.muted-hint.center-text
 | 
			
		||||
    = t 'admin.action_logs.empty'
 | 
			
		||||
- else
 | 
			
		||||
  .announcements-list
 | 
			
		||||
    = render @action_logs
 | 
			
		||||
 | 
			
		||||
= paginate @action_logs
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,6 @@
 | 
			
		||||
 | 
			
		||||
Kaminari.configure do |config|
 | 
			
		||||
  config.default_per_page = 40
 | 
			
		||||
  config.window = 1
 | 
			
		||||
  config.window = 2
 | 
			
		||||
  config.outer_window = 1
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -195,6 +195,42 @@ en:
 | 
			
		||||
      web: Web
 | 
			
		||||
      whitelisted: Whitelisted
 | 
			
		||||
    action_logs:
 | 
			
		||||
      action_types:
 | 
			
		||||
        assigned_to_self_report: Assign Report
 | 
			
		||||
        change_email_user: Change E-mail for User
 | 
			
		||||
        confirm_user: Confirm User
 | 
			
		||||
        create_account_warning: Create Warning
 | 
			
		||||
        create_announcement: Create Announcement
 | 
			
		||||
        create_custom_emoji: Create Custom Emoji
 | 
			
		||||
        create_domain_allow: Create Domain Allow
 | 
			
		||||
        create_domain_block: Create Domain Block
 | 
			
		||||
        create_email_domain_block: Create E-mail Domain Block
 | 
			
		||||
        demote_user: Demote User
 | 
			
		||||
        destroy_announcement: Delete Announcement
 | 
			
		||||
        destroy_custom_emoji: Delete Custom Emoji
 | 
			
		||||
        destroy_domain_allow: Delete Domain Allow
 | 
			
		||||
        destroy_domain_block: Delete Domain Block
 | 
			
		||||
        destroy_email_domain_block: Delete e-mail domain block
 | 
			
		||||
        destroy_status: Delete Status
 | 
			
		||||
        disable_2fa_user: Disable 2FA
 | 
			
		||||
        disable_custom_emoji: Disable Custom Emoji
 | 
			
		||||
        disable_user: Disable User
 | 
			
		||||
        enable_custom_emoji: Enable Custom Emoji
 | 
			
		||||
        enable_user: Enable User
 | 
			
		||||
        memorialize_account: Memorialize Account
 | 
			
		||||
        promote_user: Promote User
 | 
			
		||||
        remove_avatar_user: Remove Avatar
 | 
			
		||||
        reopen_report: Reopen Report
 | 
			
		||||
        reset_password_user: Reset Password
 | 
			
		||||
        resolve_report: Resolve Report
 | 
			
		||||
        silence_account: Silence Account
 | 
			
		||||
        suspend_account: Suspend Account
 | 
			
		||||
        unassigned_report: Unassign Report
 | 
			
		||||
        unsilence_account: Unsilence Account
 | 
			
		||||
        unsuspend_account: Unsuspend Account
 | 
			
		||||
        update_announcement: Update Announcement
 | 
			
		||||
        update_custom_emoji: Update Custom Emoji
 | 
			
		||||
        update_status: Update Status
 | 
			
		||||
      actions:
 | 
			
		||||
        assigned_to_self_report: "%{name} assigned report %{target} to themselves"
 | 
			
		||||
        change_email_user: "%{name} changed the e-mail address of user %{target}"
 | 
			
		||||
@@ -232,6 +268,9 @@ en:
 | 
			
		||||
        update_custom_emoji: "%{name} updated emoji %{target}"
 | 
			
		||||
        update_status: "%{name} updated status by %{target}"
 | 
			
		||||
      deleted_status: "(deleted status)"
 | 
			
		||||
      empty: No logs found.
 | 
			
		||||
      filter_by_action: Filter by action
 | 
			
		||||
      filter_by_user: Filter by user
 | 
			
		||||
      title: Audit log
 | 
			
		||||
    announcements:
 | 
			
		||||
      destroyed_msg: Announcement successfully deleted!
 | 
			
		||||
 
 | 
			
		||||
@@ -31,242 +31,4 @@ RSpec.describe Admin::ActionLogsHelper, type: :helper do
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#relevant_log_changes' do
 | 
			
		||||
    let(:log) { double(target_type: target_type, action: log_action, recorded_changes: recorded_changes) }
 | 
			
		||||
    let(:recorded_changes) { double }
 | 
			
		||||
 | 
			
		||||
    after do
 | 
			
		||||
      hoge.relevant_log_changes(log)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'CustomEmoji' && [:enable, :disable, :destroy].include?(log.action)" do
 | 
			
		||||
      let(:target_type) { 'CustomEmoji' }
 | 
			
		||||
      let(:log_action)  { :enable }
 | 
			
		||||
 | 
			
		||||
      it "calls log.recorded_changes.slice('domain')" do
 | 
			
		||||
        expect(recorded_changes).to receive(:slice).with('domain')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'CustomEmoji' && log.action == :update" do
 | 
			
		||||
      let(:target_type) { 'CustomEmoji' }
 | 
			
		||||
      let(:log_action)  { :update }
 | 
			
		||||
 | 
			
		||||
      it "calls log.recorded_changes.slice('domain', 'visible_in_picker')" do
 | 
			
		||||
        expect(recorded_changes).to receive(:slice).with('domain', 'visible_in_picker')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'User' && [:promote, :demote].include?(log.action)" do
 | 
			
		||||
      let(:target_type) { 'User' }
 | 
			
		||||
      let(:log_action)  { :promote }
 | 
			
		||||
 | 
			
		||||
      it "calls log.recorded_changes.slice('moderator', 'admin')" do
 | 
			
		||||
        expect(recorded_changes).to receive(:slice).with('moderator', 'admin')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'User' && [:change_email].include?(log.action)" do
 | 
			
		||||
      let(:target_type) { 'User' }
 | 
			
		||||
      let(:log_action)  { :change_email }
 | 
			
		||||
 | 
			
		||||
      it "calls log.recorded_changes.slice('email', 'unconfirmed_email')" do
 | 
			
		||||
        expect(recorded_changes).to receive(:slice).with('email', 'unconfirmed_email')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'DomainBlock'" do
 | 
			
		||||
      let(:target_type) { 'DomainBlock' }
 | 
			
		||||
      let(:log_action)  { nil }
 | 
			
		||||
 | 
			
		||||
      it "calls log.recorded_changes.slice('severity', 'reject_media')" do
 | 
			
		||||
        expect(recorded_changes).to receive(:slice).with('severity', 'reject_media')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'Status' && log.action == :update" do
 | 
			
		||||
      let(:target_type) { 'Status' }
 | 
			
		||||
      let(:log_action)  { :update }
 | 
			
		||||
 | 
			
		||||
      it "log.recorded_changes.slice('sensitive')" do
 | 
			
		||||
        expect(recorded_changes).to receive(:slice).with('sensitive')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#log_extra_attributes' do
 | 
			
		||||
    after do
 | 
			
		||||
      hoge.log_extra_attributes(hoge: 'hoge')
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it "calls content_tag(:span, key, class: 'diff-key')" do
 | 
			
		||||
      allow(hoge).to receive(:log_change).with(anything)
 | 
			
		||||
      expect(hoge).to receive(:content_tag).with(:span, :hoge, class: 'diff-key')
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it 'calls safe_join twice' do
 | 
			
		||||
      expect(hoge).to receive(:safe_join).with(
 | 
			
		||||
        ['<span class="diff-key">hoge</span>',
 | 
			
		||||
         '=',
 | 
			
		||||
         '<span class="diff-neutral">hoge</span>']
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      expect(hoge).to receive(:safe_join).with([nil], ' ')
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#log_change' do
 | 
			
		||||
    after do
 | 
			
		||||
      hoge.log_change(val)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context '!val.is_a?(Array)' do
 | 
			
		||||
      let(:val) { 'hoge' }
 | 
			
		||||
 | 
			
		||||
      it "calls content_tag(:span, val, class: 'diff-neutral')" do
 | 
			
		||||
        expect(hoge).to receive(:content_tag).with(:span, val, class: 'diff-neutral')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'val.is_a?(Array)' do
 | 
			
		||||
      let(:val) { %w(foo bar) }
 | 
			
		||||
 | 
			
		||||
      it 'calls #content_tag twice and #safe_join' do
 | 
			
		||||
        expect(hoge).to receive(:content_tag).with(:span, 'foo', class: 'diff-old')
 | 
			
		||||
        expect(hoge).to receive(:content_tag).with(:span, 'bar', class: 'diff-new')
 | 
			
		||||
        expect(hoge).to receive(:safe_join).with([nil, nil], '→')
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#icon_for_log' do
 | 
			
		||||
    subject   { hoge.icon_for_log(log) }
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'Account'" do
 | 
			
		||||
      let(:log) { double(target_type: 'Account') }
 | 
			
		||||
 | 
			
		||||
      it 'returns "user"' do
 | 
			
		||||
        expect(subject).to be 'user'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'User'" do
 | 
			
		||||
      let(:log) { double(target_type: 'User') }
 | 
			
		||||
 | 
			
		||||
      it 'returns "user"' do
 | 
			
		||||
        expect(subject).to be 'user'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'CustomEmoji'" do
 | 
			
		||||
      let(:log) { double(target_type: 'CustomEmoji') }
 | 
			
		||||
 | 
			
		||||
      it 'returns "file"' do
 | 
			
		||||
        expect(subject).to be 'file'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'Report'" do
 | 
			
		||||
      let(:log) { double(target_type: 'Report') }
 | 
			
		||||
 | 
			
		||||
      it 'returns "flag"' do
 | 
			
		||||
        expect(subject).to be 'flag'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'DomainBlock'" do
 | 
			
		||||
      let(:log) { double(target_type: 'DomainBlock') }
 | 
			
		||||
 | 
			
		||||
      it 'returns "lock"' do
 | 
			
		||||
        expect(subject).to be 'lock'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'EmailDomainBlock'" do
 | 
			
		||||
      let(:log) { double(target_type: 'EmailDomainBlock') }
 | 
			
		||||
 | 
			
		||||
      it 'returns "envelope"' do
 | 
			
		||||
        expect(subject).to be 'envelope'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context "log.target_type == 'Status'" do
 | 
			
		||||
      let(:log) { double(target_type: 'Status') }
 | 
			
		||||
 | 
			
		||||
      it 'returns "pencil"' do
 | 
			
		||||
        expect(subject).to be 'pencil'
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#class_for_log_icon' do
 | 
			
		||||
    subject   { hoge.class_for_log_icon(log) }
 | 
			
		||||
 | 
			
		||||
    %i(enable unsuspend unsilence confirm promote resolve).each do |action|
 | 
			
		||||
      context "log.action == #{action}" do
 | 
			
		||||
        let(:log) { double(action: action) }
 | 
			
		||||
 | 
			
		||||
        it 'returns "positive"' do
 | 
			
		||||
          expect(subject).to be 'positive'
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'log.action == :create' do
 | 
			
		||||
      context 'opposite_verbs?(log)' do
 | 
			
		||||
        let(:log) { double(action: :create, target_type: 'DomainBlock') }
 | 
			
		||||
 | 
			
		||||
        it 'returns "negative"' do
 | 
			
		||||
          expect(subject).to be 'negative'
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      context '!opposite_verbs?(log)' do
 | 
			
		||||
        let(:log) { double(action: :create, target_type: '') }
 | 
			
		||||
 | 
			
		||||
        it 'returns "positive"' do
 | 
			
		||||
          expect(subject).to be 'positive'
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    %i(update reset_password disable_2fa memorialize change_email).each do |action|
 | 
			
		||||
      context "log.action == #{action}" do
 | 
			
		||||
        let(:log) { double(action: action) }
 | 
			
		||||
 | 
			
		||||
        it 'returns "neutral"' do
 | 
			
		||||
          expect(subject).to be 'neutral'
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    %i(demote silence disable suspend remove_avatar remove_header reopen).each do |action|
 | 
			
		||||
      context "log.action == #{action}" do
 | 
			
		||||
        let(:log) { double(action: action) }
 | 
			
		||||
 | 
			
		||||
        it 'returns "negative"' do
 | 
			
		||||
          expect(subject).to be 'negative'
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'log.action == :destroy' do
 | 
			
		||||
      context 'opposite_verbs?(log)' do
 | 
			
		||||
        let(:log) { double(action: :destroy, target_type: 'DomainBlock') }
 | 
			
		||||
 | 
			
		||||
        it 'returns "positive"' do
 | 
			
		||||
          expect(subject).to be 'positive'
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      context '!opposite_verbs?(log)' do
 | 
			
		||||
        let(:log) { double(action: :destroy, target_type: '') }
 | 
			
		||||
 | 
			
		||||
        it 'returns "negative"' do
 | 
			
		||||
          expect(subject).to be 'negative'
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user