diff options
author | Mislav Marohnić <mislav.marohnic@gmail.com> | 2009-03-11 11:27:44 +0100 |
---|---|---|
committer | Mislav Marohnić <mislav.marohnic@gmail.com> | 2010-05-24 11:25:24 +0200 |
commit | 8f0b2138ee979799092e0489f7298289c90901b9 (patch) | |
tree | b4d8fd7b9e74c2f7fabcfd96ff42668a9c57bd74 /actionpack | |
parent | 133ada6ab0f0cb7bef2bd40dbc18f2d5bc6b964e (diff) | |
download | rails-8f0b2138ee979799092e0489f7298289c90901b9.tar.gz rails-8f0b2138ee979799092e0489f7298289c90901b9.tar.bz2 rails-8f0b2138ee979799092e0489f7298289c90901b9.zip |
avoid auto_linking already linked emails; more robust detection of linked URLs
References #1523 [#1862 state:resolved] [#3591 state:resolved]
Add test that shows how link text can contain HTML if needed:
the trick is using block form in combination with `raw`.
Let link text be automatically HTML-escaped
[#2017 state:resolved]
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/lib/action_view/helpers/text_helper.rb | 27 | ||||
-rw-r--r-- | actionpack/test/template/text_helper_test.rb | 37 |
2 files changed, 48 insertions, 16 deletions
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index fa930ac626..41423d4e2e 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -539,7 +539,12 @@ module ActionView AUTO_LINK_RE = %r{ (?: ([\w+.:-]+:)// | www\. ) [^\s<]+ - }x unless const_defined?(:AUTO_LINK_RE) + }x + + # regexps for determining context, used high-volume + AUTO_LINK_CRE = [/<[^>]+$/, /^[^>]*>/, /<a\b.*?>/i, /<\/a>/i] + + AUTO_EMAIL_RE = /[\w.!#\$%+-]+@[\w-]+(?:\.[\w-]+)+/ BRACKETS = { ']' => '[', ')' => '(', '}' => '{' } @@ -550,9 +555,8 @@ module ActionView text.gsub(AUTO_LINK_RE) do scheme, href = $1, $& punctuation = [] - left, right = $`, $' - # detect already linked URLs and URLs in the middle of a tag - if left =~ /<[^>]+$/ && right =~ /^[^>]*>/ + + if auto_linked?($`, $') # do not change string; URL is already linked href else @@ -568,7 +572,7 @@ module ActionView link_text = block_given?? yield(href) : href href = 'http://' + href unless scheme - content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation.reverse.join('') + content_tag(:a, link_text, link_attributes.merge('href' => href)) + punctuation.reverse.join('') end end end @@ -576,11 +580,10 @@ module ActionView # Turns all email addresses into clickable links. If a block is given, # each email is yielded and the result is used as the link text. def auto_link_email_addresses(text, html_options = {}) - body = text.dup - text.gsub(/([\w\.!#\$%\-+]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do - text = $1 + text.gsub(AUTO_EMAIL_RE) do + text = $& - if body.match(/<a\b[^>]*>(.*)(#{Regexp.escape(text)})(.*)<\/a>/) + if auto_linked?($`, $') text else display_text = (block_given?) ? yield(text) : text @@ -588,6 +591,12 @@ module ActionView end end end + + # Detects already linked context or position in the middle of a tag + def auto_linked?(left, right) + (left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or + (left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3]) + end end end end diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 13f55cacb6..0b84c8f811 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -304,6 +304,7 @@ class TextHelperTest < ActionView::TestCase assert_equal %(<p>Link #{link_result_with_options}</p>), auto_link("<p>Link #{link_raw}</p>", :all, {:target => "_blank"}) assert_equal %(Go to #{link_result}.), auto_link(%(Go to #{link_raw}.)) assert_equal %(<p>Go to #{link_result}, then say hello to #{email_result}.</p>), auto_link(%(<p>Go to #{link_raw}, then say hello to #{email_raw}.</p>)) + assert_equal %(#{link_result} #{link_result}), auto_link(%(#{link_result} #{link_raw})) email2_raw = '+david@loudthinking.com' email2_result = %{<a href="mailto:#{email2_raw}">#{email2_raw}</a>} @@ -390,9 +391,24 @@ class TextHelperTest < ActionView::TestCase def test_auto_link_already_linked linked1 = generate_result('Ruby On Rails', 'http://www.rubyonrails.com') - linked2 = generate_result('www.rubyonrails.com', 'http://www.rubyonrails.com') + linked2 = %('<a href="http://www.example.com">www.example.com</a>') + linked3 = %('<a href="http://www.example.com" rel="nofollow">www.example.com</a>') + linked4 = %('<a href="http://www.example.com"><b>www.example.com</b></a>') + linked5 = %('<a href="#close">close</a> <a href="http://www.example.com"><b>www.example.com</b></a>') assert_equal linked1, auto_link(linked1) assert_equal linked2, auto_link(linked2) + assert_equal linked3, auto_link(linked3) + assert_equal linked4, auto_link(linked4) + assert_equal linked5, auto_link(linked5) + + linked_email = %Q(<a href="mailto:david@loudthinking.com">Mail me</a>) + assert_equal linked_email, auto_link(linked_email) + end + + def test_auto_link_within_tags + link_raw = 'http://www.rubyonrails.org/images/rails.png' + link_result = %Q(<img src="#{link_raw}" />) + assert_equal link_result, auto_link(link_result) end def test_auto_link_with_brackets @@ -412,12 +428,6 @@ class TextHelperTest < ActionView::TestCase assert_equal "{link: #{link3_result}}", auto_link("{link: #{link3_raw}}") end - def test_auto_link_in_tags - link_raw = 'http://www.rubyonrails.org/images/rails.png' - link_result = %Q(<img src="#{link_raw}" />) - assert_equal link_result, auto_link(link_result) - end - def test_auto_link_at_eol url1 = "http://api.rubyonrails.com/Foo.html" url2 = "http://www.ruby-doc.org/core/Bar.html" @@ -431,6 +441,19 @@ class TextHelperTest < ActionView::TestCase assert_equal %(<p><a href="#{url}">#{url[0...7]}...</a><br /><a href="mailto:#{email}">#{email[0...7]}...</a><br /></p>), auto_link("<p>#{url}<br />#{email}<br /></p>") { |url| truncate(url, :length => 10) } end + + def test_auto_link_with_block_with_html + pic = "http://example.com/pic.png" + url = "http://example.com/album?a&b=c" + + assert_equal %(My pic: <a href="#{pic}"><img src="#{pic}" width="160px"></a> -- full album here #{generate_result(url)}), auto_link("My pic: #{pic} -- full album here #{url}") { |link| + if link =~ /\.(jpg|gif|png|bmp|tif)$/i + raw %(<img src="#{link}" width="160px">) + else + link + end + } + end def test_auto_link_with_options_hash assert_dom_equal 'Welcome to my new blog at <a href="http://www.myblog.com/" class="menu" target="_blank">http://www.myblog.com/</a>. Please e-mail me at <a href="mailto:me@email.com" class="menu" target="_blank">me@email.com</a>.', |