From 51fa8f2eb483f1f057a02c2bc205f32223cd1f3a Mon Sep 17 00:00:00 2001 From: mgabdev <> Date: Tue, 19 Jan 2021 01:25:25 -0500 Subject: [PATCH] Added/Updated admin dashboard tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Added: - New Account filtering - PreviewCard viewing/sorting/filtering deleting (todo) - DeletePreviewCardWorker, Service - Status viewing/sorting/filtering deleting - ChatMessage viewing/sorting/filtering deleting (todo) - Account > Follows view • Updated: - LinkBlock to sort alphabetically - Groups to be under "Moderation" instead of "Admin" in navigation.rb - Status in admin to have group name/link - Reports reset button - Group filtering/sorting - LinkBlock filtering/sorting - Account now has bio and few more data points in dashboard --- .../admin/account_statuses_controller.rb | 76 +++++++++++++++++++ app/controllers/admin/accounts_controller.rb | 5 +- .../admin/chat_messages_controller.rb | 29 ++++--- app/controllers/admin/follows_controller.rb | 18 +++++ app/controllers/admin/groups_controller.rb | 22 +++--- .../admin/link_blocks_controller.rb | 10 ++- .../admin/preview_cards_controller.rb | 61 +++++++++++++++ app/controllers/admin/statuses_controller.rb | 32 ++++---- app/helpers/admin/filter_helper.rb | 2 +- app/lib/status_filter.rb | 34 ++++++++- app/models/account_filter.rb | 7 ++ app/models/chat_message_filter.rb | 34 +++++++++ app/models/form/preview_card_batch.rb | 26 +++++++ app/models/group_filter.rb | 34 +++++++++ app/models/link_block.rb | 2 + app/models/link_block_filter.rb | 26 +++++++ app/models/preview_card_filter.rb | 30 ++++++++ app/policies/preview_card_policy.rb | 15 ++++ app/services/delete_preview_card_service.rb | 23 ++++++ .../admin/account_statuses/index.html.haml | 37 +++++++++ .../show.html.haml | 0 app/views/admin/accounts/index.html.haml | 8 +- app/views/admin/accounts/show.html.haml | 42 ++++++---- app/views/admin/chat_messages/index.html.haml | 29 ++++--- app/views/admin/follows/index.html.haml | 28 +++++++ app/views/admin/groups/index.html.haml | 11 ++- app/views/admin/groups/show.html.haml | 7 +- app/views/admin/link_blocks/index.html.haml | 9 +++ .../preview_cards/_preview_card.html.haml | 14 ++++ app/views/admin/preview_cards/index.html.haml | 32 ++++++++ app/views/admin/reports/_status.html.haml | 3 + app/views/admin/reports/index.html.haml | 7 +- app/views/admin/statuses/index.html.haml | 38 ++++++---- app/views/application/_card.html.haml | 23 +++--- app/workers/delete_preview_card_worker.rb | 12 +++ config/locales/en.yml | 1 + config/locales/en_GB.yml | 1 + config/navigation.rb | 11 ++- config/routes.rb | 6 +- 39 files changed, 697 insertions(+), 108 deletions(-) create mode 100644 app/controllers/admin/account_statuses_controller.rb create mode 100644 app/controllers/admin/follows_controller.rb create mode 100644 app/controllers/admin/preview_cards_controller.rb create mode 100644 app/models/chat_message_filter.rb create mode 100644 app/models/form/preview_card_batch.rb create mode 100644 app/models/group_filter.rb create mode 100644 app/models/link_block_filter.rb create mode 100644 app/models/preview_card_filter.rb create mode 100644 app/policies/preview_card_policy.rb create mode 100644 app/services/delete_preview_card_service.rb create mode 100644 app/views/admin/account_statuses/index.html.haml rename app/views/admin/{statuses => account_statuses}/show.html.haml (100%) create mode 100644 app/views/admin/follows/index.html.haml create mode 100644 app/views/admin/preview_cards/_preview_card.html.haml create mode 100644 app/views/admin/preview_cards/index.html.haml create mode 100644 app/workers/delete_preview_card_worker.rb diff --git a/app/controllers/admin/account_statuses_controller.rb b/app/controllers/admin/account_statuses_controller.rb new file mode 100644 index 00000000..2e521d55 --- /dev/null +++ b/app/controllers/admin/account_statuses_controller.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module Admin + class AccountStatusesController < BaseController + helper_method :current_params + + before_action :set_account + + PER_PAGE = 20 + + def index + authorize :status, :index? + + @statuses = @account.statuses + + if params[:media] + account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct + @statuses.merge!(Status.where(id: account_media_status_ids)) + end + + @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) + @form = Form::StatusBatch.new + end + + def show + authorize :status, :index? + + @statuses = @account.statuses.where(id: params[:id]) + authorize @statuses.first, :show? + + @form = Form::StatusBatch.new + end + + def create + authorize :status, :update? + + @form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button)) + flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save + + redirect_to admin_account_account_statuses_path(@account.id, current_params) + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.statuses.no_status_selected') + + redirect_to admin_account_account_statuses_path(@account.id, current_params) + end + + private + + def form_status_batch_params + params.require(:form_status_batch).permit(:action, status_ids: []) + end + + def set_account + @account = Account.find(params[:account_id]) + end + + def current_params + page = (params[:page] || 1).to_i + + { + media: params[:media], + page: page > 1 && page, + }.select { |_, value| value.present? } + end + + def action_from_button + if params[:nsfw_on] + 'nsfw_on' + elsif params[:nsfw_off] + 'nsfw_off' + elsif params[:delete] + 'delete' + end + end + end +end diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index a48806a6..c5b02120 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -205,7 +205,10 @@ module Admin :display_name, :email, :ip, - :staff + :staff, + :note, + :status_count_gte, + :sign_up_date_gte, ) end diff --git a/app/controllers/admin/chat_messages_controller.rb b/app/controllers/admin/chat_messages_controller.rb index 257bc7f5..b441eb8f 100644 --- a/app/controllers/admin/chat_messages_controller.rb +++ b/app/controllers/admin/chat_messages_controller.rb @@ -2,22 +2,20 @@ module Admin class ChatMessagesController < BaseController - before_action :set_account - - PER_PAGE = 100 + PER_PAGE = 50 def index authorize :chat_message, :index? - @chat_messages = ChatMessage.where(from_account: @account).page(params[:page]).per(PER_PAGE) + @chat_messages = filtered_chat_messages.page(params[:page]).per(PER_PAGE) @form = Form::ChatMessageBatch.new end def show authorize :chat_message, :index? - @chat_messages = @account.chat_messages.where(id: params[:id]) - authorize @chat_messages.first, :show? + @chat_message = ChatMessage.where(id: params[:id]) + authorize @chat_message, :show? @form = Form::ChatMessageBatch.new end @@ -37,19 +35,18 @@ module Admin private + def filtered_chat_messages + ChatMessageFilter.new(filter_params).results + end + def form_chat_message_batch_params params.require(:form_chat_message_batch).permit(:action, chat_message_ids: []) end - def set_account - @account = Account.find(params[:account_id]) - end - def current_params page = (params[:page] || 1).to_i { - media: params[:media], page: page > 1 && page, }.select { |_, value| value.present? } end @@ -59,5 +56,15 @@ module Admin 'delete' end end + + def filter_params + params.permit( + :id, + :text, + :account_id, + :created_at_lte, + :created_at_gte + ) + end end end diff --git a/app/controllers/admin/follows_controller.rb b/app/controllers/admin/follows_controller.rb new file mode 100644 index 00000000..7d331bc5 --- /dev/null +++ b/app/controllers/admin/follows_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Admin + class FollowsController < BaseController + before_action :set_account + + PER_PAGE = 40 + + def index + authorize :account, :index? + @follows = @account.following.local.recent.page(params[:page]).per(PER_PAGE) + end + + def set_account + @account = Account.find(params[:account_id]) + end + end +end diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 74c4760f..36c1b236 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -4,7 +4,6 @@ module Admin class GroupsController < BaseController before_action :set_group, except: [:index] before_action :set_accounts, only: [:show] - before_action :set_filter_params def index authorize :group, :index? @@ -47,10 +46,6 @@ module Admin @mods = GroupAccount.where(group: @group, role: 'moderator') end - def set_filter_params - @filter_params = filter_params.to_hash.symbolize_keys - end - def resource_params params.require(:group).permit( :title, @@ -66,16 +61,17 @@ module Admin end def filtered_groups - query = Group.order('is_featured DESC, member_count DESC') - - if params[:title] - query = query.where("LOWER(title) LIKE LOWER(?)", "%#{params[:title]}%") - end - return query + GroupFilter.new(filter_params).results end - + def filter_params - params.permit(:sort,) + params.permit( + :title, + :description, + :id, + :member_count_gte, + :created_at_gte + ) end end end diff --git a/app/controllers/admin/link_blocks_controller.rb b/app/controllers/admin/link_blocks_controller.rb index 50ac5c88..a36ef415 100644 --- a/app/controllers/admin/link_blocks_controller.rb +++ b/app/controllers/admin/link_blocks_controller.rb @@ -6,7 +6,7 @@ module Admin def index authorize :link_block, :index? - @link_blocks = LinkBlock.page(params[:page]) + @link_blocks = filtered_link_blocks.alphabetical.page(params[:page]) end def new @@ -36,10 +36,18 @@ module Admin private + def filtered_link_blocks + LinkBlockFilter.new(filter_params).results + end + def set_link_block @link_block = LinkBlock.find(params[:id]) end + def filter_params + params.permit(:link) + end + def resource_params params.require(:link_block).permit(:link) end diff --git a/app/controllers/admin/preview_cards_controller.rb b/app/controllers/admin/preview_cards_controller.rb new file mode 100644 index 00000000..ce83b61c --- /dev/null +++ b/app/controllers/admin/preview_cards_controller.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module Admin + class PreviewCardsController < BaseController + before_action :set_preview_card, except: [:index] + + def index + authorize :preview_card, :index? + + @preview_cards = filtered_preview_cards.page(params[:page]) + @form = Form::PreviewCardBatch.new + end + + def show + authorize @preview_card, :show? + @form = Form::PreviewCardBatch.new + end + + def create + authorize :preview_card, :update? + + @form = Form::PreviewCardBatch.new(form_preview_card_batch_params.merge(current_account: current_account, action: action_from_button)) + flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save + + redirect_to admin_preview_cards_path(@account.id, current_params) + rescue ActionController::ParameterMissing + flash[:alert] = I18n.t('admin.statuses.no_status_selected') + + redirect_to admin_preview_cards_path(@account.id, current_params) + end + + private + + def form_preview_card_batch_params + params.require(:form_preview_card_batch).permit(:action, preview_card_ids: []) + end + + def filtered_preview_cards + PreviewCardFilter.new(filter_params).results.order(id: :desc) + end + + def filter_params + params.permit( + :title, + :description, + :url, + ) + end + + def set_preview_card + @preview_card = PreviewCard.find(params[:id]) + end + + def action_from_button + if params[:delete] + 'delete' + end + end + + end +end diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb index 65019503..7b7fff30 100644 --- a/app/controllers/admin/statuses_controller.rb +++ b/app/controllers/admin/statuses_controller.rb @@ -4,21 +4,12 @@ module Admin class StatusesController < BaseController helper_method :current_params - before_action :set_account - PER_PAGE = 20 def index authorize :status, :index? - @statuses = @account.statuses.where(visibility: [:public, :unlisted]) - - if params[:media] - account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct - @statuses.merge!(Status.where(id: account_media_status_ids)) - end - - @statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) + @statuses = filtered_statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE) @form = Form::StatusBatch.new end @@ -37,11 +28,11 @@ module Admin @form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button)) flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save - redirect_to admin_account_statuses_path(@account.id, current_params) + redirect_to admin_statuses_path(current_params) rescue ActionController::ParameterMissing flash[:alert] = I18n.t('admin.statuses.no_status_selected') - redirect_to admin_account_statuses_path(@account.id, current_params) + redirect_to admin_statuses_path(current_params) end private @@ -50,8 +41,8 @@ module Admin params.require(:form_status_batch).permit(:action, status_ids: []) end - def set_account - @account = Account.find(params[:account_id]) + def filtered_statuses + StatusFilter.new(nil, nil, nil, filter_params).results end def current_params @@ -63,6 +54,18 @@ module Admin }.select { |_, value| value.present? } end + def filter_params + params.permit( + :text, + :id, + :account_id, + :group_id, + :preview_card_id, + :created_at_lte, + :created_at_gte + ) + end + def action_from_button if params[:nsfw_on] 'nsfw_on' @@ -72,5 +75,6 @@ module Admin 'delete' end end + end end diff --git a/app/helpers/admin/filter_helper.rb b/app/helpers/admin/filter_helper.rb index 0bda2597..214c6536 100644 --- a/app/helpers/admin/filter_helper.rb +++ b/app/helpers/admin/filter_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Admin::FilterHelper - ACCOUNT_FILTERS = %i(local remote by_domain active pending silenced suspended username display_name email ip staff).freeze + ACCOUNT_FILTERS = %i(local remote by_domain active pending silenced suspended username display_name email ip note staff).freeze REPORT_FILTERS = %i(resolved account_id target_account_id).freeze INVITE_FILTER = %i(available expired).freeze CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze diff --git a/app/lib/status_filter.rb b/app/lib/status_filter.rb index 94edd0dd..db90b4ba 100644 --- a/app/lib/status_filter.rb +++ b/app/lib/status_filter.rb @@ -1,12 +1,13 @@ # frozen_string_literal: true class StatusFilter - attr_reader :status, :account + attr_reader :status, :account, :params - def initialize(status, account, preloaded_relations = {}) + def initialize(status, account, preloaded_relations = {}, params) @status = status @account = account @preloaded_relations = preloaded_relations + @params = params end def filtered? @@ -14,6 +15,35 @@ class StatusFilter blocked_by_policy? || (account_present? && filtered_status?) || silenced_account? end + def results + scope = Status + params.each do |key, value| + scope = scope.merge scope_for(key, value) if !value.nil? && !value.empty? + end + scope + end + + def scope_for(key, value) + case key.to_sym + when :text + Status.where("LOWER(text) LIKE LOWER(?)", "%#{value}%") + when :id + Status.where(id: value) + when :account_id + Status.where(account_id: value) + when :group_id + Status.where(group_id: value) + when :preview_card_id + Status.joins(:preview_cards).where("preview_cards.id = #{value.to_i}") + when :created_at_lte + Status.where("created_at <= ?", value) + when :created_at_gte + Status.where("created_at >= ?", value) + else + raise "Unknown filter: #{key}" + end + end + private def account_present? diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb index d2503100..2c3778da 100644 --- a/app/models/account_filter.rb +++ b/app/models/account_filter.rb @@ -51,6 +51,13 @@ class AccountFilter valid_ip?(value) ? accounts_with_users.where('users.current_sign_in_ip <<= ?', value) : Account.none when 'staff' accounts_with_users.merge User.staff + when "note" + Account.where("LOWER(note) LIKE LOWER(?)", "%#{value}%") + when "status_count_gte" + # : todo : + Account.joins(:account_stat) + when "sign_up_date_gte" + Account.where("created_at >= ?", value) else raise "Unknown filter: #{key}" end diff --git a/app/models/chat_message_filter.rb b/app/models/chat_message_filter.rb new file mode 100644 index 00000000..e953e520 --- /dev/null +++ b/app/models/chat_message_filter.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class ChatMessageFilter + attr_reader :params + + def initialize(params) + @params = params + end + + def results + scope = ChatMessage + params.each do |key, value| + scope = scope.merge scope_for(key, value) if !value.nil? && !value.empty? + end + scope + end + + def scope_for(key, value) + case key.to_sym + when :text + ChatMessage.where("LOWER(text) LIKE LOWER(?)", "%#{value}%") + when :id + ChatMessage.where(id: value) + when :account_id + ChatMessage.where(from_account_id: value) + when :created_at_lte + ChatMessage.where("created_at <= ?", value) + when :created_at_gte + ChatMessage.where("created_at >= ?", value) + else + raise "Unknown filter: #{key}" + end + end +end diff --git a/app/models/form/preview_card_batch.rb b/app/models/form/preview_card_batch.rb new file mode 100644 index 00000000..6fccbbc7 --- /dev/null +++ b/app/models/form/preview_card_batch.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class Form::PreviewCardBatch + include ActiveModel::Model + include AccountableConcern + + attr_accessor :preview_card_ids, :action, :current_account + + def save + case action + when 'delete' + delete_preview_cards + end + end + + private + + def delete_preview_cards + PreviewCard.where(id: preview_card_ids).reorder(nil).find_each do |preview_card| + DeletePreviewCardWorker.perform_async(preview_card.id) + log_action :destroy, preview_card + end + + true + end +end diff --git a/app/models/group_filter.rb b/app/models/group_filter.rb new file mode 100644 index 00000000..0f1b9d3f --- /dev/null +++ b/app/models/group_filter.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +class GroupFilter + attr_reader :params + + def initialize(params) + @params = params + end + + def results + scope = Group + params.each do |key, value| + scope = scope.merge scope_for(key, value) if !value.nil? && !value.empty? + end + scope + end + + def scope_for(key, value) + case key.to_sym + when :id + Group.where(id: value) + when :title + Group.where("LOWER(title) LIKE LOWER(?)", "%#{value}%") + when :description + Group.where("LOWER(description) LIKE LOWER(?)", "%#{value}%") + when :member_count_gte + Group.where("member_count >= ?", value) + when :created_at_gte + Group.where("created_at >= ?", value) + else + raise "Unknown filter: #{key}" + end + end +end diff --git a/app/models/link_block.rb b/app/models/link_block.rb index 8ab34b43..ad00966c 100644 --- a/app/models/link_block.rb +++ b/app/models/link_block.rb @@ -14,6 +14,8 @@ class LinkBlock < ApplicationRecord validates :link, presence: true, uniqueness: true + scope :alphabetical, -> { reorder(link: :asc) } + def self.block?(text) return false if text.nil? return false if text.length < 1 diff --git a/app/models/link_block_filter.rb b/app/models/link_block_filter.rb new file mode 100644 index 00000000..9c87d158 --- /dev/null +++ b/app/models/link_block_filter.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class LinkBlockFilter + attr_reader :params + + def initialize(params) + @params = params + end + + def results + scope = LinkBlock + params.each do |key, value| + scope = scope.merge scope_for(key, value) + end + scope + end + + def scope_for(key, value) + case key.to_sym + when :link + LinkBlock.where("LOWER(link) LIKE LOWER(?)", "%#{value}%") + else + raise "Unknown filter: #{key}" + end + end +end diff --git a/app/models/preview_card_filter.rb b/app/models/preview_card_filter.rb new file mode 100644 index 00000000..366ddbdb --- /dev/null +++ b/app/models/preview_card_filter.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class PreviewCardFilter + attr_reader :params + + def initialize(params) + @params = params + end + + def results + scope = PreviewCard + params.each do |key, value| + scope = scope.merge scope_for(key, value) + end + scope + end + + def scope_for(key, value) + case key.to_sym + when :title + PreviewCard.where("LOWER(title) LIKE LOWER(?)", "%#{value}%") + when :description + PreviewCard.where("LOWER(description) LIKE LOWER(?)", "%#{value}%") + when :url + PreviewCard.where("LOWER(url) LIKE LOWER(?)", "%#{value}%") + else + raise "Unknown filter: #{key}" + end + end +end diff --git a/app/policies/preview_card_policy.rb b/app/policies/preview_card_policy.rb new file mode 100644 index 00000000..84d39f5f --- /dev/null +++ b/app/policies/preview_card_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class PreviewCardPolicy < ApplicationPolicy + def destroy? + staff? + end + + def index? + staff? + end + + def show? + staff? + end +end diff --git a/app/services/delete_preview_card_service.rb b/app/services/delete_preview_card_service.rb new file mode 100644 index 00000000..8072f9f7 --- /dev/null +++ b/app/services/delete_preview_card_service.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class DeletePreviewCardService < BaseService + include Redisable + + def call(preview_card) + return if preview_card.nil? + + @preview_card = preview_card + + RedisLock.acquire(lock_options) do |lock| + if lock.acquired? + @preview_card.destroy! + else + raise GabSocial::RaceConditionError + end + end + end + + def lock_options + { redis: Redis.current, key: "distribute_preview_card:#{@preview_card.id}" } + end +end diff --git a/app/views/admin/account_statuses/index.html.haml b/app/views/admin/account_statuses/index.html.haml new file mode 100644 index 00000000..0e34cc85 --- /dev/null +++ b/app/views/admin/account_statuses/index.html.haml @@ -0,0 +1,37 @@ +- content_for :header_tags do + = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' + +- content_for :page_title do + = t('admin.statuses.title') + \- + = "@#{@account.acct}" + +.filters + .filter-subset + %strong= t('admin.statuses.media.title') + %ul + %li= link_to t('admin.statuses.no_media'), admin_account_account_statuses_path(@account.id, current_params.merge(media: nil)), class: !params[:media] && 'selected' + %li= link_to t('admin.statuses.with_media'), admin_account_account_statuses_path(@account.id, current_params.merge(media: true)), class: params[:media] && 'selected' + .back-link{ style: 'flex: 1 1 auto; text-align: right' } + = link_to admin_account_path(@account.id) do + = fa_icon 'chevron-left fw' + = t('admin.statuses.back_to_account') + +%hr.spacer/ + += form_for(@form, url: admin_account_account_statuses_path(@account.id)) do |f| + = hidden_field_tag :page, params[:page] + = hidden_field_tag :media, params[:media] + + .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 + = f.button safe_join([fa_icon('eye-slash'), t('admin.statuses.batch.nsfw_on')]), name: :nsfw_on, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + = f.button safe_join([fa_icon('eye'), t('admin.statuses.batch.nsfw_off')]), name: :nsfw_off, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + = f.button safe_join([fa_icon('trash'), t('admin.statuses.batch.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + .batch-table__body + = render partial: 'admin/reports/status', collection: @statuses, locals: { f: f } + += paginate @statuses diff --git a/app/views/admin/statuses/show.html.haml b/app/views/admin/account_statuses/show.html.haml similarity index 100% rename from app/views/admin/statuses/show.html.haml rename to app/views/admin/account_statuses/show.html.haml diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml index c99a2116..1c80d3de 100644 --- a/app/views/admin/accounts/index.html.haml +++ b/app/views/admin/accounts/index.html.haml @@ -20,11 +20,17 @@ - if params[key].present? = hidden_field_tag key, params[key] - - %i(username by_domain display_name email ip).each do |key| + - %i(username by_domain display_name email ip note).each do |key| - unless key == :by_domain && params[:remote].blank? .input.string.optional = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.accounts.#{key}") + .input.string.optional + = text_field_tag :status_count_gte, params[:status_count_gte], class: 'string optional', placeholder: "Status Count >=" + + .input.string.optional + = text_field_tag :sign_up_date_gte, params[:sign_up_date_gte], class: 'string optional', placeholder: "Sign up Date >= (MM-DD-YYYY)" + .actions %button= t('admin.accounts.search') = link_to t('admin.accounts.reset'), admin_accounts_path, class: 'button negative' diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index 12f00c7a..006ab6a4 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -5,25 +5,13 @@ .dashboard__counters{ style: 'margin-top: 10px' } %div - = link_to admin_account_statuses_path(@account.id) do + = link_to admin_account_account_statuses_path(@account.id) do .dashboard__counters__num= number_with_delimiter @account.statuses_count .dashboard__counters__label= t 'admin.accounts.statuses' %div - = link_to admin_account_statuses_path(@account.id, { media: true }) do + = link_to admin_account_account_statuses_path(@account.id, { media: true }) do .dashboard__counters__num= number_to_human_size @account.media_attachments.sum('file_file_size') .dashboard__counters__label= t 'admin.accounts.media_attachments' - %div - = link_to admin_account_followers_path(@account.id) do - .dashboard__counters__num= number_with_delimiter @account.local_followers_count - .dashboard__counters__label= t 'admin.accounts.followers' - %div - = link_to admin_reports_path(account_id: @account.id) do - .dashboard__counters__num= number_with_delimiter @account.reports.count - .dashboard__counters__label= t '.created_reports' - %div - = link_to admin_reports_path(target_account_id: @account.id) do - .dashboard__counters__num= number_with_delimiter @account.targeted_reports.count - .dashboard__counters__label= t '.targeted_reports' %div %div .dashboard__counters__text @@ -42,12 +30,36 @@ - else %span.neutral= t('admin.accounts.no_limits_imposed') .dashboard__counters__label= t 'admin.accounts.login_status' + %div + = link_to admin_account_followers_path(@account.id) do + .dashboard__counters__num= number_with_delimiter @account.local_followers_count + .dashboard__counters__label= t 'admin.accounts.followers' + %div + = link_to admin_account_follows_path(@account.id) do + .dashboard__counters__num= number_with_delimiter @account.local_following_count + .dashboard__counters__label= t 'admin.accounts.following' + %div + %a + .dashboard__counters__num= number_with_delimiter @account.blocked_by.count + .dashboard__counters__label Blocked By + %div + = link_to admin_reports_path(account_id: @account.id) do + .dashboard__counters__num= number_with_delimiter @account.reports.count + .dashboard__counters__label= t '.created_reports' + %div + = link_to admin_reports_path(target_account_id: @account.id) do + .dashboard__counters__num= number_with_delimiter @account.targeted_reports.count + .dashboard__counters__label= t '.targeted_reports' %div = link_to admin_account_joined_groups_path(@account.id) do .dashboard__counters__num= number_with_delimiter @account.groups.count .dashboard__counters__label Joined Groups %div - = link_to admin_account_chat_conversation_accounts_path(@account.id) do + = link_to admin_account_joined_groups_path(@account.id) do + .dashboard__counters__num= number_with_delimiter @account.group_removed_accounts.count + .dashboard__counters__label Removed by Groups + %div + %a{href: "/admin/chat_messages?account_id=#{@account.id}"} .dashboard__counters__num= number_with_delimiter @account.chat_conversation_accounts_count .dashboard__counters__label Chat Conversations %div diff --git a/app/views/admin/chat_messages/index.html.haml b/app/views/admin/chat_messages/index.html.haml index 698ab7b0..032519f1 100644 --- a/app/views/admin/chat_messages/index.html.haml +++ b/app/views/admin/chat_messages/index.html.haml @@ -2,19 +2,26 @@ = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' - content_for :page_title do - %span Chat Messages - \- - = "@#{@account.acct}" + = "Chat Messages" -.filters - .back-link{ style: 'flex: 1 1 auto; text-align: right' } - = link_to admin_account_path(@account.id) do - = fa_icon 'chevron-left fw' - %span Back to account += form_tag admin_chat_messages_url, method: 'GET', class: 'simple_form' do + .fields-group + .input.string.optional + = text_field_tag :text, params[:text], class: 'string optional', placeholder: "Text contains" + .input.string.optional + = text_field_tag :id, params[:id], class: 'string optional', placeholder: "Id" + .input.string.optional + = text_field_tag :account_id, params[:account_id], class: 'string optional', placeholder: "Account Id" + .input.string.optional + = text_field_tag :created_at_gte, params[:created_at_gte], class: 'string optional', placeholder: "Created >= (MM-DD-YYYY)" + .input.string.optional + = text_field_tag :created_at_lte, params[:created_at_lte], class: 'string optional', placeholder: "Created <= (MM-DD-YYYY)" + + .actions + %button= t('admin.accounts.search') + = link_to t('admin.accounts.reset'), admin_chat_messages_path, class: 'button negative' -%hr.spacer/ - -= form_for(@form, url: admin_account_chat_messages_path(@account.id)) do |f| += form_for(@form, url: admin_chat_messages_path('')) do |f| = hidden_field_tag :page, params[:page] .batch-table diff --git a/app/views/admin/follows/index.html.haml b/app/views/admin/follows/index.html.haml new file mode 100644 index 00000000..0388a3f6 --- /dev/null +++ b/app/views/admin/follows/index.html.haml @@ -0,0 +1,28 @@ +- content_for :page_title do + = "#{@account.acct} is Following..." + +.filters + .filter-subset + %strong= t('admin.accounts.location.title') + %ul + %li= link_to t('admin.accounts.location.local'), admin_account_follows_path(@account.id), class: 'selected' + .back-link{ style: 'flex: 1 1 auto; text-align: right' } + = link_to admin_account_path(@account.id) do + = fa_icon 'chevron-left fw' + = t('admin.followers.back_to_account') + +%hr.spacer/ + +.table-wrapper + %table.table + %thead + %tr + %th= t('admin.accounts.username') + %th= t('admin.accounts.role') + %th= t('admin.accounts.most_recent_ip') + %th= t('admin.accounts.most_recent_activity') + %th + %tbody + = render partial: 'admin/accounts/account', collection: @follows + += paginate @follows diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index b106ab4f..5cdebd75 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -4,8 +4,15 @@ = form_tag admin_groups_url, method: 'GET', class: 'simple_form' do .fields-group .input.string.optional - = text_field_tag :title, params[:title], class: 'string optional', placeholder: I18n.t("admin.groups.name") - + = text_field_tag :id, params[:id], class: 'string optional', placeholder: "Id" + .input.string.optional + = text_field_tag :title, params[:title], class: 'string optional', placeholder: "Title" + .input.string.optional + = text_field_tag :description, params[:description], class: 'string optional', placeholder: "Description" + .input.string.optional + = text_field_tag :member_count_gte, params[:member_count_gte], class: 'string optional', placeholder: "Member Count >=" + .input.string.optional + = text_field_tag :created_at_gte, params[:created_at_gte], class: 'string optional', placeholder: "Created >= (MM-DD-YYYY)" .actions %button= t('admin.accounts.search') = link_to t('admin.accounts.reset'), admin_groups_path, class: 'button negative' diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index 5c8c2d68..b297ace8 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -15,7 +15,7 @@ .dashboard__counters{ style: 'margin-top: 10px' } %div - %div + %a{href: "/admin/statuses?group_id=#{@group.id}"} .dashboard__counters__num= number_with_delimiter Status.where(group:@group).count .dashboard__counters__label Status Count %div @@ -30,6 +30,11 @@ %div .dashboard__counters__num= number_with_delimiter @group.join_requests.count .dashboard__counters__label Member Requests Count + %div + %div + .dashboard__counters__num + %time.formatted{ datetime: @group.created_at.iso8601, title: l(@group.created_at) }= l(@group.created_at) + .dashboard__counters__label Created = simple_form_for(@group, url: admin_group_path(@group.id), html: { method: :put }) do |f| = render 'shared/error_messages', object: @group diff --git a/app/views/admin/link_blocks/index.html.haml b/app/views/admin/link_blocks/index.html.haml index 2b834df0..224a1a22 100644 --- a/app/views/admin/link_blocks/index.html.haml +++ b/app/views/admin/link_blocks/index.html.haml @@ -1,6 +1,15 @@ - content_for :page_title do = t('admin.link_blocks.title') += form_tag admin_link_blocks_url, method: 'GET', class: 'simple_form' do + .fields-group + .input.string.optional + = text_field_tag :link, params[:link], class: 'string optional', placeholder: "Link" + + .actions + %button= t('admin.accounts.search') + = link_to t('admin.accounts.reset'), admin_link_blocks_path, class: 'button negative' + .table-wrapper %table.table %thead diff --git a/app/views/admin/preview_cards/_preview_card.html.haml b/app/views/admin/preview_cards/_preview_card.html.haml new file mode 100644 index 00000000..484c9f77 --- /dev/null +++ b/app/views/admin/preview_cards/_preview_card.html.haml @@ -0,0 +1,14 @@ +.batch-table__row + %label.batch-table__row__select.batch-checkbox + = f.check_box :preview_card_ids, { multiple: true, include_hidden: false }, preview_card.id + .batch-table__row__content + %div + %div{style: "display:flex;flex-direction:row;"} + - if !preview_card.image.nil? + %div{style: "display:block;padding-right:10px;"} + %img{src: preview_card.image, style: "display:block;width:80px;height:60px;"} + %div + %p{style: "color:#fff;font-size:14px;font-weight:600;margin-bottom:5px;"}= preview_card.title + %p{style: "color:#bbb;font-size:12px;font-weight:400;margin-bottom:5px;"}= preview_card.description + %p{style: "color:#21D07B;font-size:12px;font-weight:400;"}= preview_card.url + %a{href: "/admin/statuses?preview_card_id=#{preview_card.id}", style: "display:block;padding:6px;background-color:#666;color:#000;margin-top:6px;font-size:13px;width:90px;line-height:20px;text-decoration:none;text-align:center;border-radius:8px;"} View Statuses \ No newline at end of file diff --git a/app/views/admin/preview_cards/index.html.haml b/app/views/admin/preview_cards/index.html.haml new file mode 100644 index 00000000..953d6d44 --- /dev/null +++ b/app/views/admin/preview_cards/index.html.haml @@ -0,0 +1,32 @@ +- content_for :page_title do + = "Preview Cards" + += form_tag admin_preview_cards_url, method: 'GET', class: 'simple_form' do + .fields-group + .input.string.optional + = text_field_tag :title, params[:title], class: 'string optional', placeholder: "Title" + + .input.string.optional + = text_field_tag :description, params[:description], class: 'string optional', placeholder: "Description" + + .input.string.optional + = text_field_tag :url, params[:url], class: 'string optional', placeholder: "Url" + + .actions + %button= t('admin.accounts.search') + = link_to t('admin.accounts.reset'), admin_preview_cards_path, class: 'button negative' + += form_for(@form, url: admin_preview_cards_path()) do |f| + = hidden_field_tag :page, params[:page] + = hidden_field_tag :media, params[:media] + + .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 + = f.button safe_join([fa_icon('trash'), "Delete"]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + .batch-table__body + = render partial: 'admin/preview_cards/preview_card', collection: @preview_cards, locals: { f: f } + += paginate @preview_cards diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml index f55be8da..d9688383 100644 --- a/app/views/admin/reports/_status.html.haml +++ b/app/views/admin/reports/_status.html.haml @@ -32,3 +32,6 @@ · = fa_icon('eye-slash fw') = t('stream_entries.sensitive_content') + - if !status.group_id.nil? + · + %a{href: "/groups/#{status.group_id}", style: "color:#757575;text-decoration:none;font-size:14px;"}= status.group.title \ No newline at end of file diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index 27e893d9..9045fe2b 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -10,13 +10,12 @@ = form_tag admin_reports_url, method: 'GET', class: 'simple_form' do .fields-group - - %i(comment).each do |key| - .input.string.optional - = text_field_tag key, params[key], class: 'string optional', placeholder: "#{key}" + .input.string.optional + = text_field_tag :comment, params[:comment], class: 'string optional', placeholder: "Comment" .actions %button= t('admin.accounts.search') - = link_to t('admin.accounts.reset'), admin_accounts_path, class: 'button negative' + = link_to t('admin.accounts.reset'), admin_reports_path, class: 'button negative' - @reports.group_by(&:target_account_id).each do |target_account_id, reports| - target_account = reports.first.target_account diff --git a/app/views/admin/statuses/index.html.haml b/app/views/admin/statuses/index.html.haml index dd3c7981..26f40f89 100644 --- a/app/views/admin/statuses/index.html.haml +++ b/app/views/admin/statuses/index.html.haml @@ -2,24 +2,30 @@ = javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous' - content_for :page_title do - = t('admin.statuses.title') - \- - = "@#{@account.acct}" + = "All Statuses" -.filters - .filter-subset - %strong= t('admin.statuses.media.title') - %ul - %li= link_to t('admin.statuses.no_media'), admin_account_statuses_path(@account.id, current_params.merge(media: nil)), class: !params[:media] && 'selected' - %li= link_to t('admin.statuses.with_media'), admin_account_statuses_path(@account.id, current_params.merge(media: true)), class: params[:media] && 'selected' - .back-link{ style: 'flex: 1 1 auto; text-align: right' } - = link_to admin_account_path(@account.id) do - = fa_icon 'chevron-left fw' - = t('admin.statuses.back_to_account') += form_tag admin_statuses_url, method: 'GET', class: 'simple_form' do + .fields-group + .input.string.optional + = text_field_tag :text, params[:text], class: 'string optional', placeholder: "Text contains" + .input.string.optional + = text_field_tag :id, params[:id], class: 'string optional', placeholder: "Id" + .input.string.optional + = text_field_tag :account_id, params[:account_id], class: 'string optional', placeholder: "Account Id" + .input.string.optional + = text_field_tag :group_id, params[:group_id], class: 'string optional', placeholder: "Group Id" + .input.string.optional + = text_field_tag :preview_card_id, params[:preview_card_id], class: 'string optional', placeholder: "Preview Card Id" + .input.string.optional + = text_field_tag :created_at_gte, params[:created_at_gte], class: 'string optional', placeholder: "Created >= (MM-DD-YYYY)" + .input.string.optional + = text_field_tag :created_at_lte, params[:created_at_lte], class: 'string optional', placeholder: "Created <= (MM-DD-YYYY)" + + .actions + %button= t('admin.accounts.search') + = link_to t('admin.accounts.reset'), admin_statuses_path, class: 'button negative' -%hr.spacer/ - -= form_for(@form, url: admin_account_statuses_path(@account.id)) do |f| += form_for(@form, url: admin_statuses_path) do |f| = hidden_field_tag :page, params[:page] = hidden_field_tag :media, params[:media] diff --git a/app/views/application/_card.html.haml b/app/views/application/_card.html.haml index e6059b03..f18418e4 100644 --- a/app/views/application/_card.html.haml +++ b/app/views/application/_card.html.haml @@ -4,14 +4,17 @@ = link_to account_url, target: '_blank', rel: 'noopener' do .card__img = image_tag account.header.url, alt: '' - .card__bar - .avatar - = image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo' + .card__bar{style: "display:flex;flex-direction:column;align-items:flex-start;"} + %div{style: "display:flex;flex-direction:row;"} + .avatar + = image_tag account.avatar.url, alt: '', width: 48, height: 48, class: 'u-photo' + .display-name + %span{id: "default_account_display_name", style: "display:none;"}= account.username + %bdi + %strong.emojify.p-name= display_name(account, custom_emojify: true) + %span + = acct(account) + = fa_icon('lock') if account.locked? + %div{style: "display:block;margin-top:10px;"} + %p{style: "display:block;font-size:14px;color:#999;"}= account.note - .display-name - %span{id: "default_account_display_name", style: "display:none;"}= account.username - %bdi - %strong.emojify.p-name= display_name(account, custom_emojify: true) - %span - = acct(account) - = fa_icon('lock') if account.locked? diff --git a/app/workers/delete_preview_card_worker.rb b/app/workers/delete_preview_card_worker.rb new file mode 100644 index 00000000..41cdaf33 --- /dev/null +++ b/app/workers/delete_preview_card_worker.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class DeletePreviewCardWorker + include Sidekiq::Worker + + sidekiq_options unique: :until_executed + + def perform(preview_card_id) + return if preview_card_id.nil? + DeletePreviewCardService.new.call(PreviewCard.find(preview_card_id)) + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 0e527f42..6419638d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -155,6 +155,7 @@ en: moderation_notes: Moderation notes most_recent_activity: Most recent activity most_recent_ip: Most recent IP + note: Bio no_account_selected: No accounts were changed as none were selected no_limits_imposed: No limits imposed not_subscribed: Not subscribed diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml index 60044551..2dd0d941 100644 --- a/config/locales/en_GB.yml +++ b/config/locales/en_GB.yml @@ -132,6 +132,7 @@ en_GB: moderation_notes: Moderation notes most_recent_activity: Most recent activity most_recent_ip: Most recent IP + note: Bio no_limits_imposed: No limits imposed not_subscribed: Not subscribed outbox_url: Outbox URL diff --git a/config/navigation.rb b/config/navigation.rb index 0ebabc9b..a4d97bb3 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -28,17 +28,20 @@ SimpleNavigation::Configuration.run do |navigation| n.item :development, safe_join([fa_icon('code fw'), t('settings.development')]), settings_applications_url, if: -> { current_user.staff? } n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), admin_reports_url, if: proc { current_user.staff? } do |s| - s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts} - s.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_url + s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url, highlights_on: %r{/admin/accounts|/admin/pending_accounts|admin/flagged_as_spam} + s.item :statuses, safe_join([fa_icon('bars fw'), "Statuses"]), admin_statuses_url + s.item :groups, safe_join([fa_icon('smile-o fw'), t('admin.groups.title')]), admin_groups_url, highlights_on: %r{/admin/groups} s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports} + s.item :chat_messages, safe_join([fa_icon('comments fw'), "Chat Messages"]), admin_chat_messages_url, if: -> { current_user.admin? } + s.item :preview_cards, safe_join([fa_icon('link fw'), "Preview Cards"]), admin_preview_cards_url + s.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_url s.item :email_domain_blocks, safe_join([fa_icon('envelope fw'), t('admin.email_domain_blocks.title')]), admin_email_domain_blocks_url, highlights_on: %r{/admin/email_domain_blocks}, if: -> { current_user.admin? } - s.item :link_blocks, safe_join([fa_icon('link fw'), t('admin.link_blocks.title')]), admin_link_blocks_url, highlights_on: %r{/admin/link_blocks}, if: -> { current_user.admin? } + s.item :link_blocks, safe_join([fa_icon('link fw'), t('admin.link_blocks.title')]), admin_link_blocks_url end n.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), admin_dashboard_url, if: proc { current_user.staff? } do |s| s.item :dashboard, safe_join([fa_icon('tachometer fw'), t('admin.dashboard.title')]), admin_dashboard_url s.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? }, highlights_on: %r{/admin/settings} - s.item :groups, safe_join([fa_icon('smile-o fw'), t('admin.groups.title')]), admin_groups_url, highlights_on: %r{/admin/groups} s.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis} s.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' }, if: -> { current_user.admin? } s.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' }, if: -> { current_user.admin? } diff --git a/config/routes.rb b/config/routes.rb index ba01c0e8..1e913d40 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -94,6 +94,9 @@ Rails.application.routes.draw do resources :email_domain_blocks, only: [:index, :new, :create, :destroy] resources :link_blocks, only: [:index, :new, :create, :destroy] + resources :statuses, only: [:index, :show, :create, :update, :destroy] + resources :preview_cards, only: [:index, :create, :destroy] + resources :chat_messages, only: [:index, :destroy] resources :action_logs, only: [:index] resources :warning_presets, except: [:new] resource :settings, only: [:edit, :update] @@ -139,8 +142,9 @@ Rails.application.routes.draw do resource :change_email, only: [:show, :update] resource :reset, only: [:create] resource :action, only: [:new, :create], controller: 'account_actions' - resources :statuses, only: [:index, :show, :create, :update, :destroy] + resources :account_statuses, only: [:index, :show, :create, :update, :destroy] resources :followers, only: [:index] + resources :follows, only: [:index] resources :joined_groups, only: [:index] resources :chat_conversation_accounts, only: [:index] resources :chat_messages, only: [:index, :show, :create, :update, :destroy]