Refactored generation of unique tags, URIs and object URLs into own classes,
as well as formatting of content
This commit is contained in:
		
							parent
							
								
									735b4cc62e
								
							
						
					
					
						commit
						3cc47beb6e
					
				|  | @ -1,54 +1,4 @@ | ||||||
| module ApplicationHelper | 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) |   def active_nav_class(path) | ||||||
|     current_page?(path) ? 'active' : '' |     current_page?(path) ? 'active' : '' | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ module AtomBuilderHelper | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def unique_id(xml, date, id, type) |   def unique_id(xml, date, id, type) | ||||||
|     xml.id_ unique_tag(date, id, type) |     xml.id_ TagManager.instance.unique_tag(date, id, type) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def simple_id(xml, id) |   def simple_id(xml, id) | ||||||
|  | @ -97,32 +97,8 @@ module AtomBuilderHelper | ||||||
|     xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' }) |     xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' }) | ||||||
|   end |   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) |   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 |   end | ||||||
| 
 | 
 | ||||||
|   def link_enclosure(xml, media) |   def link_enclosure(xml, media) | ||||||
|  | @ -145,7 +121,7 @@ module AtomBuilderHelper | ||||||
| 
 | 
 | ||||||
|   def conditionally_formatted(activity) |   def conditionally_formatted(activity) | ||||||
|     if activity.is_a?(Status) |     if activity.is_a?(Status) | ||||||
|       content_for_status(activity.reblog? ? activity.reblog : activity) |       Formatter.instance.format(activity.reblog? ? activity.reblog : activity) | ||||||
|     elsif activity.nil? |     elsif activity.nil? | ||||||
|       nil |       nil | ||||||
|     else |     else | ||||||
|  | @ -155,11 +131,11 @@ module AtomBuilderHelper | ||||||
| 
 | 
 | ||||||
|   def include_author(xml, account) |   def include_author(xml, account) | ||||||
|     object_type      xml, :person |     object_type      xml, :person | ||||||
|     uri              xml, uri_for_target(account) |     uri              xml, TagManager.instance.uri_for(account) | ||||||
|     name             xml, account.username |     name             xml, account.username | ||||||
|     email            xml, account.local? ? "#{account.acct}@#{Rails.configuration.x.local_domain}" : account.acct |     email            xml, account.local? ? "#{account.acct}@#{Rails.configuration.x.local_domain}" : account.acct | ||||||
|     summary          xml, account.note |     summary          xml, account.note | ||||||
|     link_alternate   xml, url_for_target(account) |     link_alternate   xml, TagManager.instance.url_for(account) | ||||||
|     link_avatar      xml, account |     link_avatar      xml, account | ||||||
|     portable_contact xml, account |     portable_contact xml, account | ||||||
|   end |   end | ||||||
|  | @ -176,7 +152,7 @@ module AtomBuilderHelper | ||||||
| 
 | 
 | ||||||
|     # Comments need thread element |     # Comments need thread element | ||||||
|     if stream_entry.threaded? |     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 |     end | ||||||
| 
 | 
 | ||||||
|     if stream_entry.targeted? |     if stream_entry.targeted? | ||||||
|  | @ -185,9 +161,9 @@ module AtomBuilderHelper | ||||||
|           include_author xml, stream_entry.target |           include_author xml, stream_entry.target | ||||||
|         else |         else | ||||||
|           object_type    xml, stream_entry.target.object_type |           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 |           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 |         end | ||||||
| 
 | 
 | ||||||
|         # Statuses have content and author |         # Statuses have content and author | ||||||
|  |  | ||||||
|  | @ -1,11 +1,15 @@ | ||||||
|  | require 'singleton' | ||||||
|  | 
 | ||||||
| class FeedManager | class FeedManager | ||||||
|  |   include Singleton | ||||||
|  | 
 | ||||||
|   MAX_ITEMS = 800 |   MAX_ITEMS = 800 | ||||||
| 
 | 
 | ||||||
|   def self.key(type, id) |   def key(type, id) | ||||||
|     "feed:#{type}:#{id}" |     "feed:#{type}:#{id}" | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def self.filter_status?(status, follower) |   def filter_status?(status, follower) | ||||||
|     replied_to_user = status.reply? ? status.thread.account : nil |     replied_to_user = status.reply? ? status.thread.account : nil | ||||||
|     (status.reply? && !(follower.id = replied_to_user.id || follower.following?(replied_to_user))) |     (status.reply? && !(follower.id = replied_to_user.id || follower.following?(replied_to_user))) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  | @ -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 | ||||||
|  | @ -21,7 +21,7 @@ class Feed | ||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|   def key |   def key | ||||||
|     FeedManager.key(@type, @account.id) |     FeedManager.instance.key(@type, @account.id) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def redis |   def redis | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ class FanOutOnWriteService < BaseService | ||||||
| 
 | 
 | ||||||
