about summary refs log tree commit diff
path: root/app/lib
diff options
context:
space:
mode:
Diffstat (limited to 'app/lib')
-rw-r--r--app/lib/exceptions.rb3
-rw-r--r--app/lib/feed_manager.rb20
-rw-r--r--app/lib/formatter.rb29
-rw-r--r--app/lib/tag_manager.rb10
4 files changed, 51 insertions, 11 deletions
diff --git a/app/lib/exceptions.rb b/app/lib/exceptions.rb
index 359228c29..200da9fe1 100644
--- a/app/lib/exceptions.rb
+++ b/app/lib/exceptions.rb
@@ -2,5 +2,6 @@
 
 module Mastodon
   class Error < StandardError; end
-  class NotPermitted < Error; end
+  class NotPermittedError < Error; end
+  class ValidationError < Error; end
 end
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 623a1af03..cd6ca1291 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -22,8 +22,18 @@ class FeedManager
   end
 
   def push(timeline_type, account, status)
-    redis.zadd(key(timeline_type, account.id), status.id, status.reblog? ? status.reblog_of_id : status.id)
-    trim(timeline_type, account.id)
+    timeline_key = key(timeline_type, account.id)
+
+    if status.reblog?
+      # If the original status is within 40 statuses from top, do not re-insert it into the feed
+      rank = redis.zrevrank(timeline_key, status.reblog_of_id)
+      return if !rank.nil? && rank < 40
+      redis.zadd(timeline_key, status.id, status.reblog_of_id)
+    else
+      redis.zadd(timeline_key, status.id, status.id)
+      trim(timeline_type, account.id)
+    end
+
     broadcast(account.id, event: 'update', payload: inline_render(account, 'api/v1/statuses/show', status))
   end
 
@@ -42,7 +52,7 @@ class FeedManager
     timeline_key = key(:home, into_account.id)
 
     from_account.statuses.limit(MAX_ITEMS).each do |status|
-      next if filter?(:home, status, into_account)
+      next if status.direct_visibility? || filter?(:home, status, into_account)
       redis.zadd(timeline_key, status.id, status.id)
     end
 
@@ -85,6 +95,8 @@ class FeedManager
   end
 
   def filter_from_home?(status, receiver)
+    return true if receiver.muting?(status.account)
+
     should_filter = false
 
     if status.reply? && status.in_reply_to_id.nil?
@@ -95,6 +107,8 @@ class FeedManager
       should_filter &&= !(status.account_id == status.in_reply_to_account_id) # and it's not a self-reply
     elsif status.reblog?                                                      # Filter out a reblog
       should_filter = receiver.blocking?(status.reblog.account)               # if I'm blocking the reblogged person
+      should_filter ||= receiver.muting?(status.reblog.account)               # or muting that person
+      should_filter ||= status.reblog.account.blocking?(receiver)             # or if the author of the reblogged status is blocking me
     end
 
     should_filter ||= receiver.blocking?(status.mentions.map(&:account_id))   # or if it mentions someone I blocked
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 044407a6c..da7ad2027 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -24,7 +24,12 @@ class Formatter
   end
 
   def reformat(html)
-    sanitize(html, tags: %w(a br p), attributes: %w(href rel))
+    sanitize(html, tags: %w(a br p span), attributes: %w(href rel class))
+  end
+
+  def plaintext(status)
+    return status.text if status.local?
+    strip_tags(status.text)
   end
 
   def simplified_format(account)
@@ -32,6 +37,7 @@ class Formatter
 
     html = encode(account.note)
     html = link_urls(html)
+    html = link_accounts(html)
     html = link_hashtags(html)
 
     html.html_safe # rubocop:disable Rails/OutputSafety
@@ -44,20 +50,31 @@ class Formatter
   end
 
   def link_urls(html)
-    html.gsub(URI.regexp(%w(http https))) do |match|
-      link_html(match)
-    end
+    Twitter::Autolink.auto_link_urls(html, url_target: '_blank',
+                                           link_attribute_block: lambda { |_, a| a[:rel] << ' noopener' },
+                                           link_text_block: lambda { |_, text| link_html(text) })
   end
 
   def link_mentions(html, mentions)
     html.gsub(Account::MENTION_RE) do |match|
       acct    = Account::MENTION_RE.match(match)[1]
-      mention = mentions.find { |item| item.account.acct.casecmp(acct).zero? }
+      mention = mentions.find { |item| TagManager.instance.same_acct?(item.account.acct, acct) }
 
       mention.nil? ? match : mention_html(match, mention.account)
     end
   end
 
+  def link_accounts(html)
+    html.gsub(Account::MENTION_RE) do |match|
+      acct = Account::MENTION_RE.match(match)[1]
+      username, domain = acct.split('@')
+      domain = nil if TagManager.instance.local_domain?(domain)
+      account = Account.find_remote(username, domain)
+
+      account.nil? ? match : mention_html(match, account)
+    end
+  end
+
   def link_hashtags(html)
     html.gsub(Tag::HASHTAG_RE) do |match|
       hashtag_html(match)
@@ -70,7 +87,7 @@ class Formatter
     suffix = url[prefix.length + 30..-1]
     cutoff = url[prefix.length..-1].length > 30
 
-    "<a rel=\"nofollow noopener\" target=\"_blank\" href=\"#{url}\"><span class=\"invisible\">#{prefix}</span><span class=\"#{cutoff ? 'ellipsis' : ''}\">#{text}</span><span class=\"invisible\">#{suffix}</span></a>"
+    "<span class=\"invisible\">#{prefix}</span><span class=\"#{cutoff ? 'ellipsis' : ''}\">#{text}</span><span class=\"invisible\">#{suffix}</span>"
   end
 
   def hashtag_html(match)
diff --git a/app/lib/tag_manager.rb b/app/lib/tag_manager.rb
index 9fef70fda..2a5e7a409 100644
--- a/app/lib/tag_manager.rb
+++ b/app/lib/tag_manager.rb
@@ -60,6 +60,12 @@ class TagManager
     domain.nil? || domain.gsub(/[\/]/, '').casecmp(Rails.configuration.x.local_domain).zero?
   end
 
+  def same_acct?(canonical, needle)
+    return true if canonical.casecmp(needle).zero?
+    username, domain = needle.split('@')
+    local_domain?(domain) && canonical.casecmp(username).zero?
+  end
+
   def local_url?(url)
     uri    = Addressable::URI.parse(url)
     domain = uri.host + (uri.port ? ":#{uri.port}" : '')
@@ -82,7 +88,9 @@ class TagManager
 
     case target.object_type
     when :person
-      account_url(target)
+      short_account_url(target)
+    when :note, :comment, :activity
+      short_account_status_url(target.account, target)
     else
       account_stream_entry_url(target.account, target.stream_entry)
     end