module Admin
class CustomEmojisController < BaseController
- before_action :set_custom_emoji, except: [:index, :new, :create]
- before_action :set_filter_params
-
include ObfuscateFilename
+
obfuscate_filename [:custom_emoji, :image]
def index
authorize :custom_emoji, :index?
+
@custom_emojis = filtered_custom_emojis.eager_load(:local_counterpart).page(params[:page])
+ @form = Form::CustomEmojiBatch.new
end
def new
authorize :custom_emoji, :create?
+
@custom_emoji = CustomEmoji.new
end
end
end
- def update
- authorize @custom_emoji, :update?
-
- if @custom_emoji.update(resource_params)
- log_action :update, @custom_emoji
- flash[:notice] = I18n.t('admin.custom_emojis.updated_msg')
- else
- flash[:alert] = I18n.t('admin.custom_emojis.update_failed_msg')
- end
- redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
- end
-
- def destroy
- authorize @custom_emoji, :destroy?
- @custom_emoji.destroy!
- log_action :destroy, @custom_emoji
- flash[:notice] = I18n.t('admin.custom_emojis.destroyed_msg')
- redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
- end
-
- def copy
- authorize @custom_emoji, :copy?
-
- emoji = CustomEmoji.find_or_initialize_by(domain: nil,
- shortcode: @custom_emoji.shortcode)
- emoji.image = @custom_emoji.image
-
- if emoji.save
- log_action :create, emoji
- flash[:notice] = I18n.t('admin.custom_emojis.copied_msg')
- else
- flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg')
- end
-
- redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
- end
-
- def enable
- authorize @custom_emoji, :enable?
- @custom_emoji.update!(disabled: false)
- log_action :enable, @custom_emoji
- flash[:notice] = I18n.t('admin.custom_emojis.enabled_msg')
- redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
- end
-
- def disable
- authorize @custom_emoji, :disable?
- @custom_emoji.update!(disabled: true)
- log_action :disable, @custom_emoji
- flash[:notice] = I18n.t('admin.custom_emojis.disabled_msg')
- redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params)
+ def batch
+ @form = Form::CustomEmojiBatch.new(form_custom_emoji_batch_params.merge(current_account: current_account, action: action_from_button))
+ @form.save
+ rescue ActionController::ParameterMissing
+ flash[:alert] = I18n.t('admin.accounts.no_account_selected')
+ ensure
+ redirect_to admin_custom_emojis_path(filter_params)
end
private
- def set_custom_emoji
- @custom_emoji = CustomEmoji.find(params[:id])
- end
-
- def set_filter_params
- @filter_params = filter_params.to_hash.symbolize_keys
- end
-
def resource_params
params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker)
end
end
def filter_params
- params.permit(
- :local,
- :remote,
- :by_domain,
- :shortcode
- )
+ params.slice(:local, :remote, :by_domain, :shortcode, :page).permit(:local, :remote, :by_domain, :shortcode, :page)
+ end
+
+ def action_from_button
+ if params[:update]
+ 'update'
+ elsif params[:list]
+ 'list'
+ elsif params[:unlist]
+ 'unlist'
+ elsif params[:enable]
+ 'enable'
+ elsif params[:disable]
+ 'disable'
+ elsif params[:copy]
+ 'copy'
+ elsif params[:delete]
+ 'delete'
+ end
+ end
+
+ def form_custom_emoji_batch_params
+ params.require(:form_custom_emoji_batch).permit(:action, :category_id, :category_name, custom_emoji_ids: [])
end
end
end
}
}
+ &__form {
+ padding: 16px;
+ border: 1px solid darken($ui-base-color, 8%);
+ border-top: 0;
+ background: $ui-base-color;
+
+ .fields-row {
+ padding-top: 0;
+ margin-bottom: 0;
+ }
+ }
+
&__row {
border: 1px solid darken($ui-base-color, 8%);
border-top: 0;
&--unpadded {
padding: 0;
}
+
+ &--with-image {
+ display: flex;
+ align-items: center;
+ }
+
+ &__image {
+ flex: 0 0 auto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-right: 10px;
+
+ .emojione {
+ width: 32px;
+ height: 32px;
+ }
+ }
+
+ &__text {
+ flex: 1 1 auto;
+ }
+
+ &__extra {
+ flex: 0 0 auto;
+ text-align: right;
+ color: $darker-text-color;
+ font-weight: 500;
+ }
}
.directory__tag {
:emoji
end
+ def copy!
+ copy = self.class.find_or_initialize_by(domain: nil, shortcode: shortcode)
+ copy.image = image
+ copy.save!
+ end
+
class << self
def from_text(text, domain)
return [] if text.blank?
class CustomEmojiCategory < ApplicationRecord
has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category
+
+ validates :name, presence: true, uniqueness: true
end
scope = CustomEmoji.alphabetic
params.each do |key, value|
+ next if key.to_s == 'page'
+
scope.merge!(scope_for(key, value)) if value.present?
end
def scope_for(key, value)
case key.to_s
when 'local'
- CustomEmoji.local
+ CustomEmoji.local.left_joins(:category).reorder(Arel.sql('custom_emoji_categories.name ASC NULLS FIRST, custom_emojis.shortcode ASC'))
when 'remote'
CustomEmoji.remote
when 'by_domain'
- CustomEmoji.where(domain: value.downcase)
+ CustomEmoji.where(domain: value.strip.downcase)
when 'shortcode'
- CustomEmoji.search(value)
+ CustomEmoji.search(value.strip)
else
raise "Unknown filter: #{key}"
end
--- /dev/null
+# frozen_string_literal: true
+
+class Form::CustomEmojiBatch
+ include ActiveModel::Model
+ include Authorization
+ include AccountableConcern
+
+ attr_accessor :custom_emoji_ids, :action, :current_account,
+ :category_id, :category_name, :visible_in_picker
+
+ def save
+ case action
+ when 'update'
+ update!
+ when 'list'
+ list!
+ when 'unlist'
+ unlist!
+ when 'enable'
+ enable!
+ when 'disable'
+ disable!
+ when 'copy'
+ copy!
+ when 'delete'
+ delete!
+ end
+ end
+
+ private
+
+ def custom_emojis
+ CustomEmoji.where(id: custom_emoji_ids)
+ end
+
+ def update!
+ custom_emojis.each { |custom_emoji| authorize(custom_emoji, :update?) }
+
+ category = begin
+ if category_id.present?
+ CustomEmojiCategory.find(category_id)
+ elsif category_name.present?
+ CustomEmojiCategory.create!(name: category_name)
+ end
+ end
+
+ custom_emojis.each do |custom_emoji|
+ custom_emoji.update(category_id: category&.id)
+ log_action :update, custom_emoji
+ end
+ end
+
+ def list!
+ custom_emojis.each { |custom_emoji| authorize(custom_emoji, :update?) }
+
+ custom_emojis.each do |custom_emoji|
+ custom_emoji.update(visible_in_picker: true)
+ log_action :update, custom_emoji
+ end
+ end
+
+ def unlist!
+ custom_emojis.each { |custom_emoji| authorize(custom_emoji, :update?) }
+
+ custom_emojis.each do |custom_emoji|
+ custom_emoji.update(visible_in_picker: false)
+ log_action :update, custom_emoji
+ end
+ end
+
+ def enable!
+ custom_emojis.each { |custom_emoji| authorize(custom_emoji, :enable?) }
+
+ custom_emojis.each do |custom_emoji|
+ custom_emoji.update(disabled: false)
+ log_action :enable, custom_emoji
+ end
+ end
+
+ def disable!
+ custom_emojis.each { |custom_emoji| authorize(custom_emoji, :disable?) }
+
+ custom_emojis.each do |custom_emoji|
+ custom_emoji.update(disabled: true)
+ log_action :disable, custom_emoji
+ end
+ end
+
+ def copy!
+ custom_emojis.each { |custom_emoji| authorize(custom_emoji, :copy?) }
+
+ custom_emojis.each do |custom_emoji|
+ copied_custom_emoji = custom_emoji.copy!
+ log_action :create, copied_custom_emoji
+ end
+ end
+
+ def delete!
+ custom_emojis.each { |custom_emoji| authorize(custom_emoji, :destroy?) }
+
+ custom_emojis.each do |custom_emoji|
+ custom_emoji.destroy
+ log_action :destroy, custom_emoji
+ end
+ end
+end
-%tr
- %td
- = custom_emoji_tag(custom_emoji)
- %td
- %samp= ":#{custom_emoji.shortcode}:"
- %td
- - if custom_emoji.local?
- = t('admin.accounts.location.local')
- - else
- = link_to custom_emoji.domain, admin_custom_emojis_path(by_domain: custom_emoji.domain)
- %td
- - if custom_emoji.local?
- - if custom_emoji.visible_in_picker
- = table_link_to 'eye', t('admin.custom_emojis.listed'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: false }, page: params[:page], **@filter_params), method: :patch
+.batch-table__row
+ %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
+ = f.check_box :custom_emoji_ids, { multiple: true, include_hidden: false }, custom_emoji.id
+ .batch-table__row__content.batch-table__row__content--with-image
+ .batch-table__row__content__image
+ = custom_emoji_tag(custom_emoji)
+
+ .batch-table__row__content__text
+ %samp= ":#{custom_emoji.shortcode}:"
+
+ - if custom_emoji.local?
+ %span.account-role.bot= custom_emoji.category&.name || t('admin.custom_emojis.uncategorized')
+
+ .batch-table__row__content__extra
+ - if custom_emoji.local?
+ = t('admin.accounts.location.local')
- else
- = table_link_to 'eye-slash', t('admin.custom_emojis.unlisted'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: true }, page: params[:page], **@filter_params), method: :patch
- - else
- - if custom_emoji.local_counterpart.present?
- = link_to safe_join([custom_emoji_tag(custom_emoji.local_counterpart), t('admin.custom_emojis.overwrite')]), copy_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post, class: 'table-action-link'
+ = custom_emoji.domain
+
+ %br/
+
+ - if custom_emoji.disabled?
+ = t('admin.custom_emojis.disabled')
- else
- = table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post
- %td
- - if custom_emoji.disabled?
- = table_link_to 'power-off', t('admin.custom_emojis.enable'), enable_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }
- - else
- = table_link_to 'power-off', t('admin.custom_emojis.disable'), disable_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }
- %td
- = table_link_to 'times', t('admin.custom_emojis.delete'), admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }
+ = t('admin.custom_emojis.enabled')
+ - if custom_emoji.local?
+ •
+ - if custom_emoji.visible_in_picker?
+ = t('admin.custom_emojis.listed')
+ - else
+ = t('admin.custom_emojis.unlisted')
- content_for :page_title do
= t('admin.custom_emojis.title')
+- content_for :header_tags do
+ = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
+
.filters
.filter-subset
%strong= t('admin.accounts.location.title')
= form_tag admin_custom_emojis_url, method: 'GET', class: 'simple_form' do
.fields-group
- Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key|
- - if params[key].present?
- = hidden_field_tag key, params[key]
+ = hidden_field_tag key, params[key] if params[key].present?
- %i(shortcode by_domain).each do |key|
.input.string.optional
%button= t('admin.accounts.search')
= link_to t('admin.accounts.reset'), admin_custom_emojis_path, class: 'button negative'
-.table-wrapper
- %table.table
- %thead
- %tr
- %th= t('admin.custom_emojis.emoji')
- %th= t('admin.custom_emojis.shortcode')
- %th= t('admin.accounts.domain')
- %th
- %th
- %th
- %tbody
- = render @custom_emojis
+= form_for(@form, url: batch_admin_custom_emojis_path) do |f|
+ = hidden_field_tag :page, params[:page] || 1
+
+ - Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key|
+ = hidden_field_tag key, params[key] if params[key].present?
+
+ .batch-table
+ .batch-table__toolbar
+ %label.batch-table__toolbar__select.batch-checkbox-all
+ = check_box_tag :batch_checkbox_all, nil, false
+ .batch-table__toolbar__actions
+ - if params[:local] == '1'
+ = f.button safe_join([fa_icon('save'), t('generic.save_changes')]), name: :update, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+
+ = f.button safe_join([fa_icon('eye'), t('admin.custom_emojis.list')]), name: :list, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+
+ = f.button safe_join([fa_icon('eye-slash'), t('admin.custom_emojis.unlist')]), name: :unlist, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+
+ = f.button safe_join([fa_icon('power-off'), t('admin.custom_emojis.enable')]), name: :enable, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+
+ = f.button safe_join([fa_icon('power-off'), t('admin.custom_emojis.disable')]), name: :disable, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+
+ = f.button safe_join([fa_icon('times'), t('admin.custom_emojis.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+
+ - unless params[:local] == '1'
+ = f.button safe_join([fa_icon('copy'), t('admin.custom_emojis.copy')]), name: :copy, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
+
+ - if params[:local] == '1'
+ .batch-table__form.simple_form
+ .fields-row
+ .fields-group.fields-row__column.fields-row__column-6
+ .input.select.optional
+ .label_input
+ = f.select :category_id, options_from_collection_for_select(CustomEmojiCategory.all, 'id', 'name'), prompt: t('admin.custom_emojis.assign_category'), class: 'select optional', 'aria-label': t('admin.custom_emojis.assign_category')
+
+ .fields-group.fields-row__column.fields-row__column-6
+ .input.string.optional
+ .label_input
+ = f.text_field :category_name, class: 'string optional', placeholder: t('admin.custom_emojis.create_new_category'), 'aria-label': t('admin.custom_emojis.create_new_category')
+
+ .batch-table__body
+ - if @custom_emojis.empty?
+ = nothing_here 'nothing-here--under-tabs'
+ - else
+ = render partial: 'custom_emoji', collection: @custom_emojis, locals: { f: f }
= paginate @custom_emojis
+
+%hr.spacer/
+
= link_to t('admin.custom_emojis.upload'), new_admin_custom_emoji_path, class: 'button'
deleted_status: "(deleted status)"
title: Audit log
custom_emojis:
+ assign_category: Assign category
by_domain: Domain
copied_msg: Successfully created local copy of the emoji
copy: Copy
copy_failed_msg: Could not make a local copy of that emoji
+ create_new_category: Create new category
created_msg: Emoji successfully created!
delete: Delete
destroyed_msg: Emojo successfully destroyed!
shortcode: Shortcode
shortcode_hint: At least 2 characters, only alphanumeric characters and underscores
title: Custom emojis
+ uncategorized: Uncategorized
unlisted: Unlisted
update_failed_msg: Could not update that emoji
updated_msg: Emoji successfully updated!
resource :two_factor_authentication, only: [:destroy]
end
- resources :custom_emojis, only: [:index, :new, :create, :update, :destroy] do
- member do
- post :copy
- post :enable
- post :disable
+ resources :custom_emojis, only: [:index, :new, :create] do
+ collection do
+ post :batch
end
end
end
end
end
-
- describe 'PUT #update' do
- let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test') }
- let(:image) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'emojo.png'), 'image/png') }
-
- before do
- put :update, params: { id: custom_emoji.id, custom_emoji: params }
- end
-
- context 'when parameter is valid' do
- let(:params) { { shortcode: 'updated', image: image } }
-
- it 'succeeds in updating custom emoji' do
- expect(flash[:notice]).to eq I18n.t('admin.custom_emojis.updated_msg')
- expect(custom_emoji.reload).to have_attributes(shortcode: 'updated')
- end
- end
-
- context 'when parameter is invalid' do
- let(:params) { { shortcode: 'u', image: image } }
-
- it 'fails to update custom emoji' do
- expect(flash[:alert]).to eq I18n.t('admin.custom_emojis.update_failed_msg')
- expect(custom_emoji.reload).to have_attributes(shortcode: 'test')
- end
- end
- end
-
- describe 'POST #copy' do
- subject { post :copy, params: { id: custom_emoji.id } }
-
- let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test') }
-
- it 'copies custom emoji' do
- expect { subject }.to change { CustomEmoji.where(shortcode: 'test').count }.by(1)
- expect(flash[:notice]).to eq I18n.t('admin.custom_emojis.copied_msg')
- end
- end
-
- describe 'POST #enable' do
- let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test', disabled: true) }
-
- before { post :enable, params: { id: custom_emoji.id } }
-
- it 'enables custom emoji' do
- expect(response).to redirect_to admin_custom_emojis_path
- expect(custom_emoji.reload).to have_attributes(disabled: false)
- end
- end
-
- describe 'POST #disable' do
- let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: 'test', disabled: false) }
-
- before { post :disable, params: { id: custom_emoji.id } }
-
- it 'enables custom emoji' do
- expect(response).to redirect_to admin_custom_emojis_path
- expect(custom_emoji.reload).to have_attributes(disabled: true)
- end
- end
end