Skip to content

Commit

Permalink
Revert "Revert "Add bio fields (mastodon#6645)""
Browse files Browse the repository at this point in the history
This reverts commit 38ee172.
  • Loading branch information
S-H-GAMELINKS committed Apr 16, 2018
1 parent 38ee172 commit 627bf47
Show file tree
Hide file tree
Showing 18 changed files with 274 additions and 7 deletions.
6 changes: 4 additions & 2 deletions app/controllers/settings/profiles_controller.rb
Expand Up @@ -11,7 +11,9 @@ class Settings::ProfilesController < ApplicationController
obfuscate_filename [:account, :avatar]
obfuscate_filename [:account, :header]

def show; end
def show
@account.build_fields
end

def update
if UpdateAccountService.new.call(@account, account_params)
Expand All @@ -25,7 +27,7 @@ def update
private

def account_params
params.require(:account).permit(:display_name, :note, :avatar, :header, :locked)
params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, fields_attributes: [:name, :value])
end

def set_account
Expand Down
8 changes: 8 additions & 0 deletions app/javascript/mastodon/actions/importer/normalizer.js
Expand Up @@ -10,6 +10,14 @@ export function normalizeAccount(account) {
account.display_name_html = emojify(escapeTextContentForBrowser(displayName));
account.note_emojified = emojify(account.note);

if (account.fields) {
account.fields = account.fields.map(pair => ({
...pair,
name_emojified: emojify(escapeTextContentForBrowser(pair.name)),
value_emojified: emojify(pair.value),
}));
}

if (account.moved) {
account.moved = account.moved.id;
}
Expand Down
14 changes: 14 additions & 0 deletions app/javascript/mastodon/features/account/components/header.js
Expand Up @@ -130,6 +130,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');

return (
<div className={classNames('account__header', { inactive: !!account.get('moved') })} style={{ backgroundImage: `url(${account.get('header')})` }}>
Expand All @@ -140,6 +141,19 @@ export default class Header extends ImmutablePureComponent {
<span className='account__header__username'>@{account.get('acct')} {lockedIcon}</span>
<div className='account__header__content' dangerouslySetInnerHTML={content} />

{fields.size > 0 && (
<table className='account__header__fields'>
<tbody>
{fields.map((pair, i) => (
<tr key={i}>
<th dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} />
<td dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} />
</tr>
))}
</tbody>
</table>
)}

{info}
{mutingInfo}
{actionBtn}
Expand Down
54 changes: 54 additions & 0 deletions app/javascript/styles/mastodon/accounts.scss
Expand Up @@ -563,3 +563,57 @@
border-color: rgba(lighten($error-red, 12%), 0.5);
}
}

.account__header__fields {
border-collapse: collapse;
padding: 0;
margin: 15px -15px -15px;
border: 0 none;
border-top: 1px solid lighten($ui-base-color, 4%);
border-bottom: 1px solid lighten($ui-base-color, 4%);

th,
td {
padding: 15px;
padding-left: 15px;
border: 0 none;
border-bottom: 1px solid lighten($ui-base-color, 4%);
vertical-align: middle;
}

th {
padding-left: 15px;
font-weight: 500;
text-align: center;
width: 94px;
color: $ui-secondary-color;
background: rgba(darken($ui-base-color, 8%), 0.5);
}

td {
color: $ui-primary-color;
text-align: center;
width: 100%;
padding-left: 0;
}

a {
color: $ui-highlight-color;
text-decoration: none;

&:hover,
&:focus,
&:active {
text-decoration: underline;
}
}

tr {
&:last-child {
th,
td {
border-bottom: 0;
}
}
}
}
37 changes: 37 additions & 0 deletions app/javascript/styles/mastodon/components.scss
Expand Up @@ -5176,3 +5176,40 @@ noscript {
background: lighten($ui-highlight-color, 7%);
}
}

.account__header .account__header__fields {
font-size: 14px;
line-height: 20px;
overflow: hidden;
border-collapse: collapse;
margin: 20px -10px -20px;
border-bottom: 0;

tr {
border-top: 1px solid lighten($ui-base-color, 8%);
text-align: center;
}

th,
td {
padding: 14px 20px;
vertical-align: middle;
max-height: 40px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

th {
color: $ui-primary-color;
background: darken($ui-base-color, 4%);
max-width: 120px;
font-weight: 500;
}

td {
flex: auto;
color: $primary-text-color;
background: $ui-base-color;
}
}
12 changes: 12 additions & 0 deletions app/javascript/styles/mastodon/forms.scss
Expand Up @@ -15,6 +15,18 @@ code {
overflow: hidden;
}

.row {
display: flex;
margin: 0 -5px;

.input {
box-sizing: border-box;
flex: 1 1 auto;
width: 50%;
padding: 0 5px;
}
}

span.hint {
display: block;
color: $ui-primary-color;
Expand Down
3 changes: 3 additions & 0 deletions app/lib/activitypub/adapter.rb
Expand Up @@ -19,6 +19,9 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
'Emoji' => 'toot:Emoji',
'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' },
'featured' => 'toot:featured',
'schema' => 'http://schema.org#',
'PropertyValue' => 'schema:PropertyValue',
'value' => 'schema:value',
},
],
}.freeze
Expand Down
18 changes: 15 additions & 3 deletions app/lib/formatter.rb
Expand Up @@ -71,6 +71,11 @@ def format_spoiler(status)
html.html_safe # rubocop:disable Rails/OutputSafety
end

def format_field(account, str)
return reformat(str).html_safe unless account.local? # rubocop:disable Rails/OutputSafety
encode_and_link_urls(str, me: true).html_safe # rubocop:disable Rails/OutputSafety
end

