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/application_extension.rb9
-rw-r--r--app/lib/feed_manager.rb10
-rw-r--r--app/lib/formatter.rb15
-rw-r--r--app/lib/hash_object.rb10
-rw-r--r--app/lib/settings/extend.rb11
-rw-r--r--app/lib/settings/scoped_settings.rb14
-rw-r--r--app/lib/status_length_validator.rb10
-rw-r--r--app/lib/url_validator.rb14
8 files changed, 90 insertions, 3 deletions
diff --git a/app/lib/application_extension.rb b/app/lib/application_extension.rb
new file mode 100644
index 000000000..93c0f42f0
--- /dev/null
+++ b/app/lib/application_extension.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module ApplicationExtension
+  extend ActiveSupport::Concern
+
+  included do
+    validates :website, url: true, unless: 'website.blank?'
+  end
+end
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 0056321fa..cdd26e69c 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -43,12 +43,22 @@ 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)
       redis.zadd(timeline_key, status.id, status.id)
     end
 
     trim(:home, into_account.id)
   end
 
+  def unmerge_from_timeline(from_account, into_account)
+    timeline_key = key(:home, into_account.id)
+
+    from_account.statuses.select('id').find_each do |status|
+      redis.zrem(timeline_key, status.id)
+      redis.zremrangebyscore(timeline_key, status.id, status.id)
+    end
+  end
+
   def inline_render(target_account, template, object)
     rabl_scope = Class.new do
       include RoutingHelper
diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb
index 04386d295..ff2a16f1b 100644
--- a/app/lib/formatter.rb
+++ b/app/lib/formatter.rb
@@ -14,7 +14,7 @@ class Formatter
 
     html = status.text
     html = encode(html)
-    html = simple_format(html, sanitize: false)
+    html = simple_format(html, {}, sanitize: false)
     html = html.gsub(/\n/, '')
     html = link_urls(html)
     html = link_mentions(html, status.mentions)
@@ -32,6 +32,7 @@ class Formatter
 
     html = encode(account.note)
     html = link_urls(html)
+    html = link_hashtags(html)
 
     html.html_safe # rubocop:disable Rails/OutputSafety
   end
@@ -43,8 +44,8 @@ class Formatter
   end
 
   def link_urls(html)
-    auto_link(html, link: :urls, html: { rel: 'nofollow noopener', target: '_blank' }) do |text|
-      truncate(text.gsub(/\Ahttps?:\/\/(www\.)?/, ''), length: 30)
+    html.gsub(URI.regexp(%w(http https))) do |match|
+      link_html(match)
     end
   end
 
@@ -63,6 +64,14 @@ class Formatter
     end
   end
 
+  def link_html(url)
+    prefix = url.match(/\Ahttps?:\/\/(www\.)?/).to_s
+    text   = url[prefix.length, 30]
+    suffix = url[prefix.length + 30..-1]
+
+    "<a rel=\"nofollow noopener\" target=\"_blank\" href=\"#{url}\"><span class=\"invisible\">#{prefix}</span><span class=\"ellipsis\">#{text}</span><span class=\"invisible\">#{suffix}</span></a>"
+  end
+
   def hashtag_html(match)
     prefix, affix = match.split('#')
     "#{prefix}<a href=\"#{tag_url(affix.downcase)}\" class=\"mention hashtag\">#<span>#{affix}</span></a>"
diff --git a/app/lib/hash_object.rb b/app/lib/hash_object.rb
new file mode 100644
index 000000000..274c020ad
--- /dev/null
+++ b/app/lib/hash_object.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class HashObject
+  def initialize(hash)
+    hash.each do |k, v|
+      instance_variable_set("@#{k}", v)
+      self.class.send(:define_method, k, proc { instance_variable_get("@#{k}") })
+    end
+  end
+end
diff --git a/app/lib/settings/extend.rb b/app/lib/settings/extend.rb
new file mode 100644
index 000000000..407c3480f
--- /dev/null
+++ b/app/lib/settings/extend.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Settings
+  module Extend
+    extend ActiveSupport::Concern
+
+    def settings
+      ScopedSettings.for_thing(self)
+    end
+  end
+end
diff --git a/app/lib/settings/scoped_settings.rb b/app/lib/settings/scoped_settings.rb
new file mode 100644
index 000000000..82b70d128
--- /dev/null
+++ b/app/lib/settings/scoped_settings.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Settings
+  class ScopedSettings < ::Setting
+    def self.for_thing(object)
+      @object = object
+      self
+    end
+
+    def self.thing_scoped
+      unscoped.where(thing_type: @object.class.base_class.to_s, thing_id: @object.id)
+    end
+  end
+end
diff --git a/app/lib/status_length_validator.rb b/app/lib/status_length_validator.rb
new file mode 100644
index 000000000..55135a598
--- /dev/null
+++ b/app/lib/status_length_validator.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class StatusLengthValidator < ActiveModel::Validator
+  MAX_CHARS = 500
+
+  def validate(status)
+    return unless status.local? && !status.reblog?
+    status.errors.add(:text, I18n.t('statuses.over_character_limit', max: MAX_CHARS)) if [status.text, status.spoiler_text].join.length > MAX_CHARS
+  end
+end
diff --git a/app/lib/url_validator.rb b/app/lib/url_validator.rb
new file mode 100644
index 000000000..4a5c4ef3f
--- /dev/null
+++ b/app/lib/url_validator.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class UrlValidator < ActiveModel::EachValidator
+  def validate_each(record, attribute, value)
+    record.errors.add(attribute, I18n.t('applications.invalid_url')) unless compliant?(value)
+  end
+
+  private
+
+  def compliant?(url)
+    parsed_url = Addressable::URI.parse(url)
+    !parsed_url.nil? && %w(http https).include?(parsed_url.scheme) && parsed_url.host
+  end
+end