From e445a8af64908b2bdb721bec74c113e8258a129b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 6 Sep 2019 13:55:51 +0200 Subject: Add timeline read markers API (#11762) Fix #4093 --- config/routes.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'config/routes.rb') diff --git a/config/routes.rb b/config/routes.rb index c5326052e..74a162f32 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -314,6 +314,7 @@ Rails.application.routes.draw do resources :trends, only: [:index] resources :filters, only: [:index, :create, :show, :update, :destroy] resources :endorsements, only: [:index] + resources :markers, only: [:index, :create] namespace :apps do get :verify_credentials, to: 'credentials#show' -- cgit From d7268befa853ac4a99f9d066c38330d4fc0bfc31 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sat, 7 Sep 2019 09:47:51 +0900 Subject: Add healthcheck endpoint for web (#11770) --- Gemfile | 1 + Gemfile.lock | 3 +++ app/controllers/application_controller.rb | 2 +- config/initializers/health_check.rb | 6 ++++++ config/routes.rb | 2 ++ docker-compose.yml | 4 ++-- 6 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 config/initializers/health_check.rb (limited to 'config/routes.rb') diff --git a/Gemfile b/Gemfile index 1cbf23cf2..d9740702f 100644 --- a/Gemfile +++ b/Gemfile @@ -50,6 +50,7 @@ gem 'fastimage' gem 'goldfinger', '~> 2.1' gem 'hiredis', '~> 0.6' gem 'redis-namespace', '~> 1.5' +gem 'health_check', '~> 3.0' gem 'htmlentities', '~> 4.3' gem 'http', '~> 3.3' gem 'http_accept_language', '~> 2.1' diff --git a/Gemfile.lock b/Gemfile.lock index 8ecc8ebb7..b0f6bf083 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -278,6 +278,8 @@ GEM concurrent-ruby (~> 1.0) hashdiff (1.0.0) hashie (3.6.0) + health_check (3.0.0) + railties (>= 5.0) heapy (0.1.4) highline (2.0.1) hiredis (0.6.3) @@ -706,6 +708,7 @@ DEPENDENCIES fuubar (~> 2.4) goldfinger (~> 2.1) hamlit-rails (~> 0.2) + health_check (~> 3.0) hiredis (~> 0.6) htmlentities (~> 4.3) http (~> 3.3) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5b343a276..bd3d13774 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -41,7 +41,7 @@ class ApplicationController < ActionController::Base private def https_enabled? - Rails.env.production? + Rails.env.production? && !request.path.start_with?('/health') end def authorized_fetch_mode? diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb new file mode 100644 index 000000000..eece67b10 --- /dev/null +++ b/config/initializers/health_check.rb @@ -0,0 +1,6 @@ +HealthCheck.setup do |config| + config.uri = 'health' + + config.standard_checks = %w(database migrations cache) + config.full_checks = %w(database migrations cache) +end diff --git a/config/routes.rb b/config/routes.rb index 74a162f32..fe8425341 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -10,6 +10,8 @@ Rails.application.routes.draw do mount LetterOpenerWeb::Engine, at: 'letter_opener' if Rails.env.development? + health_check_routes + authenticate :user, lambda { |u| u.admin? } do mount Sidekiq::Web, at: 'sidekiq', as: :sidekiq mount PgHero::Engine, at: 'pghero', as: :pghero diff --git a/docker-compose.yml b/docker-compose.yml index 740684966..20649e424 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: - external_network - internal_network healthcheck: - test: ["CMD-SHELL", "wget -q --spider --header 'x-forwarded-proto: https' --proxy=off localhost:3000/api/v1/instance || exit 1"] + test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:3000/health || exit 1"] ports: - "127.0.0.1:3000:3000" depends_on: @@ -63,7 +63,7 @@ services: - external_network - internal_network healthcheck: - test: ["CMD-SHELL", "wget -q --spider --header 'x-forwarded-proto: https' --proxy=off localhost:4000/api/v1/streaming/health || exit 1"] + test: ["CMD-SHELL", "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1"] ports: - "127.0.0.1:4000:4000" depends_on: -- cgit From 9c9dcf580ab9b40c3fd420159a0a02ea4dd11925 Mon Sep 17 00:00:00 2001 From: Takeshi Umeda Date: Mon, 9 Sep 2019 17:50:33 +0900 Subject: Add featured tags API (#11778) * Add featured tags API * Remove show and update, change scope, fix code style --- .../api/v1/featured_tags/suggestions_controller.rb | 20 +++++++++++ app/controllers/api/v1/featured_tags_controller.rb | 40 ++++++++++++++++++++++ app/serializers/rest/featured_tag_serializer.rb | 9 +++++ config/routes.rb | 6 ++++ 4 files changed, 75 insertions(+) create mode 100644 app/controllers/api/v1/featured_tags/suggestions_controller.rb create mode 100644 app/controllers/api/v1/featured_tags_controller.rb create mode 100644 app/serializers/rest/featured_tag_serializer.rb (limited to 'config/routes.rb') diff --git a/app/controllers/api/v1/featured_tags/suggestions_controller.rb b/app/controllers/api/v1/featured_tags/suggestions_controller.rb new file mode 100644 index 000000000..fb27ef88b --- /dev/null +++ b/app/controllers/api/v1/featured_tags/suggestions_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index + + before_action :require_user! + before_action :set_most_used_tags, only: :index + + respond_to :json + + def index + render json: @most_used_tags, each_serializer: REST::TagSerializer + end + + private + + def set_most_used_tags + @most_used_tags = Tag.most_used(current_account).where.not(id: current_account.featured_tags).limit(10) + end +end diff --git a/app/controllers/api/v1/featured_tags_controller.rb b/app/controllers/api/v1/featured_tags_controller.rb new file mode 100644 index 000000000..e4e836c97 --- /dev/null +++ b/app/controllers/api/v1/featured_tags_controller.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class Api::V1::FeaturedTagsController < Api::BaseController + before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index + before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index + + before_action :require_user! + before_action :set_featured_tags, only: :index + before_action :set_featured_tag, except: [:index, :create] + + def index + render json: @featured_tags, each_serializer: REST::FeaturedTagSerializer + end + + def create + @featured_tag = current_account.featured_tags.new(featured_tag_params) + @featured_tag.reset_data + @featured_tag.save! + render json: @featured_tag, serializer: REST::FeaturedTagSerializer + end + + def destroy + @featured_tag.destroy! + render_empty + end + + private + + def set_featured_tag + @featured_tag = current_account.featured_tags.find(params[:id]) + end + + def set_featured_tags + @featured_tags = current_account.featured_tags.order(statuses_count: :desc) + end + + def featured_tag_params + params.permit(:name) + end +end diff --git a/app/serializers/rest/featured_tag_serializer.rb b/app/serializers/rest/featured_tag_serializer.rb new file mode 100644 index 000000000..08121ff16 --- /dev/null +++ b/app/serializers/rest/featured_tag_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class REST::FeaturedTagSerializer < ActiveModel::Serializer + attributes :id, :name, :statuses_count, :last_status_at + + def id + object.id.to_s + end +end diff --git a/config/routes.rb b/config/routes.rb index fe8425341..1ebf9e066 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -380,6 +380,12 @@ Rails.application.routes.draw do resource :accounts, only: [:show, :create, :destroy], controller: 'lists/accounts' end + namespace :featured_tags do + get :suggestions, to: 'suggestions#index' + end + + resources :featured_tags, only: [:index, :create, :destroy] + resources :polls, only: [:create, :show] do resources :votes, only: :create, controller: 'polls/votes' end -- cgit From 261e52268c05d2da4459a23e2898555dd5db5771 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 9 Sep 2019 12:50:09 +0200 Subject: Add batch approve/reject for pending hashtags in admin UI (#11791) --- app/controllers/admin/tags_controller.rb | 41 +++++++++++++++++++++++++++--- app/javascript/styles/mastodon/tables.scss | 10 ++++++++ app/models/form/tag_batch.rb | 33 ++++++++++++++++++++++++ app/views/admin/tags/_tag.html.haml | 30 ++++++++++++---------- app/views/admin/tags/index.html.haml | 37 ++++++++++++++++++++++++++- config/locales/en.yml | 1 + config/routes.rb | 9 ++++++- 7 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 app/models/form/tag_batch.rb (limited to 'config/routes.rb') diff --git a/app/controllers/admin/tags_controller.rb b/app/controllers/admin/tags_controller.rb index 8bd4e5f8b..376ebe44d 100644 --- a/app/controllers/admin/tags_controller.rb +++ b/app/controllers/admin/tags_controller.rb @@ -3,12 +3,33 @@ module Admin class TagsController < BaseController before_action :set_tags, only: :index - before_action :set_tag, except: :index - before_action :set_usage_by_domain, except: :index - before_action :set_counters, except: :index + before_action :set_tag, except: [:index, :batch, :approve_all, :reject_all] + before_action :set_usage_by_domain, except: [:index, :batch, :approve_all, :reject_all] + before_action :set_counters, except: [:index, :batch, :approve_all, :reject_all] def index authorize :tag, :index? + + @form = Form::TagBatch.new + end + + def batch + @form = Form::TagBatch.new(form_tag_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_tags_path(filter_params) + end + + def approve_all + Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'approve').save + redirect_to admin_tags_path(filter_params) + end + + def reject_all + Form::TagBatch.new(current_account: current_account, tag_ids: Tag.pending_review.pluck(:id), action: 'reject').save + redirect_to admin_tags_path(filter_params) end def show @@ -61,7 +82,7 @@ module Admin end def filter_params - params.slice(:context, :review).permit(:context, :review) + params.slice(:context, :review, :page).permit(:context, :review, :page) end def tag_params @@ -75,5 +96,17 @@ module Admin date.to_time(:utc).beginning_of_day.to_i end end + + def form_tag_batch_params + params.require(:form_tag_batch).permit(:action, tag_ids: []) + end + + def action_from_button + if params[:approve] + 'approve' + elsif params[:reject] + 'reject' + end + end end end diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index fe6beba5d..2aef099e6 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -211,6 +211,16 @@ a.table-action-link { padding: 0; } } + + .directory__tag { + margin: 0; + width: 100%; + + a { + background: transparent; + border-radius: 0; + } + } } .status__content { diff --git a/app/models/form/tag_batch.rb b/app/models/form/tag_batch.rb new file mode 100644 index 000000000..fd517a1a6 --- /dev/null +++ b/app/models/form/tag_batch.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class Form::TagBatch + include ActiveModel::Model + include Authorization + + attr_accessor :tag_ids, :action, :current_account + + def save + case action + when 'approve' + approve! + when 'reject' + reject! + end + end + + private + + def tags + Tag.where(id: tag_ids) + end + + def approve! + tags.each { |tag| authorize(tag, :update?) } + tags.update_all(trendable: true, reviewed_at: Time.now.utc) + end + + def reject! + tags.each { |tag| authorize(tag, :update?) } + tags.update_all(trendable: false, reviewed_at: Time.now.utc) + end +end diff --git a/app/views/admin/tags/_tag.html.haml b/app/views/admin/tags/_tag.html.haml index 91af8e492..670f3bc05 100644 --- a/app/views/admin/tags/_tag.html.haml +++ b/app/views/admin/tags/_tag.html.haml @@ -1,16 +1,20 @@ -.directory__tag - = link_to admin_tag_path(tag.id) do - %h4 - = fa_icon 'hashtag' - = tag.name +.batch-table__row + %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox + = f.check_box :tag_ids, { multiple: true, include_hidden: false }, tag.id - %small - = t('admin.tags.in_directory', count: tag.accounts_count) - • - = t('admin.tags.unique_uses_today', count: tag.history.first[:accounts]) + .directory__tag + = link_to admin_tag_path(tag.id) do + %h4 + = fa_icon 'hashtag' + = tag.name - - if tag.trending? - = fa_icon 'fire fw' - = t('admin.tags.trending_right_now') + %small + = t('admin.tags.in_directory', count: tag.accounts_count) + • + = t('admin.tags.unique_uses_today', count: tag.history.first[:accounts]) - .trends__item__current= number_to_human tag.history.first[:uses], strip_insignificant_zeros: true + - if tag.trending? + = fa_icon 'fire fw' + = t('admin.tags.trending_right_now') + + .trends__item__current= number_to_human tag.history.first[:uses], strip_insignificant_zeros: true diff --git a/app/views/admin/tags/index.html.haml b/app/views/admin/tags/index.html.haml index d994955ef..324d13d3e 100644 --- a/app/views/admin/tags/index.html.haml +++ b/app/views/admin/tags/index.html.haml @@ -1,6 +1,9 @@ - content_for :page_title do = t('admin.tags.title') +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + .filters .filter-subset %strong= t('admin.tags.context') @@ -18,5 +21,37 @@ %hr.spacer/ -= render @tags += form_for(@form, url: batch_admin_tags_path) do |f| + = hidden_field_tag :page, params[:page] || 1 + = hidden_field_tag :context, params[:context] + = hidden_field_tag :review, params[:review] + + .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[:review] == 'pending_review' + = f.button safe_join([fa_icon('check'), t('admin.accounts.approve')]), name: :approve, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + + = f.button safe_join([fa_icon('times'), t('admin.accounts.reject')]), name: :reject, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + - else + %span.neutral-hint= t('generic.no_batch_actions_available') + + .batch-table__body + - if @tags.empty? + = nothing_here 'nothing-here--under-tabs' + - else + = render partial: 'tag', collection: @tags, locals: { f: f } + = paginate @tags + +- if params[:review] == 'pending_review' + %hr.spacer/ + + %div{ style: 'overflow: hidden' } + %div{ style: 'float: right' } + = link_to t('admin.accounts.reject_all'), reject_all_admin_tags_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' + + %div + = link_to t('admin.accounts.approve_all'), approve_all_admin_tags_path, method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' diff --git a/config/locales/en.yml b/config/locales/en.yml index 687f5f2a0..42d8e0eb8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -727,6 +727,7 @@ en: all: All changes_saved_msg: Changes successfully saved! copy: Copy + no_batch_actions_available: No batch actions available on this page order_by: Order by save_changes: Save changes validation_errors: diff --git a/config/routes.rb b/config/routes.rb index 1ebf9e066..534e68814 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -251,7 +251,14 @@ Rails.application.routes.draw do end resources :account_moderation_notes, only: [:create, :destroy] - resources :tags, only: [:index, :show, :update] + + resources :tags, only: [:index, :show, :update] do + collection do + post :approve_all + post :reject_all + post :batch + end + end end get '/admin', to: redirect('/admin/dashboard', status: 302) -- cgit From 1110ea1a9162d5488e1ed5dbccd0803618e713f8 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 9 Sep 2019 22:44:17 +0200 Subject: Add batch actions and categories to admin UI for custom emojis (#11793) --- app/controllers/admin/custom_emojis_controller.rb | 102 +++++++------------- app/javascript/styles/mastodon/tables.scss | 41 ++++++++ app/models/custom_emoji.rb | 6 ++ app/models/custom_emoji_category.rb | 2 + app/models/custom_emoji_filter.rb | 8 +- app/models/form/custom_emoji_batch.rb | 106 +++++++++++++++++++++ .../admin/custom_emojis/_custom_emoji.html.haml | 55 ++++++----- app/views/admin/custom_emojis/index.html.haml | 66 ++++++++++--- config/locales/en.yml | 3 + config/routes.rb | 8 +- .../admin/custom_emojis_controller_spec.rb | 60 ------------ 11 files changed, 281 insertions(+), 176 deletions(-) create mode 100644 app/models/form/custom_emoji_batch.rb (limited to 'config/routes.rb') diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index f77699166..2af90f051 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -2,19 +2,20 @@ 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 @@ -31,69 +32,17 @@ module Admin 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 @@ -103,12 +52,29 @@ module Admin 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 diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index 2aef099e6..d6403986f 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -180,6 +180,18 @@ a.table-action-link { } } + &__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; @@ -210,6 +222,35 @@ a.table-action-link { &--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 { diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index b21ad9042..0a4201a14 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -59,6 +59,12 @@ class CustomEmoji < ApplicationRecord :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? diff --git a/app/models/custom_emoji_category.rb b/app/models/custom_emoji_category.rb index 7d8c0ee2d..3c87f2b2e 100644 --- a/app/models/custom_emoji_category.rb +++ b/app/models/custom_emoji_category.rb @@ -12,4 +12,6 @@ class CustomEmojiCategory < ApplicationRecord has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category + + validates :name, presence: true, uniqueness: true end diff --git a/app/models/custom_emoji_filter.rb b/app/models/custom_emoji_filter.rb index 7649055d2..15b8da1d1 100644 --- a/app/models/custom_emoji_filter.rb +++ b/app/models/custom_emoji_filter.rb @@ -11,6 +11,8 @@ class CustomEmojiFilter scope = CustomEmoji.alphabetic params.each do |key, value| + next if key.to_s == 'page' + scope.merge!(scope_for(key, value)) if value.present? end @@ -22,13 +24,13 @@ class CustomEmojiFilter 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 diff --git a/app/models/form/custom_emoji_batch.rb b/app/models/form/custom_emoji_batch.rb new file mode 100644 index 000000000..076e8c9e3 --- /dev/null +++ b/app/models/form/custom_emoji_batch.rb @@ -0,0 +1,106 @@ +# 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 diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml index fbaa9a174..9e06a3b42 100644 --- a/app/views/admin/custom_emojis/_custom_emoji.html.haml +++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml @@ -1,28 +1,31 @@ -%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') diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index 3a119276c..7320ce1bb 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -1,6 +1,9 @@ - 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') @@ -20,8 +23,7 @@ = 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 @@ -31,18 +33,54 @@ %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' diff --git a/config/locales/en.yml b/config/locales/en.yml index 42d8e0eb8..52cb4a269 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -225,10 +225,12 @@ en: 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! @@ -245,6 +247,7 @@ en: 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! diff --git a/config/routes.rb b/config/routes.rb index 534e68814..d22a9e56a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -242,11 +242,9 @@ Rails.application.routes.draw do 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 diff --git a/spec/controllers/admin/custom_emojis_controller_spec.rb b/spec/controllers/admin/custom_emojis_controller_spec.rb index b7e2894e9..a8d96948c 100644 --- a/spec/controllers/admin/custom_emojis_controller_spec.rb +++ b/spec/controllers/admin/custom_emojis_controller_spec.rb @@ -52,64 +52,4 @@ describe Admin::CustomEmojisController do 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 -- cgit From 18331fefa2246facc818226043b1f9cc67cf6c1a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 13 Sep 2019 16:11:13 +0200 Subject: Remove deprecated `GET /api/v1/search` API (#11823) Use `GET /api/v2/search` instead --- app/controllers/api/v1/search_controller.rb | 32 ----------------------- app/controllers/api/v2/search_controller.rb | 28 ++++++++++++++++++-- app/serializers/rest/search_serializer.rb | 7 +---- app/serializers/rest/v2/search_serializer.rb | 7 ----- config/routes.rb | 2 -- spec/controllers/api/v1/search_controller_spec.rb | 22 ---------------- 6 files changed, 27 insertions(+), 71 deletions(-) delete mode 100644 app/controllers/api/v1/search_controller.rb delete mode 100644 app/serializers/rest/v2/search_serializer.rb delete mode 100644 spec/controllers/api/v1/search_controller_spec.rb (limited to 'config/routes.rb') diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb deleted file mode 100644 index 6131cbbb6..000000000 --- a/app/controllers/api/v1/search_controller.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -class Api::V1::SearchController < Api::BaseController - include Authorization - - RESULTS_LIMIT = 20 - - before_action -> { doorkeeper_authorize! :read, :'read:search' } - before_action :require_user! - - respond_to :json - - def index - @search = Search.new(search_results) - render json: @search, serializer: REST::SearchSerializer - end - - private - - def search_results - SearchService.new.call( - params[:q], - current_account, - limit_param(RESULTS_LIMIT), - search_params.merge(resolve: truthy_param?(:resolve)) - ) - end - - def search_params - params.permit(:type, :offset, :min_id, :max_id, :account_id) - end -end diff --git a/app/controllers/api/v2/search_controller.rb b/app/controllers/api/v2/search_controller.rb index 9aa6edc69..c14cd22d7 100644 --- a/app/controllers/api/v2/search_controller.rb +++ b/app/controllers/api/v2/search_controller.rb @@ -1,8 +1,32 @@ # frozen_string_literal: true -class Api::V2::SearchController < Api::V1::SearchController +class Api::V2::SearchController < Api::BaseController + include Authorization + + RESULTS_LIMIT = 20 + + before_action -> { doorkeeper_authorize! :read, :'read:search' } + before_action :require_user! + + respond_to :json + def index @search = Search.new(search_results) - render json: @search, serializer: REST::V2::SearchSerializer + render json: @search, serializer: REST::SearchSerializer + end + + private + + def search_results + SearchService.new.call( + params[:q], + current_account, + limit_param(RESULTS_LIMIT), + search_params.merge(resolve: truthy_param?(:resolve)) + ) + end + + def search_params + params.permit(:type, :offset, :min_id, :max_id, :account_id) end end diff --git a/app/serializers/rest/search_serializer.rb b/app/serializers/rest/search_serializer.rb index 157f543ae..ee9b421eb 100644 --- a/app/serializers/rest/search_serializer.rb +++ b/app/serializers/rest/search_serializer.rb @@ -1,12 +1,7 @@ # frozen_string_literal: true class REST::SearchSerializer < ActiveModel::Serializer - attributes :hashtags - has_many :accounts, serializer: REST::AccountSerializer has_many :statuses, serializer: REST::StatusSerializer - - def hashtags - object.hashtags.map(&:name) - end + has_many :hashtags, serializer: REST::TagSerializer end diff --git a/app/serializers/rest/v2/search_serializer.rb b/app/serializers/rest/v2/search_serializer.rb deleted file mode 100644 index cdb6b3a53..000000000 --- a/app/serializers/rest/v2/search_serializer.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class REST::V2::SearchSerializer < ActiveModel::Serializer - has_many :accounts, serializer: REST::AccountSerializer - has_many :statuses, serializer: REST::StatusSerializer - has_many :hashtags, serializer: REST::TagSerializer -end diff --git a/config/routes.rb b/config/routes.rb index d22a9e56a..a4dee2842 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -311,8 +311,6 @@ Rails.application.routes.draw do end end - get '/search', to: 'search#index', as: :search - resources :media, only: [:create, :update] resources :blocks, only: [:index] resources :mutes, only: [:index] diff --git a/spec/controllers/api/v1/search_controller_spec.rb b/spec/controllers/api/v1/search_controller_spec.rb deleted file mode 100644 index c9e544cc7..000000000 --- a/spec/controllers/api/v1/search_controller_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Api::V1::SearchController, type: :controller do - render_views - - let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } - let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:search') } - - before do - allow(controller).to receive(:doorkeeper_token) { token } - end - - describe 'GET #index' do - it 'returns http success' do - get :index, params: { q: 'test' } - - expect(response).to have_http_status(200) - end - end -end -- cgit