diff --git a/AUTHORS_KB.md b/AUTHORS_KB.md index 3f17d5563a..2cda5fd31d 100644 --- a/AUTHORS_KB.md +++ b/AUTHORS_KB.md @@ -5,6 +5,7 @@ kmyblueフォークは、以下の方の貢献によって成り立っています。 本家Mastodonの貢献者については、`AUTHORS.md`をご覧ください。 +- [aoisensi](https://github.com/aoisensi) - [KMY](https://github.com/kmycode) - [S-H-GAMELINKS](https://github.com/S-H-GAMELINKS) - [Yuicho](https://github.com/yuicho) @@ -12,6 +13,6 @@ kmyblueフォークは、以下の方の貢献によって成り立っていま ## 特記 kmyblueフォークの開発にあたって、API・Activity仕様の設計(一部機能については内部仕様)策定の過程で下記リポジトリのコードを参考にしました。 -kmyblueフォークに直接貢献したわけではありませんが、以下のリポジトリにある絵文字リアクション機能・検索範囲機能のコードのうち、ごく一部ではありますがkmyblueへ転写した箇所がございますため、お名前記載させていただきます。 +kmyblueフォークに直接貢献したわけではありませんが、以下のリポジトリにある絵文字リアクション機能・検索範囲機能のコードのうち一部にkmyblueへ転写した箇所がございますため、お名前記載させていただきます。 - [Fedibird](https://github.com/fedibird/mastodon) diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb index 5fdea8c52f..64b516d815 100644 --- a/app/controllers/admin/statuses_controller.rb +++ b/app/controllers/admin/statuses_controller.rb @@ -34,7 +34,8 @@ module Admin UpdateStatusService.new.call( @status, edit_status_account_id, - no_history: true + no_history: true, + bypass_validation: true ) log_action(:remove_history, @status) redirect_to admin_account_status_path @@ -46,7 +47,8 @@ module Admin @status, edit_status_account_id, media_ids: [], - media_attributes: [] + media_attributes: [], + bypass_validation: true ) log_action(:remove_media, @status) redirect_to admin_account_status_path @@ -57,7 +59,8 @@ module Admin UpdateStatusService.new.call( @status, edit_status_account_id, - sensitive: true + sensitive: true, + bypass_validation: true ) log_action(:force_sensitive, @status) redirect_to admin_account_status_path @@ -68,7 +71,8 @@ module Admin UpdateStatusService.new.call( @status, edit_status_account_id, - spoiler_text: 'CW' + spoiler_text: 'CW', + bypass_validation: true ) log_action(:force_cw, @status) redirect_to admin_account_status_path diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 03392a8c79..0c161d6642 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -337,7 +337,7 @@ export function uploadCompose(files) { if (status === 200) { dispatch(uploadComposeSuccess(data, file)); - if (defaultSensitive && !spoiler) { + if (defaultSensitive && !spoiler && (media.size + i) === 0) { dispatch(changeComposeSpoilerness()); } } else if (status === 202) { diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js index 4edccd908f..906e384354 100644 --- a/app/javascript/mastodon/actions/importer/index.js +++ b/app/javascript/mastodon/actions/importer/index.js @@ -72,7 +72,7 @@ export function importFetchedStatuses(statuses) { processStatus(status.reblog); } - if (status.quote && status.quote.id) { + if (status.quote && status.quote.id && !getState().getIn(['statuses', status.id])) { processStatus(status.quote); } diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 62a5904736..28fbe9068e 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -629,7 +629,7 @@ export default function compose(state = initialState, action) { map.set('spoiler', true); map.set('spoiler_text', action.spoiler_text); } else { - map.set('spoiler', false); + map.set('spoiler', action.status.get('sensitive')); map.set('spoiler_text', ''); } diff --git a/app/javascript/styles/full-dark/diff.scss b/app/javascript/styles/full-dark/diff.scss index 1c98d95037..9483e7ecb6 100644 --- a/app/javascript/styles/full-dark/diff.scss +++ b/app/javascript/styles/full-dark/diff.scss @@ -1,6 +1,7 @@ input[type='text']:not(#cw-spoiler-input), input[type='search'], input[type='number'], +textarea[id='upload-modal__description'], input:not([type]) { background: $ui-base-color !important; color: $primary-text-color !important; @@ -24,6 +25,7 @@ input:not([type]) { color: $classic-secondary-color !important; } +.compose-form__warning, .modal-root__modal { background: lighten($classic-base-color, 12%); } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 01c397d974..ed763a4db7 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -4142,6 +4142,10 @@ a.status-card { .announcements { z-index: 1; position: relative; + + .announcements__pagination > * { + vertical-align: middle; + } } } diff --git a/app/models/admin/status_batch_action.rb b/app/models/admin/status_batch_action.rb index 99274c85b2..d295b01e28 100644 --- a/app/models/admin/status_batch_action.rb +++ b/app/models/admin/status_batch_action.rb @@ -83,7 +83,7 @@ class Admin::StatusBatchAction authorize([:admin, status], :update?) if target_account.local? - UpdateStatusService.new.call(status, representative_account.id, sensitive: true) + UpdateStatusService.new.call(status, representative_account.id, sensitive: true, bypass_validation: true) else status.update(sensitive: true) end @@ -119,7 +119,7 @@ class Admin::StatusBatchAction status_text = "#{status.spoiler_text}\n\n#{status_text}" if status.spoiler_text if target_account.local? - UpdateStatusService.new.call(status, representative_account.id, spoiler_text: 'CW', text: status_text) + UpdateStatusService.new.call(status, representative_account.id, spoiler_text: 'CW', text: status_text, bypass_validation: true) else status.update(spoiler_text: 'CW', text: status_text) end diff --git a/app/models/concerns/account/interactions.rb b/app/models/concerns/account/interactions.rb index d5c232336a..a8c9c1c5e7 100644 --- a/app/models/concerns/account/interactions.rb +++ b/app/models/concerns/account/interactions.rb @@ -186,6 +186,10 @@ module Account::Interactions active_relationships.exists?(target_account: other_account) end + def following_or_self?(other_account) + id == other_account.id || following?(other_account) + end + def following_anyone? active_relationships.exists? end diff --git a/app/models/instance_info.rb b/app/models/instance_info.rb index d3aa039721..98367f31ea 100644 --- a/app/models/instance_info.rb +++ b/app/models/instance_info.rb @@ -27,6 +27,7 @@ class InstanceInfo < ApplicationRecord meisskey misskey pleroma + rosekey sharkey ).freeze diff --git a/app/models/trends/tags.rb b/app/models/trends/tags.rb index 03707d6870..5b00fca7ec 100644 --- a/app/models/trends/tags.rb +++ b/app/models/trends/tags.rb @@ -13,6 +13,7 @@ class Trends::Tags < Trends::Base def register(status, at_time = Time.now.utc) return unless !status.reblog? && %i(public public_unlisted login).include?(status.visibility.to_sym) && !status.account.silenced? + return if !status.account.local? && DomainBlock.block_trends?(status.account.domain) status.tags.each do |tag| add(tag, status.account_id, at_time) if tag.usable? diff --git a/app/services/approve_appeal_service.rb b/app/services/approve_appeal_service.rb index 6de051edc4..40bb76b8a4 100644 --- a/app/services/approve_appeal_service.rb +++ b/app/services/approve_appeal_service.rb @@ -56,14 +56,14 @@ class ApproveAppealService < BaseService def undo_mark_statuses_as_sensitive! representative_account = Account.representative @strike.statuses.includes(:media_attachments).find_each do |status| - UpdateStatusService.new.call(status, representative_account.id, sensitive: false) if status.with_media? + UpdateStatusService.new.call(status, representative_account.id, sensitive: false, bypass_validation: true) if status.with_media? end end def undo_force_cw! representative_account = Account.representative @strike.statuses.includes(:media_attachments).find_each do |status| - UpdateStatusService.new.call(status, representative_account.id, spoiler_text: '') + UpdateStatusService.new.call(status, representative_account.id, spoiler_text: '', bypass_validation: true) if status.spoiler_text.present? end end diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 4663b9e631..0ec41fef03 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -248,12 +248,12 @@ class PostStatusService < BaseService def mention_to_stranger? return @mention_to_stranger if defined?(@mention_to_stranger) - @mention_to_stranger = @status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @account.id && !mentioned_account.following?(@account) } || - (@in_reply_to && @in_reply_to.account.id != @account.id && !@in_reply_to.account.following?(@account)) + @mention_to_stranger = @status.mentions.map(&:account).to_a.any? { |mentioned_account| !mentioned_account.following_or_self?(@account) } || + (@in_reply_to && !@in_reply_to.account.following_or_self?(@account)) end def reference_to_stranger? - referred_statuses.any? { |status| !status.account.following?(@account) } + referred_statuses.any? { |status| !status.account.following_or_self?(@account) } end def referred_statuses diff --git a/app/services/update_status_service.rb b/app/services/update_status_service.rb index 2a3bf5f179..3feec8c68d 100644 --- a/app/services/update_status_service.rb +++ b/app/services/update_status_service.rb @@ -84,6 +84,7 @@ class UpdateStatusService < BaseService end def validate_status! + return if @options[:bypass_validation] raise Mastodon::ValidationError, I18n.t('statuses.contains_ng_words') if Admin::NgWord.reject?("#{@options[:spoiler_text]}\n#{@options[:text]}") raise Mastodon::ValidationError, I18n.t('statuses.too_many_hashtags') if Admin::NgWord.hashtag_reject_with_extractor?(@options[:text] || '') raise Mastodon::ValidationError, I18n.t('statuses.too_many_mentions') if Admin::NgWord.mention_reject_with_extractor?(@options[:text] || '') @@ -91,10 +92,13 @@ class UpdateStatusService < BaseService end def validate_status_mentions! + return if @options[:bypass_validation] raise Mastodon::ValidationError, I18n.t('statuses.contains_ng_words') if (mention_to_stranger? || reference_to_stranger?) && Setting.stranger_mention_from_local_ng && Admin::NgWord.stranger_mention_reject?("#{@options[:spoiler_text]}\n#{@options[:text]}") end def validate_status_ng_rules! + return if @options[:bypass_validation] + result = check_invalid_status_for_ng_rule! @status.account, reaction_type: 'edit', spoiler_text: @options.key?(:spoiler_text) ? (@options[:spoiler_text] || '') : @status.spoiler_text, @@ -119,12 +123,12 @@ class UpdateStatusService < BaseService end def mention_to_stranger? - @status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @status.account.id && !mentioned_account.following?(@status.account) } || - (@status.thread.present? && @status.thread.account.id != @status.account.id && !@status.thread.account.following?(@status.account)) + @status.mentions.map(&:account).to_a.any? { |mentioned_account| !mentioned_account.following_or_self?(@status.account) } || + (@status.thread.present? && !@status.thread.account.following_or_self?(@status.account)) end def reference_to_stranger? - referred_statuses.any? { |status| !status.account.following?(@status.account) } + referred_statuses.any? { |status| !status.account.following_or_self?(@status.account) } end def referred_statuses diff --git a/app/workers/activitypub/fetch_instance_info_worker.rb b/app/workers/activitypub/fetch_instance_info_worker.rb index 1b25c2a551..fb71106e25 100644 --- a/app/workers/activitypub/fetch_instance_info_worker.rb +++ b/app/workers/activitypub/fetch_instance_info_worker.rb @@ -30,7 +30,7 @@ class ActivityPub::FetchInstanceInfoWorker update_info!(link) true - rescue Mastodon::UnexpectedResponseError + rescue true end diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 06563f3501..e6c3f21bd9 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -9,13 +9,13 @@ module Mastodon end def kmyblue_minor - 0 + 4 end def kmyblue_flag # 'LTS' - 'dev' - # nil + # 'dev' + nil end def major diff --git a/spec/models/trends/tags_spec.rb b/spec/models/trends/tags_spec.rb index f2818fca87..91ca91f078 100644 --- a/spec/models/trends/tags_spec.rb +++ b/spec/models/trends/tags_spec.rb @@ -23,6 +23,33 @@ RSpec.describe Trends::Tags do end end + describe '#register' do + let(:tag) { Fabricate(:tag, usable: true) } + let(:account) { Fabricate(:account) } + let(:status) { Fabricate(:status, account: account, tags: [tag], created_at: at_time, updated_at: at_time) } + + it 'records history' do + subject.register(status, at_time) + expect(tag.history.get(at_time).accounts).to eq 1 + expect(tag.history.get(at_time).uses).to eq 1 + expect(subject.send(:recently_used_ids, at_time)).to eq [tag.id] + end + + context 'when account is rejected appending trends' do + let(:account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/actor') } + + before do + Fabricate(:domain_block, domain: 'example.com', block_trends: true, severity: :noop) + end + + it 'does not record history' do + subject.register(status, at_time) + expect(tag.history.get(at_time).accounts).to eq 0 + expect(tag.history.get(at_time).uses).to eq 0 + end + end + end + describe '#query' do it 'returns a composable query scope' do expect(subject.query).to be_a Trends::Query diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb index 659cecb4e0..aab50cf7c9 100644 --- a/spec/services/post_status_service_spec.rb +++ b/spec/services/post_status_service_spec.rb @@ -663,6 +663,17 @@ RSpec.describe PostStatusService, type: :service do expect(status.text).to eq text end + it 'does not hit ng words for mention to self' do + account = Fabricate(:account, username: 'cool', domain: nil) + text = 'ng word test @cool' + Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save + + status = subject.call(account, text: text) + + expect(status).to be_persisted + expect(status.text).to eq text + end + it 'hit ng words for reply' do account = Fabricate(:account) text = 'ng word test' @@ -721,6 +732,18 @@ RSpec.describe PostStatusService, type: :service do expect(status.text).to eq text end + it 'does not hit ng words for reference to self' do + target_status = Fabricate(:status) + account = target_status.account + text = "ng word test BT: #{ActivityPub::TagManager.instance.uri_for(target_status)}" + Form::AdminSettings.new(ng_words_for_stranger_mention: 'test', stranger_mention_from_local_ng: '1').save + + status = subject.call(account, text: text) + + expect(status).to be_persisted + expect(status.text).to eq text + end + it 'using hashtag under limit' do account = Fabricate(:account) text = '#a #b' diff --git a/spec/services/update_status_service_spec.rb b/spec/services/update_status_service_spec.rb index 89184a4f4c..13dfc46379 100644 --- a/spec/services/update_status_service_spec.rb +++ b/spec/services/update_status_service_spec.rb @@ -277,6 +277,13 @@ RSpec.describe UpdateStatusService, type: :service do expect { subject.call(status, status.account_id, text: text) }.to raise_error(Mastodon::ValidationError) end + it 'bypass ng words' do + text = 'ng word test' + Form::AdminSettings.new(ng_words: 'test').save + + expect { subject.call(status, status.account_id, text: text, bypass_validation: true) }.to_not raise_error + end + it 'not hit ng words' do text = 'ng word aiueo' Form::AdminSettings.new(ng_words: 'test').save