def linkify(text)
html = encode_and_link_urls(text)
html = simple_format(html, {}, sanitize: false)
Expand All @@ -85,12 +90,17 @@ def encode(html)
HTMLEntities.new.encode(html)
end

def encode_and_link_urls(html, accounts = nil)
def encode_and_link_urls(html, accounts = nil, options = {})
entities = Extractor.extract_entities_with_indices(html, extract_url_without_protocol: false)

if accounts.is_a?(Hash)
options = accounts
accounts = nil
end

rewrite(html.dup, entities) do |entity|
if entity[:url]
link_to_url(entity)
link_to_url(entity, options)
elsif entity[:hashtag]
link_to_hashtag(entity)
elsif entity[:screen_name]
Expand Down Expand Up @@ -177,10 +187,12 @@ def rewrite(text, entities)
result.flatten.join
end

def link_to_url(entity)
def link_to_url(entity, options = {})
url = Addressable::URI.parse(entity[:url])
html_attrs = { target: '_blank', rel: 'nofollow noopener' }

html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me]

Twitter::Autolink.send(:link_to_text, entity, link_html(entity[:url]), url, html_attrs)
rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
encode(entity[:url])
Expand Down
36 changes: 36 additions & 0 deletions app/models/account.rb
Expand Up @@ -44,6 +44,7 @@
# memorial :boolean default(FALSE), not null
# moved_to_account_id :integer
# featured_collection_url :string
# fields :jsonb
#

class Account < ApplicationRecord
Expand Down Expand Up @@ -189,6 +190,30 @@ def keypair
@keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key)
end

def fields
(self[:fields] || []).map { |f| Field.new(self, f) }
end

def fields_attributes=(attributes)
fields = []

attributes.each_value do |attr|
next if attr[:name].blank?
fields << attr
end

self[:fields] = fields
end

def build_fields
return if fields.size >= 4

raw_fields = self[:fields] || []
add_fields = 4 - raw_fields.size
add_fields.times { raw_fields << { name: '', value: '' } }
self.fields = raw_fields
end

def magic_key
modulus, exponent = [keypair.public_key.n, keypair.public_key.e].map do |component|
result = []
Expand Down Expand Up @@ -238,6 +263,17 @@ def preferred_inbox_url
shared_inbox_url.presence || inbox_url
end

class Field < ActiveModelSerializers::Model
attributes :name, :value, :account, :errors

def initialize(account, attr)
@account = account
@name = attr['name']
@value = attr['value']
@errors = {}
end
end

class << self
def readonly_attributes
super - %w(statuses_count following_count followers_count)
Expand Down
17 changes: 17 additions & 0 deletions app/serializers/activitypub/actor_serializer.rb
Expand Up @@ -11,6 +11,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
has_one :public_key, serializer: ActivityPub::PublicKeySerializer

has_many :virtual_tags, key: :tag
has_many :virtual_attachments, key: :attachment

attribute :moved_to, if: :moved?

Expand Down Expand Up @@ -107,10 +108,26 @@ def virtual_tags
object.emojis
end

def virtual_attachments
object.fields
end

def moved_to
ActivityPub::TagManager.instance.uri_for(object.moved_to_account)
end

class CustomEmojiSerializer < ActivityPub::EmojiSerializer
end

class Account::FieldSerializer < ActiveModel::Serializer
attributes :type, :name, :value

def type
'PropertyValue'
end

def value
Formatter.instance.format_field(object.account, object.value)
end
end
end
10 changes: 10 additions & 0 deletions app/serializers/rest/account_serializer.rb
Expand Up @@ -9,6 +9,16 @@ class REST::AccountSerializer < ActiveModel::Serializer

has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?

class FieldSerializer < ActiveModel::Serializer
attributes :name, :value

def value
Formatter.instance.format_field(object.account, object.value)
end
end

has_many :fields

def id
object.id.to_s
end
Expand Down
6 changes: 6 additions & 0 deletions app/services/activitypub/process_account_service.rb
Expand Up @@ -70,6 +70,7 @@ def set_immediate_attributes!
@account.display_name = @json['name'] || ''
@account.note = @json['summary'] || ''
@account.locked = @json['manuallyApprovesFollowers'] || false
@account.fields = property_values || {}
end

def set_fetchable_attributes!
Expand Down Expand Up @@ -126,6 +127,11 @@ def url
end
end

def property_values
return unless @json['attachment'].is_a?(Array)
@json['attachment'].select { |attachment| attachment['type'] == 'PropertyValue' }.map { |attachment| attachment.slice('name', 'value') }
end

def mismatching_origin?(url)
needle = Addressable::URI.parse(url).host
haystack = Addressable::URI.parse(@uri).host
Expand Down
8 changes: 8 additions & 0 deletions app/views/accounts/_header.html.haml
Expand Up @@ -23,6 +23,14 @@
.bio
.account__header__content.p-note.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)

- unless account.fields.empty?
%table.account__header__fields
%tbody
- account.fields.each do |field|
%tr
%th.emojify= field.name
%td.emojify= Formatter.instance.format_field(account, field.value)

.details-counters
.counter{ class: active_nav_class(short_account_url(account)) }
= link_to short_account_url(account), class: 'u-url u-uid' do
Expand Down
10 changes: 10 additions & 0 deletions app/views/settings/profiles/show.html.haml
Expand Up @@ -19,6 +19,16 @@
.fields-group
= f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')

.fields-group
.input.with_block_label
%label= t('simple_form.labels.defaults.fields')
%span.hint= t('simple_form.hints.defaults.fields')

= f.simple_fields_for :fields do |fields_f|
.row
= fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name')
= fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value')

.actions
= f.button :button, t('generic.save_changes'), type: :submit

Expand Down

0 comments on commit 627bf47

Please sign in to comment.