From c53046913923ae946446cb47a6f5e1ddd61cb504 Mon Sep 17 00:00:00 2001 From: KMY Date: Tue, 25 Jul 2023 15:17:23 +0900 Subject: [PATCH 01/22] Fix ngwords null bug --- app/models/admin/ng_word.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/admin/ng_word.rb b/app/models/admin/ng_word.rb index 878151f648..42c84b0f8e 100644 --- a/app/models/admin/ng_word.rb +++ b/app/models/admin/ng_word.rb @@ -17,7 +17,7 @@ class Admin::NgWord private def ng_words - Setting.ng_words + Setting.ng_words || [] end def post_hash_tags_max From 6b8c81b4c07f345c335f04af37efd523f022efa4 Mon Sep 17 00:00:00 2001 From: KMY Date: Tue, 25 Jul 2023 15:22:32 +0900 Subject: [PATCH 02/22] Fix error in update activity --- .../activitypub/process_status_update_service.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index e058290cf4..77ea22fc81 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -18,9 +18,12 @@ class ActivityPub::ProcessStatusUpdateService < BaseService @request_id = request_id # Only native types can be updated at the moment - return @status if !expected_type? || already_updated_more_recently? || !valid_status? + return @status if !expected_type? || already_updated_more_recently? if @status_parser.edited_at.present? && (@status.edited_at.nil? || @status_parser.edited_at > @status.edited_at) + read_metadata + return @status unless valid_status? + handle_explicit_update! else handle_implicit_update! @@ -169,7 +172,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService @status.save! end - def update_metadata! + def read_metadata @raw_tags = [] @raw_mentions = [] @raw_emojis = [] @@ -183,7 +186,9 @@ class ActivityPub::ProcessStatusUpdateService < BaseService @raw_emojis << tag end end + end + def update_metadata! update_tags! update_mentions! update_emojis! From b3d8d9a44ba94370311874d8c51409b6e1141ead Mon Sep 17 00:00:00 2001 From: KMY Date: Tue, 25 Jul 2023 15:23:15 +0900 Subject: [PATCH 03/22] Fix variable name --- app/services/activitypub/process_status_update_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 77ea22fc81..418b757248 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -156,7 +156,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService end def valid_status? - !Admin::NgWord.reject?("#{@status_parser.spoiler_text}\n#{@status_parser.text}") && !Admin::NgWord.hashtag_reject?(@tags.size) + !Admin::NgWord.reject?("#{@status_parser.spoiler_text}\n#{@status_parser.text}") && !Admin::NgWord.hashtag_reject?(@raw_tags.size) end def update_immediate_attributes! From 46f8d7e1f5497d64757a876ba49a5714699b825a Mon Sep 17 00:00:00 2001 From: KMY Date: Tue, 25 Jul 2023 17:02:56 +0900 Subject: [PATCH 04/22] Fix image size to smaller --- app/javascript/styles/mastodon/components.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 12a6f3c672..e588938db8 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -3722,6 +3722,7 @@ a.status-card { margin: 0; width: 100%; height: 100%; + max-height: 286px; object-fit: cover; background-size: cover; background-position: center center; @@ -6316,6 +6317,7 @@ a.status-card { img { height: 100%; width: 100%; + max-height: 70vh; } img { From 5cd97aa3cb63b83b0e6a6b161c78140c4605e08b Mon Sep 17 00:00:00 2001 From: KMY Date: Tue, 25 Jul 2023 17:11:17 +0900 Subject: [PATCH 05/22] Fix media gallery item height --- app/javascript/styles/mastodon/components.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index e588938db8..1b6e5626ec 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -6266,6 +6266,7 @@ a.status-card { position: relative; width: 100%; min-height: 64px; + max-height: 70vh; display: grid; grid-template-columns: 50% 50%; grid-template-rows: 50% 50%; @@ -6317,7 +6318,6 @@ a.status-card { img { height: 100%; width: 100%; - max-height: 70vh; } img { From ee008fe389089d691a9e13f84c24fe80871df600 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Jul 2023 09:29:48 +0900 Subject: [PATCH 06/22] fix poll input active style (#26162) --- app/javascript/styles/mastodon/polls.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss index bcc8cfaaec..5bc017524d 100644 --- a/app/javascript/styles/mastodon/polls.scss +++ b/app/javascript/styles/mastodon/polls.scss @@ -128,6 +128,11 @@ border-width: 4px; } + &.active { + background-color: $ui-button-focus-background-color; + border-color: $ui-button-focus-background-color; + } + &::-moz-focus-inner { outline: 0 !important; border: 0; From c9a7c5723d4f0b7c81504794b2412b3049a27932 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Jul 2023 09:30:35 +0900 Subject: [PATCH 07/22] Revert poll colors to green outside of compose form (#26164) --- app/javascript/styles/mastodon/polls.scss | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss index 5bc017524d..8a26e611ca 100644 --- a/app/javascript/styles/mastodon/polls.scss +++ b/app/javascript/styles/mastodon/polls.scss @@ -105,7 +105,7 @@ &__input { display: inline-block; position: relative; - border: 1px solid $ui-button-background-color; + border: 1px solid $ui-primary-color; box-sizing: border-box; width: 18px; height: 18px; @@ -124,13 +124,13 @@ &:active, &:focus, &:hover { - border-color: $ui-button-focus-background-color; + border-color: lighten($valid-value-color, 15%); border-width: 4px; } &.active { - background-color: $ui-button-focus-background-color; - border-color: $ui-button-focus-background-color; + background-color: $valid-value-color; + border-color: $valid-value-color; } &::-moz-focus-inner { @@ -216,6 +216,14 @@ padding: 10px; } + .poll__input { + &:active, + &:focus, + &:hover { + border-color: $ui-button-focus-background-color; + } + } + .poll__footer { border-top: 1px solid darken($simple-background-color, 8%); padding: 10px; From 3258d09c64048fe7fc95a10f8611e5ad89c24385 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Jul 2023 11:15:37 +0900 Subject: [PATCH 08/22] Add sensitive checkbox with inputing link --- .../mastodon/features/compose/components/compose_form.jsx | 6 +++++- .../mastodon/features/compose/components/upload_form.jsx | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index f366ef5c6e..1cb7c89fa5 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -23,6 +23,7 @@ import PollFormContainer from '../containers/poll_form_container'; import PrivacyDropdownContainer from '../containers/privacy_dropdown_container'; import ReplyIndicatorContainer from '../containers/reply_indicator_container'; import SearchabilityDropdownContainer from '../containers/searchability_dropdown_container'; +import SensitiveButtonContainer from '../containers/sensitive_button_container'; import SpoilerButtonContainer from '../containers/spoiler_button_container'; import UploadButtonContainer from '../containers/upload_button_container'; import UploadFormContainer from '../containers/upload_form_container'; @@ -234,7 +235,7 @@ class ComposeForm extends ImmutablePureComponent { }; render () { - const { intl, onPaste, autoFocus } = this.props; + const { intl, onPaste, autoFocus, anyMedia, text } = this.props; const { highlighted } = this.state; const disabled = this.props.isSubmitting; @@ -248,6 +249,8 @@ class ComposeForm extends ImmutablePureComponent { publishText = (this.props.privacy !== 'unlisted' && this.props.privacy !== 'public_unlisted' && this.props.privacy !== 'login') ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish); } + const anyLink = text.indexOf('https://') >= 0; + return (
@@ -297,6 +300,7 @@ class ComposeForm extends ImmutablePureComponent {
+ {(anyMedia || anyLink) && }
diff --git a/app/javascript/mastodon/features/compose/components/upload_form.jsx b/app/javascript/mastodon/features/compose/components/upload_form.jsx index cf2e53ad90..103be65b03 100644 --- a/app/javascript/mastodon/features/compose/components/upload_form.jsx +++ b/app/javascript/mastodon/features/compose/components/upload_form.jsx @@ -1,7 +1,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import SensitiveButtonContainer from '../containers/sensitive_button_container'; import UploadContainer from '../containers/upload_container'; import UploadProgressContainer from '../containers/upload_progress_container'; @@ -23,8 +22,6 @@ export default class UploadForm extends ImmutablePureComponent { ))} - - {!mediaIds.isEmpty() && } ); } From c30d4b5162f3b3f2ee5643205e3ae3ce7204e984 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Jul 2023 11:33:28 +0900 Subject: [PATCH 09/22] Change emoji reaction duplication check --- app/lib/activitypub/activity/like.rb | 13 +++++++++++-- app/models/emoji_reaction.rb | 1 - app/services/emoji_react_service.rb | 18 +++++++++++------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/lib/activitypub/activity/like.rb b/app/lib/activitypub/activity/like.rb index 3f98591e99..c552403346 100644 --- a/app/lib/activitypub/activity/like.rb +++ b/app/lib/activitypub/activity/like.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true class ActivityPub::Activity::Like < ActivityPub::Activity + include Redisable + include Lockable + def perform @original_status = status_from_uri(object_uri) @@ -44,9 +47,15 @@ class ActivityPub::Activity::Like < ActivityPub::Activity Trends.statuses.register(@original_status) end - return if EmojiReaction.where(account: @account, status: @original_status).count >= EmojiReaction::EMOJI_REACTION_PER_ACCOUNT_LIMIT + reaction = nil + + with_redis_lock("emoji_reaction:#{@original_status.id}") do + return if EmojiReaction.where(account: @account, status: @original_status).count >= EmojiReaction::EMOJI_REACTION_PER_ACCOUNT_LIMIT + return if EmojiReaction.find_by(account: @account, status: @original_status, name: shortcode) + + reaction = @original_status.emoji_reactions.create!(account: @account, name: shortcode, custom_emoji: emoji, uri: @json['id']) + end - reaction = @original_status.emoji_reactions.create!(account: @account, name: shortcode, custom_emoji: emoji, uri: @json['id']) write_stream(reaction) if @original_status.account.local? diff --git a/app/models/emoji_reaction.rb b/app/models/emoji_reaction.rb index b3cc11cf32..bbe00a2c54 100644 --- a/app/models/emoji_reaction.rb +++ b/app/models/emoji_reaction.rb @@ -28,7 +28,6 @@ class EmojiReaction < ApplicationRecord has_one :notification, as: :activity, dependent: :destroy - validate :status_same_emoji_reaction validate :status_emoji_reactions_count validates_with EmojiReactionValidator diff --git a/app/services/emoji_react_service.rb b/app/services/emoji_react_service.rb index 325897e198..7be550c391 100644 --- a/app/services/emoji_react_service.rb +++ b/app/services/emoji_react_service.rb @@ -4,6 +4,7 @@ class EmojiReactService < BaseService include Authorization include Payloadable include Redisable + include Lockable # React a status with emoji and notify remote user # @param [Account] account @@ -14,17 +15,20 @@ class EmojiReactService < BaseService status = status.reblog if status.reblog? && !status.reblog.nil? authorize_with account, status, :emoji_reaction? - emoji_reaction = EmojiReaction.find_by(account: account, status: status, name: name) + emoji_reaction = nil - return emoji_reaction unless emoji_reaction.nil? + with_redis_lock("emoji_reaction:#{status.id}") do + emoji_reaction = EmojiReaction.find_by(account: account, status: status, name: name) + raise Mastodon::ValidationError, I18n.t('reactions.errors.duplication') unless emoji_reaction.nil? - shortcode, domain = name.split('@') + shortcode, domain = name.split('@') + custom_emoji = CustomEmoji.find_by(shortcode: shortcode, domain: domain) + emoji_reaction = EmojiReaction.create!(account: account, status: status, name: shortcode, custom_emoji: custom_emoji) - custom_emoji = CustomEmoji.find_by(shortcode: shortcode, domain: domain) + status.touch # rubocop:disable Rails/SkipsModelValidations + end - emoji_reaction = EmojiReaction.create!(account: account, status: status, name: shortcode, custom_emoji: custom_emoji) - - status.touch # rubocop:disable Rails/SkipsModelValidations + raise Mastodon::ValidationError, I18n.t('reactions.errors.duplication') if emoji_reaction.nil? Trends.statuses.register(status) From 7fe6f4a65c241e1876a906a6923e69e528e8f101 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Jul 2023 11:35:41 +0900 Subject: [PATCH 10/22] Change alert mail --- config/locales/ja.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 3adc594476..5a2a37d0e0 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1851,8 +1851,8 @@ ja: explanation: delete_statuses: あなたの投稿のいくつかは、1つ以上のコミュニティガイドラインに違反していることが判明し、%{instance}のモデレータによって削除されました。 disable: アカウントは使用できませんが、プロフィールやその他のデータはそのまま残ります。 データのバックアップをリクエストしたり、アカウント設定を変更したり、アカウントを削除したりできます。 - force_cw: あなたのいくつかの投稿は、%{instance}のモデレータによって閲覧注意としてマークされています。これは、投稿本文が表示される前にユーザが投稿内のボタンをタップする必要があることを意味します。あなたは将来投稿する際に自分自身で文章に警告を記述することができます。 - mark_statuses_as_sensitive: あなたのいくつかの投稿は、%{instance}のモデレータによって閲覧注意としてマークされています。これは、プレビューが表示される前にユーザが投稿内のメディアをタップする必要があることを意味します。あなたは将来投稿する際に自分自身でメディアを閲覧注意としてマークすることができます。 + force_cw: あなたのいくつかの投稿は、%{instance}のモデレータによって閲覧注意としてマークされています。これは、投稿本文が表示される前にユーザが投稿内のボタンをタップする必要があることを意味します。あなたはこの投稿を削除する必要はありませんが、将来同様の投稿をする際に自分自身で文章に警告を追加してください。 + mark_statuses_as_sensitive: あなたのいくつかの投稿は、%{instance}のモデレータによって閲覧注意としてマークされています。これは、プレビューが表示される前にユーザが投稿内のメディアをタップする必要があることを意味します。あなたはこの投稿を削除する必要はありませんが、将来同様の投稿をする際に自分自身でメディアを閲覧注意としてマークしてください。 sensitive: 今後、アップロードされたすべてのメディアファイルは閲覧注意としてマークされ、クリック解除式の警告で覆われるようになります。 silence: アカウントが制限されています。このサーバーでは既にフォローしている人だけがあなたの投稿を見ることができます。 様々な発見機能から除外されるかもしれません。他の人があなたを手動でフォローすることは可能です。 suspend: アカウントは使用できなくなり、プロフィールなどのデータにもアクセスできなくなります。約30日後にデータが完全に削除されるまでは、ログインしてデータのバックアップを要求することができますが、アカウントの停止回避を防ぐために一部の基本データを保持します。 From 715f1555537f3b2ee830f164d91210e7de14b986 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Jul 2023 12:19:54 +0900 Subject: [PATCH 11/22] Revert "Add sensitive checkbox with inputing link" This reverts commit 3258d09c64048fe7fc95a10f8611e5ad89c24385. --- .../mastodon/features/compose/components/compose_form.jsx | 6 +----- .../mastodon/features/compose/components/upload_form.jsx | 3 +++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx index 1cb7c89fa5..f366ef5c6e 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.jsx +++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx @@ -23,7 +23,6 @@ import PollFormContainer from '../containers/poll_form_container'; import PrivacyDropdownContainer from '../containers/privacy_dropdown_container'; import ReplyIndicatorContainer from '../containers/reply_indicator_container'; import SearchabilityDropdownContainer from '../containers/searchability_dropdown_container'; -import SensitiveButtonContainer from '../containers/sensitive_button_container'; import SpoilerButtonContainer from '../containers/spoiler_button_container'; import UploadButtonContainer from '../containers/upload_button_container'; import UploadFormContainer from '../containers/upload_form_container'; @@ -235,7 +234,7 @@ class ComposeForm extends ImmutablePureComponent { }; render () { - const { intl, onPaste, autoFocus, anyMedia, text } = this.props; + const { intl, onPaste, autoFocus } = this.props; const { highlighted } = this.state; const disabled = this.props.isSubmitting; @@ -249,8 +248,6 @@ class ComposeForm extends ImmutablePureComponent { publishText = (this.props.privacy !== 'unlisted' && this.props.privacy !== 'public_unlisted' && this.props.privacy !== 'login') ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish); } - const anyLink = text.indexOf('https://') >= 0; - return ( @@ -300,7 +297,6 @@ class ComposeForm extends ImmutablePureComponent {
- {(anyMedia || anyLink) && }
diff --git a/app/javascript/mastodon/features/compose/components/upload_form.jsx b/app/javascript/mastodon/features/compose/components/upload_form.jsx index 103be65b03..cf2e53ad90 100644 --- a/app/javascript/mastodon/features/compose/components/upload_form.jsx +++ b/app/javascript/mastodon/features/compose/components/upload_form.jsx @@ -1,6 +1,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import SensitiveButtonContainer from '../containers/sensitive_button_container'; import UploadContainer from '../containers/upload_container'; import UploadProgressContainer from '../containers/upload_progress_container'; @@ -22,6 +23,8 @@ export default class UploadForm extends ImmutablePureComponent { ))} + + {!mediaIds.isEmpty() && } ); } From af86c0982494a0a339110a072ef20de77a4daaaa Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Jul 2023 12:27:15 +0900 Subject: [PATCH 12/22] Turn sensitive false if media is empty --- app/javascript/mastodon/actions/compose.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index dadcdee5dd..cf695f79b4 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -206,7 +206,7 @@ export function submitCompose(routerHistory) { in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null), media_ids: media.map(item => item.get('id')), media_attributes, - sensitive: getState().getIn(['compose', 'sensitive']), + sensitive: media.size > 0 ? getState().getIn(['compose', 'sensitive']) : false, spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '', markdown: getState().getIn(['compose', 'markdown']), visibility: getState().getIn(['compose', 'privacy']), From e7492e07459645dac7142b2346ccca31e487f447 Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Jul 2023 17:37:54 +0900 Subject: [PATCH 13/22] Add account_warning notification --- .../notifications/components/notification.jsx | 28 +++++++++++++++++++ app/javascript/mastodon/locales/ja.json | 1 + .../mastodon/reducers/notifications.js | 3 +- .../styles/mastodon/components.scss | 5 ++++ app/models/admin/account_action.rb | 5 ++++ app/models/admin/status_batch_action.rb | 5 ++++ app/models/notification.rb | 14 +++++++++- .../rest/account_warning_serializer.rb | 5 ++++ .../rest/notification_serializer.rb | 7 ++++- 9 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 app/serializers/rest/account_warning_serializer.rb diff --git a/app/javascript/mastodon/features/notifications/components/notification.jsx b/app/javascript/mastodon/features/notifications/components/notification.jsx index cb3f92b71c..1872d9f689 100644 --- a/app/javascript/mastodon/features/notifications/components/notification.jsx +++ b/app/javascript/mastodon/features/notifications/components/notification.jsx @@ -30,6 +30,7 @@ const messages = defineMessages({ status: { id: 'notification.status', defaultMessage: '{name} just posted' }, statusReference: { id: 'notification.status_reference', defaultMessage: '{name} refered' }, update: { id: 'notification.update', defaultMessage: '{name} edited a post' }, + warning: { id: 'notification.warning', defaultMessage: 'You have been warned and "{action}" has been executed. Check your mailbox' }, adminSignUp: { id: 'notification.admin.sign_up', defaultMessage: '{name} signed up' }, adminReport: { id: 'notification.admin.report', defaultMessage: '{name} reported {target}' }, }); @@ -443,6 +444,31 @@ class Notification extends ImmutablePureComponent { ); } + renderWarning (notification) { + const { intl, unread } = this.props; + console.dir(notification); + + return ( + +
+
+
+ +
+ + + + +
+ +
+ {notification.getIn(['account_warning', 'text'])} +
+
+
+ ); + } + renderAdminSignUp (notification, account, link) { const { intl, unread } = this.props; @@ -522,6 +548,8 @@ class Notification extends ImmutablePureComponent { return this.renderUpdate(notification, link); case 'poll': return this.renderPoll(notification, account); + case 'warning': + return this.renderWarning(notification); case 'admin.sign_up': return this.renderAdminSignUp(notification, account, link); case 'admin.report': diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index cca4facea9..40e19d3a7a 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -439,6 +439,7 @@ "notification.status": "{name}さんが投稿しました", "notification.status_reference": "{name}さんがあなたの投稿を参照しました", "notification.update": "{name}さんが投稿を編集しました", + "notification.warning": "あなたは警告を出され、「{action}」が実行されました。詳細はメールをご確認ください", "notifications.clear": "通知を消去", "notifications.clear_confirmation": "本当に通知を消去しますか?", "notifications.column_settings.admin.report": "新しい通報:", diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index 984097ca0f..8ba9cf3c18 100644 --- a/app/javascript/mastodon/reducers/notifications.js +++ b/app/javascript/mastodon/reducers/notifications.js @@ -52,10 +52,11 @@ const notificationToMap = notification => ImmutableMap({ id: notification.id, type: notification.type, account: notification.account.id, - emoji_reaction: ImmutableMap(notification.emoji_reaction), created_at: notification.created_at, + emoji_reaction: ImmutableMap(notification.emoji_reaction), status: notification.status ? notification.status.id : null, report: notification.report ? fromJS(notification.report) : null, + account_warning: notification.account_warning ? ImmutableMap(notification.account_warning) : null, }); const normalizeNotification = (state, notification, usePendingItems) => { diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 1b6e5626ec..9cfce31355 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1848,6 +1848,11 @@ a.account__display-name { } } +.notification__warning-text { + padding: 16px 16px 16px 48px; + color: $darker-text-color; +} + .notification__display-name { color: inherit; font-weight: 500; diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb index 2b5560e2eb..c85d733619 100644 --- a/app/models/admin/account_action.rb +++ b/app/models/admin/account_action.rb @@ -54,6 +54,7 @@ class Admin::AccountAction process_email! process_queue! + notify! end def report @@ -107,6 +108,10 @@ class Admin::AccountAction log_action(:create, @warning) if @warning.text.present? && type == 'none' end + def notify! + LocalNotificationWorker.perform_async(target_account.id, @warning.id, 'AccountWarning', 'warning') if @warning && %w(none sensitive silence).include?(type) + end + def process_reports! # If we're doing "mark as resolved" on a single report, # then we want to keep other reports open in case they diff --git a/app/models/admin/status_batch_action.rb b/app/models/admin/status_batch_action.rb index b5178c672e..a6c0260758 100644 --- a/app/models/admin/status_batch_action.rb +++ b/app/models/admin/status_batch_action.rb @@ -17,6 +17,7 @@ class Admin::StatusBatchAction def save! process_action! + notify! end private @@ -157,6 +158,10 @@ class Admin::StatusBatchAction report.save! end + def notify! + LocalNotificationWorker.perform_async(target_account.id, @warning.id, 'AccountWarning', 'warning') if warnable? && @warning + end + def report @report ||= Report.find(report_id) if report_id.present? end diff --git a/app/models/notification.rb b/app/models/notification.rb index 8f15435c2a..50e45f05b3 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -28,6 +28,7 @@ class Notification < ApplicationRecord 'EmojiReaction' => :emoji_reaction, 'StatusReference' => :status_reference, 'Poll' => :poll, + 'AccountWarning' => :warning, }.freeze TYPES = %i( @@ -42,6 +43,7 @@ class Notification < ApplicationRecord reaction poll update + warning admin.sign_up admin.report ).freeze @@ -73,6 +75,7 @@ class Notification < ApplicationRecord belongs_to :status_reference, inverse_of: :notification belongs_to :poll, inverse_of: false belongs_to :report, inverse_of: false + belongs_to :account_warning, inverse_of: false end validates :type, inclusion: { in: TYPES } @@ -159,6 +162,15 @@ class Notification < ApplicationRecord end end + def from_account_web + case activity_type + when 'AccountWarning' + account_warning&.target_account + else + from_account + end + end + after_initialize :set_from_account before_validation :set_from_account @@ -168,7 +180,7 @@ class Notification < ApplicationRecord return unless new_record? case activity_type - when 'Status', 'Follow', 'Favourite', 'EmojiReaction', 'EmojiReact', 'FollowRequest', 'Poll', 'Report' + when 'Status', 'Follow', 'Favourite', 'EmojiReaction', 'EmojiReact', 'FollowRequest', 'Poll', 'Report', 'AccountWarning' self.from_account_id = activity&.account_id when 'Mention', 'StatusReference' self.from_account_id = activity&.status&.account_id diff --git a/app/serializers/rest/account_warning_serializer.rb b/app/serializers/rest/account_warning_serializer.rb new file mode 100644 index 0000000000..3f5940b71c --- /dev/null +++ b/app/serializers/rest/account_warning_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class REST::AccountWarningSerializer < ActiveModel::Serializer + attributes :id, :action, :text, :status_ids +end diff --git a/app/serializers/rest/notification_serializer.rb b/app/serializers/rest/notification_serializer.rb index 5a7d6e11b5..71b5d69fc5 100644 --- a/app/serializers/rest/notification_serializer.rb +++ b/app/serializers/rest/notification_serializer.rb @@ -3,10 +3,11 @@ class REST::NotificationSerializer < ActiveModel::Serializer attributes :id, :type, :created_at - belongs_to :from_account, key: :account, serializer: REST::AccountSerializer + belongs_to :from_account_web, key: :account, serializer: REST::AccountSerializer belongs_to :target_status, key: :status, if: :status_type?, serializer: REST::StatusSerializer belongs_to :report, if: :report_type?, serializer: REST::ReportSerializer belongs_to :emoji_reaction, if: :emoji_reaction_type?, serializer: REST::NotifyEmojiReactionSerializer + belongs_to :account_warning, if: :warning_type?, serializer: REST::AccountWarningSerializer def id object.id.to_s @@ -20,6 +21,10 @@ class REST::NotificationSerializer < ActiveModel::Serializer object.type == :'admin.report' end + def warning_type? + object.type == :warning + end + def emoji_reaction_type? object.type == :emoji_reaction end From 0dbc070037d8609bc6cf15b4c2f5d2a964ba2a2d Mon Sep 17 00:00:00 2001 From: KMY Date: Wed, 26 Jul 2023 18:44:23 +0900 Subject: [PATCH 14/22] Remove status medias from notification --- app/javascript/mastodon/components/status.jsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/status.jsx b/app/javascript/mastodon/components/status.jsx index 6a195201d5..c8fb36d199 100644 --- a/app/javascript/mastodon/components/status.jsx +++ b/app/javascript/mastodon/components/status.jsx @@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { HotKeys } from 'react-hotkeys'; +import AttachmentList from 'mastodon/components/attachment_list'; import { Icon } from 'mastodon/components/icon'; import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder'; @@ -466,7 +467,14 @@ class Status extends ImmutablePureComponent { } else if (status.get('media_attachments').size > 0) { const language = status.getIn(['translation', 'language']) || status.get('language'); - if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { + if (this.props.muted) { + media = ( + + ); + } else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { const attachment = status.getIn(['media_attachments', 0]); const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); From cd00d7f533d5fca1cb2f4410a07f4989aa570145 Mon Sep 17 00:00:00 2001 From: KMY Date: Thu, 27 Jul 2023 12:47:33 +0900 Subject: [PATCH 15/22] Add sensitive words setting --- .../admin/sensitive_words_controller.rb | 34 +++++++++++++++++++ app/models/admin/sensitive_word.rb | 25 ++++++++++++++ app/models/form/admin_settings.rb | 4 +++ app/models/user_role.rb | 2 ++ app/policies/sensitive_words_policy.rb | 11 ++++++ app/services/post_status_service.rb | 8 +++++ app/services/update_status_service.rb | 8 +++++ .../admin/sensitive_words/show.html.haml | 19 +++++++++++ config/locales/en.yml | 6 ++++ config/locales/ja.yml | 8 +++++ config/navigation.rb | 3 +- config/routes/admin.rb | 1 + 12 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 app/controllers/admin/sensitive_words_controller.rb create mode 100644 app/models/admin/sensitive_word.rb create mode 100644 app/policies/sensitive_words_policy.rb create mode 100644 app/views/admin/sensitive_words/show.html.haml diff --git a/app/controllers/admin/sensitive_words_controller.rb b/app/controllers/admin/sensitive_words_controller.rb new file mode 100644 index 0000000000..d97da2a927 --- /dev/null +++ b/app/controllers/admin/sensitive_words_controller.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Admin + class SensitiveWordsController < BaseController + def show + authorize :sensitive_words, :show? + + @admin_settings = Form::AdminSettings.new + end + + def create + authorize :sensitive_words, :create? + + @admin_settings = Form::AdminSettings.new(settings_params) + + if @admin_settings.save + flash[:notice] = I18n.t('generic.changes_saved_msg') + redirect_to after_update_redirect_path + else + render :index + end + end + + private + + def after_update_redirect_path + admin_sensitive_words_path + end + + def settings_params + params.require(:form_admin_settings).permit(*Form::AdminSettings::KEYS) + end + end +end diff --git a/app/models/admin/sensitive_word.rb b/app/models/admin/sensitive_word.rb new file mode 100644 index 0000000000..0e4f3e6a16 --- /dev/null +++ b/app/models/admin/sensitive_word.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class Admin::SensitiveWord + class << self + def sensitive?(text, spoiler_text) + exposure_text = (spoiler_text.presence || text) + (spoiler_text.blank? && sensitive_words.any? { |word| text.include?(word) }) || + sensitive_words_for_full.any? { |word| exposure_text.include?(word) } + end + + def modified_text(text, spoiler_text) + spoiler_text.present? ? "#{spoiler_text}\n\n#{text}" : text + end + + private + + def sensitive_words + Setting.sensitive_words || [] + end + + def sensitive_words_for_full + Setting.sensitive_words_for_full || [] + end + end +end diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index ff7ef50c12..e11fe50961 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -38,6 +38,8 @@ class Form::AdminSettings enable_block_emoji_reaction_settings hide_local_users_for_anonymous post_hash_tags_max + sensitive_words + sensitive_words_for_full ).freeze INTEGER_KEYS = %i( @@ -70,6 +72,8 @@ class Form::AdminSettings STRING_ARRAY_KEYS = %i( ng_words + sensitive_words + sensitive_words_for_full ).freeze attr_accessor(*KEYS) diff --git a/app/models/user_role.rb b/app/models/user_role.rb index 0fd9b6e498..5368ae5ed2 100644 --- a/app/models/user_role.rb +++ b/app/models/user_role.rb @@ -36,6 +36,7 @@ class UserRole < ApplicationRecord manage_roles: (1 << 17), manage_user_access: (1 << 18), delete_user_data: (1 << 19), + manage_sensitive_words: (1 << 29), manage_ng_words: (1 << 30), }.freeze @@ -63,6 +64,7 @@ class UserRole < ApplicationRecord manage_taxonomies manage_invites manage_ng_words + manage_sensitive_words ).freeze, administration: %w( diff --git a/app/policies/sensitive_words_policy.rb b/app/policies/sensitive_words_policy.rb new file mode 100644 index 0000000000..0812aa9751 --- /dev/null +++ b/app/policies/sensitive_words_policy.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class SensitiveWordsPolicy < ApplicationPolicy + def show? + role.can?(:manage_sensitive_words) + end + + def create? + role.can?(:manage_sensitive_words) + end +end diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 08d3f1998a..eb5038b332 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -82,10 +82,18 @@ class PostStatusService < BaseService @scheduled_at = @options[:scheduled_at]&.to_datetime @scheduled_at = nil if scheduled_in_the_past? @reference_ids = (@options[:status_reference_ids] || []).map(&:to_i).filter(&:positive?) + process_sensitive_words rescue ArgumentError raise ActiveRecord::RecordInvalid end + def process_sensitive_words + if [:public, :public_unlisted, :login].include?(@visibility&.to_sym) && Admin::SensitiveWord.sensitive?(@text, @options[:spoiler_text] || '') + @text = Admin::SensitiveWord.modified_text(@text, @options[:spoiler_text]) + @options[:spoiler_text] = I18n.t('admin.sensitive_words.alert') + end + end + def searchability return :private if @options[:searchability]&.to_sym == :public && @visibility&.to_sym == :unlisted && @account.user&.setting_disallow_unlisted_public_searchability diff --git a/app/services/update_status_service.rb b/app/services/update_status_service.rb index be0bf526bc..9a8d23cb7e 100644 --- a/app/services/update_status_service.rb +++ b/app/services/update_status_service.rb @@ -127,6 +127,7 @@ class UpdateStatusService < BaseService @status.markdown = @options[:markdown] || false @status.sensitive = @options[:sensitive] || @options[:spoiler_text].present? if @options.key?(:sensitive) || @options.key?(:spoiler_text) @status.language = valid_locale_cascade(@options[:language], @status.language, @status.account.user&.preferred_posting_language, I18n.default_locale) + process_sensitive_words # We raise here to rollback the entire transaction raise NoChangesSubmittedError unless significant_changes? @@ -137,6 +138,13 @@ class UpdateStatusService < BaseService @status.save! end + def process_sensitive_words + return unless [:public, :public_unlisted, :login].include?(@status.visibility&.to_sym) && Admin::SensitiveWord.sensitive?(@status.text, @status.spoiler_text || '') + + @status.text = Admin::SensitiveWord.modified_text(@status.text, @status.spoiler_text) + @status.spoiler_text = I18n.t('admin.sensitive_words.alert') + end + def update_expiration! UpdateStatusExpirationService.new.call(@status) end diff --git a/app/views/admin/sensitive_words/show.html.haml b/app/views/admin/sensitive_words/show.html.haml new file mode 100644 index 0000000000..bb5447d3e1 --- /dev/null +++ b/app/views/admin/sensitive_words/show.html.haml @@ -0,0 +1,19 @@ +- content_for :page_title do + = t('admin.sensitive_words.title') + +- 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') + + .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') + + .actions + = f.button :button, t('generic.save_changes'), type: :submit diff --git a/config/locales/en.yml b/config/locales/en.yml index 93593e2569..5ee09dadbf 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -773,6 +773,12 @@ en: edit: Edit rule empty: No server rules have been defined yet. title: Server rules + sensitive_words: + alert: This post contains sensitive words, so alert added + hint: This keywords is applied to public posts only.. + keywords: Sensitive keywords + keywords_for_all: Sensitive keywords (Contains CW alert) + title: Sensitive words and moderation options settings: about: manage_rules: Manage server rules diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 5a2a37d0e0..1690c7f3dd 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -769,6 +769,14 @@ ja: edit: ルールを編集 empty: サーバーのルールが定義されていません。 title: サーバーのルール + sensitive_words: + alert: この投稿にはセンシティブなキーワードが含まれるため、警告文が追加されました + hint: センシティブなキーワードの設定は、当サーバーのローカルユーザーによる公開範囲「公開」「ローカル公開」「ログインユーザーのみ」に対して適用されます。 + keywords: センシティブなキーワード(警告文は除外) + keywords_for_all: センシティブなキーワード(警告文にも適用) + keywords_for_all_hint: ここで指定したキーワードを含む投稿は強制的にCWになります。警告文にも含まれていればCWになります + keywords_hint: ここで指定したキーワードを含む投稿は強制的にCWになります。ただし警告文に使用していた場合は無視されます + title: センシティブ単語と設定 settings: about: manage_rules: サーバーのルールを管理 diff --git a/config/navigation.rb b/config/navigation.rb index 8b7b6b7df2..d64565e369 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -40,7 +40,8 @@ SimpleNavigation::Configuration.run do |navigation| s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_path, highlights_on: %r{/admin/reports}, if: -> { current_user.can?(:manage_reports) } s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_path(origin: 'local'), highlights_on: %r{/admin/accounts|/admin/pending_accounts|/admin/disputes|/admin/users}, if: -> { current_user.can?(:manage_users) } s.item :media_attachments, safe_join([fa_icon('picture-o fw'), t('admin.media_attachments.title')]), admin_media_attachments_path, highlights_on: %r{/admin/media_attachments}, if: -> { current_user.can?(:manage_users) } - s.item :ng_words, safe_join([fa_icon('picture-o fw'), t('admin.ng_words.title')]), admin_ng_words_path, highlights_on: %r{/admin/ng_words}, if: -> { current_user.can?(:manage_ng_words) } + s.item :ng_words, safe_join([fa_icon('list fw'), t('admin.ng_words.title')]), admin_ng_words_path, highlights_on: %r{/admin/ng_words}, if: -> { current_user.can?(:manage_ng_words) } + s.item :sensitive_words, safe_join([fa_icon('list fw'), t('admin.sensitive_words.title')]), admin_sensitive_words_path, highlights_on: %r{/admin/sensitive_words}, if: -> { current_user.can?(:manage_sensitive_words) } s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path, if: -> { current_user.can?(:manage_invites) } s.item :follow_recommendations, safe_join([fa_icon('user-plus fw'), t('admin.follow_recommendations.title')]), admin_follow_recommendations_path, highlights_on: %r{/admin/follow_recommendations}, if: -> { current_user.can?(:manage_taxonomies) } s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_path(limited: whitelist_mode? ? nil : '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks|/admin/domain_allows}, if: -> { current_user.can?(:manage_federation) } diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 6c2a37f3c2..aeca09acdc 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -34,6 +34,7 @@ namespace :admin do resources :warning_presets, except: [:new, :show] resources :media_attachments, only: [:index] resource :ng_words, only: [:show, :create] + resource :sensitive_words, only: [:show, :create] resources :announcements, except: [:show] do member do From 608edfb3d3afa698c2dfd2f55f4ab0e4890acd52 Mon Sep 17 00:00:00 2001 From: KMY Date: Thu, 27 Jul 2023 13:02:17 +0900 Subject: [PATCH 16/22] Fix navigation permissions --- config/navigation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/navigation.rb b/config/navigation.rb index d64565e369..82e1c26d8a 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -36,7 +36,7 @@ SimpleNavigation::Configuration.run do |navigation| s.item :links, safe_join([fa_icon('newspaper-o fw'), t('admin.trends.links.title')]), admin_trends_links_path, highlights_on: %r{/admin/trends/links} end - n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), nil, if: -> { current_user.can?(:manage_reports, :view_audit_log, :manage_users, :manage_invites, :manage_taxonomies, :manage_federation, :manage_blocks, :manage_ng_words) } do |s| + n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), nil, if: -> { current_user.can?(:manage_reports, :view_audit_log, :manage_users, :manage_invites, :manage_taxonomies, :manage_federation, :manage_blocks, :manage_ng_words, :manage_sensitive_words) } do |s| s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_path, highlights_on: %r{/admin/reports}, if: -> { current_user.can?(:manage_reports) } s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_path(origin: 'local'), highlights_on: %r{/admin/accounts|/admin/pending_accounts|/admin/disputes|/admin/users}, if: -> { current_user.can?(:manage_users) } s.item :media_attachments, safe_join([fa_icon('picture-o fw'), t('admin.media_attachments.title')]), admin_media_attachments_path, highlights_on: %r{/admin/media_attachments}, if: -> { current_user.can?(:manage_users) } From c141ef0728c68dcc93445484db183c97d8b723f6 Mon Sep 17 00:00:00 2001 From: KMY Date: Sat, 29 Jul 2023 16:53:26 +0900 Subject: [PATCH 17/22] Fix public feed returns all visibilities --- app/models/public_feed.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/public_feed.rb b/app/models/public_feed.rb index 3579623232..c87b9361b6 100644 --- a/app/models/public_feed.rb +++ b/app/models/public_feed.rb @@ -107,7 +107,7 @@ class PublicFeed end def anonymous_scope - Status.where.not(visibility: :login) + Status.where(visibility: [:public, :public_unlisted]) end def account_filters_scope From 1c15ccb6b20dc534ee7f4b61b1427d0411d90bd1 Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 31 Jul 2023 09:31:51 +0900 Subject: [PATCH 18/22] Fix merge methods --- app/models/account_statuses_filter.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/models/account_statuses_filter.rb b/app/models/account_statuses_filter.rb index 03dd4a34f0..7a4cfe4a27 100644 --- a/app/models/account_statuses_filter.rb +++ b/app/models/account_statuses_filter.rb @@ -26,11 +26,17 @@ class AccountStatusesFilter scope.merge!(no_reblogs_scope) if exclude_reblogs? scope.merge!(hashtag_scope) if tagged? - scope.merge!(scope.where(searchability: :public)) if domain_block&.reject_send_not_public_searchability - scope.merge!(scope.where.not(visibility: :public_unlisted)) if domain_block&.reject_send_public_unlisted || (domain_block&.detect_invalid_subscription && @account.user&.setting_reject_public_unlisted_subscription) - scope.merge!(scope.where.not(visibility: :unlisted)) if domain_block&.detect_invalid_subscription && @account.user&.setting_reject_unlisted_subscription + available_searchabilities = [:public, :unlisted, :private, :direct, :limited] + available_visibilities = [:public, :public_unlisted, :login, :unlisted, :private, :direct, :limited] + + available_searchabilities = [:public] if domain_block&.reject_send_not_public_searchability + available_visibilities -= [:public_unlisted] if domain_block&.reject_send_public_unlisted || (domain_block&.detect_invalid_subscription && @account.user&.setting_reject_public_unlisted_subscription) + available_visibilities -= [:unlisted] if domain_block&.detect_invalid_subscription && @account.user&.setting_reject_unlisted_subscription + available_visibilities -= [:login] if current_account.nil? + scope.merge!(scope.where(spoiler_text: ['', nil])) if domain_block&.reject_send_sensitive - scope.merge!(scope.where.not(visibility: :login)) if current_account.nil? + scope.merge!(scope.where(searchability: available_searchabilities)) + scope.merge!(scope.where(visibility: available_visibilities)) scope end From 266823643e30c45de136d2a891612b85645fd980 Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 31 Jul 2023 09:57:51 +0900 Subject: [PATCH 19/22] Regexp is available in ng_words --- app/controllers/admin/ng_words_controller.rb | 15 ++++++++++++++- app/models/admin/ng_word.rb | 14 +++++++++++++- config/locales/en.yml | 1 + config/locales/ja.yml | 1 + 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/ng_words_controller.rb b/app/controllers/admin/ng_words_controller.rb index 1af50acb42..e26ca96b8c 100644 --- a/app/controllers/admin/ng_words_controller.rb +++ b/app/controllers/admin/ng_words_controller.rb @@ -11,18 +11,31 @@ module Admin def create authorize :ng_words, :create? + begin + test_words + rescue + flash[:alert] = I18n.t('admin.ng_words.test_error') + redirect_to after_update_redirect_path + return + end + @admin_settings = Form::AdminSettings.new(settings_params) if @admin_settings.save flash[:notice] = I18n.t('generic.changes_saved_msg') redirect_to after_update_redirect_path else - render :index + render :show end end private + def test_words + ng_words = settings_params['ng_words'].split(/\r\n|\r|\n/) + Admin::NgWord.reject_with_custom_words?('Sample text', ng_words) + end + def after_update_redirect_path admin_ng_words_path end diff --git a/app/models/admin/ng_word.rb b/app/models/admin/ng_word.rb index 42c84b0f8e..b99c52a714 100644 --- a/app/models/admin/ng_word.rb +++ b/app/models/admin/ng_word.rb @@ -3,7 +3,11 @@ class Admin::NgWord class << self def reject?(text) - ng_words.any? { |word| text.include?(word) } + ng_words.any? { |word| include?(text, word) } + end + + def reject_with_custom_words?(text, custom_ng_words) + custom_ng_words.any? { |word| include?(text, word) } end def hashtag_reject?(hashtag_count) @@ -16,6 +20,14 @@ class Admin::NgWord private + def include?(text, word) + if word.start_with?('?') && word.size >= 2 + text =~ /#{word[1..]}/i + else + text.include?(word) + end + end + def ng_words Setting.ng_words || [] end diff --git a/config/locales/en.yml b/config/locales/en.yml index 5ee09dadbf..e89c236b0f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -600,6 +600,7 @@ en: hide_local_users_for_anonymous: Hide timeline local user posts from anonymous keywords: Reject keywords post_hash_tags_max: Hash tags max for posts + test_error: Testing is returned any errors title: NG words and against spams relationships: title: "%{acct}'s relationships" diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 1690c7f3dd..d27e475cb6 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -599,6 +599,7 @@ ja: hide_local_users_for_anonymous: ログインしていない状態でローカルユーザーの投稿をタイムラインから取得できないようにする keywords: 投稿できないキーワード post_hash_tags_max: 投稿に設定可能なハッシュタグの最大数 + test_error: NGワードのテストに失敗しました。正規表現のミスが含まれているかもしれません title: NGワードとスパム relationships: title: "%{acct} さんのフォロー・フォロワー" From bfdc7c41f229080e72bd7e7ee821b1bd3e67a16d Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 31 Jul 2023 10:00:10 +0900 Subject: [PATCH 20/22] Block account bio with ng_words --- app/services/activitypub/process_account_service.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 476dabdd77..61a2d2e9d1 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -21,6 +21,8 @@ class ActivityPub::ProcessAccountService < BaseService @domain = TagManager.instance.normalize_domain(domain) @collections = {} + return unless valid_account? + # The key does not need to be unguessable, it just needs to be somewhat unique @options[:request_id] ||= "#{Time.now.utc.to_i}-#{username}@#{domain}" @@ -123,6 +125,12 @@ class ActivityPub::ProcessAccountService < BaseService @account.settings = other_settings end + def valid_account? + display_name = @json['name'] + note = @json['summary'] + !Admin::NgWord.reject?(display_name) && !Admin::NgWord.reject?(note) + end + def set_fetchable_key! @account.public_key = public_key || '' end From 303c14acadd64b4e161f8ce4b12d92a5c4190f27 Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 31 Jul 2023 10:40:03 +0900 Subject: [PATCH 21/22] Fix account statuses sometimes return empty array --- app/models/account_statuses_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/account_statuses_filter.rb b/app/models/account_statuses_filter.rb index 7a4cfe4a27..5b9579191f 100644 --- a/app/models/account_statuses_filter.rb +++ b/app/models/account_statuses_filter.rb @@ -26,7 +26,7 @@ class AccountStatusesFilter scope.merge!(no_reblogs_scope) if exclude_reblogs? scope.merge!(hashtag_scope) if tagged? - available_searchabilities = [:public, :unlisted, :private, :direct, :limited] + available_searchabilities = [:public, :unlisted, :private, :direct, :limited, nil] available_visibilities = [:public, :public_unlisted, :login, :unlisted, :private, :direct, :limited] available_searchabilities = [:public] if domain_block&.reject_send_not_public_searchability From 1a5f8964e136094598573a4ec05cc41c51267045 Mon Sep 17 00:00:00 2001 From: KMY Date: Mon, 31 Jul 2023 11:04:01 +0900 Subject: [PATCH 22/22] Regexp is available in sensitive_words --- .../admin/sensitive_words_controller.rb | 14 ++++++++++++++ app/models/admin/sensitive_word.rb | 12 ++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/sensitive_words_controller.rb b/app/controllers/admin/sensitive_words_controller.rb index d97da2a927..eaa6ae802e 100644 --- a/app/controllers/admin/sensitive_words_controller.rb +++ b/app/controllers/admin/sensitive_words_controller.rb @@ -11,6 +11,14 @@ module Admin def create authorize :sensitive_words, :create? + begin + test_words + rescue + flash[:alert] = I18n.t('admin.ng_words.test_error') + redirect_to after_update_redirect_path + return + end + @admin_settings = Form::AdminSettings.new(settings_params) if @admin_settings.save @@ -23,6 +31,12 @@ module Admin private + def test_words + sensitive_words = settings_params['sensitive_words'].split(/\r\n|\r|\n/) + sensitive_words_for_full = settings_params['sensitive_words_for_full'].split(/\r\n|\r|\n/) + Admin::NgWord.reject_with_custom_words?('Sample text', sensitive_words + sensitive_words_for_full) + end + def after_update_redirect_path admin_sensitive_words_path end diff --git a/app/models/admin/sensitive_word.rb b/app/models/admin/sensitive_word.rb index 0e4f3e6a16..ab457382b9 100644 --- a/app/models/admin/sensitive_word.rb +++ b/app/models/admin/sensitive_word.rb @@ -4,8 +4,8 @@ class Admin::SensitiveWord class << self def sensitive?(text, spoiler_text) exposure_text = (spoiler_text.presence || text) - (spoiler_text.blank? && sensitive_words.any? { |word| text.include?(word) }) || - sensitive_words_for_full.any? { |word| exposure_text.include?(word) } + (spoiler_text.blank? && sensitive_words.any? { |word| include?(text, word) }) || + sensitive_words_for_full.any? { |word| include?(exposure_text, word) } end def modified_text(text, spoiler_text) @@ -14,6 +14,14 @@ class Admin::SensitiveWord private + def include?(text, word) + if word.start_with?('?') && word.size >= 2 + text =~ /#{word[1..]}/i + else + text.include?(word) + end + end + def sensitive_words Setting.sensitive_words || [] end