]> git.openstreetmap.org Git - rails.git/blob - lib/rich_text.rb
Merge remote-tracking branch 'upstream/pull/5050'
[rails.git] / lib / rich_text.rb
1 module RichText
2   SPAMMY_PHRASES = [
3     "Business Description:", "Additional Keywords:"
4   ].freeze
5
6   def self.new(format, text)
7     case format
8     when "html" then HTML.new(text || "")
9     when "markdown" then Markdown.new(text || "")
10     when "text" then Text.new(text || "")
11     end
12   end
13
14   class SimpleFormat
15     include ActionView::Helpers::TextHelper
16     include ActionView::Helpers::OutputSafetyHelper
17
18     def sanitize(text, _options = {})
19       Sanitize.clean(text, Sanitize::Config::OSM).html_safe
20     end
21   end
22
23   class Base < String
24     include ActionView::Helpers::TagHelper
25
26     def spam_score
27       link_count = 0
28       link_size = 0
29
30       doc = Nokogiri::HTML(to_html)
31
32       if doc.content.empty?
33         link_proportion = 0
34       else
35         doc.xpath("//a").each do |link|
36           link_count += 1
37           link_size += link.content.length
38         end
39
40         link_proportion = link_size.to_f / doc.content.length
41       end
42
43       spammy_phrases = SPAMMY_PHRASES.count do |phrase|
44         doc.content.include?(phrase)
45       end
46
47       ([link_proportion - 0.2, 0.0].max * 200) +
48         (link_count * 40) +
49         (spammy_phrases * 40)
50     end
51
52     def image
53       nil
54     end
55
56     def image_alt
57       nil
58     end
59
60     protected
61
62     def simple_format(text)
63       SimpleFormat.new.simple_format(text)
64     end
65
66     def sanitize(text)
67       Sanitize.clean(text, Sanitize::Config::OSM).html_safe
68     end
69
70     def linkify(text, mode = :urls)
71       if text.html_safe?
72         Rinku.auto_link(text, mode, tag_builder.tag_options(:rel => "nofollow noopener noreferrer")).html_safe
73       else
74         Rinku.auto_link(text, mode, tag_builder.tag_options(:rel => "nofollow noopener noreferrer"))
75       end
76     end
77   end
78
79   class HTML < Base
80     def to_html
81       linkify(sanitize(simple_format(self)))
82     end
83
84     def to_text
85       to_s
86     end
87   end
88
89   class Markdown < Base
90     def to_html
91       linkify(sanitize(document.to_html), :all)
92     end
93
94     def to_text
95       to_s
96     end
97
98     def image
99       @image_element = first_image_element(document.root) unless defined? @image_element
100       @image_element.attr["src"] if @image_element
101     end
102
103     def image_alt
104       @image_element = first_image_element(document.root) unless defined? @image_element
105       @image_element.attr["alt"] if @image_element
106     end
107
108     private
109
110     def document
111       @document ||= Kramdown::Document.new(self)
112     end
113
114     def first_image_element(element)
115       return element if image?(element) && element.attr["src"].present?
116
117       element.children.find do |child|
118         nested_image = first_image_element(child)
119         break nested_image if nested_image
120       end
121     end
122
123     def image?(element)
124       element.type == :img || (element.type == :html_element && element.value == "img")
125     end
126   end
127
128   class Text < Base
129     def to_html
130       linkify(simple_format(ERB::Util.html_escape(self)))
131     end
132
133     def to_text
134       to_s
135     end
136   end
137 end