]> source.dussan.org Git - redmine.git/commitdiff
Fixed that Redmine links should not be parsed inside links (#18301).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Fri, 14 Nov 2014 21:31:27 +0000 (21:31 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Fri, 14 Nov 2014 21:31:27 +0000 (21:31 +0000)
git-svn-id: http://svn.redmine.org/redmine/trunk@13596 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/helpers/application_helper.rb
test/unit/helpers/application_helper_test.rb

index 6476fba5174108eafe374c92fc5c29587b32ae0a..f93755240165eb044f3f7c79b15fcc3df19efdc4 100644 (file)
@@ -754,144 +754,148 @@ module ApplicationHelper
   #     identifier:version:1.0.0
   #     identifier:source:some/file
   def parse_redmine_links(text, default_project, obj, attr, only_path, options)
-    text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-_]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
-      leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $18, $14 || $19, $15, $17
-      link = nil
-      project = default_project
-      if project_identifier
-        project = Project.visible.find_by_identifier(project_identifier)
-      end
-      if esc.nil?
-        if prefix.nil? && sep == 'r'
-          if project
-            repository = nil
-            if repo_identifier
-              repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
-            else
-              repository = project.repository
-            end
-            # project.changesets.visible raises an SQL error because of a double join on repositories
-            if repository &&
-                 (changeset = Changeset.visible.
-                                  find_by_repository_id_and_revision(repository.id, identifier))
-              link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"),
-                             {:only_path => only_path, :controller => 'repositories',
-                              :action => 'revision', :id => project,
-                              :repository_id => repository.identifier_param,
-                              :rev => changeset.revision},
-                             :class => 'changeset',
-                             :title => truncate_single_line_raw(changeset.comments, 100))
-            end
-          end
-        elsif sep == '#'
-          oid = identifier.to_i
-          case prefix
-          when nil
-            if oid.to_s == identifier &&
-                  issue = Issue.visible.includes(:status).find_by_id(oid)
-              anchor = comment_id ? "note-#{comment_id}" : nil
-              link = link_to(h("##{oid}#{comment_suffix}"),
-                             {:only_path => only_path, :controller => 'issues',
-                              :action => 'show', :id => oid, :anchor => anchor},
-                             :class => issue.css_classes,
-                             :title => "#{issue.subject.truncate(100)} (#{issue.status.name})")
-            end
-          when 'document'
-            if document = Document.visible.find_by_id(oid)
-              link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
-                                                :class => 'document'
-            end
-          when 'version'
-            if version = Version.visible.find_by_id(oid)
-              link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
-                                              :class => 'version'
-            end
-          when 'message'
-            if message = Message.visible.includes(:parent).find_by_id(oid)
-              link = link_to_message(message, {:only_path => only_path}, :class => 'message')
-            end
-          when 'forum'
-            if board = Board.visible.find_by_id(oid)
-              link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
-                                             :class => 'board'
-            end
-          when 'news'
-            if news = News.visible.find_by_id(oid)
-              link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
-                                            :class => 'news'
-            end
-          when 'project'
-            if p = Project.visible.find_by_id(oid)
-              link = link_to_project(p, {:only_path => only_path}, :class => 'project')
-            end
-          end
-        elsif sep == ':'
-          # removes the double quotes if any
-          name = identifier.gsub(%r{^"(.*)"$}, "\\1")
-          name = CGI.unescapeHTML(name)
-          case prefix
-          when 'document'
-            if project && document = project.documents.visible.find_by_title(name)
-              link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
-                                                :class => 'document'
-            end
-          when 'version'
-            if project && version = project.versions.visible.find_by_name(name)
-              link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
-                                              :class => 'version'
-            end
-          when 'forum'
-            if project && board = project.boards.visible.find_by_name(name)
-              link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
-                                             :class => 'board'
-            end
-          when 'news'
-            if project && news = project.news.visible.find_by_title(name)
-              link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
-                                            :class => 'news'
-            end
-          when 'commit', 'source', 'export'
+    text.gsub!(%r{<a( [^>]+?)?>(.*?)</a>|([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-_]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-_]+)\|)?(r)))((\d+)((#note)?-(\d+))?)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
+      tag_content, leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier, comment_suffix, comment_id = $1, $3, $4, $5, $6, $7, $12, $13, $10 || $14 || $20, $16 || $21, $17, $19
+      if tag_content
+        $&
+      else
+        link = nil
+        project = default_project
+        if project_identifier
+          project = Project.visible.find_by_identifier(project_identifier)
+        end
+        if esc.nil?
+          if prefix.nil? && sep == 'r'
             if project
               repository = nil
-              if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$}
-                repo_prefix, repo_identifier, name = $1, $2, $3
+              if repo_identifier
                 repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
               else
                 repository = project.repository
               end
-              if prefix == 'commit'
-                if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
-                  link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier},
-                                               :class => 'changeset',
-                                               :title => truncate_single_line_raw(changeset.comments, 100)
-                end
-              else
-                if repository && User.current.allowed_to?(:browse_repository, project)
-                  name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
-                  path, rev, anchor = $1, $3, $5
-                  link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
-                                                          :path => to_path_param(path),
-                                                          :rev => rev,
-                                                          :anchor => anchor},
-                                                         :class => (prefix == 'export' ? 'source download' : 'source')
-                end
+              # project.changesets.visible raises an SQL error because of a double join on repositories
+              if repository &&
+                   (changeset = Changeset.visible.
+                                    find_by_repository_id_and_revision(repository.id, identifier))
+                link = link_to(h("#{project_prefix}#{repo_prefix}r#{identifier}"),
+                               {:only_path => only_path, :controller => 'repositories',
+                                :action => 'revision', :id => project,
+                                :repository_id => repository.identifier_param,
+                                :rev => changeset.revision},
+                               :class => 'changeset',
+                               :title => truncate_single_line_raw(changeset.comments, 100))
               end
