Add: #600 NGルール (#602)

* Wip

* Wip

* Wip: History

* Wip: テストコード作成

* Fix test

* Wip

* Wip

* Wip

* Fix test

* Wip

* Wip

* Wip

* Wip

* なんとか完成、これから動作確認

* spell miss

* Change ng rule timings

* Fix test

* Wip

* Fix test

* Wip

* Fix form

* 表示まわりの改善
This commit is contained in:
KMY(雪あすか) 2024-02-26 17:45:41 +09:00 committed by GitHub
parent 0779c748a6
commit 7d96d5828e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
56 changed files with 2062 additions and 42 deletions

View file

@ -0,0 +1,42 @@
.batch-table__row
%label.batch-table__row__select.batch-checkbox
-# = f.check_box :history_ids, { multiple: true, include_hidden: false }, history.id
.batch-table__row__content
- if history.hidden
.simple_form
%p.hint= t('admin.ng_rule_histories.hidden')
- else
.status__content><
= html_aware_format(history.text, history.local)
.detailed-status__meta
= t("admin.ng_rule_histories.reason_actions.#{history.reason_action}")
- if history.data.present? && !history.hidden
- if history.data['media_count'].present? && history.data['media_count'].positive?
·
= t('admin.ng_rule_histories.data.media_count', count: history.data['media_count'])
- if history.data['poll_count'].present? && history.data['poll_count'].positive?
·
= t('admin.ng_rule_histories.data.poll_count', count: history.data['poll_count'])
%br/
- if history.account.present?
- if history.hidden
- if history.account.local?
= t('admin.ng_rule_histories.from_local_user')
- else
= history.account.domain
·
- else
= link_to t('admin.ng_rule_histories.moderate_account'), admin_account_path(history.account.id)
·
%time.formatted{ datetime: history.created_at.iso8601, title: l(history.created_at) }= l(history.created_at)
- if history.uri.present? && !history.hidden
·
- if history.data.present? && history.data['url'].present?
= link_to history.uri, history.data['url'] || history.uri, target: '_blank', rel: 'noopener'
- else
= link_to history.uri, target: '_blank', rel: 'noopener'

View file

@ -0,0 +1,25 @@
- content_for :page_title do
= t('admin.ng_rule_histories.title', title: @ng_rule.title)
.filters
.back-link
= link_to edit_admin_ng_rule_path(id: @ng_rule.id) do
= fa_icon 'chevron-left fw'
= t('admin.ng_rule_histories.back_to_ng_rule')
= link_to admin_ng_rules_path do
= fa_icon 'chevron-left fw'
= t('admin.ng_rule_histories.back_to_ng_rules')
%hr.spacer/
.batch-table
.batch-table__toolbar
%label.batch-table__toolbar__select.batch-checkbox-all
= check_box_tag :batch_checkbox_all, nil, false
.batch-table__body
- if @histories.empty?
= nothing_here 'nothing-here--under-tabs'
- else
= render partial: 'admin/ng_rule_histories/history', collection: @histories
= paginate @histories

View file

@ -0,0 +1,25 @@
.filters-list__item{ class: [(ng_rule.expired? || !ng_rule.available) && 'expired'] }
= link_to edit_admin_ng_rule_path(ng_rule), class: 'filters-list__item__title' do
= ng_rule.title.presence || "(#{t('admin.ng_rules.index.empty_title')})"
- if ng_rule.expires?
.expiration{ title: t('filters.index.expires_on', date: l(ng_rule.expires_at)) }
- if ng_rule.expired?
= t('invites.expired')
- else
= t('filters.index.expires_in', distance: distance_of_time_in_words_to_now(ng_rule.expires_at))
- elsif !ng_rule.available
.expiration
= t('admin.ng_rules.index.disabled')
.filters-list__item__permissions
%ul.permissions-list
.announcements-list__item__action-bar
.announcements-list__item__meta
= link_to t('admin.ng_rules.index.hit_count', count: ng_rule.hit_count), admin_ng_rule_history_path(ng_rule)
%div
= table_link_to 'pencil', t('admin.ng_rules.index.edit.title'), edit_admin_ng_rule_path(ng_rule)
= table_link_to 'files-o', t('admin.ng_rules.copy'), duplicate_admin_ng_rule_path(ng_rule), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }
= table_link_to 'times', t('admin.ng_rules.index.delete'), admin_ng_rule_path(ng_rule), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }

