]> source.dussan.org Git - redmine.git/commitdiff
Adds support for multiple repositories to redmine links (#779).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 22 Jan 2012 14:23:10 +0000 (14:23 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 22 Jan 2012 14:23:10 +0000 (14:23 +0000)
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@8694 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/helpers/application_helper.rb
public/help/wiki_syntax_detailed.html
test/unit/helpers/application_helper_test.rb

index c3f0d4876e5476f4ba1f03ef01d75fd92d3eb8b8..fcfb2e7818bbd1c825e031b1d6083d33c7bba299 100644 (file)
@@ -655,19 +655,27 @@ module ApplicationHelper
   #     identifier:version:1.0.0
   #     identifier:source:some/file
   def parse_redmine_links(text, project, obj, attr, only_path, options)
-    text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|forum|news|commit|source|export|message|project)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|\]|<|$)}) do |m|
-      leading, esc, project_prefix, project_identifier, prefix, sep, identifier = $1, $2, $3, $4, $5, $7 || $9, $8 || $10
+    text.gsub!(%r{([\s\(,\-\[\>]|^)(!)?(([a-z0-9\-]+):)?(attachment|document|version|forum|news|message|project|commit|source|export)?(((#)|((([a-z0-9\-]+)\|)?(r)))(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]][^A-Za-z0-9_/])|,|\s|\]|<|$)}) do |m|
+      leading, esc, project_prefix, project_identifier, prefix, repo_prefix, repo_identifier, sep, identifier = $1, $2, $3, $4, $5, $10, $11, $8 || $12 || $14, $13 || $15
       link = nil
       if project_identifier
         project = Project.visible.find_by_identifier(project_identifier)
       end
       if esc.nil?
         if prefix.nil? && sep == 'r'
-          # project.changesets.visible raises an SQL error because of a double join on repositories
-          if project && project.repository && (changeset = Changeset.visible.find_by_repository_id_and_revision(project.repository.id, identifier))
-            link = link_to(h("#{project_prefix}r#{identifier}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
-                                      :class => 'changeset',
-                                      :title => truncate_single_line(changeset.comments, :length => 100))
+          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(changeset.comments, :length => 100))
+            end
           end
         elsif sep == '#'
           oid = identifier.to_i
@@ -731,22 +739,34 @@ module ApplicationHelper
               link = link_to h(news.title), {:only_path => only_path, :controller => 'news', :action => 'show', :id => news},
                                             :class => 'news'
             end
-          when 'commit'
-            if project && project.repository && (changeset = Changeset.visible.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", project.repository.id, "#{name}%"]))
-              link = link_to h("#{project_prefix}#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.identifier},
-                                           :class => 'changeset',
-                                           :title => truncate_single_line(h(changeset.comments), :length => 100)
-            end
-          when 'source', 'export'
-            if project && project.repository && User.current.allowed_to?(:browse_repository, project)
-              name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
-              path, rev, anchor = $1, $3, $5
-              link = link_to h("#{project_prefix}#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
-                                                      :path => to_path_param(path),
-                                                      :rev => rev,
-                                                      :anchor => anchor,
-                                                      :format => (prefix == 'export' ? 'raw' : nil)},
-                                                     :class => (prefix == 'export' ? 'source download' : 'source')
+          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.find(:first, :conditions => ["repository_id = ? AND scmid LIKE ?", repository.id, "#{name}%"]))
+                  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(h(changeset.comments), :length => 100)
+                end
+              else
+                if repository && User.current.allowed_to?(:browse_repository, project)
+                  name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
+                  path, rev, anchor = $1, $3, $5
+                  link = link_to h("#{project_prefix}#{prefix}:#{repo_prefix}#{name}"), {:controller => 'repositories', :action => 'entry', :id => project, :repository_id => repository.identifier_param,
+                                                          :path => to_path_param(path),
+                                                          :rev => rev,
+                                                          :anchor => anchor,
+                                                          :format => (prefix == 'export' ? 'raw' : nil)},
+                                                         :class => (prefix == 'export' ? 'source download' : 'source')
+                end
+              end
+              repo_prefix = nil
             end
           when 'attachment'
             attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
@@ -761,7 +781,7 @@ module ApplicationHelper
           end
         end
       end
-      (leading + (link || "#{project_prefix}#{prefix}#{sep}#{identifier}")).html_safe
+      (leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}")).html_safe
     end
   end
 
index 048edbd3a21d155e8c0c430492446b498d7ccae8..18a822c58d355a173e9a93884b93eae4b24ef86a 100644 (file)
 
         <h3><a name="3" class="wiki-page"></a>Redmine links</h3>
 
-        <p>Redmine allows hyperlinking between issues, changesets and wiki pages from anywhere wiki formatting is used.</p>
+        <p>Redmine allows hyperlinking between resources (issues, changesets, wiki pages...) from anywhere wiki formatting is used.</p>
         <ul>
             <li>Link to an issue: <strong>#124</strong> (displays <del><a href="#" class="issue" title="bulk edit doesn't change the category or fixed version properties (Closed)">#124</a></del>, link is striked-through if the issue is closed)</li>
-            <li>Link to a changeset: <strong>r758</strong> (displays <a href="#" class="changeset" title="Search engine now only searches objects the user is allowed to view.">r758</a>)</li>
-            <li>Link to a changeset with a non-numeric hash: <strong>commit:c6f4d0fd</strong> (displays <a href="#" class="changeset">c6f4d0fd</a>).</li>
-            <li>Link to a changeset of another project: <strong>sandbox:r758</strong> (displays <a href="#" class="changeset" title="Search engine now only searches objects the user is allowed to view.">sandbox:r758</a>)</li>
-            <li>Link to a changeset with a non-numeric hash: <strong>sandbox:c6f4d0fd</strong> (displays <a href="#" class="changeset">sandbox:c6f4d0fd</a>).</li>
         </ul>
 
         <p>Wiki links:</p>
                 </ul></li>
         </ul>
 
+        <ul>
+            <li>Changesets:
+                <ul>
+                                           <li><strong>r758</strong>                       (link to a changeset)</li>
+                                           <li><strong>commit:c6f4d0fd</strong>            (link to a changeset with a non-numeric hash)</li>
+                                           <li><strong>svn1|r758</strong>                  (link to a changeset of a specific repository, for projects with multiple repositories)</li>
+                                           <li><strong>commit:hg|c6f4d0fd</strong>         (link to a changeset with a non-numeric hash of a specific repository)</li>
+                                           <li><strong>sandbox:r758</strong>               (link to a changeset of another project)</li>
+                                           <li><strong>sandbox:commit:c6f4d0fd</strong>    (link to a changeset with a non-numeric hash of another project)</li>
+                </ul></li>
+        </ul>
+
         <ul>
              <li>Repository files:
                 <ul>
                     <li><strong>source:some/file@52#L120</strong>   (link to line 120 of the file's revision 52)</li>
                     <li><strong>source:"some file@52#L120"</strong> (use double quotes when the URL contains spaces</li>
                     <li><strong>export:some/file</strong>           (force the download of the file)</li>
+                    <li><strong>source:svn1|some/file</strong>      (link to a file of a specific repository, for projects with multiple repositories)</li>
                     <li><strong>sandbox:source:some/file</strong>   (link to the file located at /some/file in the repository of the project "sandbox")</li>
                     <li><strong>sandbox:export:some/file</strong>   (force the download of the file)</li>
                 </ul></li>
index 3d98aafe8cdd1b2ff99b5eaa5c85e3002a183ee2..20e458b7bebc8d4bfe02ef07837253090a2520cf 100644 (file)
@@ -267,6 +267,7 @@ RAW
       'version:1.0'                 => version_link,
       'version:"1.0"'               => version_link,
       # source
+      'source:some/file'            => link_to('source:some/file', source_url, :class => 'source'),
       'source:/some/file'           => link_to('source:/some/file', source_url, :class => 'source'),
       'source:/some/file.'          => link_to('source:/some/file', source_url, :class => 'source') + ".",
       'source:/some/file.ext.'      => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".",
@@ -341,6 +342,72 @@ RAW
     to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
   end
 
+  def test_multiple_repositories_redmine_links
+    svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
+    Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
+    hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
+    Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
+
+    changeset_link = link_to('r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
+                                    :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
+    svn_changeset_link = link_to('svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
+                                    :class => 'changeset', :title => '')
+    hg_changeset_link = link_to('hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
+                                    :class => 'changeset', :title => '')
+
+    source_link = link_to('source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
+    hg_source_link = link_to('source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
+
+    to_test = {
+      'r2'                          => changeset_link,
+      'svn1|r123'                   => svn_changeset_link,
+      'invalid|r123'                => 'invalid|r123',
+      'commit:hg1|abcd'             => hg_changeset_link,
+      'commit:invalid|abcd'         => 'commit:invalid|abcd',
+      # source
+      'source:some/file'            => source_link,
+      'source:hg1|some/file'        => hg_source_link,
+      'source:invalid|some/file'    => 'source:invalid|some/file',
+    }
+
+    @project = Project.find(1)
+    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
+  end
+
+  def test_cross_project_multiple_repositories_redmine_links
+    svn = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///foo/hg')
+    Changeset.create!(:repository => svn, :committed_on => Time.now, :revision => '123')
+    hg = Repository::Mercurial.create!(:project_id => 1, :identifier => 'hg1', :url => '/foo/hg')
+    Changeset.create!(:repository => hg, :committed_on => Time.now, :revision => '123', :scmid => 'abcd')
+
+    changeset_link = link_to('ecookbook:r2', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :rev => 2},
+                                    :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3')
+    svn_changeset_link = link_to('ecookbook:svn1|r123', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'svn1', :rev => 123},
+                                    :class => 'changeset', :title => '')
+    hg_changeset_link = link_to('ecookbook:hg1|abcd', {:controller => 'repositories', :action => 'revision', :id => 'ecookbook', :repository_id => 'hg1', :rev => 'abcd'},
+                                    :class => 'changeset', :title => '')
+
+    source_link = link_to('ecookbook:source:some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}, :class => 'source')
+    hg_source_link = link_to('ecookbook:source:hg1|some/file', {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :repository_id => 'hg1', :path => ['some', 'file']}, :class => 'source')
+
+    to_test = {
+      'ecookbook:r2'                           => changeset_link,
+      'ecookbook:svn1|r123'                    => svn_changeset_link,
+      'ecookbook:invalid|r123'                 => 'ecookbook:invalid|r123',
+      'ecookbook:commit:hg1|abcd'              => hg_changeset_link,
+      'ecookbook:commit:invalid|abcd'          => 'ecookbook:commit:invalid|abcd',
+      'invalid:commit:invalid|abcd'            => 'invalid:commit:invalid|abcd',
+      # source
+      'ecookbook:source:some/file'             => source_link,
+      'ecookbook:source:hg1|some/file'         => hg_source_link,
+      'ecookbook:source:invalid|some/file'     => 'ecookbook:source:invalid|some/file',
+      'invalid:source:invalid|some/file'       => 'invalid:source:invalid|some/file',
+    }
+
+    @project = Project.find(3)
+    to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text), "#{text} failed" }
+  end
+
   def test_redmine_links_git_commit
     changeset_link = link_to('abcd',
                                {