-              repo_prefix = nil
             end
-          when 'attachment'
-            attachments = options[:attachments] || []
-            attachments += obj.attachments if obj.respond_to?(:attachments)
-            if attachments && attachment = Attachment.latest_attach(attachments, name)
-              link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
+          elsif sep == '#'
+            oid = identifier.to_i
+            case prefix
+            when nil
+              if oid.to_s == identifier &&
+                    issue = Issue.visible.includes(:status).find_by_id(oid)
+                anchor = comment_id ? "note-#{comment_id}" : nil
+                link = link_to(h("##{oid}#{comment_suffix}"),
+                               {:only_path => only_path, :controller => 'issues',
+                                :action => 'show', :id => oid, :anchor => anchor},
+                               :class => issue.css_classes,
+                               :title => "#{issue.subject.truncate(100)} (#{issue.status.name})")
+              end
+            when 'document'
+              if document = Document.visible.find_by_id(oid)
+                link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
+                                                  :class => 'document'
+              end
+            when 'version'
+              if version = Version.visible.find_by_id(oid)
+                link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
+                                                :class => 'version'
+              end
+            when 'message'
+              if message = Message.visible.includes(:parent).find_by_id(oid)
+                link = link_to_message(message, {:only_path => only_path}, :class => 'message')
+              end
+            when 'forum'
+              if board = Board.visible.find_by_id(oid)
+                link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
+                                               :class => 'board'
+              end
+            when 'news'
+              if news = News.visible.find_by_id(oid)
+                link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
+                                              :class => 'news'
+              end
+            when 'project'
+              if p = Project.visible.find_by_id(oid)
+                link = link_to_project(p, {:only_path => only_path}, :class => 'project')
+              end
             end
-          when 'project'
-            if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
-              link = link_to_project(p, {:only_path => only_path}, :class => 'project')
+          elsif sep == ':'
+            # removes the double quotes if any
+            name = identifier.gsub(%r{^"(.*)"$}, "\\1")
+            name = CGI.unescapeHTML(name)
+            case prefix
+            when 'document'
+              if project && document = project.documents.visible.find_by_title(name)
+                link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
+                                                  :class => 'document'
+              end
+            when 'version'
+              if project && version = project.versions.visible.find_by_name(name)
+                link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
+                                                :class => 'version'
+              end
+            when 'forum'
+              if project && board = project.boards.visible.find_by_name(name)
+                link = link_to h(board.name), {:only_path => only_path, :controller => 'boards', :action => 'show', :id => board, :project_id => board.project},
+                                               :class => 'board'
+              end
+            when 'news'
+              if project && news = project.news.visible.find_by_title(name)
+                link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
+                                              :class => 'news'
+              end
+            when 'commit', 'source', 'export'
+              if project
+                repository = nil
+                if name =~ %r{^(([a-z0-9\-_]+)\|)(.+)$}
+                  repo_prefix, repo_identifier, name = $1, $2, $3
+                  repository = project.repositories.detect {|repo| repo.identifier == repo_identifier}
+                else
+                  repository = project.repository
+                end
+                if prefix == 'commit'
+                  if repository && (changeset = Changeset.visible.where("repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%").first)
+                    link = link_to h("#{project_prefix}#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :repository_id => repository.identifier_param, :rev => changeset.identifier},
+                                                 :class => 'changeset',
+                                                 :title => truncate_single_line_raw(changeset.comments, 100)
+                  end
+                else
+                  if repository && User.current.allowed_to?(:browse_repository, project)
+                    name =~ %r{^[/\\]*(.*?)(@([^/\\@]+?))?(#(L\d+))?$}
+                    path, rev, anchor = $1, $3, $5
+                    link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => (prefix == 'export' ? 'raw' : 'entry'), :id => project, :repository_id => repository.identifier_param,
+                                                            :path => to_path_param(path),
+                                                            :rev => rev,
+                                                            :anchor => anchor},
+                                                           :class => (prefix == 'export' ? 'source download' : 'source')
+                  end
+                end
+                repo_prefix = nil
+              end
+            when 'attachment'
+              attachments = options[:attachments] || []
+              attachments += obj.attachments if obj.respond_to?(:attachments)
+              if attachments && attachment = Attachment.latest_attach(attachments, name)
+                link = link_to_attachment(attachment, :only_path => only_path, :download => true, :class => 'attachment')
+              end
+            when 'project'
+              if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first
+                link = link_to_project(p, {:only_path => only_path}, :class => 'project')
+              end
             end
           end
         end
+        (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
       end
-      (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}"))
     end
   end
 
index 6ce52ed72cdf821807db52b9b06775647fde2015..369057890110826b98d3ac747737c0194460c446 100644 (file)
@@ -363,6 +363,12 @@ RAW
     to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
   end
 
+  def test_should_not_parse_redmine_links_inside_link
+    raw = "r1 should not be parsed in http://example.com/url-r1/"
+    assert_match %r{<p><a class="changeset".*>r1</a> should not be parsed in <a class="external" href="http://example.com/url-r1/">http://example.com/url-r1/</a></p>},
+      textilizable(raw, :project => Project.find(1))
+  end
+
   def test_redmine_links_with_a_different_project_before_current_project
     vp1 = Version.generate!(:project_id => 1, :name => '1.4.4')
     vp3 = Version.generate!(:project_id => 3, :name => '1.4.4')