View file

@ -0,0 +1,123 @@
.fields-group
%p= t('admin.ng_rules.edit.helps.generic')
.fields-group
%p
= t('admin.ng_rules.edit.helps.textarea_html')
= link_to t('admin.ng_rules.rubular'), 'https://rubular.com/', target: '_blank', rel: 'noopener'
.fields-group
%p= t('admin.ng_rules.edit.helps.threshold_html')
- if @ng_rule.id.present?
%p.hint= link_to t('admin.ng_rules.edit.history'), admin_ng_rule_history_path(id: @ng_rule.id)
%hr.spacer/
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :title, as: :string, wrapper: :with_label, hint: false
.fields-row__column.fields-row__column-6.fields-group
= f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week, 2.weeks, 1.month, 3.months].map(&:to_i), label_method: ->(i) { I18n.t("invites.expires_in.#{i}") }, include_blank: I18n.t('invites.expires_in_prompt')
%h4= t('admin.ng_rules.edit.headers.account')
%p.lead= t('admin.ng_rules.edit.summary.account')
.fields-group
= f.input :account_allow_followed_by_local, as: :boolean, wrapper: :with_label, hint: t('admin.ng_rules.account_allow_followed_by_local_hint'), label: t('admin.ng_rules.account_allow_followed_by_local')
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :account_domain, as: :text, input_html: { rows: 4 }, wrapper: :with_label, label: t('admin.ng_rules.account_domain'), hint: false
.fields-row__column.fields-row__column-6.fields-group
= f.input :account_include_local, as: :boolean, wrapper: :with_label, label: t('admin.ng_rules.account_include_local'), hint: false
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :account_username, as: :text, input_html: { rows: 4 }, wrapper: :with_label, label: t('admin.ng_rules.account_username'), hint: false
.fields-row__column.fields-row__column-6.fields-group
= f.input :account_display_name, as: :text, input_html: { rows: 4 }, wrapper: :with_label, label: t('admin.ng_rules.account_display_name'), hint: false
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :account_field_name, as: :text, input_html: { rows: 4 }, wrapper: :with_label, label: t('admin.ng_rules.account_field_name'), hint: false
.fields-row__column.fields-row__column-6.fields-group
= f.input :account_field_value, as: :text, input_html: { rows: 4 }, wrapper: :with_label, label: t('admin.ng_rules.account_field_value'), hint: false
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :account_note, as: :text, input_html: { rows: 4 }, wrapper: :with_label, label: t('admin.ng_rules.account_note'), hint: false
.fields-row__column.fields-row__column-6.fields-group
= f.input :account_avatar_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.account_avatar_state')
= f.input :account_header_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.account_header_state')
%h4= t('admin.ng_rules.edit.headers.status')
%p.lead= t('admin.ng_rules.edit.summary.status')
.fields-group
= f.input :status_allow_follower_mention, as: :boolean, wrapper: :with_label, hint: t('admin.ng_rules.status_allow_follower_mention_hint'), label: t('admin.ng_rules.status_allow_follower_mention')
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_text, as: :text, input_html: { rows: 4 }, wrapper: :with_label, label: t('admin.ng_rules.status_text'), hint: false
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_spoiler_text, as: :text, input_html: { rows: 4 }, wrapper: :with_label, label: t('admin.ng_rules.status_spoiler_text'), hint: false
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_tag, as: :text, input_html: { rows: 4 }, wrapper: :with_label, label: t('admin.ng_rules.status_tag'), hint: false
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_sensitive_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.status_sensitive_state')
= f.input :status_cw_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.status_cw_state')
.fields-group
= f.input :status_visibility, wrapper: :with_block_label, collection: Status.all_visibilities, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', label_method: ->(context) { I18n.t("statuses.visibilities.#{context}") }, include_blank: false, label: t('admin.ng_rules.status_visibility')
.fields-group
= f.input :status_searchability, wrapper: :with_block_label, collection: Status.all_searchabilities + %w(unset), as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', label_method: ->(context) { I18n.t("statuses.searchabilities.#{context}") }, include_blank: false, label: t('admin.ng_rules.status_searchability')
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_media_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.status_media_state')
= f.input :status_media_threshold, as: :string, wrapper: :with_label, hint: false, label: t('admin.ng_rules.status_media_threshold')
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_poll_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.status_poll_state')
= f.input :status_poll_threshold, as: :string, wrapper: :with_label, hint: false, label: t('admin.ng_rules.status_poll_threshold')
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_quote_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.status_quote_state')
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_reply_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.status_reply_state')
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_mention_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.status_mention_state')
= f.input :status_mention_threshold, as: :string, wrapper: :with_label, hint: false, label: t('admin.ng_rules.status_mention_threshold')
.fields-row__column.fields-row__column-6.fields-group
= f.input :status_reference_state, wrapper: :with_label, collection: %i(optional needed no_needed), include_blank: false, label_method: ->(i) { I18n.t("admin.ng_rules.states.#{i}") }, label: t('admin.ng_rules.status_reference_state')
= f.input :status_reference_threshold, as: :string, wrapper: :with_label, hint: false, label: t('admin.ng_rules.status_reference_threshold')
%h4= t('admin.ng_rules.edit.headers.reaction')
%p.lead= t('admin.ng_rules.edit.summary.reaction')
.fields-row
= f.input :reaction_allow_follower, wrapper: :with_label, hint: t('admin.ng_rules.reaction_allow_follower_hint'), label: t('admin.ng_rules.reaction_allow_follower')
.fields-group
= f.input :reaction_type, wrapper: :with_block_label, collection: %i(favourite emoji_reaction reblog follow vote), as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', label_method: ->(context) { I18n.t("admin.ng_rules.reaction_types.#{context}") }, include_blank: false, label: t('admin.ng_rules.reaction_type')
.fields-row
.fields-row__column.fields-row__column-6.fields-group
= f.input :emoji_reaction_name, as: :text, input_html: { rows: 4 }, wrapper: :with_label, hint: false, label: t('admin.ng_rules.emoji_reaction_name')
.fields-row__column.fields-row__column-6.fields-group
= f.input :emoji_reaction_origin_domain, as: :text, input_html: { rows: 4 }, wrapper: :with_label, hint: t('admin.ng_rules.emoji_reaction_origin_domain_hint'), label: t('admin.ng_rules.emoji_reaction_origin_domain')
%h4= t('admin.ng_rules.edit.headers.options')
.fields-group
= f.input :available, wrapper: :with_label, label: t('admin.ng_rules.available'), hint: false
.fields-group
= f.input :record_history_also_local, wrapper: :with_label, label: t('admin.ng_rules.record_history_also_local'), hint: false

