Skip to content
Extraits de code Groupes Projets
formatter.rb 6,77 ko
Newer Older
  • Learn to ignore specific revisions
  • require_relative './sanitize_config'
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
      include RoutingHelper
    
      def format(status, **options)
    
        if status.reblog?
          prepend_reblog = status.reblog.account.acct
          status         = status.proper
        else
          prepend_reblog = false
        end
    
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
        unless status.local?
          html = reformat(raw_content)
          html = encode_custom_emojis(html, status.emojis) if options[:custom_emojify]
    
          return html.html_safe # rubocop:disable Rails/OutputSafety
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
        end
    
        linkable_accounts = status.mentions.map(&:account)
        linkable_accounts << status.account
    
    
        html = raw_content
    
        html = "RT @#{prepend_reblog} #{html}" if prepend_reblog
        html = encode_and_link_urls(html, linkable_accounts)
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
        html = encode_custom_emojis(html, status.emojis) if options[:custom_emojify]
    
        html = simple_format(html, {}, sanitize: false)
    
        html.html_safe # rubocop:disable Rails/OutputSafety
    
        sanitize(html, Sanitize::Config::MASTODON_STRICT)
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
      def plaintext(status)
        return status.text if status.local?
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
    
        text = status.text.gsub(/(<br \/>|<br>|<\/p>)+/) { |match| "#{match}\n" }
        strip_tags(text)
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
      end
    
    
      def simplified_format(account, **options)
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
        html = account.local? ? linkify(account.note) : reformat(account.note)
        html = encode_custom_emojis(html, account.emojis) if options[:custom_emojify]
    
        html.html_safe # rubocop:disable Rails/OutputSafety
    
      def sanitize(html, config)
        Sanitize.fragment(html, config)
      end
    
    
      def format_spoiler(status)
        html = encode(status.spoiler_text)
        html = encode_custom_emojis(html, status.emojis)
        html.html_safe # rubocop:disable Rails/OutputSafety
      end
    
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
      def format_field(account, str)
        return reformat(str).html_safe unless account.local? # rubocop:disable Rails/OutputSafety
        encode_and_link_urls(str, me: true).html_safe # rubocop:disable Rails/OutputSafety
      end
    
    
      def linkify(text)
        html = encode_and_link_urls(text)
        html = simple_format(html, {}, sanitize: false)
        html = html.delete("\n")
    
        html.html_safe # rubocop:disable Rails/OutputSafety
      end
    
    
      private
    
      def encode(html)
        HTMLEntities.new.encode(html)
      end
    
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
      def encode_and_link_urls(html, accounts = nil, options = {})
    
        entities = Extractor.extract_entities_with_indices(html, extract_url_without_protocol: false)
    
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
        if accounts.is_a?(Hash)
          options  = accounts
          accounts = nil
        end
    
    
        rewrite(html.dup, entities) do |entity|
          if entity[:url]
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
            link_to_url(entity, options)
    
          elsif entity[:hashtag]
            link_to_hashtag(entity)
          elsif entity[:screen_name]
    
            link_to_mention(entity, accounts)
    
      def count_tag_nesting(tag)
        if tag[1] == '/' then -1
        elsif tag[-2] == '/' then 0
        else 1
        end
      end
    
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
      def encode_custom_emojis(html, emojis)
        return html if emojis.empty?
    
    
        emoji_map = emojis.map { |e| [e.shortcode, full_asset_url(e.image.url(:static))] }.to_h
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
    
        i                     = -1
    
        tag_open_index        = nil
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
        inside_shortname      = false
        shortname_start_index = -1
    
        invisible_depth       = 0
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
    
        while i + 1 < html.size
          i += 1
    
    
          if invisible_depth.zero? && inside_shortname && html[i] == ':'
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
            shortcode = html[shortname_start_index + 1..i - 1]
            emoji     = emoji_map[shortcode]
    
            if emoji
              replacement = "<img draggable=\"false\" class=\"emojione\" alt=\":#{shortcode}:\" title=\":#{shortcode}:\" src=\"#{emoji}\" />"
              before_html = shortname_start_index.positive? ? html[0..shortname_start_index - 1] : ''
              html        = before_html + replacement + html[i + 1..-1]
              i          += replacement.size - (shortcode.size + 2) - 1
            else
              i -= 1
            end
    
            inside_shortname = false
    
          elsif tag_open_index && html[i] == '>'
            tag = html[tag_open_index..i]
            tag_open_index = nil
            if invisible_depth.positive?
              invisible_depth += count_tag_nesting(tag)
            elsif tag == '<span class="invisible">'
              invisible_depth = 1
            end
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
          elsif html[i] == '<'
    
            tag_open_index   = i
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
            inside_shortname = false
    
          elsif !tag_open_index && html[i] == ':'
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
            inside_shortname      = true
            shortname_start_index = i
          end
        end
    
        html
      end
    
    
      def rewrite(text, entities)
        chars = text.to_s.to_char_a
    
        entities = entities.sort_by do |entity|
          indices = entity.respond_to?(:indices) ? entity.indices : entity[:indices]
          indices.first
        end
    
        result = []
    
        last_index = entities.reduce(0) do |index, entity|
    
          indices = entity.respond_to?(:indices) ? entity.indices : entity[:indices]
          result << encode(chars[index...indices.first].join)
          result << yield(entity)
    
        result << encode(chars[last_index..-1].join)
    
        result.flatten.join
      end
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
      def link_to_url(entity, options = {})
    
        url        = Addressable::URI.parse(entity[:url])
        html_attrs = { target: '_blank', rel: 'nofollow noopener' }
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
        html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me]
    
    
        Twitter::Autolink.send(:link_to_text, entity, link_html(entity[:url]), url, html_attrs)
    
      rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
    
      def link_to_mention(entity, linkable_accounts)
    
        acct = entity[:screen_name]
    
    
        return link_to_account(acct) unless linkable_accounts
    
        account = linkable_accounts.find { |item| TagManager.instance.same_acct?(item.acct, acct) }
        account ? mention_html(account) : "@#{acct}"
    
      def link_to_account(acct)
        username, domain = acct.split('@')
    
    
        domain  = nil if TagManager.instance.local_domain?(domain)
    
    Eugen Rochko's avatar
    Eugen Rochko a validé
        account = EntityCache.instance.mention(username, domain)
    
        account ? mention_html(account) : "@#{acct}"
    
      def link_to_hashtag(entity)
        hashtag_html(entity[:hashtag])
    
        url    = Addressable::URI.parse(url).to_s
    
        prefix = url.match(/\Ahttps?:\/\/(www\.)?/).to_s
        text   = url[prefix.length, 30]
        suffix = url[prefix.length + 30..-1]
    
        cutoff = url[prefix.length..-1].length > 30
    
        "<span class=\"invisible\">#{encode(prefix)}</span><span class=\"#{cutoff ? 'ellipsis' : ''}\">#{encode(text)}</span><span class=\"invisible\">#{encode(suffix)}</span>"
    
      def hashtag_html(tag)
    
        "<a href=\"#{tag_url(tag.downcase)}\" class=\"mention hashtag\" rel=\"tag\">#<span>#{tag}</span></a>"
    
      def mention_html(account)
        "<span class=\"h-card\"><a href=\"#{TagManager.instance.url_for(account)}\" class=\"u-url mention\">@<span>#{account.username}</span></a></span>"