about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorDavid Underwood <davefp@gmail.com>2018-04-01 17:55:42 -0400
committerEugen Rochko <eugen@zeonfederated.com>2018-04-01 23:55:42 +0200
commit123a343d116d3e83cbd04460cc7bd0b6f3d208c4 (patch)
tree7d834be5438d835fe90744e0ba011f585cf252b1 /app
parentf464f98fd3b8ef33b3afa5acf09e829c046134de (diff)
[WIP] Enable custom emoji on account pages and in the sidebar (#6124)
Federate custom emojis with accounts
Diffstat (limited to 'app')
-rw-r--r--app/lib/formatter.rb11
-rw-r--r--app/lib/ostatus/atom_serializer.rb3
-rw-r--r--app/models/account.rb4
-rw-r--r--app/models/remote_profile.rb4
-rw-r--r--app/serializers/activitypub/actor_serializer.rb9
-rw-r--r--app/serializers/rest/account_serializer.rb2
-rw-r--r--app/services/activitypub/process_account_service.rb28
-rw-r--r--app/services/update_remote_profile_service.rb21
-rw-r--r--app/views/accounts/_header.html.haml2
9 files changed, 79 insertions, 5 deletions
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 1df4ff8d4..f7e7a3c23 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -51,9 +51,14 @@ class Formatter
     strip_tags(text)
   end
 
-  def simplified_format(account)
-    return reformat(account.note).html_safe unless account.local? # rubocop:disable Rails/OutputSafety
-    linkify(account.note)
+  def simplified_format(account, **options)
+    html = if account.local?
+             linkify(account.note)
+           else
+             reformat(account.note)
+           end
+    html = encode_custom_emojis(html, CustomEmoji.from_text(account.note, account.domain)) if options[:custom_emojify]
+    html.html_safe # rubocop:disable Rails/OutputSafety
   end
 
   def sanitize(html, config)
diff --git a/app/lib/ostatus/atom_serializer.rb b/app/lib/ostatus/atom_serializer.rb
index 46d0a8b37..055b4649c 100644
--- a/app/lib/ostatus/atom_serializer.rb
+++ b/app/lib/ostatus/atom_serializer.rb
@@ -26,6 +26,9 @@ class OStatus::AtomSerializer
     append_element(author, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(account))
     append_element(author, 'link', nil, rel: :avatar, type: account.avatar_content_type, 'media:width': 120, 'media:height': 120, href: full_asset_url(account.avatar.url(:original))) if account.avatar?
     append_element(author, 'link', nil, rel: :header, type: account.header_content_type, 'media:width': 700, 'media:height': 335, href: full_asset_url(account.header.url(:original))) if account.header?
+    account.emojis.each do |emoji|
+      append_element(author, 'link', nil, rel: :emoji, href: full_asset_url(emoji.image.url), name: emoji.shortcode)
+    end
     append_element(author, 'poco:preferredUsername', account.username)
     append_element(author, 'poco:displayName', account.display_name) if account.display_name?
     append_element(author, 'poco:note', account.local? ? account.note : strip_tags(account.note)) if account.note?
diff --git a/app/models/account.rb b/app/models/account.rb
index 25e7d7436..a34b6a2d3 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -350,6 +350,10 @@ class Account < ApplicationRecord
     end
   end
 
+  def emojis
+    CustomEmoji.from_text(note, domain)
+  end
+
   before_create :generate_keys
   before_validation :normalize_domain
   before_validation :prepare_contents, if: :local?
diff --git a/app/models/remote_profile.rb b/app/models/remote_profile.rb
index 613911c57..742d2b56f 100644
--- a/app/models/remote_profile.rb
+++ b/app/models/remote_profile.rb
@@ -41,6 +41,10 @@ class RemoteProfile
     @header ||= link_href_from_xml(author, 'header')
   end
 
+  def emojis
+    @emojis ||= author.xpath('./xmlns:link[@rel="emoji"]', xmlns: OStatus::TagManager::XMLNS)
+  end
+
   def locked?
     scope == 'private'
   end
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index afcd37771..df3090726 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -10,6 +10,8 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
 
   has_one :public_key, serializer: ActivityPub::PublicKeySerializer
 
+  has_many :virtual_tags, key: :tag
+
   attribute :moved_to, if: :moved?
 
   class EndpointsSerializer < ActiveModel::Serializer
@@ -101,7 +103,14 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
     object.locked
   end
 
+  def virtual_tags
+    object.emojis
+  end
+
   def moved_to
     ActivityPub::TagManager.instance.uri_for(object.moved_to_account)
   end
+
+  class CustomEmojiSerializer < ActivityPub::EmojiSerializer
+  end
 end
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 19b746520..6097acda5 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -14,7 +14,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
   end
 
   def note
-    Formatter.instance.simplified_format(object)
+    Formatter.instance.simplified_format(object, custom_emojify: true)
   end
 
   def url
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index 7d8dc1369..cf8462821 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -22,6 +22,7 @@ class ActivityPub::ProcessAccountService < BaseService
 
         create_account if @account.nil?
         update_account
+        process_tags(@account)
       end
     end
 
@@ -187,4 +188,31 @@ class ActivityPub::ProcessAccountService < BaseService
   def lock_options
     { redis: Redis.current, key: "process_account:#{@uri}" }
   end
+
+  def process_tags(account)
+    return if @json['tag'].blank?
+    as_array(@json['tag']).each do |tag|
+      case tag['type']
+      when 'Emoji'
+        process_emoji tag, account
+      end
+    end
+  end
+
+  def process_emoji(tag, _account)
+    return if skip_download?
+    return if tag['name'].blank? || tag['icon'].blank? || tag['icon']['url'].blank?
+
+    shortcode = tag['name'].delete(':')
+    image_url = tag['icon']['url']
+    uri       = tag['id']
+    updated   = tag['updated']
+    emoji     = CustomEmoji.find_by(shortcode: shortcode, domain: @account.domain)
+
+    return unless emoji.nil? || emoji.updated_at >= updated
+
+    emoji ||= CustomEmoji.new(domain: @account.domain, shortcode: shortcode, uri: uri)
+    emoji.image_remote_url = image_url
+    emoji.save
+  end
 end
diff --git a/app/services/update_remote_profile_service.rb b/app/services/update_remote_profile_service.rb
index 49a907682..aca1185de 100644
--- a/app/services/update_remote_profile_service.rb
+++ b/app/services/update_remote_profile_service.rb
@@ -40,6 +40,27 @@ class UpdateRemoteProfileService < BaseService
         account.header_remote_url = ''
         account.header.destroy
       end
+
+      save_emojis(account) if remote_profile.emojis.present?
+    end
+  end
+
+  def save_emojis(parent)
+    do_not_download = DomainBlock.find_by(domain: parent.account.domain)&.reject_media?
+
+    return if do_not_download
+
+    remote_account.emojis.each do |link|
+      next unless link['href'] && link['name']
+
+      shortcode = link['name'].delete(':')
+      emoji     = CustomEmoji.find_by(shortcode: shortcode, domain: parent.account.domain)
+
+      next unless emoji.nil?
+
+      emoji = CustomEmoji.new(shortcode: shortcode, domain: parent.account.domain)
+      emoji.image_remote_url = link['href']
+      emoji.save
     end
   end
 end
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index b3c91b869..b78998e9e 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -21,7 +21,7 @@
             = t 'accounts.roles.moderator'
 
     .bio
-      .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account)
+      .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)
 
     .details-counters
       .counter{ class: active_nav_class(short_account_url(account)) }