Skip to content

Commit

Permalink
Add: ローカル公開の検索許可版
Browse files Browse the repository at this point in the history
  • Loading branch information
kmycode committed Oct 5, 2023
1 parent deb8642 commit 04e506f
Show file tree
Hide file tree
Showing 24 changed files with 210 additions and 25 deletions.
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? && !@status.public_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? && !@status.reblog? && !@account.silenced?
@status.unlisted_visibility? && @status.compute_searchability == 'public' && !@status.reblog? && !@account.silenced?
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

0 comments on commit 04e506f

Please sign in to comment.