|   def deliver_to_followers(status) |   def deliver_to_followers(status) | ||||||
|     status.account.followers.each do |follower| |     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) |       push(:home, follower, status) | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  | @ -29,16 +29,16 @@ class FanOutOnWriteService < BaseService | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def push(type, receiver, status) |   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) |     trim(type, receiver) | ||||||
|     ActionCable.server.broadcast("timeline:#{receiver.id}", type: 'update', timeline: type, message: inline_render(receiver, status)) |     ActionCable.server.broadcast("timeline:#{receiver.id}", type: 'update', timeline: type, message: inline_render(receiver, status)) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def trim(type, receiver) |   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) |     last = redis.zrevrange(FeedManager.instance.key(type, receiver.id), FeedManager::MAX_ITEMS - 1, FeedManager::MAX_ITEMS - 1) | ||||||
|     redis.zremrangebyscore(FeedManager.key(type, receiver.id), '-inf', "(#{last.last}") |     redis.zremrangebyscore(FeedManager.instance.key(type, receiver.id), '-inf', "(#{last.last}") | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def redis |   def redis | ||||||
|  |  | ||||||
|  | @ -7,8 +7,8 @@ class PrecomputeFeedService < BaseService | ||||||
|     instant_return = [] |     instant_return = [] | ||||||
| 
 | 
 | ||||||
|     Status.send("as_#{type}_timeline", account).order('created_at desc').limit(FeedManager::MAX_ITEMS).each do |status| |     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) |       next if type == :home && FeedManager.instance.filter_status?(status, account) | ||||||
|       redis.zadd(FeedManager.key(type, account.id), status.id, status.id) |       redis.zadd(FeedManager.instance.key(type, account.id), status.id, status.id) | ||||||
|       instant_return << status unless instant_return.size > limit |       instant_return << status unless instant_return.size > limit | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -112,8 +112,8 @@ class ProcessFeedService < BaseService | ||||||
|   def find_original_status(_xml, id) |   def find_original_status(_xml, id) | ||||||
|     return nil if id.nil? |     return nil if id.nil? | ||||||
| 
 | 
 | ||||||
|     if local_id?(id) |     if TagManager.instance.local_id?(id) | ||||||
|       Status.find(unique_tag_to_local_id(id, 'Status')) |       Status.find(TagManager.instance.unique_tag_to_local_id(id, 'Status')) | ||||||
|     else |     else | ||||||
|       Status.find_by(uri: id) |       Status.find_by(uri: id) | ||||||
|     end |     end | ||||||
|  |  | ||||||
|  | @ -45,7 +45,7 @@ class ProcessInteractionService < BaseService | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def mentions_account?(xml, account) |   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 |     false | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  | @ -85,7 +85,7 @@ class ProcessInteractionService < BaseService | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def status(xml) |   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 |   end | ||||||
| 
 | 
 | ||||||
|   def activity_id(xml) |   def activity_id(xml) | ||||||
|  |  | ||||||
|  | @ -44,7 +44,7 @@ class RemoveStatusService < BaseService | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def unpush(type, receiver, status) |   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) |     ActionCable.server.broadcast("timeline:#{receiver.id}", type: 'delete', id: status.id) | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
|   .account-grid-card__header |   .account-grid-card__header | ||||||
|     .avatar= image_tag account.avatar.url(:medium) |     .avatar= image_tag account.avatar.url(:medium) | ||||||
|     .name |     .name | ||||||
|       = link_to url_for_target(account) do |       = link_to TagManager.instance.url_for(account) do | ||||||
|         %span.display_name= display_name(account) |         %span.display_name= display_name(account) | ||||||
|         %span.username= "@#{account.acct}" |         %span.username= "@#{account.acct}" | ||||||
|   %p.note= truncate(strip_tags(account.note), length: 150) |   %p.note= truncate(strip_tags(account.note), length: 150) | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ Nokogiri::XML::Builder.new do |xml| | ||||||
|       include_author xml, @account |       include_author xml, @account | ||||||
|     end |     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_self      xml, account_url(@account, format: 'atom') | ||||||
|     link_hub       xml, Rails.configuration.x.hub_url |     link_hub       xml, Rails.configuration.x.hub_url | ||||||
|     link_salmon    xml, api_salmon_url(@account.id) |     link_salmon    xml, api_salmon_url(@account.id) | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ object @account | ||||||
| 
 | 
 | ||||||
