diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7503437c..394e05ae 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -502,7 +502,7 @@ module ApplicationHelper @parsed_headings = [] text = parse_non_pre_blocks(text) do |text| - [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings].each do |method_name| + [:parse_inline_attachments, :parse_wiki_links, :parse_redmine_links, :parse_headings, :parse_relative_urls].each do |method_name| send method_name, text, project, obj, attr, only_path, options end end @@ -543,6 +543,41 @@ module ApplicationHelper parsed end + RELATIVE_LINK_RE = %r{ + ]+?)')| + (?:"(\/[^>]+?)") + ) + )| + [^>] + )* + > + [^<]*?<\/a> # content and closing link tag. + }x unless const_defined?(:RELATIVE_LINK_RE) + + def parse_relative_urls(text, project, obj, attr, only_path, options) + return if only_path + text.gsub!(RELATIVE_LINK_RE) do |m| + href, relative_url = $1, $2 || $3 + next m unless href.present? + if defined?(request) && request.present? + # we have a request! + protocol, host_with_port = request.protocol, request.host_with_port + elsif @controller + # use the same methods as url_for in the Mailer + url_opts = @controller.class.default_url_options + next m unless url_opts && url_opts[:protocol] && url_opts[:host] + protocol, host_with_port = "#{url_opts[:protocol]}://", url_opts[:host] + else + next m + end + m.sub href, " href=\"#{protocol}#{host_with_port}#{relative_url}\"" + end + end + def parse_inline_attachments(text, project, obj, attr, only_path, options) # when using an image link, try to use an attachment, if possible if options[:attachments] || (obj && obj.respond_to?(:attachments)) diff --git a/test/unit/helpers/application_helper_test.rb b/test/unit/helpers/application_helper_test.rb index 48c99c05..8c9af006 100644 --- a/test/unit/helpers/application_helper_test.rb +++ b/test/unit/helpers/application_helper_test.rb @@ -27,6 +27,10 @@ class ApplicationHelperTest < ActionView::TestCase super end + def request + @request ||= ActionController::TestRequest.new + end + context "#link_to_if_authorized" do context "authorized user" do should "be tested" @@ -144,6 +148,34 @@ RAW to_test.each { |text, result| assert_equal "

#{result}

", textilizable(text) } end + def test_textile_relative_to_full_links_in_a_controller + # we have a request here + { + # shouldn't change non-relative links + 'This is a "link":http://foo.bar' => 'This is a
link', + 'This is an intern "link":/foo/bar' => 'This is an intern link', + 'This is an intern "link":/foo/bar and an extern "link":http://foo.bar' => 'This is an intern link and an extern link', + }.each { |text, result| assert_equal "

#{result}

", textilizable(text, :only_path => false) } + end + + def test_textile_relative_to_full_links_in_the_mailer + # we don't a request here + undef request + # mimic the mailer default_url_options + @controller.class.class_eval { + def self.default_url_options + ::Mailer.default_url_options + end + } + + { + # shouldn't change non-relative links + 'This is a "link":http://foo.bar' => 'This is a link', + 'This is an intern "link":/foo/bar' => 'This is an intern link', + 'This is an intern "link":/foo/bar and an extern "link":http://foo.bar' => 'This is an intern link and an extern link', + }.each { |text, result| assert_equal "

#{result}

", textilizable(text, :only_path => false) } + end + def test_redmine_links issue_link = link_to('#3', {:controller => 'issues', :action => 'show', :id => 3}, :class => 'issue status-1 priority-1 overdue', :title => 'Error 281 when updating a recipe (New)')