Merge remote-tracking branch 'openstreetmap/pull/903'
[rails.git] / lib / rich_text.rb
1 module RichText
2   def self.new(format, text)
3     case format
4     when "html" then HTML.new(text || "")
5     when "markdown" then Markdown.new(text || "")
6     when "text" then Text.new(text || "")
7     end
8   end
9
10   class SimpleFormat
11     include ActionView::Helpers::TextHelper
12     include ActionView::Helpers::OutputSafetyHelper
13
14     def sanitize(text)
15       Sanitize.clean(text, Sanitize::Config::OSM).html_safe
16     end
17   end
18
19   class Base < String
20     include ActionView::Helpers::TagHelper
21
22     def spam_score
23       link_count = 0
24       link_size = 0
25
26       doc = Nokogiri::HTML(to_html)
27
28       if doc.content.length > 0
29         doc.xpath("//a").each do |link|
30           link_count += 1
31           link_size += link.content.length
32         end
33
34         link_proportion = link_size.to_f / doc.content.length.to_f
35       else
36         link_proportion = 0
37       end
38
39       [link_proportion - 0.2, 0.0].max * 200 + link_count * 40
40     end
41
42     protected
43
44     def simple_format(text)
45       SimpleFormat.new.simple_format(text)
46     end
47
48     def linkify(text)
49       if text.html_safe?
50         Rinku.auto_link(text, :urls, tag_options(:rel => "nofollow")).html_safe
51       else
52         Rinku.auto_link(text, :urls, tag_options(:rel => "nofollow"))
53       end
54     end
55   end
56
57   class HTML < Base
58     def to_html
59       linkify(sanitize(simple_format(self)))
60     end
61
62     def to_text
63       to_s
64     end
65
66     private
67
68     def sanitize(text)
69       Sanitize.clean(text, Sanitize::Config::OSM).html_safe
70     end
71   end
72
73   class Markdown < Base
74     def to_html
75       Markdown.html_parser.render(self).html_safe
76     end
77
78     def to_text
79       to_s
80     end
81
82     def self.html_renderer
83       @html_renderer ||= Renderer.new(:filter_html => true, :safe_links_only => true)
84     end
85
86     def self.html_parser
87       @html_parser ||= Redcarpet::Markdown.new(html_renderer, :no_intra_emphasis => true, :autolink => true, :space_after_headers => true)
88     end
89
90     class Renderer < Redcarpet::Render::XHTML
91       def link(link, _title, alt_text)
92         "<a rel=\"nofollow\" href=\"#{link}\">#{alt_text}</a>"
93       end
94
95       def autolink(link, link_type)
96         if link_type == :email
97           "<a rel=\"nofollow\" href=\"mailto:#{link}\">#{link}</a>"
98         else
99           "<a rel=\"nofollow\" href=\"#{link}\">#{link}</a>"
100         end
101       end
102     end
103   end
104
105   class Text < Base
106     def to_html
107       linkify(simple_format(ERB::Util.html_escape(self)))
108     end
109
110     def to_text
111       to_s
112     end
113   end
114 end