diff --git a/app/helpers/html_helper.rb b/app/helpers/html_helper.rb new file mode 100644 index 000000000..b989adf12 --- /dev/null +++ b/app/helpers/html_helper.rb @@ -0,0 +1,46 @@ +module HtmlHelper + def format_html(html) + fragment = Nokogiri::HTML.fragment(html) + + auto_link(fragment) + + fragment.to_html.html_safe + end + + private + EXCLUDED_ELEMENTS = %w[ a figcaption pre code ] + EMAIL_REGEXP = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/ + URL_REGEXP = URI::DEFAULT_PARSER.make_regexp(%w[http https]) + + def auto_link(fragment) + fragment.traverse do |node| + next unless auto_linkable_node?(node) + + content = node.text + linked_content = content.dup + + auto_link_urls(linked_content) + auto_link_emails(linked_content) + + if linked_content != content + node.replace(Nokogiri::HTML.fragment(linked_content)) + end + end + end + + def auto_linkable_node?(node) + node.text? && node.ancestors.none? { |ancestor| EXCLUDED_ELEMENTS.include?(ancestor.name) } + end + + def auto_link_urls(linked_content) + linked_content.gsub!(URL_REGEXP) do |match| + %(#{match}) + end + end + + def auto_link_emails(text) + text.gsub!(EMAIL_REGEXP) do |match| + %(#{match}) + end + end +end diff --git a/app/views/layouts/action_text/contents/_content.html.erb b/app/views/layouts/action_text/contents/_content.html.erb index 0dec72775..b264733d7 100644 --- a/app/views/layouts/action_text/contents/_content.html.erb +++ b/app/views/layouts/action_text/contents/_content.html.erb @@ -1,3 +1,3 @@
Check this: https://example.com
), + format_html("Check this: https://example.com
") + end + + test "respect existing links" do + assert_equal_html \ + %(Check this: https://example.com
), + format_html("Check this: https://example.com
") + end + + test "converts email addresses into mailto links" do + assert_equal_html \ + %(Contact us at support@example.com
), + format_html("Contact us at support@example.com
") + end + + test "respect existing linked emails" do + assert_equal_html \ + %(Contact us at support@example.com
), + format_html(%(Contact us at support@example.com
)) + end + + test "don't autolink content in excluded elements" do + %w[ figcaption pre code ].each do |element| + assert_equal_html \ + "<#{element}>Check this: https://example.com#{element}>", + format_html("<#{element}>Check this: https://example.com#{element}>") + end + end +end