summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/helpers/application_helper.rb50
-rw-r--r--app/helpers/atom_builder_helper.rb40
-rw-r--r--app/lib/feed_manager.rb8
-rw-r--r--app/lib/formatter.rb47
-rw-r--r--app/lib/tag_manager.rb41
-rw-r--r--app/models/feed.rb2
-rw-r--r--app/services/fan_out_on_write_service.rb10
-rw-r--r--app/services/precompute_feed_service.rb4
-rw-r--r--app/services/process_feed_service.rb8
-rw-r--r--app/services/process_interaction_service.rb4
-rw-r--r--app/services/remove_status_service.rb2
-rw-r--r--app/views/accounts/_grid_card.html.haml2
-rw-r--r--app/views/accounts/show.atom.ruby2
-rw-r--r--app/views/api/accounts/show.rabl2
-rw-r--r--app/views/api/statuses/show.rabl6
-rw-r--r--app/views/notification_mailer/follow.text.erb2
-rw-r--r--app/views/notification_mailer/mention.text.erb2
-rw-r--r--app/views/stream_entries/_follow.html.haml2
-rw-r--r--app/views/stream_entries/_status.html.haml8
-rw-r--r--app/views/xrd/webfinger.xml.ruby4
-rw-r--r--spec/controllers/oauth/applications_controller_spec.rb9
-rw-r--r--spec/helpers/application_helper_spec.rb54
-rw-r--r--spec/helpers/atom_builder_helper_spec.rb12
-rw-r--r--spec/lib/feed_manager_spec.rb23
-rw-r--r--spec/lib/formatter_spec.rb39
-rw-r--r--spec/lib/tag_manager_spec.rb107
-rw-r--r--spec/models/account_spec.rb4
-rw-r--r--spec/models/media_attachment_spec.rb2
28 files changed, 316 insertions, 180 deletions
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index e43875544a3..5ed8499aadb 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,54 +1,4 @@
module ApplicationHelper
- def unique_tag(date, id, type)
- "tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
- end
-
- def unique_tag_to_local_id(tag, expected_type)
- matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag)
- return matches[1] unless matches.nil?
- end
-
- def local_id?(id)
- id.start_with?("tag:#{Rails.configuration.x.local_domain}")
- end
-
- def content_for_status(actual_status)
- if actual_status.local?
- linkify(actual_status)
- else
- sanitize(actual_status.content, tags: %w(a br p), attributes: %w(href rel))
- end
- end
-
- def account_from_mentions(search_string, mentions)
- mentions.each { |x| return x.account if x.account.acct.eql?(search_string) }
- nil
-
- # If that was unsuccessful, try fetching user from db separately
- # But this shouldn't ever happen if the mentions were created correctly!
- # username, domain = search_string.split('@')
-
- # if domain == Rails.configuration.x.local_domain
- # account = Account.find_local(username)
- # else
- # account = Account.find_remote(username, domain)
- # end
-
- # account
- end
-
- def linkify(status)
- auto_link(HTMLEntities.new.encode(status.text), link: :urls, html: { rel: 'nofollow noopener' }).gsub(Account::MENTION_RE) do |m|
- account = account_from_mentions(Account::MENTION_RE.match(m)[1], status.mentions)
-
- unless account.nil?
- "#{m.split('@').first}<a href=\"#{url_for_target(account)}\" class=\"mention\">@<span>#{account.acct}</span></a>"
- else
- m
- end
- end.html_safe
- end
-
def active_nav_class(path)
current_page?(path) ? 'active' : ''
end
diff --git a/app/helpers/atom_builder_helper.rb b/app/helpers/atom_builder_helper.rb
index c8046182faf..39ea20e3123 100644
--- a/app/helpers/atom_builder_helper.rb
+++ b/app/helpers/atom_builder_helper.rb
@@ -16,7 +16,7 @@ module AtomBuilderHelper
end
def unique_id(xml, date, id, type)
- xml.id_ unique_tag(date, id, type)
+ xml.id_ TagManager.instance.unique_tag(date, id, type)
end
def simple_id(xml, id)
@@ -97,32 +97,8 @@ module AtomBuilderHelper
xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' })
end
- def uri_for_target(target)
- if target.local?
- if target.object_type == :person
- account_url(target)
- else
- unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
- end
- else
- target.uri
- end
- end
-
- def url_for_target(target)
- if target.local?
- if target.object_type == :person
- account_url(target)
- else
- account_stream_entry_url(target.account, target.stream_entry)
- end
- else
- target.url
- end
- end
-
def link_mention(xml, account)
- xml.link(rel: 'mentioned', href: uri_for_target(account))
+ xml.link(rel: 'mentioned', href: TagManager.instance.uri_for(account))
end
def link_enclosure(xml, media)
@@ -145,7 +121,7 @@ module AtomBuilderHelper
def conditionally_formatted(activity)
if activity.is_a?(Status)
- content_for_status(activity.reblog? ? activity.reblog : activity)
+ Formatter.instance.format(activity.reblog? ? activity.reblog : activity)
elsif activity.nil?
nil
else
@@ -155,11 +131,11 @@ module AtomBuilderHelper
def include_author(xml, account)
object_type xml, :person
- uri xml, uri_for_target(account)
+ uri xml, TagManager.instance.uri_for(account)
name xml, account.username
email xml, account.local? ? "#{account.acct}@#{Rails.configuration.x.local_domain}" : account.acct
summary xml, account.note
- link_alternate xml, url_for_target(account)
+ link_alternate xml, TagManager.instance.url_for(account)
link_avatar xml, account
portable_contact xml, account
end
@@ -176,7 +152,7 @@ module AtomBuilderHelper
# Comments need thread element
if stream_entry.threaded?
- in_reply_to xml, uri_for_target(stream_entry.thread), url_for_target(stream_entry.thread)
+ in_reply_to xml, TagManager.instance.uri_for(stream_entry.thread), TagManager.instance.url_for(stream_entry.thread)
end
if stream_entry.targeted?
@@ -185,9 +161,9 @@ module AtomBuilderHelper
include_author xml, stream_entry.target
else
object_type xml, stream_entry.target.object_type
- simple_id xml, uri_for_target(stream_entry.target)
+ simple_id xml, TagManager.instance.uri_for(stream_entry.target)
title xml, stream_entry.target.title
- link_alternate xml, url_for_target(stream_entry.target)
+ link_alternate xml, TagManager.instance.url_for(stream_entry.target)
end
# Statuses have content and author
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index a19d06a85a1..a0c480b94ba 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -1,11 +1,15 @@
+require 'singleton'
+
class FeedManager
+ include Singleton
+
MAX_ITEMS = 800
- def self.key(type, id)
+ def key(type, id)
"feed:#{type}:#{id}"
end
- def self.filter_status?(status, follower)
+ def filter_status?(status, follower)
replied_to_user = status.reply? ? status.thread.account : nil
(status.reply? && !(follower.id = replied_to_user.id || follower.following?(replied_to_user)))
end
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
new file mode 100644
index 00000000000..52e2295208c
--- /dev/null
+++ b/app/lib/formatter.rb
@@ -0,0 +1,47 @@
+require 'singleton'
+
+class Formatter
+ include Singleton
+
+ include ActionView::Helpers::TextHelper
+ include ActionView::Helpers::SanitizeHelper
+
+ def format(status)
+ return reformat(status) unless status.local?
+
+ html = status.text
+ html = encode(html)
+ html = link_urls(html)
+ html = link_mentions(html, status.mentions)
+
+ html.html_safe
+ end
+
+ def reformat(status)
+ sanitize(status.content, tags: %w(a br p), attributes: %w(href rel))
+ end
+
+ private
+
+ def encode(html)
+ HTMLEntities.new.encode(html)
+ end
+
+ def link_urls(html)
+ auto_link(html, link: :urls, html: { rel: 'nofollow noopener' })
+ end
+
+ def link_mentions(html, mentions)
+ html.gsub(Account::MENTION_RE) do |match|
+ acct = Account::MENTION_RE.match(match)[1]
+ mention = mentions.find { |mention| mention.account.acct.eql?(acct) }
+
+ return match if mention.nil?
+ mention_html(match, mention.account)
+ end
+ end
+
+ def mention_html(match, account)
+ "#{match.split('@').first}<a href=\"#{TagManager.instance.url_for(account)}\" class=\"mention\">@<span>#{account.acct}</span></a>"
+ end
+end
diff --git a/app/lib/tag_manager.rb b/app/lib/tag_manager.rb
new file mode 100644
index 00000000000..4d29ca1f8b0
--- /dev/null
+++ b/app/lib/tag_manager.rb
@@ -0,0 +1,41 @@
+require 'singleton'
+
+class TagManager
+ include Singleton
+ include RoutingHelper
+
+ def unique_tag(date, id, type)
+ "tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
+ end
+
+ def unique_tag_to_local_id(tag, expected_type)
+ matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag)
+ return matches[1] unless matches.nil?
+ end
+
+ def local_id?(id)
+ id.start_with?("tag:#{Rails.configuration.x.local_domain}")
+ end
+
+ def uri_for(target)
+ return target.uri if target.respond_to?(:local?) && !target.local?
+
+ case target.object_type
+ when :person
+ account_url(target)
+ else
+ unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
+ end
+ end
+
+ def url_for(target)
+ return target.url if target.respond_to?(:local?) && !target.local?
+
+ case target.object_type
+ when :person
+ account_url(target)
+ else
+ account_stream_entry_url(target.account, target.stream_entry)
+ end
+ end
+end
diff --git a/app/models/feed.rb b/app/models/feed.rb
index e7574956e57..bc3e960d46a 100644
--- a/app/models/feed.rb
+++ b/app/models/feed.rb
@@ -21,7 +21,7 @@ class Feed
private
def key
- FeedManager.key(@type, @account.id)
+ FeedManager.instance.key(@type, @account.id)
end
def redis
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index d51681e5389..973451e3398 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -15,7 +15,7 @@ class FanOutOnWriteService < BaseService
def deliver_to_followers(status)
status.account.followers.each do |follower|
- next if !follower.local? || FeedManager.filter_status?(status, follower)
+ next if !follower.local? || FeedManager.instance.filter_status?(status, follower)
push(:home, follower, status)
end
end
@@ -29,16 +29,16 @@ class FanOutOnWriteService < BaseService
end
def push(type, receiver, status)
- redis.zadd(FeedManager.key(type, receiver.id), status.id, status.id)
+ redis.zadd(FeedManager.instance.key(type, receiver.id), status.id, status.id)
trim(type, receiver)
ActionCable.server.broadcast("timeline:#{receiver.id}", type: 'update', timeline: type, message: inline_render(receiver, status))
end
def trim(type, receiver)
- return unless redis.zcard(FeedManager.key(type, receiver.id)) > FeedManager::MAX_ITEMS
+ return unless redis.zcard(FeedManager.instance.key(type, receiver.id)) > FeedManager::MAX_ITEMS
- last = redis.zrevrange(FeedManager.key(type, receiver.id), FeedManager::MAX_ITEMS - 1, FeedManager::MAX_ITEMS - 1)
- redis.zremrangebyscore(FeedManager.key(type, receiver.id), '-inf', "(#{last.last}")
+ last = redis.zrevrange(FeedManager.instance.key(type, receiver.id), FeedManager::MAX_ITEMS - 1, FeedManager::MAX_ITEMS - 1)
+ redis.zremrangebyscore(FeedManager.instance.key(type, receiver.id), '-inf', "(#{last.last}")
end
def redis
diff --git a/app/services/precompute_feed_service.rb b/app/services/precompute_feed_service.rb
index c8050bbd0d6..df2330d0927 100644
--- a/app/services/precompute_feed_service.rb
+++ b/app/services/precompute_feed_service.rb
@@ -7,8 +7,8 @@ class PrecomputeFeedService < BaseService
instant_return = []
Status.send("as_#{type}_timeline", account).order('created_at desc').limit(FeedManager::MAX_ITEMS).each do |status|
- next if type == :home && FeedManager.filter_status?(status, account)
- redis.zadd(FeedManager.key(type, account.id), status.id, status.id)
+ next if type == :home && FeedManager.instance.filter_status?(status, account)
+ redis.zadd(FeedManager.instance.key(type, account.id), status.id, status.id)
instant_return << status unless instant_return.size > limit
end
diff --git a/app/services/process_feed_service.rb b/app/services/process_feed_service.rb
index 97033a0047b..ba7558e1f27 100644
--- a/app/services/process_feed_service.rb
+++ b/app/services/process_feed_service.rb
@@ -39,10 +39,10 @@ class ProcessFeedService < BaseService
# Also record all media attachments for the status and for the reblogged status if present
unless status.new_record?
record_remote_mentions(status, entry.xpath('./xmlns:link[@rel="mentioned"]'))
-
+
process_attachments(entry, status)
process_attachments(entry.xpath('./activity:object'), status.reblog) if status.reblog?
-
+
DistributionWorker.perform_async(status.id)
end
end
@@ -112,8 +112,8 @@ class ProcessFeedService < BaseService
def find_original_status(_xml, id)
return nil if id.nil?
- if local_id?(id)
- Status.find(unique_tag_to_local_id(id, 'Status'))
+ if TagManager.instance.local_id?(id)
+ Status.find(TagManager.instance.unique_tag_to_local_id(id, 'Status'))
else
Status.find_by(uri: id)
end
diff --git a/app/services/process_interaction_service.rb b/app/services/process_interaction_service.rb
index 536911e2f5f..d9fcf9032df 100644
--- a/app/services/process_interaction_service.rb
+++ b/app/services/process_interaction_service.rb
@@ -45,7 +45,7 @@ class ProcessInteractionService < BaseService
end
def mentions_account?(xml, account)
- xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == url_for_target(account) }
+ xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == TagManager.instance.url_for(account) }
false
end
@@ -85,7 +85,7 @@ class ProcessInteractionService < BaseService
end
def status(xml)
- Status.find(unique_tag_to_local_id(activity_id(xml), 'Status'))
+ Status.find(TagManager.instance.unique_tag_to_local_id(activity_id(xml), 'Status'))
end
def activity_id(xml)
diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb
index db043df08b1..e48a43be07f 100644
--- a/app/services/remove_status_service.rb
+++ b/app/services/remove_status_service.rb
@@ -44,7 +44,7 @@ class RemoveStatusService < BaseService
end
def unpush(type, receiver, status)
- redis.zremrangebyscore(FeedManager.key(type, receiver.id), status.id, status.id)
+ redis.zremrangebyscore(FeedManager.instance.key(type, receiver.id), status.id, status.id)
ActionCable.server.broadcast("timeline:#{receiver.id}", type: 'delete', id: status.id)
end
diff --git a/app/views/accounts/_grid_card.html.haml b/app/views/accounts/_grid_card.html.haml
index d107f5274e8..f65b78470c9 100644
--- a/app/views/accounts/_grid_card.html.haml
+++ b/app/views/accounts/_grid_card.html.haml
@@ -2,7 +2,7 @@
.account-grid-card__header
.avatar= image_tag account.avatar.url(:medium)
.name
- = link_to url_for_target(account) do
+ = link_to TagManager.instance.url_for(account) do
%span.display_name= display_name(account)
%span.username= "@#{account.acct}"
%p.note= truncate(strip_tags(account.note), length: 150)
diff --git a/app/views/accounts/show.atom.ruby b/app/views/accounts/show.atom.ruby
index 8a7bc1b929d..d7b2201d40e 100644
--- a/app/views/accounts/show.atom.ruby
+++ b/app/views/accounts/show.atom.ruby
@@ -10,7 +10,7 @@ Nokogiri::XML::Builder.new do |xml|
include_author xml, @account
end
- link_alternate xml, url_for_target(@account)
+ link_alternate xml, TagManager.instance.url_for(@account)
link_self xml, account_url(@account, format: 'atom')
link_hub xml, Rails.configuration.x.hub_url
link_salmon xml, api_salmon_url(@account.id)
diff --git a/app/views/api/accounts/show.rabl b/app/views/api/accounts/show.rabl
index 4f7cee680ec..d779393d138 100644
--- a/app/views/api/accounts/show.rabl
+++ b/app/views/api/accounts/show.rabl
@@ -2,7 +2,7 @@ object @account
attributes :id, :username, :acct, :display_name, :note
-node(:url) { |account| url_for_target(account) }
+node(:url) { |account| TagManager.instance.url_for(account) }
node(:avatar) { |account| full_asset_url(account.avatar.url(:large, false)) }
node(:followers_count) { |account| account.followers.count }
node(:following_count) { |account| account.following.count }
diff --git a/app/views/api/statuses/show.rabl b/app/views/api/statuses/show.rabl
index c7d028e1410..047436b6118 100644
--- a/app/views/api/statuses/show.rabl
+++ b/app/views/api/statuses/show.rabl
@@ -1,9 +1,9 @@
object @status
attributes :id, :created_at, :in_reply_to_id
-node(:uri) { |status| uri_for_target(status) }
-node(:content) { |status| content_for_status(status) }
-node(:url) { |status| url_for_target(status) }
+node(:uri) { |status| TagManager.instance.uri_for(status) }
+node(:content) { |status| Formatter.instance.format(status) }
+node(:url) { |status| TagManager.instance.url_for(status) }
node(:reblogs_count) { |status| status.reblogs_count }
node(:favourites_count) { |status| status.favourites_count }
node(:favourited) { |status| current_account.favourited?(status) }
diff --git a/app/views/notification_mailer/follow.text.erb b/app/views/notification_mailer/follow.text.erb
index 6f70c0d9017..a13b6d7a996 100644
--- a/app/views/notification_mailer/follow.text.erb
+++ b/app/views/notification_mailer/follow.text.erb
@@ -2,4 +2,4 @@
<%= @account.acct %> is now following you!
-<%= url_for_target(@account) %>
+<%= TagManager.instance.url_for(@account) %>
diff --git a/app/views/notification_mailer/mention.text.erb b/app/views/notification_mailer/mention.text.erb
index b093f4ec022..f3582749d48 100644
--- a/app/views/notification_mailer/mention.text.erb
+++ b/