forked from fedi/mastodon
Add basic support for group actors (#12071)
* Show badge on group actor in WebUI * Do not notify in case of by following group actor * If you mention group actor, also mention group actor followers * Relax characters that can be used in username (same as Application) * Revert "Relax characters that can be used in username (same as Application)" This reverts commit 7e10a137b878d0db1b5252c52106faef5e09ca4b. * Delete display_name method
This commit is contained in:
parent
f92ed32df4
commit
f43f1e0184
|
@ -4,6 +4,74 @@ module StatusesHelper
|
||||||
EMBEDDED_CONTROLLER = 'statuses'
|
EMBEDDED_CONTROLLER = 'statuses'
|
||||||
EMBEDDED_ACTION = 'embed'
|
EMBEDDED_ACTION = 'embed'
|
||||||
|
|
||||||
|
def account_action_button(account)
|
||||||
|
if user_signed_in?
|
||||||
|
if account.id == current_user.account_id
|
||||||
|
link_to settings_profile_url, class: 'button logo-button' do
|
||||||
|
safe_join([svg_logo, t('settings.edit_profile')])
|
||||||
|
end
|
||||||
|
elsif current_account.following?(account) || current_account.requested?(account)
|
||||||
|
link_to account_unfollow_path(account), class: 'button logo-button button--destructive', data: { method: :post } do
|
||||||
|
safe_join([svg_logo, t('accounts.unfollow')])
|
||||||
|
end
|
||||||
|
elsif !(account.memorial? || account.moved?)
|
||||||
|
link_to account_follow_path(account), class: "button logo-button#{account.blocking?(current_account) ? ' disabled' : ''}", data: { method: :post } do
|
||||||
|
safe_join([svg_logo, t('accounts.follow')])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elsif !(account.memorial? || account.moved?)
|
||||||
|
link_to account_remote_follow_path(account), class: 'button logo-button modal-button', target: '_new' do
|
||||||
|
safe_join([svg_logo, t('accounts.follow')])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def minimal_account_action_button(account)
|
||||||
|
if user_signed_in?
|
||||||
|
return if account.id == current_user.account_id
|
||||||
|
|
||||||
|
if current_account.following?(account) || current_account.requested?(account)
|
||||||
|
link_to account_unfollow_path(account), class: 'icon-button active', data: { method: :post }, title: t('accounts.unfollow') do
|
||||||
|
fa_icon('user-times fw')
|
||||||
|
end
|
||||||
|
elsif !(account.memorial? || account.moved?)
|
||||||
|
link_to account_follow_path(account), class: "icon-button#{account.blocking?(current_account) ? ' disabled' : ''}", data: { method: :post }, title: t('accounts.follow') do
|
||||||
|
fa_icon('user-plus fw')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elsif !(account.memorial? || account.moved?)
|
||||||
|
link_to account_remote_follow_path(account), class: 'icon-button modal-button', target: '_new', title: t('accounts.follow') do
|
||||||
|
fa_icon('user-plus fw')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def svg_logo
|
||||||
|
content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo'), 'viewBox' => '0 0 216.4144 232.00976')
|
||||||
|
end
|
||||||
|
|
||||||
|
def svg_logo_full
|
||||||
|
content_tag(:svg, tag(:use, 'xlink:href' => '#mastodon-svg-logo-full'), 'viewBox' => '0 0 713.35878 175.8678')
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_badge(account, all: false)
|
||||||
|
if account.bot?
|
||||||
|
content_tag(:div, content_tag(:div, t('accounts.roles.bot'), class: 'account-role bot'), class: 'roles')
|
||||||
|
elsif account.group?
|
||||||
|
content_tag(:div, content_tag(:div, t('accounts.roles.group'), class: 'account-role group'), class: 'roles')
|
||||||
|
elsif (Setting.show_staff_badge && account.user_staff?) || all
|
||||||
|
content_tag(:div, class: 'roles') do
|
||||||
|
if all && !account.user_staff?
|
||||||
|
content_tag(:div, t('admin.accounts.roles.user'), class: 'account-role')
|
||||||
|
elsif account.user_admin?
|
||||||
|
content_tag(:div, t('accounts.roles.admin'), class: 'account-role admin')
|
||||||
|
elsif account.user_moderator?
|
||||||
|
content_tag(:div, t('accounts.roles.moderator'), class: 'account-role moderator')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def link_to_more(url)
|
def link_to_more(url)
|
||||||
link_to t('statuses.show_more'), url, class: 'load-more load-gap'
|
link_to t('statuses.show_more'), url, class: 'load-more load-gap'
|
||||||
end
|
end
|
||||||
|
|
|
@ -238,9 +238,18 @@ class Header extends ImmutablePureComponent {
|
||||||
const content = { __html: account.get('note_emojified') };
|
const content = { __html: account.get('note_emojified') };
|
||||||
const displayNameHtml = { __html: account.get('display_name_html') };
|
const displayNameHtml = { __html: account.get('display_name_html') };
|
||||||
const fields = account.get('fields');
|
const fields = account.get('fields');
|
||||||
const badge = account.get('bot') ? (<div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Bot' /></div>) : null;
|
|
||||||
const acct = account.get('acct').indexOf('@') === -1 && domain ? `${account.get('acct')}@${domain}` : account.get('acct');
|
const acct = account.get('acct').indexOf('@') === -1 && domain ? `${account.get('acct')}@${domain}` : account.get('acct');
|
||||||
|
|
||||||
|
let badge;
|
||||||
|
|
||||||
|
if (account.get('bot')) {
|
||||||
|
badge = (<div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Bot' /></div>);
|
||||||
|
} else if (account.get('group')) {
|
||||||
|
badge = (<div className='account-role group'><FormattedMessage id='account.badges.group' defaultMessage='Group' /></div>);
|
||||||
|
} else {
|
||||||
|
badge = null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('account__header', { inactive: !!account.get('moved') })} ref={this.setRef}>
|
<div className={classNames('account__header', { inactive: !!account.get('moved') })} ref={this.setRef}>
|
||||||
<div className='account__header__image'>
|
<div className='account__header__image'>
|
||||||
|
|
|
@ -89,7 +89,7 @@ class ActivityPub::Activity
|
||||||
def distribute(status)
|
def distribute(status)
|
||||||
crawl_links(status)
|
crawl_links(status)
|
||||||
|
|
||||||
notify_about_reblog(status) if reblog_of_local_account?(status)
|
notify_about_reblog(status) if reblog_of_local_account?(status) && !reblog_by_following_group_account?(status)
|
||||||
notify_about_mentions(status)
|
notify_about_mentions(status)
|
||||||
|
|
||||||
# Only continue if the status is supposed to have arrived in real-time.
|
# Only continue if the status is supposed to have arrived in real-time.
|
||||||
|
@ -105,6 +105,10 @@ class ActivityPub::Activity
|
||||||
status.reblog? && status.reblog.account.local?
|
status.reblog? && status.reblog.account.local?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reblog_by_following_group_account?(status)
|
||||||
|
status.reblog? && status.account.group? && status.reblog.account.following?(status.account)
|
||||||
|
end
|
||||||
|
|
||||||
def notify_about_reblog(status)
|
def notify_about_reblog(status)
|
||||||
NotifyService.new.call(status.reblog.account, status)
|
NotifyService.new.call(status.reblog.account, status)
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,10 +68,19 @@ class ActivityPub::TagManager
|
||||||
if status.account.silenced?
|
if status.account.silenced?
|
||||||
# Only notify followers if the account is locally silenced
|
# Only notify followers if the account is locally silenced
|
||||||
account_ids = status.active_mentions.pluck(:account_id)
|
account_ids = status.active_mentions.pluck(:account_id)
|
||||||
to = status.account.followers.where(id: account_ids).map { |account| uri_for(account) }
|
to = status.account.followers.where(id: account_ids).each_with_object([]) do |account, result|
|
||||||
to.concat(FollowRequest.where(target_account_id: status.account_id, account_id: account_ids).map { |request| uri_for(request.account) })
|
result << uri_for(account)
|
||||||
|
result << account.followers_url if account.group?
|
||||||
|
end
|
||||||
|
to.concat(FollowRequest.where(target_account_id: status.account_id, account_id: account_ids).each_with_object([]) do |request, result|
|
||||||
|
result << uri_for(request.account)
|
||||||
|
result << request.account.followers_url if request.account.group?
|
||||||
|
end)
|
||||||
else
|
else
|
||||||
status.active_mentions.map { |mention| uri_for(mention.account) }
|
status.active_mentions.each_with_object([]) do |mention, result|
|
||||||
|
result << uri_for(mention.account)
|
||||||
|
result << mention.account.followers_url if mention.account.group?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -97,10 +106,19 @@ class ActivityPub::TagManager
|
||||||
if status.account.silenced?
|
if status.account.silenced?
|
||||||
# Only notify followers if the account is locally silenced
|
# Only notify followers if the account is locally silenced
|
||||||
account_ids = status.active_mentions.pluck(:account_id)
|
account_ids = status.active_mentions.pluck(:account_id)
|
||||||
cc.concat(status.account.followers.where(id: account_ids).map { |account| uri_for(account) })
|
cc.concat(status.account.followers.where(id: account_ids).each_with_object([]) do |account, result|
|
||||||
cc.concat(FollowRequest.where(target_account_id: status.account_id, account_id: account_ids).map { |request| uri_for(request.account) })
|
result << uri_for(account)
|
||||||
|
result << account.followers_url if account.group?
|
||||||
|
end)
|
||||||
|
cc.concat(FollowRequest.where(target_account_id: status.account_id, account_id: account_ids).each_with_object([]) do |request, result|
|
||||||
|
result << uri_for(request.account)
|
||||||
|
result << request.account.followers_url if request.account.group?
|
||||||
|
end)
|
||||||
else
|
else
|
||||||
cc.concat(status.active_mentions.map { |mention| uri_for(mention.account) })
|
cc.concat(status.active_mentions.each_with_object([]) do |mention, result|
|
||||||
|
result << uri_for(mention.account)
|
||||||
|
result << mention.account.followers_url if mention.account.group?
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,7 @@ class Account < ApplicationRecord
|
||||||
scope :without_silenced, -> { where(silenced_at: nil) }
|
scope :without_silenced, -> { where(silenced_at: nil) }
|
||||||
scope :recent, -> { reorder(id: :desc) }
|
scope :recent, -> { reorder(id: :desc) }
|
||||||
scope :bots, -> { where(actor_type: %w(Application Service)) }
|
scope :bots, -> { where(actor_type: %w(Application Service)) }
|
||||||
|
scope :groups, -> { where(actor_type: 'Group') }
|
||||||
scope :alphabetic, -> { order(domain: :asc, username: :asc) }
|
scope :alphabetic, -> { order(domain: :asc, username: :asc) }
|
||||||
scope :by_domain_accounts, -> { group(:domain).select(:domain, 'COUNT(*) AS accounts_count').order('accounts_count desc') }
|
scope :by_domain_accounts, -> { group(:domain).select(:domain, 'COUNT(*) AS accounts_count').order('accounts_count desc') }
|
||||||
scope :matches_username, ->(value) { where(arel_table[:username].matches("#{value}%")) }
|
scope :matches_username, ->(value) { where(arel_table[:username].matches("#{value}%")) }
|
||||||
|
@ -153,6 +154,12 @@ class Account < ApplicationRecord
|
||||||
self.actor_type = ActiveModel::Type::Boolean.new.cast(val) ? 'Service' : 'Person'
|
self.actor_type = ActiveModel::Type::Boolean.new.cast(val) ? 'Service' : 'Person'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def group?
|
||||||
|
actor_type == 'Group'
|
||||||
|
end
|
||||||
|
|
||||||
|
alias group group?
|
||||||
|
|
||||||
def acct
|
def acct
|
||||||
local? ? username : "#{username}@#{domain}"
|
local? ? username : "#{username}@#{domain}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,6 +49,8 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
||||||
'Application'
|
'Application'
|
||||||
elsif object.bot?
|
elsif object.bot?
|
||||||
'Service'
|
'Service'
|
||||||
|
elsif object.group?
|
||||||
|
'Group'
|
||||||
else
|
else
|
||||||
'Person'
|
'Person'
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
class REST::AccountSerializer < ActiveModel::Serializer
|
class REST::AccountSerializer < ActiveModel::Serializer
|
||||||
include RoutingHelper
|
include RoutingHelper
|
||||||
|
|
||||||
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :created_at,
|
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
|
||||||
:note, :url, :avatar, :avatar_static, :header, :header_static,
|
:note, :url, :avatar, :avatar_static, :header, :header_static,
|
||||||
:followers_count, :following_count, :statuses_count, :last_status_at
|
:followers_count, :following_count, :statuses_count, :last_status_at
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ en:
|
||||||
roles:
|
roles:
|
||||||
admin: Admin
|
admin: Admin
|
||||||
bot: Bot
|
bot: Bot
|
||||||
|
group: Group
|
||||||
moderator: Mod
|
moderator: Mod
|
||||||
unavailable: Profile unavailable
|
unavailable: Profile unavailable
|
||||||
unfollow: Unfollow
|
unfollow: Unfollow
|
||||||
|
|
Loading…
Reference in a new issue