diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb
index a3c4008e64..259d07be87 100644
--- a/app/controllers/api/v1/accounts/credentials_controller.rb
+++ b/app/controllers/api/v1/accounts/credentials_controller.rb
@@ -21,7 +21,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
private
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
def user_settings_params
diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb
index 5d81668dea..1b01fc75f0 100644
--- a/app/controllers/settings/profiles_controller.rb
+++ b/app/controllers/settings/profiles_controller.rb
@@ -27,7 +27,7 @@ class Settings::ProfilesController < ApplicationController
private
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
def set_account
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index 47915e6fb8..7358053da0 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -131,6 +131,7 @@ export default class Header extends ImmutablePureComponent {
const content = { __html: account.get('note_emojified') };
const displayNameHtml = { __html: account.get('display_name_html') };
const fields = account.get('fields');
+ const badge = account.get('bot') ? (
@@ -139,6 +140,9 @@ export default class Header extends ImmutablePureComponent {
@{account.get('acct')} {lockedIcon}
+
+ {badge}
+
{fields.size > 0 && (
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 3e2a1ae104..158bf68511 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -5159,6 +5159,12 @@ noscript {
}
}
+.account__header .roles {
+ margin-top: 20px;
+ margin-bottom: 20px;
+ padding: 0 15px;
+}
+
.account__header .account__header__fields {
font-size: 14px;
line-height: 20px;
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index f978901870..de16784a87 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -87,6 +87,10 @@ code {
align-items: flex-start;
}
+ &.file .label_input {
+ flex-wrap: nowrap;
+ }
+
&.select .label_input {
align-items: initial;
}
diff --git a/app/models/account.rb b/app/models/account.rb
index 4467d1512a..2b3ef5cdc1 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -45,6 +45,7 @@
# moved_to_account_id :bigint(8)
# featured_collection_url :string
# fields :jsonb
+# actor_type :string
#
class Account < ApplicationRecord
@@ -149,6 +150,16 @@ class Account < ApplicationRecord
moved_to_account_id.present?
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
local? ? username : "#{username}@#{domain}"
end
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index fcf3bdf175..41c9aa44e8 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -37,7 +37,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
end
def type
- 'Person'
+ object.bot? ? 'Service' : 'Person'
end
def following
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 8761bbb5e7..6adcd70390 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -3,7 +3,7 @@
class REST::AccountSerializer < ActiveModel::Serializer
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,
:followers_count, :following_count, :statuses_count
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index f67ebb443a..cc416b6717 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -71,6 +71,7 @@ class ActivityPub::ProcessAccountService < BaseService
@account.note = @json['summary'] || ''
@account.locked = @json['manuallyApprovesFollowers'] || false
@account.fields = property_values || {}
+ @account.actor_type = @json['type']
end
def set_fetchable_attributes!
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index 13dcaf616a..4098d6778a 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -10,7 +10,11 @@
%span>< @#{account.local_username_and_domain}
= 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?
.roles
.account-role.admin
diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml
index f28834d72e..a84f8a7da7 100644
--- a/app/views/settings/profiles/show.html.haml
+++ b/app/views/settings/profiles/show.html.haml
@@ -19,6 +19,9 @@
.fields-group
= 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
.input.with_block_label
%label= t('simple_form.labels.defaults.fields')
diff --git a/config/locales/en.yml b/config/locales/en.yml
index e18354eac4..5369311ac5 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -49,6 +49,7 @@ en:
reserved_username: The username is reserved
roles:
admin: Admin
+ bot: Bot
moderator: Mod
unfollow: Unfollow
admin:
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 7ba32906d4..495c6166b4 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -4,6 +4,7 @@ en:
hints:
defaults:
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
display_name:
one:
1 character left
@@ -29,6 +30,7 @@ en:
value: Content
defaults:
avatar: Avatar
+ bot: This is a bot account
confirm_new_password: Confirm new password
confirm_password: Confirm password
current_password: Current password
diff --git a/db/migrate/20180506221944_add_actor_type_to_accounts.rb b/db/migrate/20180506221944_add_actor_type_to_accounts.rb
new file mode 100644
index 0000000000..7cfed640f7
--- /dev/null
+++ b/db/migrate/20180506221944_add_actor_type_to_accounts.rb
@@ -0,0 +1,5 @@
+class AddActorTypeToAccounts < ActiveRecord::Migration[5.2]
+ def change
+ add_column :accounts, :actor_type, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 566a320d87..f7fa24b835 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# 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
enable_extension "plpgsql"
@@ -75,6 +75,7 @@ ActiveRecord::Schema.define(version: 2018_04_16_210259) do
t.bigint "moved_to_account_id"
t.string "featured_collection_url"
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 "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower"
t.index ["uri"], name: "index_accounts_on_uri"