Bot nameplates (#7391)
* Store actor type in database * Add bot nameplate to web UI, add setting to preferences, API, AP Fix #7365 * Fix code style issuesgh/stable
parent
0f0cc3f2eb
commit
42cd363542
|
@ -21,7 +21,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||||
private
|
private
|
||||||
|
|
||||||
def account_params
|
def account_params
|
||||||
params.permit(:display_name, :note, :avatar, :header, :locked, fields_attributes: [:name, :value])
|
params.permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_settings_params
|
def user_settings_params
|
||||||
|
|
|
@ -27,7 +27,7 @@ class Settings::ProfilesController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def account_params
|
def account_params
|
||||||
params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, fields_attributes: [:name, :value])
|
params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, fields_attributes: [:name, :value])
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_account
|
def set_account
|
||||||
|
|
|
@ -131,6 +131,7 @@ export default 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='roles'><div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Bot' /></div></div>) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}>
|
<div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}>
|
||||||
|
@ -139,6 +140,9 @@ export default class Header extends ImmutablePureComponent {
|
||||||
|
|
||||||
<span className='account__header__display-name' dangerouslySetInnerHTML={displayNameHtml} />
|
<span className='account__header__display-name' dangerouslySetInnerHTML={displayNameHtml} />
|
||||||
<span className='account__header__username'>@{account.get('acct')} {lockedIcon}</span>
|
<span className='account__header__username'>@{account.get('acct')} {lockedIcon}</span>
|
||||||
|
|
||||||
|
{badge}
|
||||||
|
|
||||||
<div className='account__header__content' dangerouslySetInnerHTML={content} />
|
<div className='account__header__content' dangerouslySetInnerHTML={content} />
|
||||||
|
|
||||||
{fields.size > 0 && (
|
{fields.size > 0 && (
|
||||||
|
|
|
@ -5159,6 +5159,12 @@ noscript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.account__header .roles {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.account__header .account__header__fields {
|
.account__header .account__header__fields {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
|
|
@ -87,6 +87,10 @@ code {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.file .label_input {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
&.select .label_input {
|
&.select .label_input {
|
||||||
align-items: initial;
|
align-items: initial;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
# moved_to_account_id :bigint(8)
|
# moved_to_account_id :bigint(8)
|
||||||
# featured_collection_url :string
|
# featured_collection_url :string
|
||||||
# fields :jsonb
|
# fields :jsonb
|
||||||
|
# actor_type :string
|
||||||
#
|
#
|
||||||
|
|
||||||
class Account < ApplicationRecord
|
class Account < ApplicationRecord
|
||||||
|
@ -149,6 +150,16 @@ class Account < ApplicationRecord
|
||||||
moved_to_account_id.present?
|
moved_to_account_id.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bot?
|
||||||
|
%w(Application Service).include? actor_type
|
||||||
|
end
|
||||||
|
|
||||||
|
alias bot bot?
|
||||||
|
|
||||||
|
def bot=(val)
|
||||||
|
self.actor_type = ActiveModel::Type::Boolean.new.cast(val) ? 'Service' : 'Person'
|
||||||
|
end
|
||||||
|
|
||||||
def acct
|
def acct
|
||||||
local? ? username : "#{username}@#{domain}"
|
local? ? username : "#{username}@#{domain}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,7 +37,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def type
|
def type
|
||||||
'Person'
|
object.bot? ? 'Service' : 'Person'
|
||||||
end
|
end
|
||||||
|
|
||||||
def following
|
def following
|
||||||
|
|
|
@ -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, :created_at,
|
attributes :id, :username, :acct, :display_name, :locked, :bot, :created_at,
|
||||||
:note, :url, :avatar, :avatar_static, :header, :header_static,
|
:note, :url, :avatar, :avatar_static, :header, :header_static,
|
||||||
:followers_count, :following_count, :statuses_count
|
:followers_count, :following_count, :statuses_count
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ class ActivityPub::ProcessAccountService < BaseService
|
||||||
@account.note = @json['summary'] || ''
|
@account.note = @json['summary'] || ''
|
||||||
@account.locked = @json['manuallyApprovesFollowers'] || false
|
@account.locked = @json['manuallyApprovesFollowers'] || false
|
||||||
@account.fields = property_values || {}
|
@account.fields = property_values || {}
|
||||||
|
@account.actor_type = @json['type']
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_fetchable_attributes!
|
def set_fetchable_attributes!
|
||||||
|
|
|
@ -10,7 +10,11 @@
|
||||||
%span>< @#{account.local_username_and_domain}
|
%span>< @#{account.local_username_and_domain}
|
||||||
= fa_icon('lock') if account.locked?
|
= fa_icon('lock') if account.locked?
|
||||||
|
|
||||||
- if Setting.show_staff_badge
|
- if account.bot?
|
||||||
|
.roles
|
||||||
|
.account-role.bot
|
||||||
|
= t 'accounts.roles.bot'
|
||||||
|
- elsif Setting.show_staff_badge
|
||||||
- if account.user_admin?
|
- if account.user_admin?
|
||||||
.roles
|
.roles
|
||||||
.account-role.admin
|
.account-role.admin
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')
|
= f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')
|
||||||
|
|
||||||
|
.fields-group
|
||||||
|
= f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
.input.with_block_label
|
.input.with_block_label
|
||||||
%label= t('simple_form.labels.defaults.fields')
|
%label= t('simple_form.labels.defaults.fields')
|
||||||
|
|
|
@ -49,6 +49,7 @@ en:
|
||||||
reserved_username: The username is reserved
|
reserved_username: The username is reserved
|
||||||
roles:
|
roles:
|
||||||
admin: Admin
|
admin: Admin
|
||||||
|
bot: Bot
|
||||||
moderator: Mod
|
moderator: Mod
|
||||||
unfollow: Unfollow
|
unfollow: Unfollow
|
||||||
admin:
|
admin:
|
||||||
|
|
|
@ -4,6 +4,7 @@ en:
|
||||||
hints:
|
hints:
|
||||||
defaults:
|
defaults:
|
||||||
avatar: PNG, GIF or JPG. At most 2MB. Will be downscaled to 400x400px
|
avatar: PNG, GIF or JPG. At most 2MB. Will be downscaled to 400x400px
|
||||||
|
bot: Warns people that the account does not represent a person
|
||||||
digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence
|
digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence
|
||||||
display_name:
|
display_name:
|
||||||
one: <span class="name-counter">1</span> character left
|
one: <span class="name-counter">1</span> character left
|
||||||
|
@ -29,6 +30,7 @@ en:
|
||||||
value: Content
|
value: Content
|
||||||
defaults:
|
defaults:
|
||||||
avatar: Avatar
|
avatar: Avatar
|
||||||
|
bot: This is a bot account
|
||||||
confirm_new_password: Confirm new password
|
confirm_new_password: Confirm new password
|
||||||
confirm_password: Confirm password
|
confirm_password: Confirm password
|
||||||
current_password: Current password
|
current_password: Current password
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddActorTypeToAccounts < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :accounts, :actor_type, :string
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2018_04_16_210259) do
|
ActiveRecord::Schema.define(version: 2018_05_06_221944) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -75,6 +75,7 @@ ActiveRecord::Schema.define(version: 2018_04_16_210259) do
|
||||||
t.bigint "moved_to_account_id"
|
t.bigint "moved_to_account_id"
|
||||||
t.string "featured_collection_url"
|
t.string "featured_collection_url"
|
||||||
t.jsonb "fields"
|
t.jsonb "fields"
|
||||||
|
t.string "actor_type"
|
||||||
t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin
|
t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin
|
||||||
t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower"
|
t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower"
|
||||||
t.index ["uri"], name: "index_accounts_on_uri"
|
t.index ["uri"], name: "index_accounts_on_uri"
|
||||||
|
|
Reference in New Issue