View file

@ -0,0 +1,9 @@
- content_for :page_title do
= t('admin.ng_rules.edit.title')
= simple_form_for @ng_rule, url: admin_ng_rule_path(@ng_rule), method: :put do |f|
= render 'shared/error_messages', object: @ng_rule
= render 'ng_rule_fields', f: f
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View file

@ -0,0 +1,14 @@
- content_for :page_title do
= t('admin.ng_rules.index.title')
- content_for :heading_actions do
= link_to t('admin.ng_rules.new.title'), new_admin_ng_rule_path, class: 'button'
.simple_form
%p.lead= t('admin.ng_rules.index.preamble')
- if @ng_rules.empty?
.muted-hint.center-text= t 'admin.ng_rules.index.empty'
- else
.applications-list
= render partial: 'ng_rule', collection: @ng_rules

View file

@ -0,0 +1,8 @@
- content_for :page_title do
= t('admin.ng_rules.new.title')
= simple_form_for @ng_rule, url: admin_ng_rules_path do |f|
= render 'ng_rule_fields', f: f
.actions
= f.button :button, t('admin.ng_rules.new.save'), type: :submit

View file

@ -7,6 +7,8 @@
= simple_form_for @admin_settings, url: admin_ng_words_path, html: { method: :post } do |f|
= render 'shared/error_messages', object: @admin_settings
%p.lead= t('admin.ng_words.preamble')
%p.hint
= t 'admin.ng_words.history_hint'
= link_to t('admin.ngword_histories.title'), admin_ngword_histories_path
@ -14,9 +16,6 @@
.fields-group
= f.input :ng_words_for_stranger_mention, wrapper: :with_label, as: :text, input_html: { rows: 10 }, label: t('admin.ng_words.keywords_for_stranger_mention'), hint: t('admin.ng_words.keywords_for_stranger_mention_hint')
.fields-group
= f.input :stranger_mention_from_local_ng, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.stranger_mention_from_local_ng'), hint: t('admin.ng_words.stranger_mention_from_local_ng_hint')
.fields-group
= f.input :ng_words, wrapper: :with_label, as: :text, input_html: { rows: 10 }, label: t('admin.ng_words.keywords'), hint: t('admin.ng_words.keywords_hint')
@ -29,21 +28,28 @@
.fields-group
= f.input :post_mentions_max, wrapper: :with_label, as: :integer, label: t('admin.ng_words.post_mentions_max')
.fields-group
= f.input :hide_local_users_for_anonymous, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.hide_local_users_for_anonymous')
.fields-group
= f.input :block_unfollow_account_mention, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.block_unfollow_account_mention')
%p.hint
= t 'admin.ng_words.remote_approval_hint'
%h4= t('admin.ng_words.white_list')
%p.lead
= t('admin.ng_words.white_list_hint')
= link_to t('admin.ng_words.remote_approval_list'), admin_accounts_path(status: 'remote_pending', origin: 'remote')
.fields-group
= f.input :hold_remote_new_accounts, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.hold_remote_new_accounts')
= f.input :hold_remote_new_accounts, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.hold_remote_new_accounts'), hint: t('admin.ng_words.remote_approval_hint')
.fields-group
= f.input :permit_new_account_domains, wrapper: :with_label, as: :text, kmyblue: true, input_html: { rows: 6 }, label: t('admin.ng_words.permit_new_account_domains')
%h4= t('admin.ng_words.deprecated')
%p.hint= t('admin.ng_words.deprecated_hint')
.fields-group
= f.input :stranger_mention_from_local_ng, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.stranger_mention_from_local_ng'), hint: t('admin.ng_words.stranger_mention_from_local_ng_hint')
.fields-group
= f.input :hide_local_users_for_anonymous, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.hide_local_users_for_anonymous'), hint: t('admin.ng_words.hide_local_users_for_anonymous_hint')
.fields-group
= f.input :block_unfollow_account_mention, wrapper: :with_label, as: :boolean, label: t('admin.ng_words.block_unfollow_account_mention'), hint: t('admin.ng_words.block_unfollow_account_mention_hint')
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View file

