Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add: 検索許可「ローカルとフォロワー」 #60

Merged
merged 1 commit into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { IconButton } from '../../../components/icon_button';
const messages = defineMessages({
public_short: { id: 'searchability.public.short', defaultMessage: 'Public' },
public_long: { id: 'searchability.public.long', defaultMessage: 'Anyone can find' },
public_unlisted_short: { id: 'searchability.public_unlisted.short', defaultMessage: 'Public unlisted' },
public_unlisted_long: { id: 'searchability.public_unlisted.long', defaultMessage: 'Local users and followers can find' },
private_short: { id: 'searchability.unlisted.short', defaultMessage: 'Followers' },
private_long: { id: 'searchability.unlisted.long', defaultMessage: 'Your followers can find' },
direct_short: { id: 'searchability.private.short', defaultMessage: 'Reactionners' },
Expand Down Expand Up @@ -223,6 +225,7 @@ class SearchabilityDropdown extends PureComponent {

this.options = [
{ icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) },
{ icon: 'cloud', value: 'public_unlisted', text: formatMessage(messages.public_unlisted_short), meta: formatMessage(messages.public_unlisted_long) },
{ icon: 'unlock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) },
{ icon: 'lock', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) },
{ icon: 'at', value: 'limited', text: formatMessage(messages.limited_short), meta: formatMessage(messages.limited_long) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const messages = defineMessages({
personal_short: { id: 'privacy.personal.short', defaultMessage: 'Yourself only' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
searchability_public_short: { id: 'searchability.public.short', defaultMessage: 'Public' },
searchability_public_unlisted_short: { id: 'searchability.public_unlisted.short', defaultMessage: 'Public unlisted' },
searchability_private_short: { id: 'searchability.unlisted.short', defaultMessage: 'Followers' },
searchability_direct_short: { id: 'searchability.private.short', defaultMessage: 'Reactionners' },
searchability_limited_short: { id: 'searchability.direct.short', defaultMessage: 'Self only' },
Expand Down Expand Up @@ -270,6 +271,7 @@ class DetailedStatus extends ImmutablePureComponent {

const searchabilityIconInfo = {
'public': { icon: 'globe', text: intl.formatMessage(messages.searchability_public_short) },
'public_unlisted': { icon: 'cloud', text: intl.formatMessage(messages.searchability_public_unlisted_short) },
'private': { icon: 'unlock', text: intl.formatMessage(messages.searchability_private_short) },
'direct': { icon: 'lock', text: intl.formatMessage(messages.searchability_direct_short) },
'limited': { icon: 'at', text: intl.formatMessage(messages.searchability_limited_short) },
Expand Down
2 changes: 2 additions & 0 deletions app/javascript/mastodon/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,8 @@
"searchability.private.short": "Reactionners",
"searchability.public.long": "Anyone can find",
"searchability.public.short": "Everyone",
"searchability.public_unlisted.long": "Local users and followers can find",
"searchability.public_unlisted.short": "Local and followers",
"searchability.unlisted.long": "Your followers and reactionners can find",
"searchability.unlisted.short": "Followers and reactionners",
"search_popout.domain": "domain",
Expand Down
2 changes: 2 additions & 0 deletions app/javascript/mastodon/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,8 @@
"searchability.private.short": "反応者のみ",
"searchability.public.long": "この投稿は誰でも検索できます",
"searchability.public.short": "誰でも",
"searchability.public_unlisted.long": "ローカルユーザーとフォロワーが検索できます",
"searchability.public_unlisted.short": "ローカルとフォロワー",
"searchability.unlisted.long": "この投稿はあなたのフォロワーと反応者だけが検索できます",
"searchability.unlisted.short": "フォロワーと反応者",
"search_popout.domain": "ドメイン",
Expand Down
2 changes: 1 addition & 1 deletion app/lib/account_statuses_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def results
scope.merge!(no_reblogs_scope) if exclude_reblogs?
scope.merge!(hashtag_scope) if tagged?

available_searchabilities = [:public, :unlisted, :private, :direct, :limited, nil]
available_searchabilities = [:public, :public_unlisted, :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
Expand Down
2 changes: 1 addition & 1 deletion app/lib/importer/statuses_index_importer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def import!
to_index.map do |object|
# This is unlikely to happen, but the post may have been
# un-interacted with since it was queued for indexing
if object.searchable_by.empty? && %w(public private).exclude?(object.searchability)
if object.searchable_by.empty? && %w(public public_unlisted private).exclude?(object.searchability)
deleted += 1
{ delete: { _id: object.id } }
else
Expand Down
12 changes: 6 additions & 6 deletions app/lib/search_query_transformer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ def default_filter
public_index,
searchability_limited,
]
definition_should << searchability_public if %i(public).include?(@searchability)
definition_should << searchability_private if %i(public unlisted private).include?(@searchability)
definition_should << searchable_by_me if %i(public unlisted private direct).include?(@searchability)
definition_should << self_posts if %i(public unlisted private direct).exclude?(@searchability)
definition_should << searchability_public if %i(public public_unlisted).include?(@searchability)
definition_should << searchability_private if %i(public public_unlisted unlisted private).include?(@searchability)
definition_should << searchable_by_me if %i(public public_unlisted unlisted private direct).include?(@searchability)
definition_should << self_posts if %i(public public_unlisted unlisted private direct).exclude?(@searchability)

{
bool: {
Expand Down Expand Up @@ -199,8 +199,8 @@ def searchability_limited
def following_account_ids
return @following_account_ids if defined?(@following_account_ids)

account_exists_sql = Account.where('accounts.id = follows.target_account_id').where(searchability: %w(public private)).reorder(nil).select(1).to_sql
status_exists_sql = Status.where('statuses.account_id = follows.target_account_id').where(reblog_of_id: nil).where(searchability: %w(public private)).reorder(nil).select(1).to_sql
account_exists_sql = Account.where('accounts.id = follows.target_account_id').where(searchability: %w(public public_unlisted private)).reorder(nil).select(1).to_sql
status_exists_sql = Status.where('statuses.account_id = follows.target_account_id').where(reblog_of_id: nil).where(searchability: %w(public public_unlisted private)).reorder(nil).select(1).to_sql
following_accounts = Follow.where(account_id: @options[:current_account].id).merge(Account.where("EXISTS (#{account_exists_sql})").or(Account.where("EXISTS (#{status_exists_sql})")))
@following_account_ids = following_accounts.pluck(:target_account_id)
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/concerns/status_search_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module StatusSearchConcern

included do
scope :indexable, -> { without_reblogs.where(visibility: [:public, :login], searchability: nil).joins(:account).where(account: { indexable: true }) }
scope :remote_dynamic_searchability, -> { remote.where(searchability: [:public, :private]) }
scope :remote_dynamic_searchability, -> { remote.where(searchability: [:public, :public_unlisted, :private]) }
end

def searchable_by
Expand Down
25 changes: 18 additions & 7 deletions app/models/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class Status < ApplicationRecord
scope :without_replies, -> { where('statuses.reply = FALSE OR statuses.in_reply_to_account_id = statuses.account_id') }
scope :without_reblogs, -> { where(statuses: { reblog_of_id: nil }) }
scope :with_public_visibility, -> { where(visibility: [:public, :public_unlisted, :login]) }
scope :with_public_search_visibility, -> { merge(where(visibility: [:public, :public_unlisted, :login]).or(Status.where(searchability: :public))) }
scope :with_public_search_visibility, -> { merge(where(visibility: [:public, :public_unlisted, :login]).or(Status.where(searchability: [:public, :public_unlisted]))) }
scope :with_global_timeline_visibility, -> { where(visibility: [:public, :login]) }
scope :tagged_with, ->(tag_ids) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag_ids }) }
scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced_at: nil }) }
Expand Down Expand Up @@ -442,19 +442,26 @@ def requires_review_notification?

def compute_searchability
local = account.local?
check_searchability = public_unlisted_searchability? ? 'public' : searchability

return 'private' if public_searchability? && account.silenced?
return 'private' if %w(public public_unlisted).include?(check_searchability) && account.silenced?
return 'direct' if unsupported_searchability?
return searchability if local && !searchability.nil?
return 'direct' if local || [:public, :private, :direct, :limited].exclude?(account.searchability.to_sym)
return check_searchability if local && !check_searchability.nil?
return 'direct' if local || %i(public private direct limited).exclude?(account.searchability.to_sym)

account_searchability = Status.searchabilities[account.searchability]
status_searchability = Status.searchabilities[searchability.nil? ? 'direct' : searchability]
status_searchability = Status.searchabilities[check_searchability.nil? ? 'direct' : check_searchability]
Status.searchabilities.invert.fetch([account_searchability, status_searchability].max) || 'direct'
end

def compute_searchability_activitypub
return 'private' if public_unlisted_visibility? && public_searchability?
return 'private' if public_unlisted_searchability?

compute_searchability
end

def compute_searchability_local
return 'public_unlisted' if public_unlisted_searchability?

compute_searchability
end
Expand All @@ -477,6 +484,10 @@ def selectable_reblog_visibilities
end

def selectable_searchabilities
searchabilities.keys - %w(unsupported)
end

def selectable_searchabilities_for_search
searchabilities.keys - %w(public_unlisted unsupported)
end

Expand Down Expand Up @@ -620,7 +631,7 @@ def set_searchability
elsif visibility == 'limited'
:limited
elsif visibility == 'private'
searchability == 'public' ? :private : searchability
searchability == 'public' || searchability == 'public_unlisted' ? :private : searchability
elsif visibility == 'direct'
searchability == 'limited' ? :limited : :direct
else
Expand Down
3 changes: 2 additions & 1 deletion app/models/trends/statuses.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ def klass
private

def eligible?(status)
(status.searchability.nil? || status.public_searchability?) && (status.public_visibility? || status.public_unlisted_visibility?) &&
(status.searchability.nil? || status.compute_searchability == 'public') &&
(status.public_visibility? || status.public_unlisted_visibility?) &&
status.account.discoverable? && !status.account.silenced? && status.spoiler_text.blank? && (!status.sensitive? || status.media_attachments.none?) &&
!status.reply? && valid_locale?(status.language)
end
Expand Down
2 changes: 1 addition & 1 deletion app/serializers/rest/status_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def limited_scope
end

def searchability
object.compute_searchability
object.compute_searchability_local
end

def sensitive
Expand Down
8 changes: 5 additions & 3 deletions app/services/delivery_antenna_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ def delivery!
next if antenna.exclude_accounts&.include?(@status.account_id)
next if antenna.exclude_domains&.include?(domain)
next if antenna.exclude_tags&.any? { |tag_id| tag_ids.include?(tag_id) }
next if @status.unlisted_visibility? && [email protected]_searchability? && follower_ids.exclude?(antenna.account_id)
next if @status.unlisted_visibility? && @status.public_searchability? && follower_ids.exclude?(antenna.account_id) && antenna.any_keywords && antenna.any_tags

searchability = @status.compute_searchability
next if @status.unlisted_visibility? && searchability != 'public' && follower_ids.exclude?(antenna.account_id)
next if @status.unlisted_visibility? && searchability == 'public' && follower_ids.exclude?(antenna.account_id) && antenna.any_keywords && antenna.any_tags

collection.push(antenna)
end
Expand Down Expand Up @@ -121,7 +123,7 @@ def followers_only?
when :public, :public_unlisted, :login, :limited
false
when :unlisted
!@status.public_searchability?
@status.compute_searchability != 'public'
else
true
end
Expand Down
2 changes: 1 addition & 1 deletion app/services/fan_out_on_write_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,6 @@ def broadcastable_unlisted?
end

def broadcastable_unlisted2?
@status.unlisted_visibility? && @status.public_searchability? && [email protected]? && [email protected]?
@status.unlisted_visibility? && @status.compute_searchability == 'public' && [email protected]? && [email protected]?
end
end
4 changes: 3 additions & 1 deletion app/services/post_status_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def preprocess_attributes!
@visibility = :public_unlisted if @visibility&.to_sym == :public && !@options[:force_visibility] && !@options[:application]&.superapp && @account.user&.setting_public_post_to_unlisted && Setting.enable_public_unlisted_visibility
@limited_scope = @options[:visibility]&.to_sym if @visibility == :limited
@searchability = searchability
@searchability = :private if @account.silenced? && @searchability&.to_sym == :public
@searchability = :private if @account.silenced? && %i(public public_unlisted).include?(@searchability&.to_sym)
@markdown = @options[:markdown] || false
@scheduled_at = @options[:scheduled_at]&.to_datetime
@scheduled_at = nil if scheduled_in_the_past?
Expand Down Expand Up @@ -129,6 +129,8 @@ def searchability
case @options[:searchability]&.to_sym
when :public
case @visibility&.to_sym when :public, :public_unlisted, :login, :unlisted then :public when :private then :private else :direct end
when :public_unlisted
case @visibility&.to_sym when :public, :public_unlisted, :login, :unlisted then :public_unlisted when :private then :private else :direct end
when :private
case @visibility&.to_sym when :public, :public_unlisted, :login, :unlisted, :private then :private else :direct end
when :direct
Expand Down
2 changes: 1 addition & 1 deletion app/views/settings/preferences/reaching/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

.fields-row
.fields-group.fields-row__column.fields-row__column-12
= ff.input :default_searchability_of_search, collection: Status.selectable_searchabilities, wrapper: :with_label, kmyblue: true, include_blank: false, label_method: lambda { |searchability| safe_join([I18n.t("statuses.searchabilities.#{searchability}"), I18n.t("statuses.searchabilities.#{searchability}_search_long")], ' - ') }, required: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_searchability_of_search')
= ff.input :default_searchability_of_search, collection: Status.selectable_searchabilities_for_search, wrapper: :with_label, kmyblue: true, include_blank: false, label_method: lambda { |searchability| safe_join([I18n.t("statuses.searchabilities.#{searchability}"), I18n.t("statuses.searchabilities.#{searchability}_search_long")], ' - ') }, required: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_searchability_of_search')

.fields-group
= ff.input :use_public_index, wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_use_public_index')
Expand Down
2 changes: 2 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1836,6 +1836,8 @@ en:
public: Public
public_long: Anyone can find
public_search_long: You can search all posts permitted to search
public_unlisted: Local and followers
public_unlisted_long: Local users and followers can find
show_more: Show more
show_newer: Show newer
show_older: Show older
Expand Down
2 changes: 2 additions & 0 deletions config/locales/ja.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1814,6 +1814,8 @@ ja:
public: 誰でも
public_long: この投稿は誰でも検索できます
public_search_long: 検索が許可された全ての投稿が検索できます
public_unlisted: ローカルとフォロワー
public_unlisted_long: ローカル・フォロワー・反応者のみが検索できます
show_more: もっと見る
show_newer: 新しいものを表示
show_older: 古いものを表示
Expand Down
16 changes: 16 additions & 0 deletions spec/lib/status_reach_finder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@
end
end

context 'when misskey with public_unlisted searchability' do
let(:sender_software) { 'misskey' }
let(:searchability) { :public_unlisted }

it 'send status without setting' do
expect(subject.inboxes).to include 'https://foo.bar/inbox'
expect(subject.inboxes_for_misskey).to_not include 'https://foo.bar/inbox'
end

it 'send status with setting' do
alice.user.settings.update(reject_unlisted_subscription: 'true')
expect(subject.inboxes).to_not include 'https://foo.bar/inbox'
expect(subject.inboxes_for_misskey).to include 'https://foo.bar/inbox'
end
end

context 'when misskey with public searchability' do
let(:sender_software) { 'misskey' }

Expand Down
27 changes: 27 additions & 0 deletions spec/models/status_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@
end
end

context 'when public-public_unlisted but silenced' do
let(:silenced_at) { Time.now.utc }
let(:status_searchability) { :public_unlisted }

it 'returns private' do
expect(subject.compute_searchability).to eq 'private'
end
end

context 'when public-private' do
let(:status_searchability) { :private }

Expand Down Expand Up @@ -215,6 +224,24 @@
expect(subject.compute_searchability).to eq 'public'
end
end

context 'when public-public_unlisted of local account' do
let(:account_searchability) { :public }
let(:account_domain) { nil }
let(:status_searchability) { :public_unlisted }

it 'returns public' do
expect(subject.compute_searchability).to eq 'public'
end

it 'returns public_unlisted for local' do
expect(subject.compute_searchability_local).to eq 'public_unlisted'
end

it 'returns private for activitypub' do
expect(subject.compute_searchability_activitypub).to eq 'private'
end
end
end

describe '#quote' do
Expand Down
18 changes: 18 additions & 0 deletions spec/models/tag_feed_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,36 @@
expect(results).to include status_tagged_with_cats
end

it 'unlisted/public_unlisted_searchability post returns' do
status_tagged_with_cats.update(visibility: :unlisted, searchability: :public_unlisted)
results = described_class.new(tag_cats, nil).get(20)
expect(results).to include status_tagged_with_cats
end

it 'unlisted/public_searchability post returns with account' do
status_tagged_with_cats.update(visibility: :unlisted, searchability: :public)
results = described_class.new(tag_cats, account).get(20)
expect(results).to include status_tagged_with_cats
end

it 'unlisted/public_unlisted_searchability post returns with account' do
status_tagged_with_cats.update(visibility: :unlisted, searchability: :public_unlisted)
results = described_class.new(tag_cats, account).get(20)
expect(results).to include status_tagged_with_cats
end

it 'private post not returns' do
status_tagged_with_cats.update(visibility: :private, searchability: :public)
results = described_class.new(tag_cats, nil).get(20)
expect(results).to_not include status_tagged_with_cats
end

it 'private, public_unlisted post not returns' do
status_tagged_with_cats.update(visibility: :private, searchability: :public_unlisted)
results = described_class.new(tag_cats, nil).get(20)
expect(results).to_not include status_tagged_with_cats
end

it 'private post not returns with account' do
status_tagged_with_cats.update(visibility: :private, searchability: :public)
results = described_class.new(tag_cats, account).get(20)
Expand Down
Loading