| attributes :id, :username, :acct, :display_name, :note | 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(:avatar)          { |account| full_asset_url(account.avatar.url(:large, false)) } | ||||||
| node(:followers_count) { |account| account.followers.count } | node(:followers_count) { |account| account.followers.count } | ||||||
| node(:following_count) { |account| account.following.count } | node(:following_count) { |account| account.following.count } | ||||||
|  |  | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| object @status | object @status | ||||||
| attributes :id, :created_at, :in_reply_to_id | attributes :id, :created_at, :in_reply_to_id | ||||||
| 
 | 
 | ||||||
| node(:uri)              { |status| uri_for_target(status) } | node(:uri)              { |status| TagManager.instance.uri_for(status) } | ||||||
| node(:content)          { |status| content_for_status(status) } | node(:content)          { |status| Formatter.instance.format(status) } | ||||||
| node(:url)              { |status| url_for_target(status) } | node(:url)              { |status| TagManager.instance.url_for(status) } | ||||||
| node(:reblogs_count)    { |status| status.reblogs_count } | node(:reblogs_count)    { |status| status.reblogs_count } | ||||||
| node(:favourites_count) { |status| status.favourites_count } | node(:favourites_count) { |status| status.favourites_count } | ||||||
| node(:favourited)       { |status| current_account.favourited?(status) } | node(:favourited)       { |status| current_account.favourited?(status) } | ||||||
|  |  | ||||||
|  | @ -2,4 +2,4 @@ | ||||||
| 
 | 
 | ||||||
| <%= @account.acct %> is now following you! | <%= @account.acct %> is now following you! | ||||||
| 
 | 
 | ||||||
| <%= url_for_target(@account) %> | <%= TagManager.instance.url_for(@account) %> | ||||||
|  |  | ||||||
|  | @ -4,4 +4,4 @@ You were mentioned by <%= @status.account.acct %> in: | ||||||
| 
 | 
 | ||||||
| <%= strip_tags(@status.content) %> | <%= strip_tags(@status.content) %> | ||||||
| 
 | 
 | ||||||
| <%= url_for_target(@status) %> | <%= TagManager.instance.url_for(@status) %> | ||||||
|  |  | ||||||
|  | @ -2,4 +2,4 @@ | ||||||
|   .content |   .content | ||||||
|     %strong= link_to follow.account.acct, account_path(follow.account) |     %strong= link_to follow.account.acct, account_path(follow.account) | ||||||
|     is now following |     is now following | ||||||
|     %strong= link_to follow.target_account.acct, url_for_target(follow.target_account) |     %strong= link_to follow.target_account.acct, TagManager.instance.url_for(follow.target_account) | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ | ||||||
|     .pre-header |     .pre-header | ||||||
|       %i.fa.fa-retweet |       %i.fa.fa-retweet | ||||||
|       Shared by |       Shared by | ||||||
|       = link_to display_name(status.account), url_for_target(status.account), class: 'name' |       = link_to display_name(status.account), TagManager.instance.url_for(status.account), class: 'name' | ||||||
| 
 | 
 | ||||||
|   .entry__container |   .entry__container | ||||||
|     .avatar |     .avatar | ||||||
|  | @ -21,10 +21,10 @@ | ||||||
|     .entry__container__container |     .entry__container__container | ||||||
|       .header |       .header | ||||||
|         .header__left |         .header__left | ||||||
|           = link_to url_for_target(proper_status(status).account), class: 'name' do |           = link_to TagManager.instance.url_for(proper_status(status).account), class: 'name' do | ||||||
|             %strong= display_name(proper_status(status).account) |             %strong= display_name(proper_status(status).account) | ||||||
|             = "@#{proper_status(status).account.acct}" |             = "@#{proper_status(status).account.acct}" | ||||||
|           = link_to url_for_target(proper_status(status)), class: 'time' do |           = link_to TagManager.instance.url_for(proper_status(status)), class: 'time' do | ||||||
|             %span{ title: proper_status(status).created_at } |             %span{ title: proper_status(status).created_at } | ||||||
|               = relative_time(proper_status(status).created_at) |               = relative_time(proper_status(status).created_at) | ||||||
| 
 | 
 | ||||||
