From 059945c97cb9a9f3cbddda729f499b44800bdc68 Mon Sep 17 00:00:00 2001 From: abcang Date: Fri, 27 Sep 2019 22:23:30 +0900 Subject: Improve status pin query (#11972) --- app/controllers/api/v1/accounts/statuses_controller.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/controllers/api') diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb index 0787cd636..333db9618 100644 --- a/app/controllers/api/v1/accounts/statuses_controller.rb +++ b/app/controllers/api/v1/accounts/statuses_controller.rb @@ -57,6 +57,8 @@ class Api::V1::Accounts::StatusesController < Api::BaseController end def pinned_scope + return Status.none if @account.blocking?(current_account) + @account.pinned_statuses end -- cgit From 18b451c0e6cf6a927a22084f94b423982de0ee8b Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 27 Sep 2019 21:13:51 +0200 Subject: Change silences to always require approval on follow (#11975) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change silenced accounts to require approval on follow * Also require approval for follows by people explicitly muted by target accounts * Do not auto-accept silenced or muted accounts when switching from locked to unlocked * Add `follow_requests_count` to verify_credentials * Show “Follow requests” menu item if needed even if account is locked * Add tests * Correctly reflect that follow requests weren't auto-accepted when local account is silenced * Accept follow requests from user-muted accounts to avoid leaking mutes --- app/controllers/api/v1/accounts_controller.rb | 2 +- .../mastodon/features/getting_started/index.js | 8 ++--- app/lib/activitypub/activity/follow.rb | 2 +- .../rest/credential_account_serializer.rb | 1 + app/services/follow_service.rb | 2 +- app/services/update_account_service.rb | 4 ++- spec/lib/activitypub/activity/follow_spec.rb | 30 +++++++++++++++++ spec/services/follow_service_spec.rb | 27 +++++++++++++++ spec/services/update_account_service_spec.rb | 38 ++++++++++++++++++++++ 9 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 spec/services/update_account_service_spec.rb (limited to 'app/controllers/api') diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index b306e8e8c..c12e1c12e 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -33,7 +33,7 @@ class Api::V1::AccountsController < Api::BaseController def follow FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs)) - options = @account.locked? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } } + options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, requested_map: { @account.id => false } } render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options) end diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index f6d90580b..67ec7665b 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -77,16 +77,14 @@ class GettingStarted extends ImmutablePureComponent { }; componentDidMount () { - const { myAccount, fetchFollowRequests, multiColumn } = this.props; + const { fetchFollowRequests, multiColumn } = this.props; if (!multiColumn && window.innerWidth >= NAVIGATION_PANEL_BREAKPOINT) { this.context.router.history.replace('/timelines/home'); return; } - if (myAccount.get('locked')) { - fetchFollowRequests(); - } + fetchFollowRequests(); } render () { @@ -134,7 +132,7 @@ class GettingStarted extends ImmutablePureComponent { height += 48*3; - if (myAccount.get('locked')) { + if (myAccount.get('locked') || unreadFollowRequests > 0) { navItems.push(); height += 48; } diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb index 28f1da19f..ec92f4255 100644 --- a/app/lib/activitypub/activity/follow.rb +++ b/app/lib/activitypub/activity/follow.rb @@ -21,7 +21,7 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity follow_request = FollowRequest.create!(account: @account, target_account: target_account, uri: @json['id']) - if target_account.locked? + if target_account.locked? || @account.silenced? NotifyService.new.call(target_account, follow_request) else AuthorizeFollowService.new.call(@account, target_account) diff --git a/app/serializers/rest/credential_account_serializer.rb b/app/serializers/rest/credential_account_serializer.rb index fb195eb07..be0d763dc 100644 --- a/app/serializers/rest/credential_account_serializer.rb +++ b/app/serializers/rest/credential_account_serializer.rb @@ -12,6 +12,7 @@ class REST::CredentialAccountSerializer < REST::AccountSerializer language: user.setting_default_language, note: object.note, fields: object.fields.map(&:to_h), + follow_requests_count: FollowRequest.where(target_account: object).limit(40).count, } end end diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb index 101acdaf9..1941c2e2d 100644 --- a/app/services/follow_service.rb +++ b/app/services/follow_service.rb @@ -30,7 +30,7 @@ class FollowService < BaseService ActivityTracker.increment('activity:interactions') - if target_account.locked? || target_account.activitypub? + if target_account.locked? || source_account.silenced? || target_account.activitypub? request_follow(source_account, target_account, reblogs: reblogs) elsif target_account.local? direct_follow(source_account, target_account, reblogs: reblogs) diff --git a/app/services/update_account_service.rb b/app/services/update_account_service.rb index 01756a73d..ebf24be37 100644 --- a/app/services/update_account_service.rb +++ b/app/services/update_account_service.rb @@ -20,7 +20,9 @@ class UpdateAccountService < BaseService private def authorize_all_follow_requests(account) - AuthorizeFollowWorker.push_bulk(FollowRequest.where(target_account: account).select(:account_id, :target_account_id)) do |req| + follow_requests = FollowRequest.where(target_account: account) + follow_requests = follow_requests.select { |req| !req.account.silenced? } + AuthorizeFollowWorker.push_bulk(follow_requests) do |req| [req.account_id, req.target_account_id] end end diff --git a/spec/lib/activitypub/activity/follow_spec.rb b/spec/lib/activitypub/activity/follow_spec.rb index 6bbacdbe6..05112cc18 100644 --- a/spec/lib/activitypub/activity/follow_spec.rb +++ b/spec/lib/activitypub/activity/follow_spec.rb @@ -31,6 +31,36 @@ RSpec.describe ActivityPub::Activity::Follow do end end + context 'silenced account following an unlocked account' do + before do + sender.touch(:silenced_at) + subject.perform + end + + it 'does not create a follow from sender to recipient' do + expect(sender.following?(recipient)).to be false + end + + it 'creates a follow request' do + expect(sender.requested?(recipient)).to be true + end + end + + context 'unlocked account muting the sender' do + before do + recipient.mute!(sender) + subject.perform + end + + it 'creates a follow from sender to recipient' do + expect(sender.following?(recipient)).to be true + end + + it 'does not create a follow request' do + expect(sender.requested?(recipient)).to be false + end + end + context 'locked account' do before do recipient.update(locked: true) diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb index 86c85293e..ae863a9f0 100644 --- a/spec/services/follow_service_spec.rb +++ b/spec/services/follow_service_spec.rb @@ -30,6 +30,33 @@ RSpec.describe FollowService, type: :service do end end + describe 'unlocked account, from silenced account' do + let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + + before do + sender.touch(:silenced_at) + subject.call(sender, bob.acct) + end + + it 'creates a follow request with reblogs' do + expect(FollowRequest.find_by(account: sender, target_account: bob, show_reblogs: true)).to_not be_nil + end + end + + describe 'unlocked account, from a muted account' do + let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + + before do + bob.mute!(sender) + subject.call(sender, bob.acct) + end + + it 'creates a following relation with reblogs' do + expect(sender.following?(bob)).to be true + expect(sender.muting_reblogs?(bob)).to be false + end + end + describe 'unlocked account' do let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } diff --git a/spec/services/update_account_service_spec.rb b/spec/services/update_account_service_spec.rb new file mode 100644 index 000000000..960b26891 --- /dev/null +++ b/spec/services/update_account_service_spec.rb @@ -0,0 +1,38 @@ +require 'rails_helper' + +RSpec.describe UpdateAccountService, type: :service do + subject { UpdateAccountService.new } + + describe 'switching form locked to unlocked accounts' do + let(:account) { Fabricate(:account, locked: true) } + let(:alice) { Fabricate(:user, email: 'alice@example.com', account: Fabricate(:account, username: 'alice')).account } + let(:bob) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account } + let(:eve) { Fabricate(:user, email: 'eve@example.com', account: Fabricate(:account, username: 'eve')).account } + + before do + bob.touch(:silenced_at) + account.mute!(eve) + + FollowService.new.call(alice, account) + FollowService.new.call(bob, account) + FollowService.new.call(eve, account) + + subject.call(account, { locked: false }) + end + + it 'auto-accepts pending follow requests' do + expect(alice.following?(account)).to be true + expect(alice.requested?(account)).to be false + end + + it 'does not auto-accept pending follow requests from silenced users' do + expect(bob.following?(account)).to be false + expect(bob.requested?(account)).to be true + end + + it 'auto-accepts pending follow requests from muted users so as to not leak mute' do + expect(eve.following?(account)).to be true + expect(eve.requested?(account)).to be false + end + end +end -- cgit From ab33c4df942ec3fdc4d891f3db7ac8cdd3436945 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 28 Sep 2019 01:02:21 +0200 Subject: Add `exclude_unreviewed` param to `GET /api/v2/search` REST API (#11977) Make it so normal search returns even unreviewed matches, but autosuggestions do not. Fix #11960 --- app/controllers/api/v2/search_controller.rb | 2 +- app/javascript/mastodon/actions/compose.js | 1 + app/models/tag.rb | 13 ++++++------- app/services/search_service.rb | 3 ++- app/services/tag_search_service.rb | 16 ++++++++++------ spec/services/search_service_spec.rb | 4 ++-- 6 files changed, 22 insertions(+), 17 deletions(-) (limited to 'app/controllers/api') diff --git a/app/controllers/api/v2/search_controller.rb b/app/controllers/api/v2/search_controller.rb index c14cd22d7..cbd9b551d 100644 --- a/app/controllers/api/v2/search_controller.rb +++ b/app/controllers/api/v2/search_controller.rb @@ -22,7 +22,7 @@ class Api::V2::SearchController < Api::BaseController params[:q], current_account, limit_param(RESULTS_LIMIT), - search_params.merge(resolve: truthy_param?(:resolve)) + search_params.merge(resolve: truthy_param?(:resolve), exclude_unreviewed: truthy_param?(:exclude_unreviewed)) ) end diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 79d64dc3e..8e7906c73 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -369,6 +369,7 @@ const fetchComposeSuggestionsTags = throttle((dispatch, getState, token) => { q: token.slice(1), resolve: false, limit: 4, + exclude_unreviewed: true, }, }).then(({ data }) => { dispatch(readyComposeSuggestionsTags(token, data.hashtags)); diff --git a/app/models/tag.rb b/app/models/tag.rb index b52b9bc9f..9aca3983f 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -124,16 +124,15 @@ class Tag < ApplicationRecord end end - def search_for(term, limit = 5, offset = 0) + def search_for(term, limit = 5, offset = 0, options = {}) normalized_term = normalize(term.strip).mb_chars.downcase.to_s pattern = sanitize_sql_like(normalized_term) + '%' + query = Tag.listable.where(arel_table[:name].lower.matches(pattern)) + query = query.where(arel_table[:name].lower.eq(normalized_term).or(arel_table[:reviewed_at].not_eq(nil))) if options[:exclude_unreviewed] - Tag.listable - .where(arel_table[:name].lower.matches(pattern)) - .where(arel_table[:name].lower.eq(normalized_term).or(arel_table[:reviewed_at].not_eq(nil))) - .order(Arel.sql('length(name) ASC, name ASC')) - .limit(limit) - .offset(offset) + query.order(Arel.sql('length(name) ASC, name ASC')) + .limit(limit) + .offset(offset) end def find_normalized(name) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index a5ba5dd11..3a498dcf4 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -60,7 +60,8 @@ class SearchService < BaseService TagSearchService.new.call( @query, limit: @limit, - offset: @offset + offset: @offset, + exclude_unreviewed: @options[:exclude_unreviewed] ) end diff --git a/app/services/tag_search_service.rb b/app/services/tag_search_service.rb index 47b0e876e..b78d65625 100644 --- a/app/services/tag_search_service.rb +++ b/app/services/tag_search_service.rb @@ -2,11 +2,12 @@ class TagSearchService < BaseService def call(query, options = {}) - @query = query.strip.gsub(/\A#/, '') - @offset = options[:offset].to_i - @limit = options[:limit].to_i + @query = query.strip.gsub(/\A#/, '') + @offset = options.delete(:offset).to_i + @limit = options.delete(:limit).to_i + @options = options - results = from_elasticsearch if Chewy.enabled? + results = from_elasticsearch if Chewy.enabled? results ||= from_database results @@ -72,12 +73,15 @@ class TagSearchService < BaseService }, } - TagsIndex.query(query).filter(filter).limit(@limit).offset(@offset).objects.compact + definition = TagsIndex.query(query) + definition = definition.filter(filter) if @options[:exclude_unreviewed] + + definition.limit(@limit).offset(@offset).objects.compact rescue Faraday::ConnectionFailed, Parslet::ParseFailed nil end def from_database - Tag.search_for(@query, @limit, @offset) + Tag.search_for(@query, @limit, @offset, @options) end end diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index ade306ed2..739bb9cf5 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -77,10 +77,10 @@ describe SearchService, type: :service do it 'includes the tag in the results' do query = '#tag' tag = Tag.new - allow(Tag).to receive(:search_for).with('tag', 10, 0).and_return([tag]) + allow(Tag).to receive(:search_for).with('tag', 10, 0, exclude_unreviewed: nil).and_return([tag]) results = subject.call(query, nil, 10) - expect(Tag).to have_received(:search_for).with('tag', 10, 0) + expect(Tag).to have_received(:search_for).with('tag', 10, 0, exclude_unreviewed: nil) expect(results).to eq empty_results.merge(hashtags: [tag]) end it 'does not include tag when starts with @ character' do -- cgit