@ -4,16 +4,16 @@
- content_for :header_tags do
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
%p.hint= t 'admin.sensitive_words.hint'
= simple_form_for @admin_settings, url: admin_sensitive_words_path, html: { method: :post } do |f|
= render 'shared/error_messages', object: @admin_settings
.fields-group
= f.input :sensitive_words_for_full, wrapper: :with_label, as: :text, input_html: { rows: 12 }, label: t('admin.sensitive_words.keywords_for_all'), hint: t('admin.sensitive_words.keywords_for_all_hint')
%p.lead= t 'admin.sensitive_words.hint'
.fields-group
= f.input :sensitive_words, wrapper: :with_label, as: :text, input_html: { rows: 12 }, label: t('admin.sensitive_words.keywords'), hint: t('admin.sensitive_words.keywords_hint')
= f.input :sensitive_words_for_full, wrapper: :with_label, as: :text, input_html: { rows: 8 }, label: t('admin.sensitive_words.keywords_for_all'), hint: t('admin.sensitive_words.keywords_for_all_hint')
.fields-group
= f.input :sensitive_words, wrapper: :with_label, as: :text, input_html: { rows: 8 }, label: t('admin.sensitive_words.keywords'), hint: t('admin.sensitive_words.keywords_hint')
.actions
= f.button :button, t('generic.save_changes'), type: :submit