* Store actor type in database * Add bot nameplate to web UI, add setting to preferences, API, AP Fix #7365 * Fix code style issuesmaster
@@ -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 | ||||
@@ -20,6 +20,9 @@ | |||||
= 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 | .fields-group | ||||
= f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot') | |||||
.fields-group | |||||
.input.with_block_label | .input.with_block_label | ||||
%label= t('simple_form.labels.defaults.fields') | %label= t('simple_form.labels.defaults.fields') | ||||
%span.hint= t('simple_form.hints.defaults.fields') | %span.hint= t('simple_form.hints.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" | ||||