|  | @ -36,7 +36,7 @@ | ||||||
|             %i.fa.fa-star |             %i.fa.fa-star | ||||||
|             %span.counter-number= proper_status(status).favourites_count |             %span.counter-number= proper_status(status).favourites_count | ||||||
| 
 | 
 | ||||||
|       .content= content_for_status(proper_status(status)) |       .content= Formatter.instance.format(proper_status(status)) | ||||||
| 
 | 
 | ||||||
|       %ul.media-attachments |       %ul.media-attachments | ||||||
|         - status.media_attachments.each do |media| |         - status.media_attachments.each do |media| | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| Nokogiri::XML::Builder.new do |xml| | Nokogiri::XML::Builder.new do |xml| | ||||||
|   xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do |   xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do | ||||||
|     xml.Subject @canonical_account_uri |     xml.Subject @canonical_account_uri | ||||||
|     xml.Alias url_for_target(@account) |     xml.Alias TagManager.instance.url_for(@account) | ||||||
|     xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: url_for_target(@account)) |     xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: TagManager.instance.url_for(@account)) | ||||||
|     xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(@account, format: 'atom')) |     xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: account_url(@account, format: 'atom')) | ||||||
|     xml.Link(rel: 'salmon', href: api_salmon_url(@account.id)) |     xml.Link(rel: 'salmon', href: api_salmon_url(@account.id)) | ||||||
|     xml.Link(rel: 'magic-public-key', href: @magic_key) |     xml.Link(rel: 'magic-public-key', href: @magic_key) | ||||||
|  |  | ||||||
|  | @ -1,8 +1,15 @@ | ||||||
| require 'rails_helper' | require 'rails_helper' | ||||||
| 
 | 
 | ||||||
| RSpec.describe Oauth::ApplicationsController, type: :controller do | RSpec.describe Oauth::ApplicationsController, type: :controller do | ||||||
|  |   before do | ||||||
|  |     sign_in Fabricate(:user), scope: :user | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   describe 'GET #index' do |   describe 'GET #index' do | ||||||
|     it 'returns http success' |     it 'returns http success' do | ||||||
|  |       get :index | ||||||
|  |       expect(response).to have_http_status(:success) | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe 'POST #create' do |   describe 'POST #create' do | ||||||
|  |  | ||||||
|  | @ -1,59 +1,5 @@ | ||||||
| require 'rails_helper' | require 'rails_helper' | ||||||
| 
 | 
 | ||||||
| RSpec.describe ApplicationHelper, type: :helper do | RSpec.describe ApplicationHelper, type: :helper do | ||||||
|   let(:local_domain) { Rails.configuration.x.local_domain } |  | ||||||
| 
 | 
 | ||||||
|   describe '#unique_tag' do |  | ||||||
|     it 'returns a string' do |  | ||||||
|       expect(helper.unique_tag(Time.now, 12, 'Status')).to be_a String |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   describe '#unique_tag_to_local_id' do |  | ||||||
|     it 'returns the ID part' do |  | ||||||
|       expect(helper.unique_tag_to_local_id("tag:#{local_domain};objectId=12:objectType=Status", 'Status')).to eql '12' |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   describe '#local_id?' do |  | ||||||
|     it 'returns true for a local ID' do |  | ||||||
|       expect(helper.local_id?("tag:#{local_domain};objectId=12:objectType=Status")).to be true |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     it 'returns false for a foreign ID' do |  | ||||||
|       expect(helper.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   describe '#linkify' do |  | ||||||
|     let(:alice) { Fabricate(:account, username: 'alice') } |  | ||||||
|     let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', url: 'http://example.com/bob') } |  | ||||||
| 
 |  | ||||||
|     it 'turns mention of remote user into link' do |  | ||||||
|       status = Fabricate(:status, text: 'Hello @bob@example.com', account: bob) |  | ||||||
|       status.mentions.create(account: bob) |  | ||||||
|       expect(helper.linkify(status)).to match('<a href="http://example.com/bob" class="mention">@<span>bob@example.com</span></a>') |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     it 'turns mention of local user into link' do |  | ||||||
|       status = Fabricate(:status, text: 'Hello @alice', account: bob) |  | ||||||
|       status.mentions.create(account: alice) |  | ||||||
|       expect(helper.linkify(status)).to match('<a href="http://test.host/users/alice" class="mention">@<span>alice</span></a>') |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     it 'leaves mention of unresolvable user alone' do |  | ||||||
|       status = Fabricate(:status, text: 'Hello @foo', account: bob) |  | ||||||
|       expect(helper.linkify(status)).to match('Hello @foo') |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   describe '#account_from_mentions' do |  | ||||||
|     let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com') } |  | ||||||
|     let(:status) { Fabricate(:status, text: 'Hello @bob@example.com', account: bob) } |  | ||||||
|     let(:mentions) { [Mention.create(status: status, account: bob)] } |  | ||||||
| 
 |  | ||||||
|     it 'returns account' do |  | ||||||
|       expect(helper.account_from_mentions('bob@example.com', mentions)).to eq bob |  | ||||||
|     end |  | ||||||
|   end |  | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do | ||||||
|   describe '#unique_id' do |   describe '#unique_id' do | ||||||
|     it 'creates an id' do |     it 'creates an id' do | ||||||
|       time = Time.now |       time = Time.now | ||||||
|       expect(used_in_builder { |xml| helper.unique_id(xml, time, 1, 'Status') }).to match "<id>#{helper.unique_tag(time, 1, 'Status')}</id>" |       expect(used_in_builder { |xml| helper.unique_id(xml, time, 1, 'Status') }).to match "<id>#{TagManager.instance.unique_tag(time, 1, 'Status')}</id>" | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  | @ -146,18 +146,10 @@ RSpec.describe AtomBuilderHelper, type: :helper do | ||||||
|     let(:account) { Fabricate(:account, username: 'alice') } |     let(:account) { Fabricate(:account, username: 'alice') } | ||||||
| 
 | 
 | ||||||
|     it 'creates a link' do |     it 'creates a link' do | ||||||
|       expect(used_in_builder { |xml| helper.link_mention(xml, account) }).to match '<link rel="mentioned" href="http://test.host/users/alice"/>' |       expect(used_in_builder { |xml| helper.link_mention(xml, account) }).to match '<link rel="mentioned" href="https://cb6e6126.ngrok.io/users/alice"/>' | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe '#disambiguate_uri' do |  | ||||||
|     pending |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   describe '#disambiguate_url' do |  | ||||||
|     pending |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   describe '#include_author' do |   describe '#include_author' do | ||||||
|     pending |     pending | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | require 'rails_helper' | ||||||
|  | 
 | ||||||
|  | RSpec.describe FeedManager do | ||||||
|  |   describe '#key' do | ||||||
|  |     subject { FeedManager.instance.key(:home, 1) } | ||||||
|  | 
 | ||||||
|  |     it 'returns a string' do | ||||||
|  |       expect(subject).to be_a String | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '#filter_status?' do | ||||||
|  |     let(:followee) { Fabricate(:account, username: 'alice') } | ||||||
|  |     let(:status)   { Fabricate(:status, text: 'Hello world', account: followee) } | ||||||
|  |     let(:follower) { Fabricate(:account, username: 'bob') } | ||||||
|  | 
 | ||||||
|  |     subject { FeedManager.instance.filter_status?(status, follower) } | ||||||
|  | 
 | ||||||
|  |     it 'returns a boolean value' do | ||||||
|  |       expect(subject).to be false | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | require 'rails_helper' | ||||||
|  | 
 | ||||||
|  | RSpec.describe Formatter do | ||||||
|  |   let(:account)       { Fabricate(:account, username: 'alice') } | ||||||
|  |   let(:local_status)  { Fabricate(:status, text: 'Hello world http://google.com', account: account) } | ||||||
|  |   let(:remote_status) { Fabricate(:status, text: '<script>alert("Hello")</script> Beep boop', uri: 'beepboop', account: account) } | ||||||
|  | 
 | ||||||
|  |   describe '#format' do | ||||||
|  |     subject { Formatter.instance.format(local_status) } | ||||||
|  | 
 | ||||||
|  |     it 'returns a string' do | ||||||
|  |       expect(subject).to be_a String | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'contains plain text' do | ||||||
|  |       expect(subject).to match('Hello world') | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'contains a link' do | ||||||
|  |       expect(subject).to match('<a rel="nofollow noopener" href="http://google.com">http://google.com</a>') | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '#reformat' do | ||||||
|  |     subject { Formatter.instance.format(remote_status) } | ||||||
|  | 
 | ||||||
|  |     it 'returns a string' do | ||||||
|  |       expect(subject).to be_a String | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'contains plain text' do | ||||||
|  |       expect(subject).to match('Beep boop') | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'does not contain scripts' do | ||||||
|  |       expect(subject).to_not match('<script>alert("Hello")</script>') | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -0,0 +1,107 @@ | ||||||
|  | require 'rails_helper' | ||||||
|  | 
 | ||||||
|  | RSpec.describe TagManager do | ||||||
|  |   let(:local_domain) { Rails.configuration.x.local_domain } | ||||||
|  | 
 | ||||||
|  |   describe '#unique_tag' do | ||||||
|  |     it 'returns a string' do | ||||||
|  |       expect(TagManager.instance.unique_tag(Time.now, 12, 'Status')).to be_a String | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '#unique_tag_to_local_id' do | ||||||
|  |     it 'returns the ID part' do | ||||||
|  |       expect(TagManager.instance.unique_tag_to_local_id("tag:#{local_domain};objectId=12:objectType=Status", 'Status')).to eql '12' | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '#local_id?' do | ||||||
|  |     it 'returns true for a local ID' do | ||||||
|  |       expect(TagManager.instance.local_id?("tag:#{local_domain};objectId=12:objectType=Status")).to be true | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     it 'returns false for a foreign ID' do | ||||||
|  |       expect(TagManager.instance.local_id?('tag:foreign.tld;objectId=12:objectType=Status')).to be false | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '#uri_for' do | ||||||
|  |     let(:alice)  { Fabricate(:account, username: 'alice') } | ||||||
|  |     let(:bob)    { Fabricate(:account, username: 'bob') } | ||||||
|  |     let(:status) { Fabricate(:status, text: 'Hello world', account: alice) } | ||||||
|  | 
 | ||||||
|  |     subject { TagManager.instance.uri_for(target) } | ||||||
|  | 
 | ||||||
|  |     context 'Account' do | ||||||
|  |       let(:target) { alice } | ||||||
|  | 
 | ||||||
|  |       it 'returns a string' do | ||||||
|  |         expect(subject).to be_a String | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'Status' do | ||||||
|  |       let(:target) { status } | ||||||
|  | 
 | ||||||
|  |       it 'returns a string' do | ||||||
|  |         expect(subject).to be_a String | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'Follow' do | ||||||
|  |       let(:target) { Fabricate(:follow, account: alice, target_account: bob) } | ||||||
|  | 
 | ||||||
|  |       it 'returns a string' do | ||||||
|  |         expect(subject).to be_a String | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'Favourite' do | ||||||
|  |       let(:target) { Fabricate(:favourite, account: bob, status: status) } | ||||||
|  | 
 | ||||||
|  |       it 'returns a string' do | ||||||
|  |         expect(subject).to be_a String | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '#url_for' do | ||||||
|  |     let(:alice)  { Fabricate(:account, username: 'alice') } | ||||||
|  |     let(:bob)    { Fabricate(:account, username: 'bob') } | ||||||
|  |     let(:status) { Fabricate(:status, text: 'Hello world', account: alice) } | ||||||
|  | 
 | ||||||
|  |     subject { TagManager.instance.url_for(target) } | ||||||
|  | 
 | ||||||
|  |     context 'Account' do | ||||||
|  |       let(:target) { alice } | ||||||
|  | 
 | ||||||
|  |       it 'returns a URL' do | ||||||
|  |         expect(subject).to be_a String | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'Status' do | ||||||
|  |       let(:target) { status } | ||||||
|  | 
 | ||||||
|  |       it 'returns a URL' do | ||||||
|  |         expect(subject).to be_a String | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'Follow' do | ||||||
|  |       let(:target) { Fabricate(:follow, account: alice, target_account: bob) } | ||||||
|  | 
 | ||||||
|  |       it 'returns a URL' do | ||||||
|  |         expect(subject).to be_a String | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'Favourite' do | ||||||
|  |       let(:target) { Fabricate(:favourite, account: bob, status: status) } | ||||||
|  | 
 | ||||||
|  |       it 'returns a URL' do | ||||||
|  |         expect(subject).to be_a String | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
|  | @ -112,6 +112,10 @@ RSpec.describe Account, type: :model do | ||||||
|     pending |     pending | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |   describe '.find_remote' do | ||||||
|  |     pending | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   describe 'MENTION_RE' do |   describe 'MENTION_RE' do | ||||||
|     subject { Account::MENTION_RE } |     subject { Account::MENTION_RE } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| require 'rails_helper' | require 'rails_helper' | ||||||
| 
 | 
 | ||||||
| RSpec.describe MediaAttachment, type: :model do | RSpec.describe MediaAttachment, type: :model do | ||||||
|   pending "add some examples to (or delete) #{__FILE__}" | 
 | ||||||
| end | end | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue